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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/chunks/{PdfViewer-HN-tp5RN.es.js → PdfViewer-CBmOiHsj.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-DyWe33pN.cjs → PdfViewer-D2BPnEBG.cjs} +1 -1
  3. package/dist/chunks/{index-BeVpZc19.cjs → index-B0CI50e4.cjs} +3 -3
  4. package/dist/chunks/{index-ir6efMuz.es.js → index-BSDcfSFc.es.js} +3 -3
  5. package/dist/chunks/{super-editor.es-BwqYS285.es.js → super-editor.es-BW1T3N2-.es.js} +1586 -1278
  6. package/dist/chunks/{super-editor.es-CKfdmK-8.cjs → super-editor.es-DOOwW-Iv.cjs} +1586 -1278
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/style.css +1 -0
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-UuZxU-p8.js} +154 -108
  11. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-Bss48sB0.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CFqh_xBx.js → editor--v3HbUU0.js} +1437 -1173
  13. package/dist/super-editor/chunks/{toolbar-DdfyWgZF.js → toolbar-BeXlvdV8.js} +2 -2
  14. package/dist/super-editor/converter.es.js +1 -1
  15. package/dist/super-editor/docx-zipper.es.js +2 -2
  16. package/dist/super-editor/editor.es.js +3 -3
  17. package/dist/super-editor/file-zipper.es.js +1 -1
  18. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  19. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  20. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  21. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  22. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  23. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  27. package/dist/super-editor/style.css +1 -0
  28. package/dist/super-editor/super-editor.es.js +7 -7
  29. package/dist/super-editor/toolbar.es.js +2 -2
  30. package/dist/super-editor.cjs +1 -1
  31. package/dist/super-editor.es.js +1 -1
  32. package/dist/superdoc.cjs +2 -2
  33. package/dist/superdoc.es.js +2 -2
  34. package/dist/superdoc.umd.js +1587 -1279
  35. package/dist/superdoc.umd.js.map +1 -1
  36. package/package.json +1 -1
@@ -14988,6 +14988,10 @@ function generateDocxRandomId(length2 = 8) {
14988
14988
  }
14989
14989
  return id.join("");
14990
14990
  }
14991
+ function generateRandomSigned32BitIntStrId() {
14992
+ const val = Math.floor(Math.random() * 2147483647);
14993
+ return val.toString();
14994
+ }
14991
14995
  function generateRandom32BitHex() {
14992
14996
  const val = Math.floor(Math.random() * 2147483647);
14993
14997
  return val.toString(16).toUpperCase().padStart(8, "0");
@@ -22566,6 +22570,7 @@ const helpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePrope
22566
22570
  generateDocxListAttributes,
22567
22571
  generateDocxRandomId,
22568
22572
  generateRandom32BitHex,
22573
+ generateRandomSigned32BitIntStrId,
22569
22574
  getActiveFormatting,
22570
22575
  getExtensionConfigField,
22571
22576
  getMarkRange,
@@ -28160,6 +28165,9 @@ function handleStructuredContentNode(params2) {
28160
28165
  const node = nodes[0];
28161
28166
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
28162
28167
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
28168
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
28169
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
28170
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
28163
28171
  if (!sdtContent) {
28164
28172
  return null;
28165
28173
  }
@@ -28171,15 +28179,16 @@ function handleStructuredContentNode(params2) {
28171
28179
  nodes: sdtContent.elements,
28172
28180
  path: [...params2.path || [], sdtContent]
28173
28181
  });
28174
- let sdtContentType = "structuredContent";
28175
- if (paragraph || table) {
28176
- sdtContentType = "structuredContentBlock";
28177
- }
28182
+ const isBlockNode2 = paragraph || table;
28183
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
28178
28184
  let result = {
28179
28185
  type: sdtContentType,
28180
28186
  content: translatedContent,
28181
28187
  marks,
28182
28188
  attrs: {
28189
+ id: id?.attributes?.["w:val"] || null,
28190
+ tag: tag?.attributes?.["w:val"] || null,
28191
+ alias: alias?.attributes?.["w:val"] || null,
28183
28192
  sdtPr
28184
28193
  }
28185
28194
  };
@@ -30437,21 +30446,55 @@ const generateSdtPrTagForDocumentSection = (id, title, tag) => {
30437
30446
  };
30438
30447
  function translateStructuredContent(params2) {
30439
30448
  const { node } = params2;
30440
- const { attrs = {} } = node;
30441
30449
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
30442
- const nodeElements = [
30443
- {
30444
- name: "w:sdtContent",
30445
- elements: childContent
30446
- }
30447
- ];
30448
- nodeElements.unshift(attrs.sdtPr);
30450
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
30451
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
30452
+ const nodeElements = [sdtPr, sdtContent];
30449
30453
  const result = {
30450
30454
  name: "w:sdt",
30451
30455
  elements: nodeElements
30452
30456
  };
30453
30457
  return result;
30454
30458
  }
30459
+ function generateSdtPrTagForStructuredContent({ node }) {
30460
+ const { attrs = {} } = node;
30461
+ const id = {
30462
+ name: "w:id",
30463
+ type: "element",
30464
+ attributes: { "w:val": attrs.id }
30465
+ };
30466
+ const alias = {
30467
+ name: "w:alias",
30468
+ type: "element",
30469
+ attributes: { "w:val": attrs.alias }
30470
+ };
30471
+ const tag = {
30472
+ name: "w:tag",
30473
+ type: "element",
30474
+ attributes: { "w:val": attrs.tag }
30475
+ };
30476
+ const resultElements = [];
30477
+ if (attrs.id) resultElements.push(id);
30478
+ if (attrs.alias) resultElements.push(alias);
30479
+ if (attrs.tag) resultElements.push(tag);
30480
+ if (attrs.sdtPr) {
30481
+ const elements = attrs.sdtPr.elements || [];
30482
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
30483
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
30484
+ const result2 = {
30485
+ name: "w:sdtPr",
30486
+ type: "element",
30487
+ elements: [...resultElements, ...restElements]
30488
+ };
30489
+ return result2;
30490
+ }
30491
+ const result = {
30492
+ name: "w:sdtPr",
30493
+ type: "element",
30494
+ elements: resultElements
30495
+ };
30496
+ return result;
30497
+ }
30455
30498
  const XML_NODE_NAME$3 = "w:sdt";
30456
30499
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
30457
30500
  const validXmlAttributes$3 = [];
@@ -31512,7 +31555,7 @@ function translateShapeContainer(params2) {
31512
31555
  const pict = {
31513
31556
  name: "w:pict",
31514
31557
  attributes: {
31515
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
31558
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
31516
31559
  },
31517
31560
  elements: [shape]
31518
31561
  };
@@ -31579,7 +31622,7 @@ function translateVRectContentBlock(params2) {
31579
31622
  const pict = {
31580
31623
  name: "w:pict",
31581
31624
  attributes: {
31582
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
31625
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
31583
31626
  },
31584
31627
  elements: [rect]
31585
31628
  };
@@ -31626,7 +31669,8 @@ generateXml_fn = function(node) {
31626
31669
  }
31627
31670
  if (elements) {
31628
31671
  if (name === "w:instrText") {
31629
- tags.push(elements[0].text);
31672
+ const textContent2 = (elements || []).map((child) => typeof child?.text === "string" ? child.text : "").join("");
31673
+ tags.push(__privateMethod$2(this, _DocxExporter_instances, replaceSpecialCharacters_fn).call(this, textContent2));
31630
31674
  } else if (name === "w:t" || name === "w:delText" || name === "wp:posOffset") {
31631
31675
  try {
31632
31676
  let text = String(elements[0].text);
@@ -33079,7 +33123,7 @@ const DEFAULT_SECTION_PROPS = Object.freeze({
33079
33123
  gutter: "0"
33080
33124
  })
33081
33125
  });
33082
- function ensureSectionProperties(bodyNode, converter) {
33126
+ function ensureSectionProperties(bodyNode) {
33083
33127
  if (!bodyNode.elements) bodyNode.elements = [];
33084
33128
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
33085
33129
  if (!sectPr) {
@@ -33447,7 +33491,7 @@ const _SuperConverter = class _SuperConverter2 {
33447
33491
  return;
33448
33492
  }
33449
33493
  }
33450
- static updateDocumentVersion(docx = this.convertedXml, version2 = "0.21.0") {
33494
+ static updateDocumentVersion(docx = this.convertedXml, version2 = "0.22.0") {
33451
33495
  const customLocation = "docProps/custom.xml";
33452
33496
  if (!docx[customLocation]) {
33453
33497
  docx[customLocation] = generateCustomXml();
@@ -33936,7 +33980,7 @@ function storeSuperdocVersion(docx) {
33936
33980
  function generateCustomXml() {
33937
33981
  return DEFAULT_CUSTOM_XML;
33938
33982
  }
33939
- function generateSuperdocVersion(pid = 2, version2 = "0.21.0") {
33983
+ function generateSuperdocVersion(pid = 2, version2 = "0.22.0") {
33940
33984
  return {
33941
33985
  type: "element",
33942
33986
  name: "property",
@@ -36578,7 +36622,7 @@ var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "rea
36578
36622
  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);
36579
36623
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
36580
36624
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
36581
- 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;
36625
+ 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;
36582
36626
  var GOOD_LEAF_SIZE = 200;
36583
36627
  var RopeSequence = function RopeSequence2() {
36584
36628
  };
@@ -48653,7 +48697,7 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
48653
48697
  if (emitParams) editor.emit("commentsUpdate", emitParams);
48654
48698
  return newTrackedChanges;
48655
48699
  };
48656
- const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48700
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
48657
48701
  let trackedChangeText = "";
48658
48702
  let deletionText = "";
48659
48703
  if (trackedChangeType === TrackInsertMarkName) {
@@ -48695,10 +48739,8 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48695
48739
  if (hasMatchingId) nodesWithMark.push(node2);
48696
48740
  });
48697
48741
  const { deletionText, trackedChangeText } = getTrackedChangeText({
48698
- state: newEditorState,
48699
48742
  nodes: nodesWithMark.length ? nodesWithMark : [node],
48700
48743
  mark: trackedMark,
48701
- marks,
48702
48744
  trackedChangeType,
48703
48745
  isDeletionInsertion
48704
48746
  });
@@ -51381,7 +51423,7 @@ const _Editor = class _Editor2 extends EventEmitter$1 {
51381
51423
  * @returns {Object | void} Migration results
51382
51424
  */
51383
51425
  processCollaborationMigrations() {
51384
- console.debug("[checkVersionMigrations] Current editor version", "0.21.0");
51426
+ console.debug("[checkVersionMigrations] Current editor version", "0.22.0");
51385
51427
  if (!this.options.ydoc) return;
51386
51428
  const metaMap = this.options.ydoc.getMap("meta");
51387
51429
  let docVersion = metaMap.get("version");
@@ -53353,228 +53395,1223 @@ const SlashMenu = Extension.create({
53353
53395
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
53354
53396
  }
53355
53397
  });
53356
- const Document = Node$1.create({
53357
- name: "doc",
53358
- topNode: true,
53359
- content: "block+",
53360
- parseDOM() {
53361
- return [{ tag: "doc" }];
53362
- },
53363
- renderDOM() {
53364
- return ["doc", 0];
53365
- },
53366
- addAttributes() {
53367
- return {
53368
- attributes: {
53369
- rendered: false,
53370
- "aria-label": "Document node"
53371
- }
53372
- };
53373
- },
53374
- addCommands() {
53375
- return {
53376
- /**
53377
- * Get document statistics
53378
- * @category Command
53379
- * @example
53380
- * // Get word and character count
53381
- * const stats = editor.commands.getDocumentStats()
53382
- * console.log(`${stats.words} words, ${stats.characters} characters`)
53383
- * @note Returns word count, character count, and paragraph count
53384
- */
53385
- getDocumentStats: () => ({ editor }) => {
53386
- const text = editor.getText();
53387
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
53388
- const characters = text.length;
53389
- const paragraphs = editor.state.doc.content.childCount;
53390
- return {
53391
- words,
53392
- characters,
53393
- paragraphs
53394
- };
53395
- },
53396
- /**
53397
- * Clear entire document
53398
- * @category Command
53399
- * @example
53400
- * editor.commands.clearDocument()
53401
- * @note Replaces all content with an empty paragraph
53402
- */
53403
- clearDocument: () => ({ commands: commands2 }) => {
53404
- return commands2.setContent("<p></p>");
53398
+ class StructuredContentViewBase {
53399
+ constructor(props) {
53400
+ __publicField$1(this, "node");
53401
+ __publicField$1(this, "view");
53402
+ __publicField$1(this, "getPos");
53403
+ __publicField$1(this, "decorations");
53404
+ __publicField$1(this, "innerDecorations");
53405
+ __publicField$1(this, "editor");
53406
+ __publicField$1(this, "extension");
53407
+ __publicField$1(this, "htmlAttributes");
53408
+ __publicField$1(this, "root");
53409
+ __publicField$1(this, "isDragging", false);
53410
+ this.node = props.node;
53411
+ this.view = props.editor.view;
53412
+ this.getPos = props.getPos;
53413
+ this.decorations = props.decorations;
53414
+ this.innerDecorations = props.innerDecorations;
53415
+ this.editor = props.editor;
53416
+ this.extension = props.extension;
53417
+ this.htmlAttributes = props.htmlAttributes;
53418
+ this.mount(props);
53419
+ }
53420
+ mount() {
53421
+ return;
53422
+ }
53423
+ get dom() {
53424
+ return this.root;
53425
+ }
53426
+ get contentDOM() {
53427
+ return null;
53428
+ }
53429
+ update(node, decorations, innerDecorations) {
53430
+ if (node.type !== this.node.type) {
53431
+ return false;
53432
+ }
53433
+ this.node = node;
53434
+ this.decorations = decorations;
53435
+ this.innerDecorations = innerDecorations;
53436
+ this.updateHTMLAttributes();
53437
+ return true;
53438
+ }
53439
+ stopEvent(event) {
53440
+ if (!this.dom) return false;
53441
+ const target = event.target;
53442
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
53443
+ if (!isInElement) return false;
53444
+ const isDragEvent = event.type.startsWith("drag");
53445
+ const isDropEvent = event.type === "drop";
53446
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
53447
+ if (isInput && !isDropEvent && !isDragEvent) return true;
53448
+ const { isEditable } = this.editor;
53449
+ const { isDragging } = this;
53450
+ const isDraggable = !!this.node.type.spec.draggable;
53451
+ const isSelectable = NodeSelection.isSelectable(this.node);
53452
+ const isCopyEvent = event.type === "copy";
53453
+ const isPasteEvent = event.type === "paste";
53454
+ const isCutEvent = event.type === "cut";
53455
+ const isClickEvent = event.type === "mousedown";
53456
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
53457
+ event.preventDefault();
53458
+ }
53459
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
53460
+ event.preventDefault();
53461
+ return false;
53462
+ }
53463
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
53464
+ const dragHandle = target.closest("[data-drag-handle]");
53465
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
53466
+ if (isValidDragHandle) {
53467
+ this.isDragging = true;
53468
+ document.addEventListener(
53469
+ "dragend",
53470
+ () => {
53471
+ this.isDragging = false;
53472
+ },
53473
+ { once: true }
53474
+ );
53475
+ document.addEventListener(
53476
+ "drop",
53477
+ () => {
53478
+ this.isDragging = false;
53479
+ },
53480
+ { once: true }
53481
+ );
53482
+ document.addEventListener(
53483
+ "mouseup",
53484
+ () => {
53485
+ this.isDragging = false;
53486
+ },
53487
+ { once: true }
53488
+ );
53405
53489
  }
53406
- };
53490
+ }
53491
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
53492
+ return false;
53493
+ }
53494
+ return true;
53407
53495
  }
53408
- });
53409
- const Text = Node$1.create({
53410
- name: "text",
53411
- group: "inline",
53412
- inline: true,
53413
- addOptions() {
53414
- return {};
53496
+ ignoreMutation(mutation) {
53497
+ if (!this.dom || !this.contentDOM) return true;
53498
+ if (this.node.isLeaf || this.node.isAtom) return true;
53499
+ if (mutation.type === "selection") return false;
53500
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
53501
+ if (this.contentDOM.contains(mutation.target)) return false;
53502
+ return true;
53415
53503
  }
53416
- });
53417
- const splitRun = () => (props) => {
53418
- const { state: state2, view, tr } = props;
53419
- const { $from, empty: empty2 } = state2.selection;
53420
- if (!empty2) return false;
53421
- if ($from.parent.type.name !== "run") return false;
53422
- const handled = splitBlock(state2, (transaction) => {
53504
+ destroy() {
53505
+ this.dom.remove();
53506
+ this.contentDOM?.remove();
53507
+ }
53508
+ updateAttributes(attrs) {
53509
+ const pos = this.getPos();
53510
+ if (typeof pos !== "number") {
53511
+ return;
53512
+ }
53513
+ return this.view.dispatch(
53514
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
53515
+ ...this.node.attrs,
53516
+ ...attrs
53517
+ })
53518
+ );
53519
+ }
53520
+ updateHTMLAttributes() {
53521
+ const { extensionService } = this.editor;
53522
+ const { attributes } = extensionService;
53523
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
53524
+ this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
53525
+ }
53526
+ createDragHandle() {
53527
+ const dragHandle = document.createElement("span");
53528
+ dragHandle.classList.add("sd-structured-content-draggable");
53529
+ dragHandle.draggable = true;
53530
+ dragHandle.contentEditable = "false";
53531
+ dragHandle.dataset.dragHandle = "";
53532
+ const textElement = document.createElement("span");
53533
+ textElement.textContent = this.node.attrs.alias || "Structured content";
53534
+ dragHandle.append(textElement);
53535
+ return dragHandle;
53536
+ }
53537
+ onDragStart(event) {
53538
+ const { view } = this.editor;
53539
+ const target = event.target;
53540
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
53541
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
53542
+ return;
53543
+ }
53544
+ let x = 0;
53545
+ let y2 = 0;
53546
+ if (this.dom !== dragHandle) {
53547
+ const domBox = this.dom.getBoundingClientRect();
53548
+ const handleBox = dragHandle.getBoundingClientRect();
53549
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
53550
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
53551
+ x = handleBox.x - domBox.x + offsetX;
53552
+ y2 = handleBox.y - domBox.y + offsetY;
53553
+ }
53554
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
53555
+ const pos = this.getPos();
53556
+ if (typeof pos !== "number") {
53557
+ return;
53558
+ }
53559
+ const selection = NodeSelection.create(view.state.doc, pos);
53560
+ const transaction = view.state.tr.setSelection(selection);
53423
53561
  view.dispatch(transaction);
53424
- });
53425
- if (handled) {
53426
- tr.setMeta("preventDispatch", true);
53427
53562
  }
53428
- return handled;
53429
- };
53430
- const Run = OxmlNode.create({
53431
- name: "run",
53432
- oXmlName: "w:r",
53433
- group: "inline",
53563
+ }
53564
+ class StructuredContentInlineView extends StructuredContentViewBase {
53565
+ constructor(props) {
53566
+ super(props);
53567
+ }
53568
+ mount() {
53569
+ this.buildView();
53570
+ }
53571
+ get contentDOM() {
53572
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
53573
+ return contentElement || null;
53574
+ }
53575
+ createElement() {
53576
+ const element = document.createElement("span");
53577
+ element.classList.add(structuredContentClass$1);
53578
+ element.setAttribute("data-structured-content", "");
53579
+ const contentElement = document.createElement("span");
53580
+ contentElement.classList.add(structuredContentInnerClass$1);
53581
+ element.append(contentElement);
53582
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53583
+ updateDOMAttributes(element, { ...domAttrs });
53584
+ return { element, contentElement };
53585
+ }
53586
+ buildView() {
53587
+ const { element } = this.createElement();
53588
+ const dragHandle = this.createDragHandle();
53589
+ element.prepend(dragHandle);
53590
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53591
+ this.root = element;
53592
+ }
53593
+ updateView() {
53594
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53595
+ updateDOMAttributes(this.dom, { ...domAttrs });
53596
+ }
53597
+ update(node, decorations, innerDecorations) {
53598
+ const result = super.update(node, decorations, innerDecorations);
53599
+ if (!result) return false;
53600
+ this.updateView();
53601
+ return true;
53602
+ }
53603
+ }
53604
+ const structuredContentClass$1 = "sd-structured-content";
53605
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
53606
+ const StructuredContent = Node$1.create({
53607
+ name: "structuredContent",
53608
+ group: "inline structuredContent",
53434
53609
  inline: true,
53435
53610
  content: "inline*",
53436
- selectable: false,
53437
- childToAttributes: ["runProperties"],
53611
+ isolating: true,
53612
+ atom: false,
53613
+ // false - has editable content.
53614
+ draggable: true,
53438
53615
  addOptions() {
53439
53616
  return {
53440
53617
  htmlAttributes: {
53441
- "data-run": "1"
53618
+ class: structuredContentClass$1,
53619
+ "aria-label": "Structured content node"
53442
53620
  }
53443
53621
  };
53444
53622
  },
53445
53623
  addAttributes() {
53446
53624
  return {
53447
- runProperties: {
53625
+ id: {
53448
53626
  default: null,
53449
- rendered: false,
53450
- keepOnSplit: true
53627
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53628
+ renderDOM: (attrs) => {
53629
+ if (!attrs.id) return {};
53630
+ return { "data-id": attrs.id };
53631
+ }
53451
53632
  },
53452
- rsidR: {
53633
+ tag: {
53453
53634
  default: null,
53454
- rendered: false,
53455
- keepOnSplit: true
53635
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53636
+ renderDOM: (attrs) => {
53637
+ if (!attrs.tag) return {};
53638
+ return { "data-tag": attrs.tag };
53639
+ }
53456
53640
  },
53457
- rsidRPr: {
53641
+ alias: {
53458
53642
  default: null,
53459
- rendered: false,
53460
- keepOnSplit: true
53643
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53644
+ renderDOM: (attrs) => {
53645
+ if (!attrs.alias) return {};
53646
+ return { "data-alias": attrs.alias };
53647
+ }
53461
53648
  },
53462
- rsidDel: {
53463
- default: null,
53464
- rendered: false,
53465
- keepOnSplit: true
53649
+ sdtPr: {
53650
+ rendered: false
53466
53651
  }
53467
53652
  };
53468
53653
  },
53469
- addCommands() {
53470
- return {
53471
- splitRun
53472
- };
53473
- },
53474
53654
  parseDOM() {
53475
- return [{ tag: "span[data-run]" }];
53655
+ return [{ tag: "span[data-structured-content]" }];
53476
53656
  },
53477
53657
  renderDOM({ htmlAttributes }) {
53478
- const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53479
- return ["span", base2, 0];
53658
+ return [
53659
+ "span",
53660
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53661
+ "data-structured-content": ""
53662
+ }),
53663
+ 0
53664
+ ];
53665
+ },
53666
+ addNodeView() {
53667
+ return (props) => {
53668
+ return new StructuredContentInlineView({ ...props });
53669
+ };
53480
53670
  }
53481
53671
  });
53482
- const inputRegex$1 = /^\s*([-+*])\s$/;
53483
- const BulletList = Node$1.create({
53484
- name: "bulletList",
53485
- group: "block list",
53486
- selectable: false,
53487
- content() {
53488
- return `${this.options.itemTypeName}+`;
53489
- },
53672
+ class StructuredContentBlockView extends StructuredContentViewBase {
53673
+ constructor(props) {
53674
+ super(props);
53675
+ }
53676
+ mount() {
53677
+ this.buildView();
53678
+ }
53679
+ get contentDOM() {
53680
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
53681
+ return contentElement || null;
53682
+ }
53683
+ createElement() {
53684
+ const element = document.createElement("div");
53685
+ element.classList.add(structuredContentClass);
53686
+ element.setAttribute("data-structured-content-block", "");
53687
+ const contentElement = document.createElement("div");
53688
+ contentElement.classList.add(structuredContentInnerClass);
53689
+ element.append(contentElement);
53690
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53691
+ updateDOMAttributes(element, { ...domAttrs });
53692
+ return { element, contentElement };
53693
+ }
53694
+ buildView() {
53695
+ const { element } = this.createElement();
53696
+ const dragHandle = this.createDragHandle();
53697
+ element.prepend(dragHandle);
53698
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53699
+ this.root = element;
53700
+ }
53701
+ updateView() {
53702
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53703
+ updateDOMAttributes(this.dom, { ...domAttrs });
53704
+ }
53705
+ update(node, decorations, innerDecorations) {
53706
+ const result = super.update(node, decorations, innerDecorations);
53707
+ if (!result) return false;
53708
+ this.updateView();
53709
+ return true;
53710
+ }
53711
+ }
53712
+ const structuredContentClass = "sd-structured-content-block";
53713
+ const structuredContentInnerClass = "sd-structured-content-block__content";
53714
+ const StructuredContentBlock = Node$1.create({
53715
+ name: "structuredContentBlock",
53716
+ group: "block structuredContent",
53717
+ content: "block*",
53718
+ isolating: true,
53719
+ atom: false,
53720
+ // false - has editable content.
53721
+ draggable: true,
53490
53722
  addOptions() {
53491
53723
  return {
53492
- itemTypeName: "listItem",
53493
53724
  htmlAttributes: {
53494
- "aria-label": "Bullet list node"
53495
- },
53496
- keepMarks: true,
53497
- keepAttributes: false
53725
+ class: structuredContentClass,
53726
+ "aria-label": "Structured content block node"
53727
+ }
53498
53728
  };
53499
53729
  },
53500
- parseDOM() {
53501
- return [{ tag: "ul" }];
53502
- },
53503
- renderDOM({ htmlAttributes }) {
53504
- const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53505
- return ["ul", attributes, 0];
53506
- },
53507
53730
  addAttributes() {
53508
53731
  return {
53509
- "list-style-type": {
53510
- default: "bullet",
53511
- rendered: false
53732
+ id: {
53733
+ default: null,
53734
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53735
+ renderDOM: (attrs) => {
53736
+ if (!attrs.id) return {};
53737
+ return { "data-id": attrs.id };
53738
+ }
53512
53739
  },
53513
- listId: {
53514
- rendered: false
53740
+ tag: {
53741
+ default: null,
53742
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53743
+ renderDOM: (attrs) => {
53744
+ if (!attrs.tag) return {};
53745
+ return { "data-tag": attrs.tag };
53746
+ }
53515
53747
  },
53516
- sdBlockId: {
53748
+ alias: {
53517
53749
  default: null,
53518
- keepOnSplit: false,
53519
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53750
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53520
53751
  renderDOM: (attrs) => {
53521
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53752
+ if (!attrs.alias) return {};
53753
+ return { "data-alias": attrs.alias };
53522
53754
  }
53523
53755
  },
53524
- attributes: {
53525
- rendered: false,
53526
- keepOnSplit: true
53527
- }
53528
- };
53529
- },
53530
- addCommands() {
53531
- return {
53532
- /**
53533
- * Toggle a bullet list at the current selection
53534
- * @category Command
53535
- * @example
53536
- * // Toggle bullet list on selected text
53537
- * editor.commands.toggleBulletList()
53538
- * @note Converts selected paragraphs to list items or removes list formatting
53539
- */
53540
- toggleBulletList: () => (params2) => {
53541
- return toggleList(this.type)(params2);
53756
+ sdtPr: {
53757
+ rendered: false
53542
53758
  }
53543
53759
  };
53544
53760
  },
53545
- addShortcuts() {
53546
- return {
53547
- "Mod-Shift-8": () => {
53548
- return this.editor.commands.toggleBulletList();
53549
- }
53550
- };
53761
+ parseDOM() {
53762
+ return [{ tag: "div[data-structured-content-block]" }];
53551
53763
  },
53552
- addInputRules() {
53764
+ renderDOM({ htmlAttributes }) {
53553
53765
  return [
53554
- new InputRule({
53555
- match: inputRegex$1,
53556
- handler: ({ state: state2, range: range2 }) => {
53557
- const $pos = state2.selection.$from;
53558
- const listItemType = state2.schema.nodes.listItem;
53559
- for (let depth = $pos.depth; depth >= 0; depth--) {
53560
- if ($pos.node(depth).type === listItemType) {
53561
- return null;
53562
- }
53563
- }
53564
- const { tr } = state2;
53565
- tr.delete(range2.from, range2.to);
53566
- ListHelpers.createNewList({
53567
- listType: this.type,
53568
- tr,
53569
- editor: this.editor
53570
- });
53571
- }
53572
- })
53766
+ "div",
53767
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53768
+ "data-structured-content-block": ""
53769
+ }),
53770
+ 0
53573
53771
  ];
53772
+ },
53773
+ addNodeView() {
53774
+ return (props) => {
53775
+ return new StructuredContentBlockView({ ...props });
53776
+ };
53574
53777
  }
53575
53778
  });
53576
- const inputRegex = /^(\d+)\.\s$/;
53577
- const OrderedList = Node$1.create({
53779
+ function getStructuredContentTagsById(idOrIds, state2) {
53780
+ const result = findChildren$5(state2.doc, (node) => {
53781
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
53782
+ if (Array.isArray(idOrIds)) {
53783
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
53784
+ } else {
53785
+ return isStructuredContent && node.attrs.id === idOrIds;
53786
+ }
53787
+ });
53788
+ return result;
53789
+ }
53790
+ function getStructuredContentTags(state2) {
53791
+ const result = findChildren$5(state2.doc, (node) => {
53792
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
53793
+ });
53794
+ return result;
53795
+ }
53796
+ function getStructuredContentInlineTags(state2) {
53797
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
53798
+ return result;
53799
+ }
53800
+ function getStructuredContentBlockTags(state2) {
53801
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
53802
+ return result;
53803
+ }
53804
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
53805
+ __proto__: null,
53806
+ getStructuredContentBlockTags,
53807
+ getStructuredContentInlineTags,
53808
+ getStructuredContentTags,
53809
+ getStructuredContentTagsById
53810
+ }, Symbol.toStringTag, { value: "Module" }));
53811
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
53812
+ const StructuredContentCommands = Extension.create({
53813
+ name: "structuredContentCommands",
53814
+ addCommands() {
53815
+ return {
53816
+ /**
53817
+ * Inserts a structured content inline at selection.
53818
+ * @category Command
53819
+ * @param {StructuredContentInlineInsert} options
53820
+ */
53821
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53822
+ const { schema } = editor;
53823
+ let { from: from2, to } = state2.selection;
53824
+ if (dispatch) {
53825
+ const selectionText = state2.doc.textBetween(from2, to);
53826
+ let content = null;
53827
+ if (selectionText) {
53828
+ content = schema.text(selectionText);
53829
+ }
53830
+ if (options.text) {
53831
+ content = schema.text(options.text);
53832
+ }
53833
+ if (options.json) {
53834
+ content = schema.nodeFromJSON(options.json);
53835
+ }
53836
+ if (!content) {
53837
+ content = schema.text(" ");
53838
+ }
53839
+ const attrs = {
53840
+ ...options.attrs,
53841
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53842
+ tag: "inline_text_sdt",
53843
+ alias: options.attrs?.alias || "Structured content"
53844
+ };
53845
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
53846
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
53847
+ if (parent) {
53848
+ const insertPos = parent.pos + parent.node.nodeSize;
53849
+ from2 = to = insertPos;
53850
+ }
53851
+ tr.replaceWith(from2, to, node);
53852
+ }
53853
+ return true;
53854
+ },
53855
+ /**
53856
+ * Inserts a structured content block at selection.
53857
+ * @category Command
53858
+ * @param {StructuredContentBlockInsert} options
53859
+ */
53860
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53861
+ const { schema } = editor;
53862
+ let { from: from2, to } = state2.selection;
53863
+ if (dispatch) {
53864
+ const selectionContent = state2.selection.content();
53865
+ let content = null;
53866
+ if (selectionContent.size) {
53867
+ content = selectionContent.content;
53868
+ }
53869
+ if (options.html) {
53870
+ const html = htmlHandler(options.html, editor);
53871
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53872
+ content = doc2.content;
53873
+ }
53874
+ if (options.json) {
53875
+ content = schema.nodeFromJSON(options.json);
53876
+ }
53877
+ if (!content) {
53878
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
53879
+ }
53880
+ const attrs = {
53881
+ ...options.attrs,
53882
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53883
+ tag: "block_table_sdt",
53884
+ alias: options.attrs?.alias || "Structured content"
53885
+ };
53886
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
53887
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
53888
+ if (parent) {
53889
+ const insertPos = parent.pos + parent.node.nodeSize;
53890
+ from2 = to = insertPos;
53891
+ }
53892
+ tr.replaceRangeWith(from2, to, node);
53893
+ }
53894
+ return true;
53895
+ },
53896
+ /**
53897
+ * Updates a structured content attributes or content.
53898
+ * If the updated node does not match the schema, it will not be updated.
53899
+ * @category Command
53900
+ * @param {string} id
53901
+ * @param {StructuredContentUpdate} options
53902
+ */
53903
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53904
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
53905
+ if (!structuredContentTags.length) {
53906
+ return true;
53907
+ }
53908
+ const { schema } = editor;
53909
+ if (dispatch) {
53910
+ const structuredContent = structuredContentTags[0];
53911
+ const { pos, node } = structuredContent;
53912
+ const posFrom = pos;
53913
+ const posTo = pos + node.nodeSize;
53914
+ let content = null;
53915
+ if (options.text) {
53916
+ content = schema.text(options.text);
53917
+ }
53918
+ if (options.html) {
53919
+ const html = htmlHandler(options.html, editor);
53920
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53921
+ content = doc2.content;
53922
+ }
53923
+ if (options.json) {
53924
+ content = schema.nodeFromJSON(options.json);
53925
+ }
53926
+ if (!content) {
53927
+ content = node.content;
53928
+ }
53929
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
53930
+ try {
53931
+ updatedNode.check();
53932
+ } catch {
53933
+ console.error("Updated node does not conform to the schema");
53934
+ return false;
53935
+ }
53936
+ tr.replaceWith(posFrom, posTo, updatedNode);
53937
+ }
53938
+ return true;
53939
+ },
53940
+ /**
53941
+ * Removes a structured content.
53942
+ * @category Command
53943
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
53944
+ */
53945
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
53946
+ if (!structuredContentTags.length) {
53947
+ return true;
53948
+ }
53949
+ if (dispatch) {
53950
+ structuredContentTags.forEach((structuredContent) => {
53951
+ const { pos, node } = structuredContent;
53952
+ const posFrom = tr.mapping.map(pos);
53953
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53954
+ const currentNode = tr.doc.nodeAt(posFrom);
53955
+ if (currentNode && node.eq(currentNode)) {
53956
+ tr.delete(posFrom, posTo);
53957
+ }
53958
+ });
53959
+ }
53960
+ return true;
53961
+ },
53962
+ /**
53963
+ * Removes a structured content by ID.
53964
+ * @category Command
53965
+ * @param {string | string[]} idOrIds
53966
+ */
53967
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
53968
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
53969
+ if (!structuredContentTags.length) {
53970
+ return true;
53971
+ }
53972
+ if (dispatch) {
53973
+ structuredContentTags.forEach((structuredContent) => {
53974
+ const { pos, node } = structuredContent;
53975
+ const posFrom = tr.mapping.map(pos);
53976
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53977
+ const currentNode = tr.doc.nodeAt(posFrom);
53978
+ if (currentNode && node.eq(currentNode)) {
53979
+ tr.delete(posFrom, posTo);
53980
+ }
53981
+ });
53982
+ }
53983
+ return true;
53984
+ },
53985
+ /**
53986
+ * Removes a structured content at cursor, preserving its content.
53987
+ * @category Command
53988
+ */
53989
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
53990
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
53991
+ const structuredContent = findParentNode(predicate)(state2.selection);
53992
+ if (!structuredContent) {
53993
+ return true;
53994
+ }
53995
+ if (dispatch) {
53996
+ const { node, pos } = structuredContent;
53997
+ const posFrom = pos;
53998
+ const posTo = posFrom + node.nodeSize;
53999
+ const content = node.content;
54000
+ tr.replaceWith(posFrom, posTo, content);
54001
+ }
54002
+ return true;
54003
+ }
54004
+ };
54005
+ },
54006
+ addHelpers() {
54007
+ return {
54008
+ ...structuredContentHelpers
54009
+ };
54010
+ }
54011
+ });
54012
+ class DocumentSectionView {
54013
+ constructor(node, getPos, decorations, editor) {
54014
+ __privateAdd$1(this, _DocumentSectionView_instances);
54015
+ this.node = node;
54016
+ this.editor = editor;
54017
+ this.decorations = decorations;
54018
+ this.view = editor.view;
54019
+ this.getPos = getPos;
54020
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
54021
+ }
54022
+ }
54023
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
54024
+ init_fn2 = function() {
54025
+ const { attrs } = this.node;
54026
+ const { id, title, description } = attrs;
54027
+ this.dom = document.createElement("div");
54028
+ this.dom.className = "sd-document-section-block";
54029
+ this.dom.setAttribute("data-id", id);
54030
+ this.dom.setAttribute("data-title", title);
54031
+ this.dom.setAttribute("data-description", description);
54032
+ this.dom.setAttribute("aria-label", "Document section");
54033
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
54034
+ this.contentDOM = document.createElement("div");
54035
+ this.contentDOM.className = "sd-document-section-block-content";
54036
+ this.contentDOM.setAttribute("contenteditable", "true");
54037
+ this.dom.appendChild(this.contentDOM);
54038
+ };
54039
+ addToolTip_fn = function() {
54040
+ const { title } = this.node.attrs;
54041
+ this.infoDiv = document.createElement("div");
54042
+ this.infoDiv.className = "sd-document-section-block-info";
54043
+ const textSpan = document.createElement("span");
54044
+ textSpan.textContent = title || "Document section";
54045
+ this.infoDiv.appendChild(textSpan);
54046
+ this.infoDiv.setAttribute("contenteditable", "false");
54047
+ this.dom.appendChild(this.infoDiv);
54048
+ };
54049
+ const getAllSections = (editor) => {
54050
+ if (!editor) return [];
54051
+ const type2 = editor.schema.nodes.documentSection;
54052
+ if (!type2) return [];
54053
+ const sections = [];
54054
+ const { state: state2 } = editor;
54055
+ state2.doc.descendants((node, pos) => {
54056
+ if (node.type.name === type2.name) {
54057
+ sections.push({ node, pos });
54058
+ }
54059
+ });
54060
+ return sections;
54061
+ };
54062
+ const exportSectionsToHTML = (editor) => {
54063
+ const sections = getAllSections(editor);
54064
+ const processedSections = /* @__PURE__ */ new Set();
54065
+ const result = [];
54066
+ sections.forEach(({ node }) => {
54067
+ const { attrs } = node;
54068
+ const { id, title, description } = attrs;
54069
+ if (processedSections.has(id)) return;
54070
+ processedSections.add(id);
54071
+ const html = getHTMLFromNode(node, editor);
54072
+ result.push({
54073
+ id,
54074
+ title,
54075
+ description,
54076
+ html
54077
+ });
54078
+ });
54079
+ return result;
54080
+ };
54081
+ const getHTMLFromNode = (node, editor) => {
54082
+ const tempDocument = document.implementation.createHTMLDocument();
54083
+ const container = tempDocument.createElement("div");
54084
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
54085
+ container.appendChild(fragment);
54086
+ let html = container.innerHTML;
54087
+ return html;
54088
+ };
54089
+ const exportSectionsToJSON = (editor) => {
54090
+ const sections = getAllSections(editor);
54091
+ const processedSections = /* @__PURE__ */ new Set();
54092
+ const result = [];
54093
+ sections.forEach(({ node }) => {
54094
+ const { attrs } = node;
54095
+ const { id, title, description } = attrs;
54096
+ if (processedSections.has(id)) return;
54097
+ processedSections.add(id);
54098
+ result.push({
54099
+ id,
54100
+ title,
54101
+ description,
54102
+ content: node.toJSON()
54103
+ });
54104
+ });
54105
+ return result;
54106
+ };
54107
+ const getLinkedSectionEditor = (id, options, editor) => {
54108
+ const sections = getAllSections(editor);
54109
+ const section = sections.find((s) => s.node.attrs.id === id);
54110
+ if (!section) return null;
54111
+ const child = editor.createChildEditor({
54112
+ ...options,
54113
+ onUpdate: ({ editor: childEditor, transaction }) => {
54114
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
54115
+ if (isFromtLinkedParent) return;
54116
+ const updatedContent = childEditor.state.doc.content;
54117
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54118
+ if (!sectionNode) return;
54119
+ const { pos, node } = sectionNode;
54120
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
54121
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
54122
+ tr.setMeta("fromLinkedChild", true);
54123
+ editor.view.dispatch(tr);
54124
+ }
54125
+ });
54126
+ editor.on("update", ({ transaction }) => {
54127
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
54128
+ if (isFromLinkedChild) return;
54129
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54130
+ if (!sectionNode) return;
54131
+ const sectionContent = sectionNode.node.content;
54132
+ const json = {
54133
+ type: "doc",
54134
+ content: sectionContent.content.map((node) => node.toJSON())
54135
+ };
54136
+ const childTr = child.state.tr;
54137
+ childTr.setMeta("fromLinkedParent", true);
54138
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
54139
+ child.view.dispatch(childTr);
54140
+ });
54141
+ return child;
54142
+ };
54143
+ const SectionHelpers = {
54144
+ getAllSections,
54145
+ exportSectionsToHTML,
54146
+ exportSectionsToJSON,
54147
+ getLinkedSectionEditor
54148
+ };
54149
+ const DocumentSection = Node$1.create({
54150
+ name: "documentSection",
54151
+ group: "block",
54152
+ content: "block*",
54153
+ atom: true,
54154
+ isolating: true,
54155
+ addOptions() {
54156
+ return {
54157
+ htmlAttributes: {
54158
+ class: "sd-document-section-block",
54159
+ "aria-label": "Structured content block"
54160
+ }
54161
+ };
54162
+ },
54163
+ parseDOM() {
54164
+ return [
54165
+ {
54166
+ tag: "div.sd-document-section-block",
54167
+ priority: 60
54168
+ }
54169
+ ];
54170
+ },
54171
+ renderDOM({ htmlAttributes }) {
54172
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54173
+ },
54174
+ addAttributes() {
54175
+ return {
54176
+ id: {},
54177
+ sdBlockId: {
54178
+ default: null,
54179
+ keepOnSplit: false,
54180
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54181
+ renderDOM: (attrs) => {
54182
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54183
+ }
54184
+ },
54185
+ title: {},
54186
+ description: {},
54187
+ sectionType: {},
54188
+ isLocked: { default: false }
54189
+ };
54190
+ },
54191
+ addNodeView() {
54192
+ return ({ node, editor, getPos, decorations }) => {
54193
+ return new DocumentSectionView(node, getPos, decorations, editor);
54194
+ };
54195
+ },
54196
+ addCommands() {
54197
+ return {
54198
+ /**
54199
+ * Create a lockable content section
54200
+ * @category Command
54201
+ * @param {SectionCreate} [options={}] - Section configuration
54202
+ * @example
54203
+ * editor.commands.createDocumentSection({
54204
+ * id: 1,
54205
+ * title: 'Terms & Conditions',
54206
+ * isLocked: true,
54207
+ * html: '<p>Legal content...</p>'
54208
+ * })
54209
+ */
54210
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
54211
+ const { selection } = state2;
54212
+ let { from: from2, to } = selection;
54213
+ let content = selection.content().content;
54214
+ const { html: optionsHTML, json: optionsJSON } = options;
54215
+ if (optionsHTML) {
54216
+ const html = htmlHandler(optionsHTML, this.editor);
54217
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
54218
+ content = doc2.content;
54219
+ }
54220
+ if (optionsJSON) {
54221
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
54222
+ }
54223
+ if (!content?.content?.length) {
54224
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
54225
+ }
54226
+ if (!options.id) {
54227
+ const allSections = SectionHelpers.getAllSections(editor);
54228
+ options.id = allSections.length + 1;
54229
+ }
54230
+ if (!options.title) {
54231
+ options.title = "Document section";
54232
+ }
54233
+ const node = this.type.createAndFill(options, content);
54234
+ if (!node) return false;
54235
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
54236
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
54237
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
54238
+ from2 = insertPos2;
54239
+ to = insertPos2;
54240
+ }
54241
+ tr.replaceRangeWith(from2, to, node);
54242
+ const nodeEnd = from2 + node.nodeSize;
54243
+ let shouldInsertParagraph = true;
54244
+ let insertPos = nodeEnd;
54245
+ if (nodeEnd >= tr.doc.content.size) {
54246
+ insertPos = tr.doc.content.size;
54247
+ if (insertPos > 0) {
54248
+ const $endPos = tr.doc.resolve(insertPos);
54249
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
54250
+ shouldInsertParagraph = false;
54251
+ }
54252
+ }
54253
+ }
54254
+ if (shouldInsertParagraph) {
54255
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
54256
+ tr.insert(insertPos, emptyParagraph);
54257
+ }
54258
+ if (dispatch) {
54259
+ tr.setMeta("documentSection", { action: "create" });
54260
+ dispatch(tr);
54261
+ setTimeout(() => {
54262
+ try {
54263
+ const currentState = editor.state;
54264
+ const docSize = currentState.doc.content.size;
54265
+ let targetPos = from2 + node.nodeSize;
54266
+ if (shouldInsertParagraph) {
54267
+ targetPos += 1;
54268
+ }
54269
+ targetPos = Math.min(targetPos, docSize);
54270
+ if (targetPos < docSize && targetPos > 0) {
54271
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
54272
+ const newTr = currentState.tr.setSelection(newSelection);
54273
+ editor.view.dispatch(newTr);
54274
+ }
54275
+ } catch (e) {
54276
+ console.warn("Could not set delayed selection:", e);
54277
+ }
54278
+ }, 0);
54279
+ }
54280
+ return true;
54281
+ },
54282
+ /**
54283
+ * Remove section wrapper at cursor, preserving its content
54284
+ * @category Command
54285
+ * @example
54286
+ * editor.commands.removeSectionAtSelection()
54287
+ * @note Content stays in document, only section wrapper is removed
54288
+ */
54289
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
54290
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
54291
+ if (!sdtNode) return false;
54292
+ const { node, pos } = sdtNode;
54293
+ const nodeStart = pos;
54294
+ const nodeEnd = nodeStart + node.nodeSize;
54295
+ const contentToPreserve = node.content;
54296
+ tr.delete(nodeStart, nodeEnd);
54297
+ if (contentToPreserve.size > 0) {
54298
+ tr.insert(nodeStart, contentToPreserve);
54299
+ }
54300
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
54301
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
54302
+ if (dispatch) {
54303
+ tr.setMeta("documentSection", { action: "delete" });
54304
+ dispatch(tr);
54305
+ }
54306
+ return true;
54307
+ },
54308
+ /**
54309
+ * Delete section and all its content
54310
+ * @category Command
54311
+ * @param {number} id - Section to delete
54312
+ * @example
54313
+ * editor.commands.removeSectionById(123)
54314
+ */
54315
+ removeSectionById: (id) => ({ tr, dispatch }) => {
54316
+ const sections = SectionHelpers.getAllSections(this.editor);
54317
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
54318
+ if (!sectionToRemove) return false;
54319
+ const { pos, node } = sectionToRemove;
54320
+ const nodeStart = pos;
54321
+ const nodeEnd = nodeStart + node.nodeSize;
54322
+ tr.delete(nodeStart, nodeEnd);
54323
+ if (dispatch) {
54324
+ tr.setMeta("documentSection", { action: "delete", id });
54325
+ dispatch(tr);
54326
+ }
54327
+ return true;
54328
+ },
54329
+ /**
54330
+ * Lock section against edits
54331
+ * @category Command
54332
+ * @param {number} id - Section to lock
54333
+ * @example
54334
+ * editor.commands.lockSectionById(123)
54335
+ */
54336
+ lockSectionById: (id) => ({ tr, dispatch }) => {
54337
+ const sections = SectionHelpers.getAllSections(this.editor);
54338
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
54339
+ if (!sectionToLock) return false;
54340
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
54341
+ if (dispatch) {
54342
+ tr.setMeta("documentSection", { action: "lock", id });
54343
+ dispatch(tr);
54344
+ }
54345
+ return true;
54346
+ },
54347
+ /**
54348
+ * Modify section attributes or content
54349
+ * @category Command
54350
+ * @param {SectionUpdate} options - Changes to apply
54351
+ * @example
54352
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
54353
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
54354
+ * editor.commands.updateSectionById({
54355
+ * id: 123,
54356
+ * html: '<p>Updated</p>',
54357
+ * attrs: { title: 'New Title' }
54358
+ * })
54359
+ */
54360
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
54361
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
54362
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
54363
+ if (!sectionToUpdate) return false;
54364
+ const { pos, node } = sectionToUpdate;
54365
+ let newContent = null;
54366
+ if (html) {
54367
+ const htmlDoc = htmlHandler(html, editor || this.editor);
54368
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
54369
+ newContent = doc2.content;
54370
+ }
54371
+ if (json) {
54372
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
54373
+ }
54374
+ if (!newContent) {
54375
+ newContent = node.content;
54376
+ }
54377
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
54378
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
54379
+ if (dispatch) {
54380
+ tr.setMeta("documentSection", { action: "update", id, attrs });
54381
+ dispatch(tr);
54382
+ }
54383
+ return true;
54384
+ }
54385
+ };
54386
+ },
54387
+ addHelpers() {
54388
+ return {
54389
+ ...SectionHelpers
54390
+ };
54391
+ }
54392
+ });
54393
+ const Document = Node$1.create({
54394
+ name: "doc",
54395
+ topNode: true,
54396
+ content: "block+",
54397
+ parseDOM() {
54398
+ return [{ tag: "doc" }];
54399
+ },
54400
+ renderDOM() {
54401
+ return ["doc", 0];
54402
+ },
54403
+ addAttributes() {
54404
+ return {
54405
+ attributes: {
54406
+ rendered: false,
54407
+ "aria-label": "Document node"
54408
+ }
54409
+ };
54410
+ },
54411
+ addCommands() {
54412
+ return {
54413
+ /**
54414
+ * Get document statistics
54415
+ * @category Command
54416
+ * @example
54417
+ * // Get word and character count
54418
+ * const stats = editor.commands.getDocumentStats()
54419
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
54420
+ * @note Returns word count, character count, and paragraph count
54421
+ */
54422
+ getDocumentStats: () => ({ editor }) => {
54423
+ const text = editor.getText();
54424
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
54425
+ const characters = text.length;
54426
+ const paragraphs = editor.state.doc.content.childCount;
54427
+ return {
54428
+ words,
54429
+ characters,
54430
+ paragraphs
54431
+ };
54432
+ },
54433
+ /**
54434
+ * Clear entire document
54435
+ * @category Command
54436
+ * @example
54437
+ * editor.commands.clearDocument()
54438
+ * @note Replaces all content with an empty paragraph
54439
+ */
54440
+ clearDocument: () => ({ commands: commands2 }) => {
54441
+ return commands2.setContent("<p></p>");
54442
+ }
54443
+ };
54444
+ }
54445
+ });
54446
+ const Text = Node$1.create({
54447
+ name: "text",
54448
+ group: "inline",
54449
+ inline: true,
54450
+ addOptions() {
54451
+ return {};
54452
+ }
54453
+ });
54454
+ const splitRun = () => (props) => {
54455
+ const { state: state2, view, tr } = props;
54456
+ const { $from, empty: empty2 } = state2.selection;
54457
+ if (!empty2) return false;
54458
+ if ($from.parent.type.name !== "run") return false;
54459
+ const handled = splitBlock(state2, (transaction) => {
54460
+ view.dispatch(transaction);
54461
+ });
54462
+ if (handled) {
54463
+ tr.setMeta("preventDispatch", true);
54464
+ }
54465
+ return handled;
54466
+ };
54467
+ const Run = OxmlNode.create({
54468
+ name: "run",
54469
+ oXmlName: "w:r",
54470
+ group: "inline",
54471
+ inline: true,
54472
+ content: "inline*",
54473
+ selectable: false,
54474
+ childToAttributes: ["runProperties"],
54475
+ addOptions() {
54476
+ return {
54477
+ htmlAttributes: {
54478
+ "data-run": "1"
54479
+ }
54480
+ };
54481
+ },
54482
+ addAttributes() {
54483
+ return {
54484
+ runProperties: {
54485
+ default: null,
54486
+ rendered: false,
54487
+ keepOnSplit: true
54488
+ },
54489
+ rsidR: {
54490
+ default: null,
54491
+ rendered: false,
54492
+ keepOnSplit: true
54493
+ },
54494
+ rsidRPr: {
54495
+ default: null,
54496
+ rendered: false,
54497
+ keepOnSplit: true
54498
+ },
54499
+ rsidDel: {
54500
+ default: null,
54501
+ rendered: false,
54502
+ keepOnSplit: true
54503
+ }
54504
+ };
54505
+ },
54506
+ addCommands() {
54507
+ return {
54508
+ splitRun
54509
+ };
54510
+ },
54511
+ parseDOM() {
54512
+ return [{ tag: "span[data-run]" }];
54513
+ },
54514
+ renderDOM({ htmlAttributes }) {
54515
+ const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54516
+ return ["span", base2, 0];
54517
+ }
54518
+ });
54519
+ const inputRegex$1 = /^\s*([-+*])\s$/;
54520
+ const BulletList = Node$1.create({
54521
+ name: "bulletList",
54522
+ group: "block list",
54523
+ selectable: false,
54524
+ content() {
54525
+ return `${this.options.itemTypeName}+`;
54526
+ },
54527
+ addOptions() {
54528
+ return {
54529
+ itemTypeName: "listItem",
54530
+ htmlAttributes: {
54531
+ "aria-label": "Bullet list node"
54532
+ },
54533
+ keepMarks: true,
54534
+ keepAttributes: false
54535
+ };
54536
+ },
54537
+ parseDOM() {
54538
+ return [{ tag: "ul" }];
54539
+ },
54540
+ renderDOM({ htmlAttributes }) {
54541
+ const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54542
+ return ["ul", attributes, 0];
54543
+ },
54544
+ addAttributes() {
54545
+ return {
54546
+ "list-style-type": {
54547
+ default: "bullet",
54548
+ rendered: false
54549
+ },
54550
+ listId: {
54551
+ rendered: false
54552
+ },
54553
+ sdBlockId: {
54554
+ default: null,
54555
+ keepOnSplit: false,
54556
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54557
+ renderDOM: (attrs) => {
54558
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54559
+ }
54560
+ },
54561
+ attributes: {
54562
+ rendered: false,
54563
+ keepOnSplit: true
54564
+ }
54565
+ };
54566
+ },
54567
+ addCommands() {
54568
+ return {
54569
+ /**
54570
+ * Toggle a bullet list at the current selection
54571
+ * @category Command
54572
+ * @example
54573
+ * // Toggle bullet list on selected text
54574
+ * editor.commands.toggleBulletList()
54575
+ * @note Converts selected paragraphs to list items or removes list formatting
54576
+ */
54577
+ toggleBulletList: () => (params2) => {
54578
+ return toggleList(this.type)(params2);
54579
+ }
54580
+ };
54581
+ },
54582
+ addShortcuts() {
54583
+ return {
54584
+ "Mod-Shift-8": () => {
54585
+ return this.editor.commands.toggleBulletList();
54586
+ }
54587
+ };
54588
+ },
54589
+ addInputRules() {
54590
+ return [
54591
+ new InputRule({
54592
+ match: inputRegex$1,
54593
+ handler: ({ state: state2, range: range2 }) => {
54594
+ const $pos = state2.selection.$from;
54595
+ const listItemType = state2.schema.nodes.listItem;
54596
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54597
+ if ($pos.node(depth).type === listItemType) {
54598
+ return null;
54599
+ }
54600
+ }
54601
+ const { tr } = state2;
54602
+ tr.delete(range2.from, range2.to);
54603
+ ListHelpers.createNewList({
54604
+ listType: this.type,
54605
+ tr,
54606
+ editor: this.editor
54607
+ });
54608
+ }
54609
+ })
54610
+ ];
54611
+ }
54612
+ });
54613
+ const inputRegex = /^(\d+)\.\s$/;
54614
+ const OrderedList = Node$1.create({
53578
54615
  name: "orderedList",
53579
54616
  group: "block list",
53580
54617
  selectable: false,
@@ -54935,7 +55972,7 @@ class ListItemNodeView {
54935
55972
  this.decorations = decorations;
54936
55973
  this.view = editor.view;
54937
55974
  this.getPos = getPos;
54938
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
55975
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
54939
55976
  activeListItemNodeViews.add(this);
54940
55977
  }
54941
55978
  refreshIndentStyling() {
@@ -54996,7 +56033,7 @@ class ListItemNodeView {
54996
56033
  }
54997
56034
  }
54998
56035
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
54999
- init_fn2 = function() {
56036
+ init_fn3 = function() {
55000
56037
  const { attrs } = this.node;
55001
56038
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
55002
56039
  let orderMarker = "";
@@ -62105,984 +63142,335 @@ const PageNumber = Node$1.create({
62105
63142
  }
62106
63143
  };
62107
63144
  },
62108
- addAttributes() {
62109
- return {
62110
- marksAsAttrs: {
62111
- default: null,
62112
- rendered: false
62113
- }
62114
- };
62115
- },
62116
- addNodeView() {
62117
- return ({ node, editor, getPos, decorations }) => {
62118
- const htmlAttributes = this.options.htmlAttributes;
62119
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62120
- };
62121
- },
62122
- parseDOM() {
62123
- return [{ tag: 'span[data-id="auto-page-number"' }];
62124
- },
62125
- renderDOM({ htmlAttributes }) {
62126
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62127
- },
62128
- addCommands() {
62129
- return {
62130
- /**
62131
- * Insert an automatic page number
62132
- * @category Command
62133
- * @returns {Function} Command function
62134
- * @example
62135
- * editor.commands.addAutoPageNumber()
62136
- * @note Only works in header/footer contexts
62137
- */
62138
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
62139
- const { options } = editor;
62140
- if (!options.isHeaderOrFooter) return false;
62141
- const { schema } = state2;
62142
- const pageNumberType = schema?.nodes?.["page-number"];
62143
- if (!pageNumberType) return false;
62144
- const pageNumberNodeJSON = { type: "page-number" };
62145
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
62146
- if (dispatch) {
62147
- tr.replaceSelectionWith(pageNumberNode, false);
62148
- tr.setMeta("forceUpdatePagination", true);
62149
- }
62150
- return true;
62151
- }
62152
- };
62153
- },
62154
- addShortcuts() {
62155
- return {
62156
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
62157
- };
62158
- }
62159
- });
62160
- const TotalPageCount = Node$1.create({
62161
- name: "total-page-number",
62162
- group: "inline",
62163
- inline: true,
62164
- atom: true,
62165
- draggable: false,
62166
- selectable: false,
62167
- content: "text*",
62168
- addOptions() {
62169
- return {
62170
- htmlAttributes: {
62171
- contenteditable: false,
62172
- "data-id": "auto-total-pages",
62173
- "aria-label": "Total page count node",
62174
- class: "sd-editor-auto-total-pages"
62175
- }
62176
- };
62177
- },
62178
- addAttributes() {
62179
- return {
62180
- marksAsAttrs: {
62181
- default: null,
62182
- rendered: false
62183
- }
62184
- };
62185
- },
62186
- addNodeView() {
62187
- return ({ node, editor, getPos, decorations }) => {
62188
- const htmlAttributes = this.options.htmlAttributes;
62189
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
62190
- };
62191
- },
62192
- parseDOM() {
62193
- return [{ tag: 'span[data-id="auto-total-pages"' }];
62194
- },
62195
- renderDOM({ htmlAttributes }) {
62196
- return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62197
- },
62198
- addCommands() {
62199
- return {
62200
- /**
62201
- * Insert total page count
62202
- * @category Command
62203
- * @returns {Function} Command function
62204
- * @example
62205
- * editor.commands.addTotalPageCount()
62206
- * @note Only works in header/footer contexts
62207
- */
62208
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
62209
- const { options } = editor;
62210
- if (!options.isHeaderOrFooter) return false;
62211
- const { schema } = state2;
62212
- const pageNumberType = schema.nodes?.["total-page-number"];
62213
- if (!pageNumberType) return false;
62214
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
62215
- const pageNumberNode = {
62216
- type: "total-page-number",
62217
- content: [{ type: "text", text: String(currentPages) }]
62218
- };
62219
- const pageNode = schema.nodeFromJSON(pageNumberNode);
62220
- if (dispatch) {
62221
- tr.replaceSelectionWith(pageNode, false);
62222
- }
62223
- return true;
62224
- }
62225
- };
62226
- },
62227
- addShortcuts() {
62228
- return {
62229
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62230
- };
62231
- }
62232
- });
62233
- const getNodeAttributes = (nodeName, editor) => {
62234
- switch (nodeName) {
62235
- case "page-number":
62236
- return {
62237
- text: editor.options.currentPageNumber || "1",
62238
- className: "sd-editor-auto-page-number",
62239
- dataId: "auto-page-number",
62240
- ariaLabel: "Page number node"
62241
- };
62242
- case "total-page-number":
62243
- return {
62244
- text: editor.options.parentEditor?.currentTotalPages || "1",
62245
- className: "sd-editor-auto-total-pages",
62246
- dataId: "auto-total-pages",
62247
- ariaLabel: "Total page count node"
62248
- };
62249
- default:
62250
- return {};
62251
- }
62252
- };
62253
- class AutoPageNumberNodeView {
62254
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
62255
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
62256
- this.node = node;
62257
- this.editor = editor;
62258
- this.view = editor.view;
62259
- this.getPos = getPos;
62260
- this.editor = editor;
62261
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62262
- }
62263
- update(node) {
62264
- const incomingType = node?.type?.name;
62265
- const currentType = this.node?.type?.name;
62266
- if (!incomingType || incomingType !== currentType) return false;
62267
- this.node = node;
62268
- return true;
62269
- }
62270
- }
62271
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
62272
- renderDom_fn = function(node, htmlAttributes) {
62273
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
62274
- const content = document.createTextNode(String(attrs.text));
62275
- const nodeContent = document.createElement("span");
62276
- nodeContent.className = attrs.className;
62277
- nodeContent.setAttribute("data-id", attrs.dataId);
62278
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
62279
- const currentPos = this.getPos();
62280
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
62281
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
62282
- Object.assign(nodeContent.style, styles);
62283
- nodeContent.appendChild(content);
62284
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
62285
- if (value) nodeContent.setAttribute(key2, value);
62286
- });
62287
- return nodeContent;
62288
- };
62289
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
62290
- setTimeout(() => {
62291
- const { state: state2 } = this.editor;
62292
- const { dispatch } = this.view;
62293
- const node = state2.doc.nodeAt(pos);
62294
- if (!node || node.isText) return;
62295
- const currentMarks = node.attrs.marksAsAttrs || [];
62296
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
62297
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
62298
- if (isEqual) return;
62299
- const newAttrs = {
62300
- ...node.attrs,
62301
- marksAsAttrs: newMarks
62302
- };
62303
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
62304
- dispatch(tr);
62305
- }, 0);
62306
- };
62307
- const getMarksFromNeighbors = (currentPos, view) => {
62308
- const $pos = view.state.doc.resolve(currentPos);
62309
- const styles = {};
62310
- const marks = [];
62311
- const before = $pos.nodeBefore;
62312
- if (before) {
62313
- Object.assign(styles, processMarks(before.marks));
62314
- marks.push(...before.marks);
62315
- }
62316
- const after = $pos.nodeAfter;
62317
- if (after) {
62318
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
62319
- marks.push(...after.marks);
62320
- }
62321
- return {
62322
- styles,
62323
- marks
62324
- };
62325
- };
62326
- const processMarks = (marks) => {
62327
- const styles = {};
62328
- marks.forEach((mark) => {
62329
- const { type: type2, attrs } = mark;
62330
- switch (type2.name) {
62331
- case "textStyle":
62332
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
62333
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
62334
- if (attrs.color) styles["color"] = attrs.color;
62335
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
62336
- break;
62337
- case "bold":
62338
- styles["font-weight"] = "bold";
62339
- break;
62340
- case "italic":
62341
- styles["font-style"] = "italic";
62342
- break;
62343
- case "underline":
62344
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
62345
- break;
62346
- case "strike":
62347
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
62348
- break;
62349
- default:
62350
- if (attrs?.style) {
62351
- Object.entries(attrs.style).forEach(([key2, value]) => {
62352
- styles[key2] = value;
62353
- });
62354
- }
62355
- break;
62356
- }
62357
- });
62358
- return styles;
62359
- };
62360
- const ShapeContainer = Node$1.create({
62361
- name: "shapeContainer",
62362
- group: "block",
62363
- content: "block+",
62364
- isolating: true,
62365
- addOptions() {
62366
- return {
62367
- htmlAttributes: {
62368
- class: "sd-editor-shape-container",
62369
- "aria-label": "Shape container node"
62370
- }
62371
- };
62372
- },
62373
- addAttributes() {
62374
- return {
62375
- fillcolor: {
62376
- renderDOM: (attrs) => {
62377
- if (!attrs.fillcolor) return {};
62378
- return {
62379
- style: `background-color: ${attrs.fillcolor}`
62380
- };
62381
- }
62382
- },
62383
- sdBlockId: {
62384
- default: null,
62385
- keepOnSplit: false,
62386
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62387
- renderDOM: (attrs) => {
62388
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62389
- }
62390
- },
62391
- style: {
62392
- renderDOM: (attrs) => {
62393
- if (!attrs.style) return {};
62394
- return {
62395
- style: attrs.style
62396
- };
62397
- }
62398
- },
62399
- wrapAttributes: {
62400
- rendered: false
62401
- },
62402
- attributes: {
62403
- rendered: false
62404
- }
62405
- };
62406
- },
62407
- parseDOM() {
62408
- return [
62409
- {
62410
- tag: `div[data-type="${this.name}"]`
62411
- }
62412
- ];
62413
- },
62414
- renderDOM({ htmlAttributes }) {
62415
- return [
62416
- "div",
62417
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62418
- 0
62419
- ];
62420
- }
62421
- });
62422
- const ShapeTextbox = Node$1.create({
62423
- name: "shapeTextbox",
62424
- group: "block",
62425
- content: "paragraph* block*",
62426
- isolating: true,
62427
- addOptions() {
62428
- return {
62429
- htmlAttributes: {
62430
- class: "sd-editor-shape-textbox",
62431
- "aria-label": "Shape textbox node"
62432
- }
62433
- };
62434
- },
62435
- addAttributes() {
62436
- return {
62437
- sdBlockId: {
62438
- default: null,
62439
- keepOnSplit: false,
62440
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62441
- renderDOM: (attrs) => {
62442
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62443
- }
62444
- },
62445
- attributes: {
62446
- rendered: false
62447
- }
62448
- };
62449
- },
62450
- parseDOM() {
62451
- return [
62452
- {
62453
- tag: `div[data-type="${this.name}"]`
62454
- }
62455
- ];
62456
- },
62457
- renderDOM({ htmlAttributes }) {
62458
- return [
62459
- "div",
62460
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62461
- 0
62462
- ];
62463
- }
62464
- });
62465
- const ContentBlock = Node$1.create({
62466
- name: "contentBlock",
62467
- group: "inline",
62468
- content: "",
62469
- isolating: true,
62470
- atom: true,
62471
- inline: true,
62472
- addOptions() {
62473
- return {
62474
- htmlAttributes: {
62475
- contenteditable: false
62476
- }
62477
- };
62478
- },
62479
- addAttributes() {
62480
- return {
62481
- horizontalRule: {
62482
- default: false,
62483
- renderDOM: ({ horizontalRule }) => {
62484
- if (!horizontalRule) return {};
62485
- return { "data-horizontal-rule": "true" };
62486
- }
62487
- },
62488
- size: {
62489
- default: null,
62490
- renderDOM: ({ size: size2 }) => {
62491
- if (!size2) return {};
62492
- let style2 = "";
62493
- if (size2.top) style2 += `top: ${size2.top}px; `;
62494
- if (size2.left) style2 += `left: ${size2.left}px; `;
62495
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
62496
- if (size2.height)
62497
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
62498
- return { style: style2 };
62499
- }
62500
- },
62501
- background: {
62502
- default: null,
62503
- renderDOM: (attrs) => {
62504
- if (!attrs.background) return {};
62505
- return {
62506
- style: `background-color: ${attrs.background}`
62507
- };
62508
- }
62509
- },
62510
- drawingContent: {
62511
- rendered: false
62512
- },
62513
- attributes: {
63145
+ addAttributes() {
63146
+ return {
63147
+ marksAsAttrs: {
63148
+ default: null,
62514
63149
  rendered: false
62515
63150
  }
62516
63151
  };
62517
63152
  },
63153
+ addNodeView() {
63154
+ return ({ node, editor, getPos, decorations }) => {
63155
+ const htmlAttributes = this.options.htmlAttributes;
63156
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63157
+ };
63158
+ },
62518
63159
  parseDOM() {
62519
- return [
62520
- {
62521
- tag: `div[data-type="${this.name}"]`
62522
- }
62523
- ];
63160
+ return [{ tag: 'span[data-id="auto-page-number"' }];
62524
63161
  },
62525
63162
  renderDOM({ htmlAttributes }) {
62526
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63163
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62527
63164
  },
62528
63165
  addCommands() {
62529
63166
  return {
62530
63167
  /**
62531
- * Insert a horizontal rule
62532
- * @category Command
62533
- * @example
62534
- * editor.commands.insertHorizontalRule()
62535
- * @note Creates a visual separator between content sections
62536
- */
62537
- insertHorizontalRule: () => ({ commands: commands2 }) => {
62538
- return commands2.insertContent({
62539
- type: this.name,
62540
- attrs: {
62541
- horizontalRule: true,
62542
- size: { width: "100%", height: 2 },
62543
- background: "#e5e7eb"
62544
- }
62545
- });
62546
- },
62547
- /**
62548
- * Insert a content block
63168
+ * Insert an automatic page number
62549
63169
  * @category Command
62550
- * @param {ContentBlockConfig} config - Block configuration
62551
- * @example
62552
- * // Insert a spacer block
62553
- * editor.commands.insertContentBlock({ size: { height: 20 } })
62554
- *
63170
+ * @returns {Function} Command function
62555
63171
  * @example
62556
- * // Insert a colored divider
62557
- * editor.commands.insertContentBlock({
62558
- * size: { width: '50%', height: 3 },
62559
- * background: '#3b82f6'
62560
- * })
62561
- * @note Used for spacing, dividers, and special inline content
63172
+ * editor.commands.addAutoPageNumber()
63173
+ * @note Only works in header/footer contexts
62562
63174
  */
62563
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
62564
- return commands2.insertContent({
62565
- type: this.name,
62566
- attrs: config2
62567
- });
63175
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
63176
+ const { options } = editor;
63177
+ if (!options.isHeaderOrFooter) return false;
63178
+ const { schema } = state2;
63179
+ const pageNumberType = schema?.nodes?.["page-number"];
63180
+ if (!pageNumberType) return false;
63181
+ const pageNumberNodeJSON = { type: "page-number" };
63182
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
63183
+ if (dispatch) {
63184
+ tr.replaceSelectionWith(pageNumberNode, false);
63185
+ tr.setMeta("forceUpdatePagination", true);
63186
+ }
63187
+ return true;
62568
63188
  }
62569
63189
  };
63190
+ },
63191
+ addShortcuts() {
63192
+ return {
63193
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
63194
+ };
62570
63195
  }
62571
63196
  });
62572
- class StructuredContentViewBase {
62573
- constructor(props) {
62574
- __publicField$1(this, "node");
62575
- __publicField$1(this, "view");
62576
- __publicField$1(this, "getPos");
62577
- __publicField$1(this, "decorations");
62578
- __publicField$1(this, "innerDecorations");
62579
- __publicField$1(this, "editor");
62580
- __publicField$1(this, "extension");
62581
- __publicField$1(this, "htmlAttributes");
62582
- __publicField$1(this, "root");
62583
- __publicField$1(this, "isDragging", false);
62584
- this.node = props.node;
62585
- this.view = props.editor.view;
62586
- this.getPos = props.getPos;
62587
- this.decorations = props.decorations;
62588
- this.innerDecorations = props.innerDecorations;
62589
- this.editor = props.editor;
62590
- this.extension = props.extension;
62591
- this.htmlAttributes = props.htmlAttributes;
62592
- this.mount(props);
62593
- }
62594
- mount() {
62595
- return;
62596
- }
62597
- get dom() {
62598
- return this.root;
62599
- }
62600
- get contentDOM() {
62601
- return null;
62602
- }
62603
- update(node, decorations, innerDecorations) {
62604
- if (node.type !== this.node.type) {
62605
- return false;
62606
- }
62607
- this.node = node;
62608
- this.decorations = decorations;
62609
- this.innerDecorations = innerDecorations;
62610
- this.updateHTMLAttributes();
62611
- return true;
62612
- }
62613
- stopEvent(event) {
62614
- if (!this.dom) return false;
62615
- const target = event.target;
62616
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
62617
- if (!isInElement) return false;
62618
- const isDragEvent = event.type.startsWith("drag");
62619
- const isDropEvent = event.type === "drop";
62620
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
62621
- if (isInput && !isDropEvent && !isDragEvent) return true;
62622
- const { isEditable } = this.editor;
62623
- const { isDragging } = this;
62624
- const isDraggable = !!this.node.type.spec.draggable;
62625
- const isSelectable = NodeSelection.isSelectable(this.node);
62626
- const isCopyEvent = event.type === "copy";
62627
- const isPasteEvent = event.type === "paste";
62628
- const isCutEvent = event.type === "cut";
62629
- const isClickEvent = event.type === "mousedown";
62630
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
62631
- event.preventDefault();
62632
- }
62633
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
62634
- event.preventDefault();
62635
- return false;
62636
- }
62637
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
62638
- const dragHandle = target.closest("[data-drag-handle]");
62639
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
62640
- if (isValidDragHandle) {
62641
- this.isDragging = true;
62642
- document.addEventListener(
62643
- "dragend",
62644
- () => {
62645
- this.isDragging = false;
62646
- },
62647
- { once: true }
62648
- );
62649
- document.addEventListener(
62650
- "drop",
62651
- () => {
62652
- this.isDragging = false;
62653
- },
62654
- { once: true }
62655
- );
62656
- document.addEventListener(
62657
- "mouseup",
62658
- () => {
62659
- this.isDragging = false;
62660
- },
62661
- { once: true }
62662
- );
62663
- }
62664
- }
62665
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
62666
- return false;
62667
- }
62668
- return true;
62669
- }
62670
- ignoreMutation(mutation) {
62671
- if (!this.dom || !this.contentDOM) return true;
62672
- if (this.node.isLeaf || this.node.isAtom) return true;
62673
- if (mutation.type === "selection") return false;
62674
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
62675
- if (this.contentDOM.contains(mutation.target)) return false;
62676
- return true;
62677
- }
62678
- destroy() {
62679
- this.dom.remove();
62680
- this.contentDOM?.remove();
62681
- }
62682
- updateAttributes(attrs) {
62683
- const pos = this.getPos();
62684
- if (typeof pos !== "number") {
62685
- return;
62686
- }
62687
- return this.view.dispatch(
62688
- this.view.state.tr.setNodeMarkup(pos, void 0, {
62689
- ...this.node.attrs,
62690
- ...attrs
62691
- })
62692
- );
62693
- }
62694
- updateHTMLAttributes() {
62695
- const { extensionService } = this.editor;
62696
- const { attributes } = extensionService;
62697
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
62698
- this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
62699
- }
62700
- createDragHandle() {
62701
- const dragHandle = document.createElement("span");
62702
- dragHandle.classList.add("sd-structured-content-draggable");
62703
- dragHandle.draggable = true;
62704
- dragHandle.contentEditable = "false";
62705
- dragHandle.dataset.dragHandle = "";
62706
- const textElement = document.createElement("span");
62707
- textElement.textContent = "Structured content";
62708
- dragHandle.append(textElement);
62709
- return dragHandle;
62710
- }
62711
- onDragStart(event) {
62712
- const { view } = this.editor;
62713
- const target = event.target;
62714
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
62715
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
62716
- return;
62717
- }
62718
- let x = 0;
62719
- let y2 = 0;
62720
- if (this.dom !== dragHandle) {
62721
- const domBox = this.dom.getBoundingClientRect();
62722
- const handleBox = dragHandle.getBoundingClientRect();
62723
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
62724
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
62725
- x = handleBox.x - domBox.x + offsetX;
62726
- y2 = handleBox.y - domBox.y + offsetY;
62727
- }
62728
- event.dataTransfer?.setDragImage(this.dom, x, y2);
62729
- const pos = this.getPos();
62730
- if (typeof pos !== "number") {
62731
- return;
62732
- }
62733
- const selection = NodeSelection.create(view.state.doc, pos);
62734
- const transaction = view.state.tr.setSelection(selection);
62735
- view.dispatch(transaction);
62736
- }
62737
- }
62738
- class StructuredContentInlineView extends StructuredContentViewBase {
62739
- constructor(props) {
62740
- super(props);
62741
- }
62742
- mount() {
62743
- this.buildView();
62744
- }
62745
- get contentDOM() {
62746
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
62747
- return contentElement || null;
62748
- }
62749
- createElement() {
62750
- const element = document.createElement("span");
62751
- element.classList.add(structuredContentClass$1);
62752
- element.setAttribute("data-structured-content", "");
62753
- const contentElement = document.createElement("span");
62754
- contentElement.classList.add(structuredContentInnerClass$1);
62755
- element.append(contentElement);
62756
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62757
- updateDOMAttributes(element, { ...domAttrs });
62758
- return { element, contentElement };
62759
- }
62760
- buildView() {
62761
- const { element } = this.createElement();
62762
- const dragHandle = this.createDragHandle();
62763
- element.prepend(dragHandle);
62764
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62765
- this.root = element;
62766
- }
62767
- updateView() {
62768
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62769
- updateDOMAttributes(this.dom, { ...domAttrs });
62770
- }
62771
- update(node, decorations, innerDecorations) {
62772
- const result = super.update(node, decorations, innerDecorations);
62773
- if (!result) return false;
62774
- this.updateView();
62775
- return true;
62776
- }
62777
- }
62778
- const structuredContentClass$1 = "sd-structured-content";
62779
- const structuredContentInnerClass$1 = "sd-structured-content__content";
62780
- const StructuredContent = Node$1.create({
62781
- name: "structuredContent",
62782
- group: "inline structuredContent",
63197
+ const TotalPageCount = Node$1.create({
63198
+ name: "total-page-number",
63199
+ group: "inline",
62783
63200
  inline: true,
62784
- content: "inline*",
62785
- isolating: true,
62786
- atom: false,
62787
- // false - has editable content.
62788
- draggable: true,
63201
+ atom: true,
63202
+ draggable: false,
63203
+ selectable: false,
63204
+ content: "text*",
62789
63205
  addOptions() {
62790
63206
  return {
62791
63207
  htmlAttributes: {
62792
- class: structuredContentClass$1,
62793
- "aria-label": "Structured content node"
63208
+ contenteditable: false,
63209
+ "data-id": "auto-total-pages",
63210
+ "aria-label": "Total page count node",
63211
+ class: "sd-editor-auto-total-pages"
62794
63212
  }
62795
63213
  };
62796
63214
  },
62797
63215
  addAttributes() {
62798
63216
  return {
62799
- id: {
63217
+ marksAsAttrs: {
62800
63218
  default: null,
62801
- parseDOM: (elem) => elem.getAttribute("data-id"),
62802
- renderDOM: (attrs) => {
62803
- if (!attrs.id) return {};
62804
- return { "data-id": attrs.id };
62805
- }
62806
- },
62807
- sdtPr: {
62808
63219
  rendered: false
62809
63220
  }
62810
63221
  };
62811
63222
  },
63223
+ addNodeView() {
63224
+ return ({ node, editor, getPos, decorations }) => {
63225
+ const htmlAttributes = this.options.htmlAttributes;
63226
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63227
+ };
63228
+ },
62812
63229
  parseDOM() {
62813
- return [{ tag: "span[data-structured-content]" }];
63230
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
62814
63231
  },
62815
63232
  renderDOM({ htmlAttributes }) {
62816
- return [
62817
- "span",
62818
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62819
- "data-structured-content": ""
62820
- }),
62821
- 0
62822
- ];
63233
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62823
63234
  },
62824
- addNodeView() {
62825
- return (props) => {
62826
- return new StructuredContentInlineView({ ...props });
63235
+ addCommands() {
63236
+ return {
63237
+ /**
63238
+ * Insert total page count
63239
+ * @category Command
63240
+ * @returns {Function} Command function
63241
+ * @example
63242
+ * editor.commands.addTotalPageCount()
63243
+ * @note Only works in header/footer contexts
63244
+ */
63245
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
63246
+ const { options } = editor;
63247
+ if (!options.isHeaderOrFooter) return false;
63248
+ const { schema } = state2;
63249
+ const pageNumberType = schema.nodes?.["total-page-number"];
63250
+ if (!pageNumberType) return false;
63251
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
63252
+ const pageNumberNode = {
63253
+ type: "total-page-number",
63254
+ content: [{ type: "text", text: String(currentPages) }]
63255
+ };
63256
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
63257
+ if (dispatch) {
63258
+ tr.replaceSelectionWith(pageNode, false);
63259
+ }
63260
+ return true;
63261
+ }
63262
+ };
63263
+ },
63264
+ addShortcuts() {
63265
+ return {
63266
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62827
63267
  };
62828
63268
  }
62829
63269
  });
62830
- class StructuredContentBlockView extends StructuredContentViewBase {
62831
- constructor(props) {
62832
- super(props);
62833
- }
62834
- mount() {
62835
- this.buildView();
62836
- }
62837
- get contentDOM() {
62838
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
62839
- return contentElement || null;
62840
- }
62841
- createElement() {
62842
- const element = document.createElement("div");
62843
- element.classList.add(structuredContentClass);
62844
- element.setAttribute("data-structured-content-block", "");
62845
- const contentElement = document.createElement("div");
62846
- contentElement.classList.add(structuredContentInnerClass);
62847
- element.append(contentElement);
62848
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62849
- updateDOMAttributes(element, { ...domAttrs });
62850
- return { element, contentElement };
62851
- }
62852
- buildView() {
62853
- const { element } = this.createElement();
62854
- const dragHandle = this.createDragHandle();
62855
- element.prepend(dragHandle);
62856
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
62857
- this.root = element;
63270
+ const getNodeAttributes = (nodeName, editor) => {
63271
+ switch (nodeName) {
63272
+ case "page-number":
63273
+ return {
63274
+ text: editor.options.currentPageNumber || "1",
63275
+ className: "sd-editor-auto-page-number",
63276
+ dataId: "auto-page-number",
63277
+ ariaLabel: "Page number node"
63278
+ };
63279
+ case "total-page-number":
63280
+ return {
63281
+ text: editor.options.parentEditor?.currentTotalPages || "1",
63282
+ className: "sd-editor-auto-total-pages",
63283
+ dataId: "auto-total-pages",
63284
+ ariaLabel: "Total page count node"
63285
+ };
63286
+ default:
63287
+ return {};
62858
63288
  }
62859
- updateView() {
62860
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62861
- updateDOMAttributes(this.dom, { ...domAttrs });
63289
+ };
63290
+ class AutoPageNumberNodeView {
63291
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
63292
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
63293
+ this.node = node;
63294
+ this.editor = editor;
63295
+ this.view = editor.view;
63296
+ this.getPos = getPos;
63297
+ this.editor = editor;
63298
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62862
63299
  }
62863
- update(node, decorations, innerDecorations) {
62864
- const result = super.update(node, decorations, innerDecorations);
62865
- if (!result) return false;
62866
- this.updateView();
63300
+ update(node) {
63301
+ const incomingType = node?.type?.name;
63302
+ const currentType = this.node?.type?.name;
63303
+ if (!incomingType || incomingType !== currentType) return false;
63304
+ this.node = node;
62867
63305
  return true;
62868
63306
  }
62869
63307
  }
62870
- const structuredContentClass = "sd-structured-content-block";
62871
- const structuredContentInnerClass = "sd-structured-content-block__content";
62872
- const StructuredContentBlock = Node$1.create({
62873
- name: "structuredContentBlock",
62874
- group: "block structuredContent",
62875
- content: "block*",
63308
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
63309
+ renderDom_fn = function(node, htmlAttributes) {
63310
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
63311
+ const content = document.createTextNode(String(attrs.text));
63312
+ const nodeContent = document.createElement("span");
63313
+ nodeContent.className = attrs.className;
63314
+ nodeContent.setAttribute("data-id", attrs.dataId);
63315
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
63316
+ const currentPos = this.getPos();
63317
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
63318
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
63319
+ Object.assign(nodeContent.style, styles);
63320
+ nodeContent.appendChild(content);
63321
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
63322
+ if (value) nodeContent.setAttribute(key2, value);
63323
+ });
63324
+ return nodeContent;
63325
+ };
63326
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
63327
+ setTimeout(() => {
63328
+ const { state: state2 } = this.editor;
63329
+ const { dispatch } = this.view;
63330
+ const node = state2.doc.nodeAt(pos);
63331
+ if (!node || node.isText) return;
63332
+ const currentMarks = node.attrs.marksAsAttrs || [];
63333
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
63334
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
63335
+ if (isEqual) return;
63336
+ const newAttrs = {
63337
+ ...node.attrs,
63338
+ marksAsAttrs: newMarks
63339
+ };
63340
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
63341
+ dispatch(tr);
63342
+ }, 0);
63343
+ };
63344
+ const getMarksFromNeighbors = (currentPos, view) => {
63345
+ const $pos = view.state.doc.resolve(currentPos);
63346
+ const styles = {};
63347
+ const marks = [];
63348
+ const before = $pos.nodeBefore;
63349
+ if (before) {
63350
+ Object.assign(styles, processMarks(before.marks));
63351
+ marks.push(...before.marks);
63352
+ }
63353
+ const after = $pos.nodeAfter;
63354
+ if (after) {
63355
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
63356
+ marks.push(...after.marks);
63357
+ }
63358
+ return {
63359
+ styles,
63360
+ marks
63361
+ };
63362
+ };
63363
+ const processMarks = (marks) => {
63364
+ const styles = {};
63365
+ marks.forEach((mark) => {
63366
+ const { type: type2, attrs } = mark;
63367
+ switch (type2.name) {
63368
+ case "textStyle":
63369
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
63370
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
63371
+ if (attrs.color) styles["color"] = attrs.color;
63372
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
63373
+ break;
63374
+ case "bold":
63375
+ styles["font-weight"] = "bold";
63376
+ break;
63377
+ case "italic":
63378
+ styles["font-style"] = "italic";
63379
+ break;
63380
+ case "underline":
63381
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
63382
+ break;
63383
+ case "strike":
63384
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
63385
+ break;
63386
+ default:
63387
+ if (attrs?.style) {
63388
+ Object.entries(attrs.style).forEach(([key2, value]) => {
63389
+ styles[key2] = value;
63390
+ });
63391
+ }
63392
+ break;
63393
+ }
63394
+ });
63395
+ return styles;
63396
+ };
63397
+ const ShapeContainer = Node$1.create({
63398
+ name: "shapeContainer",
63399
+ group: "block",
63400
+ content: "block+",
62876
63401
  isolating: true,
62877
- atom: false,
62878
- // false - has editable content.
62879
- draggable: true,
62880
63402
  addOptions() {
62881
63403
  return {
62882
63404
  htmlAttributes: {
62883
- class: structuredContentClass,
62884
- "aria-label": "Structured content block node"
63405
+ class: "sd-editor-shape-container",
63406
+ "aria-label": "Shape container node"
62885
63407
  }
62886
63408
  };
62887
63409
  },
62888
63410
  addAttributes() {
62889
63411
  return {
62890
- id: {
63412
+ fillcolor: {
63413
+ renderDOM: (attrs) => {
63414
+ if (!attrs.fillcolor) return {};
63415
+ return {
63416
+ style: `background-color: ${attrs.fillcolor}`
63417
+ };
63418
+ }
63419
+ },
63420
+ sdBlockId: {
62891
63421
  default: null,
62892
- parseDOM: (elem) => elem.getAttribute("data-id"),
63422
+ keepOnSplit: false,
63423
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62893
63424
  renderDOM: (attrs) => {
62894
- if (!attrs.id) return {};
62895
- return { "data-id": attrs.id };
63425
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62896
63426
  }
62897
63427
  },
62898
- sdtPr: {
63428
+ style: {
63429
+ renderDOM: (attrs) => {
63430
+ if (!attrs.style) return {};
63431
+ return {
63432
+ style: attrs.style
63433
+ };
63434
+ }
63435
+ },
63436
+ wrapAttributes: {
63437
+ rendered: false
63438
+ },
63439
+ attributes: {
62899
63440
  rendered: false
62900
63441
  }
62901
63442
  };
62902
63443
  },
62903
63444
  parseDOM() {
62904
- return [{ tag: "div[data-structured-content-block]" }];
63445
+ return [
63446
+ {
63447
+ tag: `div[data-type="${this.name}"]`
63448
+ }
63449
+ ];
62905
63450
  },
62906
63451
  renderDOM({ htmlAttributes }) {
62907
63452
  return [
62908
63453
  "div",
62909
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62910
- "data-structured-content-block": ""
62911
- }),
63454
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62912
63455
  0
62913
63456
  ];
62914
- },
62915
- addNodeView() {
62916
- return (props) => {
62917
- return new StructuredContentBlockView({ ...props });
62918
- };
62919
63457
  }
62920
63458
  });
62921
- class DocumentSectionView {
62922
- constructor(node, getPos, decorations, editor) {
62923
- __privateAdd$1(this, _DocumentSectionView_instances);
62924
- this.node = node;
62925
- this.editor = editor;
62926
- this.decorations = decorations;
62927
- this.view = editor.view;
62928
- this.getPos = getPos;
62929
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
62930
- }
62931
- }
62932
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
62933
- init_fn3 = function() {
62934
- const { attrs } = this.node;
62935
- const { id, title, description } = attrs;
62936
- this.dom = document.createElement("div");
62937
- this.dom.className = "sd-document-section-block";
62938
- this.dom.setAttribute("data-id", id);
62939
- this.dom.setAttribute("data-title", title);
62940
- this.dom.setAttribute("data-description", description);
62941
- this.dom.setAttribute("aria-label", "Document section");
62942
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
62943
- this.contentDOM = document.createElement("div");
62944
- this.contentDOM.className = "sd-document-section-block-content";
62945
- this.contentDOM.setAttribute("contenteditable", "true");
62946
- this.dom.appendChild(this.contentDOM);
62947
- };
62948
- addToolTip_fn = function() {
62949
- const { title } = this.node.attrs;
62950
- this.infoDiv = document.createElement("div");
62951
- this.infoDiv.className = "sd-document-section-block-info";
62952
- const textSpan = document.createElement("span");
62953
- textSpan.textContent = title || "Document section";
62954
- this.infoDiv.appendChild(textSpan);
62955
- this.infoDiv.setAttribute("contenteditable", "false");
62956
- this.dom.appendChild(this.infoDiv);
62957
- };
62958
- const getAllSections = (editor) => {
62959
- if (!editor) return [];
62960
- const type2 = editor.schema.nodes.documentSection;
62961
- if (!type2) return [];
62962
- const sections = [];
62963
- const { state: state2 } = editor;
62964
- state2.doc.descendants((node, pos) => {
62965
- if (node.type.name === type2.name) {
62966
- sections.push({ node, pos });
62967
- }
62968
- });
62969
- return sections;
62970
- };
62971
- const exportSectionsToHTML = (editor) => {
62972
- const sections = getAllSections(editor);
62973
- const processedSections = /* @__PURE__ */ new Set();
62974
- const result = [];
62975
- sections.forEach(({ node }) => {
62976
- const { attrs } = node;
62977
- const { id, title, description } = attrs;
62978
- if (processedSections.has(id)) return;
62979
- processedSections.add(id);
62980
- const html = getHTMLFromNode(node, editor);
62981
- result.push({
62982
- id,
62983
- title,
62984
- description,
62985
- html
62986
- });
62987
- });
62988
- return result;
62989
- };
62990
- const getHTMLFromNode = (node, editor) => {
62991
- const tempDocument = document.implementation.createHTMLDocument();
62992
- const container = tempDocument.createElement("div");
62993
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
62994
- container.appendChild(fragment);
62995
- let html = container.innerHTML;
62996
- return html;
62997
- };
62998
- const exportSectionsToJSON = (editor) => {
62999
- const sections = getAllSections(editor);
63000
- const processedSections = /* @__PURE__ */ new Set();
63001
- const result = [];
63002
- sections.forEach(({ node }) => {
63003
- const { attrs } = node;
63004
- const { id, title, description } = attrs;
63005
- if (processedSections.has(id)) return;
63006
- processedSections.add(id);
63007
- result.push({
63008
- id,
63009
- title,
63010
- description,
63011
- content: node.toJSON()
63012
- });
63013
- });
63014
- return result;
63015
- };
63016
- const getLinkedSectionEditor = (id, options, editor) => {
63017
- const sections = getAllSections(editor);
63018
- const section = sections.find((s) => s.node.attrs.id === id);
63019
- if (!section) return null;
63020
- const child = editor.createChildEditor({
63021
- ...options,
63022
- onUpdate: ({ editor: childEditor, transaction }) => {
63023
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
63024
- if (isFromtLinkedParent) return;
63025
- const updatedContent = childEditor.state.doc.content;
63026
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
63027
- if (!sectionNode) return;
63028
- const { pos, node } = sectionNode;
63029
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
63030
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
63031
- tr.setMeta("fromLinkedChild", true);
63032
- editor.view.dispatch(tr);
63033
- }
63034
- });
63035
- editor.on("update", ({ transaction }) => {
63036
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
63037
- if (isFromLinkedChild) return;
63038
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
63039
- if (!sectionNode) return;
63040
- const sectionContent = sectionNode.node.content;
63041
- const json = {
63042
- type: "doc",
63043
- content: sectionContent.content.map((node) => node.toJSON())
63044
- };
63045
- const childTr = child.state.tr;
63046
- childTr.setMeta("fromLinkedParent", true);
63047
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
63048
- child.view.dispatch(childTr);
63049
- });
63050
- return child;
63051
- };
63052
- const SectionHelpers = {
63053
- getAllSections,
63054
- exportSectionsToHTML,
63055
- exportSectionsToJSON,
63056
- getLinkedSectionEditor
63057
- };
63058
- const DocumentSection = Node$1.create({
63059
- name: "documentSection",
63459
+ const ShapeTextbox = Node$1.create({
63460
+ name: "shapeTextbox",
63060
63461
  group: "block",
63061
- content: "block*",
63062
- atom: true,
63462
+ content: "paragraph* block*",
63063
63463
  isolating: true,
63064
63464
  addOptions() {
63065
63465
  return {
63066
63466
  htmlAttributes: {
63067
- class: "sd-document-section-block",
63068
- "aria-label": "Structured content block"
63467
+ class: "sd-editor-shape-textbox",
63468
+ "aria-label": "Shape textbox node"
63069
63469
  }
63070
63470
  };
63071
63471
  },
63072
- parseDOM() {
63073
- return [
63074
- {
63075
- tag: "div.sd-document-section-block",
63076
- priority: 60
63077
- }
63078
- ];
63079
- },
63080
- renderDOM({ htmlAttributes }) {
63081
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
63082
- },
63083
63472
  addAttributes() {
63084
63473
  return {
63085
- id: {},
63086
63474
  sdBlockId: {
63087
63475
  default: null,
63088
63476
  keepOnSplit: false,
@@ -63091,212 +63479,131 @@ const DocumentSection = Node$1.create({
63091
63479
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
63092
63480
  }
63093
63481
  },
63094
- title: {},
63095
- description: {},
63096
- sectionType: {},
63097
- isLocked: { default: false }
63482
+ attributes: {
63483
+ rendered: false
63484
+ }
63098
63485
  };
63099
63486
  },
63100
- addNodeView() {
63101
- return ({ node, editor, getPos, decorations }) => {
63102
- return new DocumentSectionView(node, getPos, decorations, editor);
63487
+ parseDOM() {
63488
+ return [
63489
+ {
63490
+ tag: `div[data-type="${this.name}"]`
63491
+ }
63492
+ ];
63493
+ },
63494
+ renderDOM({ htmlAttributes }) {
63495
+ return [
63496
+ "div",
63497
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
63498
+ 0
63499
+ ];
63500
+ }
63501
+ });
63502
+ const ContentBlock = Node$1.create({
63503
+ name: "contentBlock",
63504
+ group: "inline",
63505
+ content: "",
63506
+ isolating: true,
63507
+ atom: true,
63508
+ inline: true,
63509
+ addOptions() {
63510
+ return {
63511
+ htmlAttributes: {
63512
+ contenteditable: false
63513
+ }
63103
63514
  };
63104
63515
  },
63105
- addCommands() {
63516
+ addAttributes() {
63106
63517
  return {
63107
- /**
63108
- * Create a lockable content section
63109
- * @category Command
63110
- * @param {SectionCreate} [options={}] - Section configuration
63111
- * @example
63112
- * editor.commands.createDocumentSection({
63113
- * id: 1,
63114
- * title: 'Terms & Conditions',
63115
- * isLocked: true,
63116
- * html: '<p>Legal content...</p>'
63117
- * })
63118
- */
63119
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
63120
- const { selection } = state2;
63121
- let { from: from2, to } = selection;
63122
- let content = selection.content().content;
63123
- const { html: optionsHTML, json: optionsJSON } = options;
63124
- if (optionsHTML) {
63125
- const html = htmlHandler(optionsHTML, this.editor);
63126
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
63127
- content = doc2.content;
63128
- }
63129
- if (optionsJSON) {
63130
- content = this.editor.schema.nodeFromJSON(optionsJSON);
63131
- }
63132
- if (!content?.content?.length) {
63133
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
63134
- }
63135
- if (!options.id) {
63136
- const allSections = SectionHelpers.getAllSections(editor);
63137
- options.id = allSections.length + 1;
63138
- }
63139
- if (!options.title) {
63140
- options.title = "Document section";
63141
- }
63142
- const node = this.type.createAndFill(options, content);
63143
- if (!node) return false;
63144
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
63145
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
63146
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
63147
- from2 = insertPos2;
63148
- to = insertPos2;
63149
- }
63150
- tr.replaceRangeWith(from2, to, node);
63151
- const nodeEnd = from2 + node.nodeSize;
63152
- let shouldInsertParagraph = true;
63153
- let insertPos = nodeEnd;
63154
- if (nodeEnd >= tr.doc.content.size) {
63155
- insertPos = tr.doc.content.size;
63156
- if (insertPos > 0) {
63157
- const $endPos = tr.doc.resolve(insertPos);
63158
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
63159
- shouldInsertParagraph = false;
63160
- }
63161
- }
63162
- }
63163
- if (shouldInsertParagraph) {
63164
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
63165
- tr.insert(insertPos, emptyParagraph);
63166
- }
63167
- if (dispatch) {
63168
- tr.setMeta("documentSection", { action: "create" });
63169
- dispatch(tr);
63170
- setTimeout(() => {
63171
- try {
63172
- const currentState = editor.state;
63173
- const docSize = currentState.doc.content.size;
63174
- let targetPos = from2 + node.nodeSize;
63175
- if (shouldInsertParagraph) {
63176
- targetPos += 1;
63177
- }
63178
- targetPos = Math.min(targetPos, docSize);
63179
- if (targetPos < docSize && targetPos > 0) {
63180
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
63181
- const newTr = currentState.tr.setSelection(newSelection);
63182
- editor.view.dispatch(newTr);
63183
- }
63184
- } catch (e) {
63185
- console.warn("Could not set delayed selection:", e);
63186
- }
63187
- }, 0);
63518
+ horizontalRule: {
63519
+ default: false,
63520
+ renderDOM: ({ horizontalRule }) => {
63521
+ if (!horizontalRule) return {};
63522
+ return { "data-horizontal-rule": "true" };
63188
63523
  }
63189
- return true;
63190
63524
  },
63191
- /**
63192
- * Remove section wrapper at cursor, preserving its content
63193
- * @category Command
63194
- * @example
63195
- * editor.commands.removeSectionAtSelection()
63196
- * @note Content stays in document, only section wrapper is removed
63197
- */
63198
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
63199
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
63200
- if (!sdtNode) return false;
63201
- const { node, pos } = sdtNode;
63202
- const nodeStart = pos;
63203
- const nodeEnd = nodeStart + node.nodeSize;
63204
- const contentToPreserve = node.content;
63205
- tr.delete(nodeStart, nodeEnd);
63206
- if (contentToPreserve.size > 0) {
63207
- tr.insert(nodeStart, contentToPreserve);
63208
- }
63209
- const newPos = Math.min(nodeStart, tr.doc.content.size);
63210
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
63211
- if (dispatch) {
63212
- tr.setMeta("documentSection", { action: "delete" });
63213
- dispatch(tr);
63525
+ size: {
63526
+ default: null,
63527
+ renderDOM: ({ size: size2 }) => {
63528
+ if (!size2) return {};
63529
+ let style2 = "";
63530
+ if (size2.top) style2 += `top: ${size2.top}px; `;
63531
+ if (size2.left) style2 += `left: ${size2.left}px; `;
63532
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
63533
+ if (size2.height)
63534
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
63535
+ return { style: style2 };
63214
63536
  }
63215
- return true;
63216
63537
  },
63217
- /**
63218
- * Delete section and all its content
63219
- * @category Command
63220
- * @param {number} id - Section to delete
63221
- * @example
63222
- * editor.commands.removeSectionById(123)
63223
- */
63224
- removeSectionById: (id) => ({ tr, dispatch }) => {
63225
- const sections = SectionHelpers.getAllSections(this.editor);
63226
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
63227
- if (!sectionToRemove) return false;
63228
- const { pos, node } = sectionToRemove;
63229
- const nodeStart = pos;
63230
- const nodeEnd = nodeStart + node.nodeSize;
63231
- tr.delete(nodeStart, nodeEnd);
63232
- if (dispatch) {
63233
- tr.setMeta("documentSection", { action: "delete", id });
63234
- dispatch(tr);
63538
+ background: {
63539
+ default: null,
63540
+ renderDOM: (attrs) => {
63541
+ if (!attrs.background) return {};
63542
+ return {
63543
+ style: `background-color: ${attrs.background}`
63544
+ };
63235
63545
  }
63236
- return true;
63237
63546
  },
63547
+ drawingContent: {
63548
+ rendered: false
63549
+ },
63550
+ attributes: {
63551
+ rendered: false
63552
+ }
63553
+ };
63554
+ },
63555
+ parseDOM() {
63556
+ return [
63557
+ {
63558
+ tag: `div[data-type="${this.name}"]`
63559
+ }
63560
+ ];
63561
+ },
63562
+ renderDOM({ htmlAttributes }) {
63563
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63564
+ },
63565
+ addCommands() {
63566
+ return {
63238
63567
  /**
63239
- * Lock section against edits
63568
+ * Insert a horizontal rule
63240
63569
  * @category Command
63241
- * @param {number} id - Section to lock
63242
63570
  * @example
63243
- * editor.commands.lockSectionById(123)
63571
+ * editor.commands.insertHorizontalRule()
63572
+ * @note Creates a visual separator between content sections
63244
63573
  */
63245
- lockSectionById: (id) => ({ tr, dispatch }) => {
63246
- const sections = SectionHelpers.getAllSections(this.editor);
63247
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
63248
- if (!sectionToLock) return false;
63249
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
63250
- if (dispatch) {
63251
- tr.setMeta("documentSection", { action: "lock", id });
63252
- dispatch(tr);
63253
- }
63254
- return true;
63574
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
63575
+ return commands2.insertContent({
63576
+ type: this.name,
63577
+ attrs: {
63578
+ horizontalRule: true,
63579
+ size: { width: "100%", height: 2 },
63580
+ background: "#e5e7eb"
63581
+ }
63582
+ });
63255
63583
  },
63256
63584
  /**
63257
- * Modify section attributes or content
63585
+ * Insert a content block
63258
63586
  * @category Command
63259
- * @param {SectionUpdate} options - Changes to apply
63587
+ * @param {ContentBlockConfig} config - Block configuration
63260
63588
  * @example
63261
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
63262
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
63263
- * editor.commands.updateSectionById({
63264
- * id: 123,
63265
- * html: '<p>Updated</p>',
63266
- * attrs: { title: 'New Title' }
63589
+ * // Insert a spacer block
63590
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
63591
+ *
63592
+ * @example
63593
+ * // Insert a colored divider
63594
+ * editor.commands.insertContentBlock({
63595
+ * size: { width: '50%', height: 3 },
63596
+ * background: '#3b82f6'
63267
63597
  * })
63598
+ * @note Used for spacing, dividers, and special inline content
63268
63599
  */
63269
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
63270
- const sections = SectionHelpers.getAllSections(editor || this.editor);
63271
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
63272
- if (!sectionToUpdate) return false;
63273
- const { pos, node } = sectionToUpdate;
63274
- let newContent = null;
63275
- if (html) {
63276
- const htmlDoc = htmlHandler(html, editor || this.editor);
63277
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
63278
- newContent = doc2.content;
63279
- }
63280
- if (json) {
63281
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
63282
- }
63283
- if (!newContent) {
63284
- newContent = node.content;
63285
- }
63286
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
63287
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
63288
- if (dispatch) {
63289
- tr.setMeta("documentSection", { action: "update", id, attrs });
63290
- dispatch(tr);
63291
- }
63292
- return true;
63600
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
63601
+ return commands2.insertContent({
63602
+ type: this.name,
63603
+ attrs: config2
63604
+ });
63293
63605
  }
63294
63606
  };
63295
- },
63296
- addHelpers() {
63297
- return {
63298
- ...SectionHelpers
63299
- };
63300
63607
  }
63301
63608
  });
63302
63609
  const { findChildren } = helpers;
@@ -70146,6 +70453,7 @@ const getStarterExtensions = () => {
70146
70453
  Search,
70147
70454
  StructuredContent,
70148
70455
  StructuredContentBlock,
70456
+ StructuredContentCommands,
70149
70457
  DocumentSection,
70150
70458
  NodeResizer,
70151
70459
  CustomSelection,