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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/chunks/{PdfViewer-HN-tp5RN.es.js → PdfViewer-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-DyWe33pN.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BeVpZc19.cjs → index-BbGPYtNy.cjs} +2 -2
  4. package/dist/chunks/{index-ir6efMuz.es.js → index-DWKEKmiB.es.js} +2 -2
  5. package/dist/chunks/{super-editor.es-BwqYS285.es.js → super-editor.es-BVxfhpAJ.es.js} +1581 -1274
  6. package/dist/chunks/{super-editor.es-CKfdmK-8.cjs → super-editor.es-BoUJEZaF.cjs} +1581 -1274
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/style.css +1 -0
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-C-yWLpFM.js} +150 -105
  11. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CFqh_xBx.js → editor-BBnC1DzI.js} +1436 -1172
  13. package/dist/super-editor/chunks/{toolbar-DdfyWgZF.js → toolbar-QJANo61B.js} +2 -2
  14. package/dist/super-editor/converter.es.js +1 -1
  15. package/dist/super-editor/docx-zipper.es.js +2 -2
  16. package/dist/super-editor/editor.es.js +3 -3
  17. package/dist/super-editor/file-zipper.es.js +1 -1
  18. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  19. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  20. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  21. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  22. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  23. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  27. package/dist/super-editor/style.css +1 -0
  28. package/dist/super-editor/super-editor.es.js +7 -7
  29. package/dist/super-editor/toolbar.es.js +2 -2
  30. package/dist/super-editor.cjs +1 -1
  31. package/dist/super-editor.es.js +1 -1
  32. package/dist/superdoc.cjs +2 -2
  33. package/dist/superdoc.es.js +2 -2
  34. package/dist/superdoc.umd.js +1581 -1274
  35. package/dist/superdoc.umd.js.map +1 -1
  36. package/package.json +1 -1
@@ -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
  };
@@ -33079,7 +33122,7 @@ const DEFAULT_SECTION_PROPS = Object.freeze({
33079
33122
  gutter: "0"
33080
33123
  })
33081
33124
  });
33082
- function ensureSectionProperties(bodyNode, converter) {
33125
+ function ensureSectionProperties(bodyNode) {
33083
33126
  if (!bodyNode.elements) bodyNode.elements = [];
33084
33127
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
33085
33128
  if (!sectPr) {
@@ -36578,7 +36621,7 @@ var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "rea
36578
36621
  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
36622
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
36580
36623
  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;
36624
+ 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
36625
  var GOOD_LEAF_SIZE = 200;
36583
36626
  var RopeSequence = function RopeSequence2() {
36584
36627
  };
@@ -48653,7 +48696,7 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
48653
48696
  if (emitParams) editor.emit("commentsUpdate", emitParams);
48654
48697
  return newTrackedChanges;
48655
48698
  };
48656
- const getTrackedChangeText = ({ state: state2, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
48699
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
48657
48700
  let trackedChangeText = "";
48658
48701
  let deletionText = "";
48659
48702
  if (trackedChangeType === TrackInsertMarkName) {
@@ -48695,10 +48738,8 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
48695
48738
  if (hasMatchingId) nodesWithMark.push(node2);
48696
48739
  });
48697
48740
  const { deletionText, trackedChangeText } = getTrackedChangeText({
48698
- state: newEditorState,
48699
48741
  nodes: nodesWithMark.length ? nodesWithMark : [node],
48700
48742
  mark: trackedMark,
48701
- marks,
48702
48743
  trackedChangeType,
48703
48744
  isDeletionInsertion
48704
48745
  });
@@ -53353,228 +53394,1223 @@ const SlashMenu = Extension.create({
53353
53394
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
53354
53395
  }
53355
53396
  });
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>");
53397
+ class StructuredContentViewBase {
53398
+ constructor(props) {
53399
+ __publicField$1(this, "node");
53400
+ __publicField$1(this, "view");
53401
+ __publicField$1(this, "getPos");
53402
+ __publicField$1(this, "decorations");
53403
+ __publicField$1(this, "innerDecorations");
53404
+ __publicField$1(this, "editor");
53405
+ __publicField$1(this, "extension");
53406
+ __publicField$1(this, "htmlAttributes");
53407
+ __publicField$1(this, "root");
53408
+ __publicField$1(this, "isDragging", false);
53409
+ this.node = props.node;
53410
+ this.view = props.editor.view;
53411
+ this.getPos = props.getPos;
53412
+ this.decorations = props.decorations;
53413
+ this.innerDecorations = props.innerDecorations;
53414
+ this.editor = props.editor;
53415
+ this.extension = props.extension;
53416
+ this.htmlAttributes = props.htmlAttributes;
53417
+ this.mount(props);
53418
+ }
53419
+ mount() {
53420
+ return;
53421
+ }
53422
+ get dom() {
53423
+ return this.root;
53424
+ }
53425
+ get contentDOM() {
53426
+ return null;
53427
+ }
53428
+ update(node, decorations, innerDecorations) {
53429
+ if (node.type !== this.node.type) {
53430
+ return false;
53431
+ }
53432
+ this.node = node;
53433
+ this.decorations = decorations;
53434
+ this.innerDecorations = innerDecorations;
53435
+ this.updateHTMLAttributes();
53436
+ return true;
53437
+ }
53438
+ stopEvent(event) {
53439
+ if (!this.dom) return false;
53440
+ const target = event.target;
53441
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
53442
+ if (!isInElement) return false;
53443
+ const isDragEvent = event.type.startsWith("drag");
53444
+ const isDropEvent = event.type === "drop";
53445
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
53446
+ if (isInput && !isDropEvent && !isDragEvent) return true;
53447
+ const { isEditable } = this.editor;
53448
+ const { isDragging } = this;
53449
+ const isDraggable = !!this.node.type.spec.draggable;
53450
+ const isSelectable = NodeSelection.isSelectable(this.node);
53451
+ const isCopyEvent = event.type === "copy";
53452
+ const isPasteEvent = event.type === "paste";
53453
+ const isCutEvent = event.type === "cut";
53454
+ const isClickEvent = event.type === "mousedown";
53455
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
53456
+ event.preventDefault();
53457
+ }
53458
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
53459
+ event.preventDefault();
53460
+ return false;
53461
+ }
53462
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
53463
+ const dragHandle = target.closest("[data-drag-handle]");
53464
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
53465
+ if (isValidDragHandle) {
53466
+ this.isDragging = true;
53467
+ document.addEventListener(
53468
+ "dragend",
53469
+ () => {
53470
+ this.isDragging = false;
53471
+ },
53472
+ { once: true }
53473
+ );
53474
+ document.addEventListener(
53475
+ "drop",
53476
+ () => {
53477
+ this.isDragging = false;
53478
+ },
53479
+ { once: true }
53480
+ );
53481
+ document.addEventListener(
53482
+ "mouseup",
53483
+ () => {
53484
+ this.isDragging = false;
53485
+ },
53486
+ { once: true }
53487
+ );
53405
53488
  }
53406
- };
53489
+ }
53490
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
53491
+ return false;
53492
+ }
53493
+ return true;
53407
53494
  }
53408
- });
53409
- const Text = Node$1.create({
53410
- name: "text",
53411
- group: "inline",
53412
- inline: true,
53413
- addOptions() {
53414
- return {};
53495
+ ignoreMutation(mutation) {
53496
+ if (!this.dom || !this.contentDOM) return true;
53497
+ if (this.node.isLeaf || this.node.isAtom) return true;
53498
+ if (mutation.type === "selection") return false;
53499
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
53500
+ if (this.contentDOM.contains(mutation.target)) return false;
53501
+ return true;
53415
53502
  }
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) => {
53503
+ destroy() {
53504
+ this.dom.remove();
53505
+ this.contentDOM?.remove();
53506
+ }
53507
+ updateAttributes(attrs) {
53508
+ const pos = this.getPos();
53509
+ if (typeof pos !== "number") {
53510
+ return;
53511
+ }
53512
+ return this.view.dispatch(
53513
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
53514
+ ...this.node.attrs,
53515
+ ...attrs
53516
+ })
53517
+ );
53518
+ }
53519
+ updateHTMLAttributes() {
53520
+ const { extensionService } = this.editor;
53521
+ const { attributes } = extensionService;
53522
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
53523
+ this.htmlAttributes = Attribute2.getAttributesToRender(this.node, extensionAttrs);
53524
+ }
53525
+ createDragHandle() {
53526
+ const dragHandle = document.createElement("span");
53527
+ dragHandle.classList.add("sd-structured-content-draggable");
53528
+ dragHandle.draggable = true;
53529
+ dragHandle.contentEditable = "false";
53530
+ dragHandle.dataset.dragHandle = "";
53531
+ const textElement = document.createElement("span");
53532
+ textElement.textContent = this.node.attrs.alias || "Structured content";
53533
+ dragHandle.append(textElement);
53534
+ return dragHandle;
53535
+ }
53536
+ onDragStart(event) {
53537
+ const { view } = this.editor;
53538
+ const target = event.target;
53539
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
53540
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
53541
+ return;
53542
+ }
53543
+ let x = 0;
53544
+ let y2 = 0;
53545
+ if (this.dom !== dragHandle) {
53546
+ const domBox = this.dom.getBoundingClientRect();
53547
+ const handleBox = dragHandle.getBoundingClientRect();
53548
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
53549
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
53550
+ x = handleBox.x - domBox.x + offsetX;
53551
+ y2 = handleBox.y - domBox.y + offsetY;
53552
+ }
53553
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
53554
+ const pos = this.getPos();
53555
+ if (typeof pos !== "number") {
53556
+ return;
53557
+ }
53558
+ const selection = NodeSelection.create(view.state.doc, pos);
53559
+ const transaction = view.state.tr.setSelection(selection);
53423
53560
  view.dispatch(transaction);
53424
- });
53425
- if (handled) {
53426
- tr.setMeta("preventDispatch", true);
53427
53561
  }
53428
- return handled;
53429
- };
53430
- const Run = OxmlNode.create({
53431
- name: "run",
53432
- oXmlName: "w:r",
53433
- group: "inline",
53562
+ }
53563
+ class StructuredContentInlineView extends StructuredContentViewBase {
53564
+ constructor(props) {
53565
+ super(props);
53566
+ }
53567
+ mount() {
53568
+ this.buildView();
53569
+ }
53570
+ get contentDOM() {
53571
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
53572
+ return contentElement || null;
53573
+ }
53574
+ createElement() {
53575
+ const element = document.createElement("span");
53576
+ element.classList.add(structuredContentClass$1);
53577
+ element.setAttribute("data-structured-content", "");
53578
+ const contentElement = document.createElement("span");
53579
+ contentElement.classList.add(structuredContentInnerClass$1);
53580
+ element.append(contentElement);
53581
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53582
+ updateDOMAttributes(element, { ...domAttrs });
53583
+ return { element, contentElement };
53584
+ }
53585
+ buildView() {
53586
+ const { element } = this.createElement();
53587
+ const dragHandle = this.createDragHandle();
53588
+ element.prepend(dragHandle);
53589
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53590
+ this.root = element;
53591
+ }
53592
+ updateView() {
53593
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53594
+ updateDOMAttributes(this.dom, { ...domAttrs });
53595
+ }
53596
+ update(node, decorations, innerDecorations) {
53597
+ const result = super.update(node, decorations, innerDecorations);
53598
+ if (!result) return false;
53599
+ this.updateView();
53600
+ return true;
53601
+ }
53602
+ }
53603
+ const structuredContentClass$1 = "sd-structured-content";
53604
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
53605
+ const StructuredContent = Node$1.create({
53606
+ name: "structuredContent",
53607
+ group: "inline structuredContent",
53434
53608
  inline: true,
53435
53609
  content: "inline*",
53436
- selectable: false,
53437
- childToAttributes: ["runProperties"],
53610
+ isolating: true,
53611
+ atom: false,
53612
+ // false - has editable content.
53613
+ draggable: true,
53438
53614
  addOptions() {
53439
53615
  return {
53440
53616
  htmlAttributes: {
53441
- "data-run": "1"
53617
+ class: structuredContentClass$1,
53618
+ "aria-label": "Structured content node"
53442
53619
  }
53443
53620
  };
53444
53621
  },
53445
53622
  addAttributes() {
53446
53623
  return {
53447
- runProperties: {
53624
+ id: {
53448
53625
  default: null,
53449
- rendered: false,
53450
- keepOnSplit: true
53626
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53627
+ renderDOM: (attrs) => {
53628
+ if (!attrs.id) return {};
53629
+ return { "data-id": attrs.id };
53630
+ }
53451
53631
  },
53452
- rsidR: {
53632
+ tag: {
53453
53633
  default: null,
53454
- rendered: false,
53455
- keepOnSplit: true
53634
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53635
+ renderDOM: (attrs) => {
53636
+ if (!attrs.tag) return {};
53637
+ return { "data-tag": attrs.tag };
53638
+ }
53456
53639
  },
53457
- rsidRPr: {
53640
+ alias: {
53458
53641
  default: null,
53459
- rendered: false,
53460
- keepOnSplit: true
53642
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53643
+ renderDOM: (attrs) => {
53644
+ if (!attrs.alias) return {};
53645
+ return { "data-alias": attrs.alias };
53646
+ }
53461
53647
  },
53462
- rsidDel: {
53463
- default: null,
53464
- rendered: false,
53465
- keepOnSplit: true
53648
+ sdtPr: {
53649
+ rendered: false
53466
53650
  }
53467
53651
  };
53468
53652
  },
53469
- addCommands() {
53470
- return {
53471
- splitRun
53472
- };
53473
- },
53474
53653
  parseDOM() {
53475
- return [{ tag: "span[data-run]" }];
53654
+ return [{ tag: "span[data-structured-content]" }];
53476
53655
  },
53477
53656
  renderDOM({ htmlAttributes }) {
53478
- const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
53479
- return ["span", base2, 0];
53657
+ return [
53658
+ "span",
53659
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53660
+ "data-structured-content": ""
53661
+ }),
53662
+ 0
53663
+ ];
53664
+ },
53665
+ addNodeView() {
53666
+ return (props) => {
53667
+ return new StructuredContentInlineView({ ...props });
53668
+ };
53480
53669
  }
53481
53670
  });
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
- },
53671
+ class StructuredContentBlockView extends StructuredContentViewBase {
53672
+ constructor(props) {
53673
+ super(props);
53674
+ }
53675
+ mount() {
53676
+ this.buildView();
53677
+ }
53678
+ get contentDOM() {
53679
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
53680
+ return contentElement || null;
53681
+ }
53682
+ createElement() {
53683
+ const element = document.createElement("div");
53684
+ element.classList.add(structuredContentClass);
53685
+ element.setAttribute("data-structured-content-block", "");
53686
+ const contentElement = document.createElement("div");
53687
+ contentElement.classList.add(structuredContentInnerClass);
53688
+ element.append(contentElement);
53689
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53690
+ updateDOMAttributes(element, { ...domAttrs });
53691
+ return { element, contentElement };
53692
+ }
53693
+ buildView() {
53694
+ const { element } = this.createElement();
53695
+ const dragHandle = this.createDragHandle();
53696
+ element.prepend(dragHandle);
53697
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
53698
+ this.root = element;
53699
+ }
53700
+ updateView() {
53701
+ const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
53702
+ updateDOMAttributes(this.dom, { ...domAttrs });
53703
+ }
53704
+ update(node, decorations, innerDecorations) {
53705
+ const result = super.update(node, decorations, innerDecorations);
53706
+ if (!result) return false;
53707
+ this.updateView();
53708
+ return true;
53709
+ }
53710
+ }
53711
+ const structuredContentClass = "sd-structured-content-block";
53712
+ const structuredContentInnerClass = "sd-structured-content-block__content";
53713
+ const StructuredContentBlock = Node$1.create({
53714
+ name: "structuredContentBlock",
53715
+ group: "block structuredContent",
53716
+ content: "block*",
53717
+ isolating: true,
53718
+ atom: false,
53719
+ // false - has editable content.
53720
+ draggable: true,
53490
53721
  addOptions() {
53491
53722
  return {
53492
- itemTypeName: "listItem",
53493
53723
  htmlAttributes: {
53494
- "aria-label": "Bullet list node"
53495
- },
53496
- keepMarks: true,
53497
- keepAttributes: false
53724
+ class: structuredContentClass,
53725
+ "aria-label": "Structured content block node"
53726
+ }
53498
53727
  };
53499
53728
  },
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
53729
  addAttributes() {
53508
53730
  return {
53509
- "list-style-type": {
53510
- default: "bullet",
53511
- rendered: false
53731
+ id: {
53732
+ default: null,
53733
+ parseDOM: (elem) => elem.getAttribute("data-id"),
53734
+ renderDOM: (attrs) => {
53735
+ if (!attrs.id) return {};
53736
+ return { "data-id": attrs.id };
53737
+ }
53512
53738
  },
53513
- listId: {
53514
- rendered: false
53739
+ tag: {
53740
+ default: null,
53741
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
53742
+ renderDOM: (attrs) => {
53743
+ if (!attrs.tag) return {};
53744
+ return { "data-tag": attrs.tag };
53745
+ }
53515
53746
  },
53516
- sdBlockId: {
53747
+ alias: {
53517
53748
  default: null,
53518
- keepOnSplit: false,
53519
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
53749
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
53520
53750
  renderDOM: (attrs) => {
53521
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
53751
+ if (!attrs.alias) return {};
53752
+ return { "data-alias": attrs.alias };
53522
53753
  }
53523
53754
  },
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);
53755
+ sdtPr: {
53756
+ rendered: false
53542
53757
  }
53543
53758
  };
53544
53759
  },
53545
- addShortcuts() {
53546
- return {
53547
- "Mod-Shift-8": () => {
53548
- return this.editor.commands.toggleBulletList();
53549
- }
53550
- };
53760
+ parseDOM() {
53761
+ return [{ tag: "div[data-structured-content-block]" }];
53551
53762
  },
53552
- addInputRules() {
53763
+ renderDOM({ htmlAttributes }) {
53553
53764
  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
- })
53765
+ "div",
53766
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
53767
+ "data-structured-content-block": ""
53768
+ }),
53769
+ 0
53573
53770
  ];
53771
+ },
53772
+ addNodeView() {
53773
+ return (props) => {
53774
+ return new StructuredContentBlockView({ ...props });
53775
+ };
53574
53776
  }
53575
53777
  });
53576
- const inputRegex = /^(\d+)\.\s$/;
53577
- const OrderedList = Node$1.create({
53778
+ function getStructuredContentTagsById(idOrIds, state2) {
53779
+ const result = findChildren$5(state2.doc, (node) => {
53780
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
53781
+ if (Array.isArray(idOrIds)) {
53782
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
53783
+ } else {
53784
+ return isStructuredContent && node.attrs.id === idOrIds;
53785
+ }
53786
+ });
53787
+ return result;
53788
+ }
53789
+ function getStructuredContentTags(state2) {
53790
+ const result = findChildren$5(state2.doc, (node) => {
53791
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
53792
+ });
53793
+ return result;
53794
+ }
53795
+ function getStructuredContentInlineTags(state2) {
53796
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
53797
+ return result;
53798
+ }
53799
+ function getStructuredContentBlockTags(state2) {
53800
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
53801
+ return result;
53802
+ }
53803
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
53804
+ __proto__: null,
53805
+ getStructuredContentBlockTags,
53806
+ getStructuredContentInlineTags,
53807
+ getStructuredContentTags,
53808
+ getStructuredContentTagsById
53809
+ }, Symbol.toStringTag, { value: "Module" }));
53810
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
53811
+ const StructuredContentCommands = Extension.create({
53812
+ name: "structuredContentCommands",
53813
+ addCommands() {
53814
+ return {
53815
+ /**
53816
+ * Inserts a structured content inline at selection.
53817
+ * @category Command
53818
+ * @param {StructuredContentInlineInsert} options
53819
+ */
53820
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53821
+ const { schema } = editor;
53822
+ let { from: from2, to } = state2.selection;
53823
+ if (dispatch) {
53824
+ const selectionText = state2.doc.textBetween(from2, to);
53825
+ let content = null;
53826
+ if (selectionText) {
53827
+ content = schema.text(selectionText);
53828
+ }
53829
+ if (options.text) {
53830
+ content = schema.text(options.text);
53831
+ }
53832
+ if (options.json) {
53833
+ content = schema.nodeFromJSON(options.json);
53834
+ }
53835
+ if (!content) {
53836
+ content = schema.text(" ");
53837
+ }
53838
+ const attrs = {
53839
+ ...options.attrs,
53840
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53841
+ tag: "inline_text_sdt",
53842
+ alias: options.attrs?.alias || "Structured content"
53843
+ };
53844
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
53845
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
53846
+ if (parent) {
53847
+ const insertPos = parent.pos + parent.node.nodeSize;
53848
+ from2 = to = insertPos;
53849
+ }
53850
+ tr.replaceWith(from2, to, node);
53851
+ }
53852
+ return true;
53853
+ },
53854
+ /**
53855
+ * Inserts a structured content block at selection.
53856
+ * @category Command
53857
+ * @param {StructuredContentBlockInsert} options
53858
+ */
53859
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53860
+ const { schema } = editor;
53861
+ let { from: from2, to } = state2.selection;
53862
+ if (dispatch) {
53863
+ const selectionContent = state2.selection.content();
53864
+ let content = null;
53865
+ if (selectionContent.size) {
53866
+ content = selectionContent.content;
53867
+ }
53868
+ if (options.html) {
53869
+ const html = htmlHandler(options.html, editor);
53870
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53871
+ content = doc2.content;
53872
+ }
53873
+ if (options.json) {
53874
+ content = schema.nodeFromJSON(options.json);
53875
+ }
53876
+ if (!content) {
53877
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
53878
+ }
53879
+ const attrs = {
53880
+ ...options.attrs,
53881
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
53882
+ tag: "block_table_sdt",
53883
+ alias: options.attrs?.alias || "Structured content"
53884
+ };
53885
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
53886
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
53887
+ if (parent) {
53888
+ const insertPos = parent.pos + parent.node.nodeSize;
53889
+ from2 = to = insertPos;
53890
+ }
53891
+ tr.replaceRangeWith(from2, to, node);
53892
+ }
53893
+ return true;
53894
+ },
53895
+ /**
53896
+ * Updates a structured content attributes or content.
53897
+ * If the updated node does not match the schema, it will not be updated.
53898
+ * @category Command
53899
+ * @param {string} id
53900
+ * @param {StructuredContentUpdate} options
53901
+ */
53902
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
53903
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
53904
+ if (!structuredContentTags.length) {
53905
+ return true;
53906
+ }
53907
+ const { schema } = editor;
53908
+ if (dispatch) {
53909
+ const structuredContent = structuredContentTags[0];
53910
+ const { pos, node } = structuredContent;
53911
+ const posFrom = pos;
53912
+ const posTo = pos + node.nodeSize;
53913
+ let content = null;
53914
+ if (options.text) {
53915
+ content = schema.text(options.text);
53916
+ }
53917
+ if (options.html) {
53918
+ const html = htmlHandler(options.html, editor);
53919
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
53920
+ content = doc2.content;
53921
+ }
53922
+ if (options.json) {
53923
+ content = schema.nodeFromJSON(options.json);
53924
+ }
53925
+ if (!content) {
53926
+ content = node.content;
53927
+ }
53928
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
53929
+ try {
53930
+ updatedNode.check();
53931
+ } catch {
53932
+ console.error("Updated node does not conform to the schema");
53933
+ return false;
53934
+ }
53935
+ tr.replaceWith(posFrom, posTo, updatedNode);
53936
+ }
53937
+ return true;
53938
+ },
53939
+ /**
53940
+ * Removes a structured content.
53941
+ * @category Command
53942
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
53943
+ */
53944
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
53945
+ if (!structuredContentTags.length) {
53946
+ return true;
53947
+ }
53948
+ if (dispatch) {
53949
+ structuredContentTags.forEach((structuredContent) => {
53950
+ const { pos, node } = structuredContent;
53951
+ const posFrom = tr.mapping.map(pos);
53952
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53953
+ const currentNode = tr.doc.nodeAt(posFrom);
53954
+ if (currentNode && node.eq(currentNode)) {
53955
+ tr.delete(posFrom, posTo);
53956
+ }
53957
+ });
53958
+ }
53959
+ return true;
53960
+ },
53961
+ /**
53962
+ * Removes a structured content by ID.
53963
+ * @category Command
53964
+ * @param {string | string[]} idOrIds
53965
+ */
53966
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
53967
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
53968
+ if (!structuredContentTags.length) {
53969
+ return true;
53970
+ }
53971
+ if (dispatch) {
53972
+ structuredContentTags.forEach((structuredContent) => {
53973
+ const { pos, node } = structuredContent;
53974
+ const posFrom = tr.mapping.map(pos);
53975
+ const posTo = tr.mapping.map(pos + node.nodeSize);
53976
+ const currentNode = tr.doc.nodeAt(posFrom);
53977
+ if (currentNode && node.eq(currentNode)) {
53978
+ tr.delete(posFrom, posTo);
53979
+ }
53980
+ });
53981
+ }
53982
+ return true;
53983
+ },
53984
+ /**
53985
+ * Removes a structured content at cursor, preserving its content.
53986
+ * @category Command
53987
+ */
53988
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
53989
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
53990
+ const structuredContent = findParentNode(predicate)(state2.selection);
53991
+ if (!structuredContent) {
53992
+ return true;
53993
+ }
53994
+ if (dispatch) {
53995
+ const { node, pos } = structuredContent;
53996
+ const posFrom = pos;
53997
+ const posTo = posFrom + node.nodeSize;
53998
+ const content = node.content;
53999
+ tr.replaceWith(posFrom, posTo, content);
54000
+ }
54001
+ return true;
54002
+ }
54003
+ };
54004
+ },
54005
+ addHelpers() {
54006
+ return {
54007
+ ...structuredContentHelpers
54008
+ };
54009
+ }
54010
+ });
54011
+ class DocumentSectionView {
54012
+ constructor(node, getPos, decorations, editor) {
54013
+ __privateAdd$1(this, _DocumentSectionView_instances);
54014
+ this.node = node;
54015
+ this.editor = editor;
54016
+ this.decorations = decorations;
54017
+ this.view = editor.view;
54018
+ this.getPos = getPos;
54019
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
54020
+ }
54021
+ }
54022
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
54023
+ init_fn2 = function() {
54024
+ const { attrs } = this.node;
54025
+ const { id, title, description } = attrs;
54026
+ this.dom = document.createElement("div");
54027
+ this.dom.className = "sd-document-section-block";
54028
+ this.dom.setAttribute("data-id", id);
54029
+ this.dom.setAttribute("data-title", title);
54030
+ this.dom.setAttribute("data-description", description);
54031
+ this.dom.setAttribute("aria-label", "Document section");
54032
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
54033
+ this.contentDOM = document.createElement("div");
54034
+ this.contentDOM.className = "sd-document-section-block-content";
54035
+ this.contentDOM.setAttribute("contenteditable", "true");
54036
+ this.dom.appendChild(this.contentDOM);
54037
+ };
54038
+ addToolTip_fn = function() {
54039
+ const { title } = this.node.attrs;
54040
+ this.infoDiv = document.createElement("div");
54041
+ this.infoDiv.className = "sd-document-section-block-info";
54042
+ const textSpan = document.createElement("span");
54043
+ textSpan.textContent = title || "Document section";
54044
+ this.infoDiv.appendChild(textSpan);
54045
+ this.infoDiv.setAttribute("contenteditable", "false");
54046
+ this.dom.appendChild(this.infoDiv);
54047
+ };
54048
+ const getAllSections = (editor) => {
54049
+ if (!editor) return [];
54050
+ const type2 = editor.schema.nodes.documentSection;
54051
+ if (!type2) return [];
54052
+ const sections = [];
54053
+ const { state: state2 } = editor;
54054
+ state2.doc.descendants((node, pos) => {
54055
+ if (node.type.name === type2.name) {
54056
+ sections.push({ node, pos });
54057
+ }
54058
+ });
54059
+ return sections;
54060
+ };
54061
+ const exportSectionsToHTML = (editor) => {
54062
+ const sections = getAllSections(editor);
54063
+ const processedSections = /* @__PURE__ */ new Set();
54064
+ const result = [];
54065
+ sections.forEach(({ node }) => {
54066
+ const { attrs } = node;
54067
+ const { id, title, description } = attrs;
54068
+ if (processedSections.has(id)) return;
54069
+ processedSections.add(id);
54070
+ const html = getHTMLFromNode(node, editor);
54071
+ result.push({
54072
+ id,
54073
+ title,
54074
+ description,
54075
+ html
54076
+ });
54077
+ });
54078
+ return result;
54079
+ };
54080
+ const getHTMLFromNode = (node, editor) => {
54081
+ const tempDocument = document.implementation.createHTMLDocument();
54082
+ const container = tempDocument.createElement("div");
54083
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
54084
+ container.appendChild(fragment);
54085
+ let html = container.innerHTML;
54086
+ return html;
54087
+ };
54088
+ const exportSectionsToJSON = (editor) => {
54089
+ const sections = getAllSections(editor);
54090
+ const processedSections = /* @__PURE__ */ new Set();
54091
+ const result = [];
54092
+ sections.forEach(({ node }) => {
54093
+ const { attrs } = node;
54094
+ const { id, title, description } = attrs;
54095
+ if (processedSections.has(id)) return;
54096
+ processedSections.add(id);
54097
+ result.push({
54098
+ id,
54099
+ title,
54100
+ description,
54101
+ content: node.toJSON()
54102
+ });
54103
+ });
54104
+ return result;
54105
+ };
54106
+ const getLinkedSectionEditor = (id, options, editor) => {
54107
+ const sections = getAllSections(editor);
54108
+ const section = sections.find((s) => s.node.attrs.id === id);
54109
+ if (!section) return null;
54110
+ const child = editor.createChildEditor({
54111
+ ...options,
54112
+ onUpdate: ({ editor: childEditor, transaction }) => {
54113
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
54114
+ if (isFromtLinkedParent) return;
54115
+ const updatedContent = childEditor.state.doc.content;
54116
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54117
+ if (!sectionNode) return;
54118
+ const { pos, node } = sectionNode;
54119
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
54120
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
54121
+ tr.setMeta("fromLinkedChild", true);
54122
+ editor.view.dispatch(tr);
54123
+ }
54124
+ });
54125
+ editor.on("update", ({ transaction }) => {
54126
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
54127
+ if (isFromLinkedChild) return;
54128
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
54129
+ if (!sectionNode) return;
54130
+ const sectionContent = sectionNode.node.content;
54131
+ const json = {
54132
+ type: "doc",
54133
+ content: sectionContent.content.map((node) => node.toJSON())
54134
+ };
54135
+ const childTr = child.state.tr;
54136
+ childTr.setMeta("fromLinkedParent", true);
54137
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
54138
+ child.view.dispatch(childTr);
54139
+ });
54140
+ return child;
54141
+ };
54142
+ const SectionHelpers = {
54143
+ getAllSections,
54144
+ exportSectionsToHTML,
54145
+ exportSectionsToJSON,
54146
+ getLinkedSectionEditor
54147
+ };
54148
+ const DocumentSection = Node$1.create({
54149
+ name: "documentSection",
54150
+ group: "block",
54151
+ content: "block*",
54152
+ atom: true,
54153
+ isolating: true,
54154
+ addOptions() {
54155
+ return {
54156
+ htmlAttributes: {
54157
+ class: "sd-document-section-block",
54158
+ "aria-label": "Structured content block"
54159
+ }
54160
+ };
54161
+ },
54162
+ parseDOM() {
54163
+ return [
54164
+ {
54165
+ tag: "div.sd-document-section-block",
54166
+ priority: 60
54167
+ }
54168
+ ];
54169
+ },
54170
+ renderDOM({ htmlAttributes }) {
54171
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
54172
+ },
54173
+ addAttributes() {
54174
+ return {
54175
+ id: {},
54176
+ sdBlockId: {
54177
+ default: null,
54178
+ keepOnSplit: false,
54179
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54180
+ renderDOM: (attrs) => {
54181
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54182
+ }
54183
+ },
54184
+ title: {},
54185
+ description: {},
54186
+ sectionType: {},
54187
+ isLocked: { default: false }
54188
+ };
54189
+ },
54190
+ addNodeView() {
54191
+ return ({ node, editor, getPos, decorations }) => {
54192
+ return new DocumentSectionView(node, getPos, decorations, editor);
54193
+ };
54194
+ },
54195
+ addCommands() {
54196
+ return {
54197
+ /**
54198
+ * Create a lockable content section
54199
+ * @category Command
54200
+ * @param {SectionCreate} [options={}] - Section configuration
54201
+ * @example
54202
+ * editor.commands.createDocumentSection({
54203
+ * id: 1,
54204
+ * title: 'Terms & Conditions',
54205
+ * isLocked: true,
54206
+ * html: '<p>Legal content...</p>'
54207
+ * })
54208
+ */
54209
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
54210
+ const { selection } = state2;
54211
+ let { from: from2, to } = selection;
54212
+ let content = selection.content().content;
54213
+ const { html: optionsHTML, json: optionsJSON } = options;
54214
+ if (optionsHTML) {
54215
+ const html = htmlHandler(optionsHTML, this.editor);
54216
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
54217
+ content = doc2.content;
54218
+ }
54219
+ if (optionsJSON) {
54220
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
54221
+ }
54222
+ if (!content?.content?.length) {
54223
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
54224
+ }
54225
+ if (!options.id) {
54226
+ const allSections = SectionHelpers.getAllSections(editor);
54227
+ options.id = allSections.length + 1;
54228
+ }
54229
+ if (!options.title) {
54230
+ options.title = "Document section";
54231
+ }
54232
+ const node = this.type.createAndFill(options, content);
54233
+ if (!node) return false;
54234
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
54235
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
54236
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
54237
+ from2 = insertPos2;
54238
+ to = insertPos2;
54239
+ }
54240
+ tr.replaceRangeWith(from2, to, node);
54241
+ const nodeEnd = from2 + node.nodeSize;
54242
+ let shouldInsertParagraph = true;
54243
+ let insertPos = nodeEnd;
54244
+ if (nodeEnd >= tr.doc.content.size) {
54245
+ insertPos = tr.doc.content.size;
54246
+ if (insertPos > 0) {
54247
+ const $endPos = tr.doc.resolve(insertPos);
54248
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
54249
+ shouldInsertParagraph = false;
54250
+ }
54251
+ }
54252
+ }
54253
+ if (shouldInsertParagraph) {
54254
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
54255
+ tr.insert(insertPos, emptyParagraph);
54256
+ }
54257
+ if (dispatch) {
54258
+ tr.setMeta("documentSection", { action: "create" });
54259
+ dispatch(tr);
54260
+ setTimeout(() => {
54261
+ try {
54262
+ const currentState = editor.state;
54263
+ const docSize = currentState.doc.content.size;
54264
+ let targetPos = from2 + node.nodeSize;
54265
+ if (shouldInsertParagraph) {
54266
+ targetPos += 1;
54267
+ }
54268
+ targetPos = Math.min(targetPos, docSize);
54269
+ if (targetPos < docSize && targetPos > 0) {
54270
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
54271
+ const newTr = currentState.tr.setSelection(newSelection);
54272
+ editor.view.dispatch(newTr);
54273
+ }
54274
+ } catch (e) {
54275
+ console.warn("Could not set delayed selection:", e);
54276
+ }
54277
+ }, 0);
54278
+ }
54279
+ return true;
54280
+ },
54281
+ /**
54282
+ * Remove section wrapper at cursor, preserving its content
54283
+ * @category Command
54284
+ * @example
54285
+ * editor.commands.removeSectionAtSelection()
54286
+ * @note Content stays in document, only section wrapper is removed
54287
+ */
54288
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
54289
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
54290
+ if (!sdtNode) return false;
54291
+ const { node, pos } = sdtNode;
54292
+ const nodeStart = pos;
54293
+ const nodeEnd = nodeStart + node.nodeSize;
54294
+ const contentToPreserve = node.content;
54295
+ tr.delete(nodeStart, nodeEnd);
54296
+ if (contentToPreserve.size > 0) {
54297
+ tr.insert(nodeStart, contentToPreserve);
54298
+ }
54299
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
54300
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
54301
+ if (dispatch) {
54302
+ tr.setMeta("documentSection", { action: "delete" });
54303
+ dispatch(tr);
54304
+ }
54305
+ return true;
54306
+ },
54307
+ /**
54308
+ * Delete section and all its content
54309
+ * @category Command
54310
+ * @param {number} id - Section to delete
54311
+ * @example
54312
+ * editor.commands.removeSectionById(123)
54313
+ */
54314
+ removeSectionById: (id) => ({ tr, dispatch }) => {
54315
+ const sections = SectionHelpers.getAllSections(this.editor);
54316
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
54317
+ if (!sectionToRemove) return false;
54318
+ const { pos, node } = sectionToRemove;
54319
+ const nodeStart = pos;
54320
+ const nodeEnd = nodeStart + node.nodeSize;
54321
+ tr.delete(nodeStart, nodeEnd);
54322
+ if (dispatch) {
54323
+ tr.setMeta("documentSection", { action: "delete", id });
54324
+ dispatch(tr);
54325
+ }
54326
+ return true;
54327
+ },
54328
+ /**
54329
+ * Lock section against edits
54330
+ * @category Command
54331
+ * @param {number} id - Section to lock
54332
+ * @example
54333
+ * editor.commands.lockSectionById(123)
54334
+ */
54335
+ lockSectionById: (id) => ({ tr, dispatch }) => {
54336
+ const sections = SectionHelpers.getAllSections(this.editor);
54337
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
54338
+ if (!sectionToLock) return false;
54339
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
54340
+ if (dispatch) {
54341
+ tr.setMeta("documentSection", { action: "lock", id });
54342
+ dispatch(tr);
54343
+ }
54344
+ return true;
54345
+ },
54346
+ /**
54347
+ * Modify section attributes or content
54348
+ * @category Command
54349
+ * @param {SectionUpdate} options - Changes to apply
54350
+ * @example
54351
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
54352
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
54353
+ * editor.commands.updateSectionById({
54354
+ * id: 123,
54355
+ * html: '<p>Updated</p>',
54356
+ * attrs: { title: 'New Title' }
54357
+ * })
54358
+ */
54359
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
54360
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
54361
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
54362
+ if (!sectionToUpdate) return false;
54363
+ const { pos, node } = sectionToUpdate;
54364
+ let newContent = null;
54365
+ if (html) {
54366
+ const htmlDoc = htmlHandler(html, editor || this.editor);
54367
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
54368
+ newContent = doc2.content;
54369
+ }
54370
+ if (json) {
54371
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
54372
+ }
54373
+ if (!newContent) {
54374
+ newContent = node.content;
54375
+ }
54376
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
54377
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
54378
+ if (dispatch) {
54379
+ tr.setMeta("documentSection", { action: "update", id, attrs });
54380
+ dispatch(tr);
54381
+ }
54382
+ return true;
54383
+ }
54384
+ };
54385
+ },
54386
+ addHelpers() {
54387
+ return {
54388
+ ...SectionHelpers
54389
+ };
54390
+ }
54391
+ });
54392
+ const Document = Node$1.create({
54393
+ name: "doc",
54394
+ topNode: true,
54395
+ content: "block+",
54396
+ parseDOM() {
54397
+ return [{ tag: "doc" }];
54398
+ },
54399
+ renderDOM() {
54400
+ return ["doc", 0];
54401
+ },
54402
+ addAttributes() {
54403
+ return {
54404
+ attributes: {
54405
+ rendered: false,
54406
+ "aria-label": "Document node"
54407
+ }
54408
+ };
54409
+ },
54410
+ addCommands() {
54411
+ return {
54412
+ /**
54413
+ * Get document statistics
54414
+ * @category Command
54415
+ * @example
54416
+ * // Get word and character count
54417
+ * const stats = editor.commands.getDocumentStats()
54418
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
54419
+ * @note Returns word count, character count, and paragraph count
54420
+ */
54421
+ getDocumentStats: () => ({ editor }) => {
54422
+ const text = editor.getText();
54423
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
54424
+ const characters = text.length;
54425
+ const paragraphs = editor.state.doc.content.childCount;
54426
+ return {
54427
+ words,
54428
+ characters,
54429
+ paragraphs
54430
+ };
54431
+ },
54432
+ /**
54433
+ * Clear entire document
54434
+ * @category Command
54435
+ * @example
54436
+ * editor.commands.clearDocument()
54437
+ * @note Replaces all content with an empty paragraph
54438
+ */
54439
+ clearDocument: () => ({ commands: commands2 }) => {
54440
+ return commands2.setContent("<p></p>");
54441
+ }
54442
+ };
54443
+ }
54444
+ });
54445
+ const Text = Node$1.create({
54446
+ name: "text",
54447
+ group: "inline",
54448
+ inline: true,
54449
+ addOptions() {
54450
+ return {};
54451
+ }
54452
+ });
54453
+ const splitRun = () => (props) => {
54454
+ const { state: state2, view, tr } = props;
54455
+ const { $from, empty: empty2 } = state2.selection;
54456
+ if (!empty2) return false;
54457
+ if ($from.parent.type.name !== "run") return false;
54458
+ const handled = splitBlock(state2, (transaction) => {
54459
+ view.dispatch(transaction);
54460
+ });
54461
+ if (handled) {
54462
+ tr.setMeta("preventDispatch", true);
54463
+ }
54464
+ return handled;
54465
+ };
54466
+ const Run = OxmlNode.create({
54467
+ name: "run",
54468
+ oXmlName: "w:r",
54469
+ group: "inline",
54470
+ inline: true,
54471
+ content: "inline*",
54472
+ selectable: false,
54473
+ childToAttributes: ["runProperties"],
54474
+ addOptions() {
54475
+ return {
54476
+ htmlAttributes: {
54477
+ "data-run": "1"
54478
+ }
54479
+ };
54480
+ },
54481
+ addAttributes() {
54482
+ return {
54483
+ runProperties: {
54484
+ default: null,
54485
+ rendered: false,
54486
+ keepOnSplit: true
54487
+ },
54488
+ rsidR: {
54489
+ default: null,
54490
+ rendered: false,
54491
+ keepOnSplit: true
54492
+ },
54493
+ rsidRPr: {
54494
+ default: null,
54495
+ rendered: false,
54496
+ keepOnSplit: true
54497
+ },
54498
+ rsidDel: {
54499
+ default: null,
54500
+ rendered: false,
54501
+ keepOnSplit: true
54502
+ }
54503
+ };
54504
+ },
54505
+ addCommands() {
54506
+ return {
54507
+ splitRun
54508
+ };
54509
+ },
54510
+ parseDOM() {
54511
+ return [{ tag: "span[data-run]" }];
54512
+ },
54513
+ renderDOM({ htmlAttributes }) {
54514
+ const base2 = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54515
+ return ["span", base2, 0];
54516
+ }
54517
+ });
54518
+ const inputRegex$1 = /^\s*([-+*])\s$/;
54519
+ const BulletList = Node$1.create({
54520
+ name: "bulletList",
54521
+ group: "block list",
54522
+ selectable: false,
54523
+ content() {
54524
+ return `${this.options.itemTypeName}+`;
54525
+ },
54526
+ addOptions() {
54527
+ return {
54528
+ itemTypeName: "listItem",
54529
+ htmlAttributes: {
54530
+ "aria-label": "Bullet list node"
54531
+ },
54532
+ keepMarks: true,
54533
+ keepAttributes: false
54534
+ };
54535
+ },
54536
+ parseDOM() {
54537
+ return [{ tag: "ul" }];
54538
+ },
54539
+ renderDOM({ htmlAttributes }) {
54540
+ const attributes = Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
54541
+ return ["ul", attributes, 0];
54542
+ },
54543
+ addAttributes() {
54544
+ return {
54545
+ "list-style-type": {
54546
+ default: "bullet",
54547
+ rendered: false
54548
+ },
54549
+ listId: {
54550
+ rendered: false
54551
+ },
54552
+ sdBlockId: {
54553
+ default: null,
54554
+ keepOnSplit: false,
54555
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
54556
+ renderDOM: (attrs) => {
54557
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
54558
+ }
54559
+ },
54560
+ attributes: {
54561
+ rendered: false,
54562
+ keepOnSplit: true
54563
+ }
54564
+ };
54565
+ },
54566
+ addCommands() {
54567
+ return {
54568
+ /**
54569
+ * Toggle a bullet list at the current selection
54570
+ * @category Command
54571
+ * @example
54572
+ * // Toggle bullet list on selected text
54573
+ * editor.commands.toggleBulletList()
54574
+ * @note Converts selected paragraphs to list items or removes list formatting
54575
+ */
54576
+ toggleBulletList: () => (params2) => {
54577
+ return toggleList(this.type)(params2);
54578
+ }
54579
+ };
54580
+ },
54581
+ addShortcuts() {
54582
+ return {
54583
+ "Mod-Shift-8": () => {
54584
+ return this.editor.commands.toggleBulletList();
54585
+ }
54586
+ };
54587
+ },
54588
+ addInputRules() {
54589
+ return [
54590
+ new InputRule({
54591
+ match: inputRegex$1,
54592
+ handler: ({ state: state2, range: range2 }) => {
54593
+ const $pos = state2.selection.$from;
54594
+ const listItemType = state2.schema.nodes.listItem;
54595
+ for (let depth = $pos.depth; depth >= 0; depth--) {
54596
+ if ($pos.node(depth).type === listItemType) {
54597
+ return null;
54598
+ }
54599
+ }
54600
+ const { tr } = state2;
54601
+ tr.delete(range2.from, range2.to);
54602
+ ListHelpers.createNewList({
54603
+ listType: this.type,
54604
+ tr,
54605
+ editor: this.editor
54606
+ });
54607
+ }
54608
+ })
54609
+ ];
54610
+ }
54611
+ });
54612
+ const inputRegex = /^(\d+)\.\s$/;
54613
+ const OrderedList = Node$1.create({
53578
54614
  name: "orderedList",
53579
54615
  group: "block list",
53580
54616
  selectable: false,
@@ -54935,7 +55971,7 @@ class ListItemNodeView {
54935
55971
  this.decorations = decorations;
54936
55972
  this.view = editor.view;
54937
55973
  this.getPos = getPos;
54938
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
55974
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
54939
55975
  activeListItemNodeViews.add(this);
54940
55976
  }
54941
55977
  refreshIndentStyling() {
@@ -54996,7 +56032,7 @@ class ListItemNodeView {
54996
56032
  }
54997
56033
  }
54998
56034
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
54999
- init_fn2 = function() {
56035
+ init_fn3 = function() {
55000
56036
  const { attrs } = this.node;
55001
56037
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
55002
56038
  let orderMarker = "";
@@ -62105,984 +63141,335 @@ const PageNumber = Node$1.create({
62105
63141
  }
62106
63142
  };
62107
63143
  },
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: {
63144
+ addAttributes() {
63145
+ return {
63146
+ marksAsAttrs: {
63147
+ default: null,
62514
63148
  rendered: false
62515
63149
  }
62516
63150
  };
62517
63151
  },
63152
+ addNodeView() {
63153
+ return ({ node, editor, getPos, decorations }) => {
63154
+ const htmlAttributes = this.options.htmlAttributes;
63155
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63156
+ };
63157
+ },
62518
63158
  parseDOM() {
62519
- return [
62520
- {
62521
- tag: `div[data-type="${this.name}"]`
62522
- }
62523
- ];
63159
+ return [{ tag: 'span[data-id="auto-page-number"' }];
62524
63160
  },
62525
63161
  renderDOM({ htmlAttributes }) {
62526
- return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63162
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
62527
63163
  },
62528
63164
  addCommands() {
62529
63165
  return {
62530
63166
  /**
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
63167
+ * Insert an automatic page number
62549
63168
  * @category Command
62550
- * @param {ContentBlockConfig} config - Block configuration
62551
- * @example
62552
- * // Insert a spacer block
62553
- * editor.commands.insertContentBlock({ size: { height: 20 } })
62554
- *
63169
+ * @returns {Function} Command function
62555
63170
  * @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
63171
+ * editor.commands.addAutoPageNumber()
63172
+ * @note Only works in header/footer contexts
62562
63173
  */
62563
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
62564
- return commands2.insertContent({
62565
- type: this.name,
62566
- attrs: config2
62567
- });
63174
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
63175
+ const { options } = editor;
63176
+ if (!options.isHeaderOrFooter) return false;
63177
+ const { schema } = state2;
63178
+ const pageNumberType = schema?.nodes?.["page-number"];
63179
+ if (!pageNumberType) return false;
63180
+ const pageNumberNodeJSON = { type: "page-number" };
63181
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
63182
+ if (dispatch) {
63183
+ tr.replaceSelectionWith(pageNumberNode, false);
63184
+ tr.setMeta("forceUpdatePagination", true);
63185
+ }
63186
+ return true;
62568
63187
  }
62569
63188
  };
63189
+ },
63190
+ addShortcuts() {
63191
+ return {
63192
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
63193
+ };
62570
63194
  }
62571
63195
  });
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",
63196
+ const TotalPageCount = Node$1.create({
63197
+ name: "total-page-number",
63198
+ group: "inline",
62783
63199
  inline: true,
62784
- content: "inline*",
62785
- isolating: true,
62786
- atom: false,
62787
- // false - has editable content.
62788
- draggable: true,
63200
+ atom: true,
63201
+ draggable: false,
63202
+ selectable: false,
63203
+ content: "text*",
62789
63204
  addOptions() {
62790
63205
  return {
62791
63206
  htmlAttributes: {
62792
- class: structuredContentClass$1,
62793
- "aria-label": "Structured content node"
63207
+ contenteditable: false,
63208
+ "data-id": "auto-total-pages",
63209
+ "aria-label": "Total page count node",
63210
+ class: "sd-editor-auto-total-pages"
62794
63211
  }
62795
63212
  };
62796
63213
  },
62797
63214
  addAttributes() {
62798
63215
  return {
62799
- id: {
63216
+ marksAsAttrs: {
62800
63217
  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
63218
  rendered: false
62809
63219
  }
62810
63220
  };
62811
63221
  },
63222
+ addNodeView() {
63223
+ return ({ node, editor, getPos, decorations }) => {
63224
+ const htmlAttributes = this.options.htmlAttributes;
63225
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
63226
+ };
63227
+ },
62812
63228
  parseDOM() {
62813
- return [{ tag: "span[data-structured-content]" }];
63229
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
62814
63230
  },
62815
63231
  renderDOM({ htmlAttributes }) {
62816
- return [
62817
- "span",
62818
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62819
- "data-structured-content": ""
62820
- }),
62821
- 0
62822
- ];
63232
+ return ["span", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
62823
63233
  },
62824
- addNodeView() {
62825
- return (props) => {
62826
- return new StructuredContentInlineView({ ...props });
63234
+ addCommands() {
63235
+ return {
63236
+ /**
63237
+ * Insert total page count
63238
+ * @category Command
63239
+ * @returns {Function} Command function
63240
+ * @example
63241
+ * editor.commands.addTotalPageCount()
63242
+ * @note Only works in header/footer contexts
63243
+ */
63244
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
63245
+ const { options } = editor;
63246
+ if (!options.isHeaderOrFooter) return false;
63247
+ const { schema } = state2;
63248
+ const pageNumberType = schema.nodes?.["total-page-number"];
63249
+ if (!pageNumberType) return false;
63250
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
63251
+ const pageNumberNode = {
63252
+ type: "total-page-number",
63253
+ content: [{ type: "text", text: String(currentPages) }]
63254
+ };
63255
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
63256
+ if (dispatch) {
63257
+ tr.replaceSelectionWith(pageNode, false);
63258
+ }
63259
+ return true;
63260
+ }
63261
+ };
63262
+ },
63263
+ addShortcuts() {
63264
+ return {
63265
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
62827
63266
  };
62828
63267
  }
62829
63268
  });
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;
63269
+ const getNodeAttributes = (nodeName, editor) => {
63270
+ switch (nodeName) {
63271
+ case "page-number":
63272
+ return {
63273
+ text: editor.options.currentPageNumber || "1",
63274
+ className: "sd-editor-auto-page-number",
63275
+ dataId: "auto-page-number",
63276
+ ariaLabel: "Page number node"
63277
+ };
63278
+ case "total-page-number":
63279
+ return {
63280
+ text: editor.options.parentEditor?.currentTotalPages || "1",
63281
+ className: "sd-editor-auto-total-pages",
63282
+ dataId: "auto-total-pages",
63283
+ ariaLabel: "Total page count node"
63284
+ };
63285
+ default:
63286
+ return {};
62858
63287
  }
62859
- updateView() {
62860
- const domAttrs = Attribute2.mergeAttributes(this.htmlAttributes);
62861
- updateDOMAttributes(this.dom, { ...domAttrs });
63288
+ };
63289
+ class AutoPageNumberNodeView {
63290
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
63291
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
63292
+ this.node = node;
63293
+ this.editor = editor;
63294
+ this.view = editor.view;
63295
+ this.getPos = getPos;
63296
+ this.editor = editor;
63297
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
62862
63298
  }
62863
- update(node, decorations, innerDecorations) {
62864
- const result = super.update(node, decorations, innerDecorations);
62865
- if (!result) return false;
62866
- this.updateView();
63299
+ update(node) {
63300
+ const incomingType = node?.type?.name;
63301
+ const currentType = this.node?.type?.name;
63302
+ if (!incomingType || incomingType !== currentType) return false;
63303
+ this.node = node;
62867
63304
  return true;
62868
63305
  }
62869
63306
  }
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*",
63307
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
63308
+ renderDom_fn = function(node, htmlAttributes) {
63309
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
63310
+ const content = document.createTextNode(String(attrs.text));
63311
+ const nodeContent = document.createElement("span");
63312
+ nodeContent.className = attrs.className;
63313
+ nodeContent.setAttribute("data-id", attrs.dataId);
63314
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
63315
+ const currentPos = this.getPos();
63316
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
63317
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
63318
+ Object.assign(nodeContent.style, styles);
63319
+ nodeContent.appendChild(content);
63320
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
63321
+ if (value) nodeContent.setAttribute(key2, value);
63322
+ });
63323
+ return nodeContent;
63324
+ };
63325
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
63326
+ setTimeout(() => {
63327
+ const { state: state2 } = this.editor;
63328
+ const { dispatch } = this.view;
63329
+ const node = state2.doc.nodeAt(pos);
63330
+ if (!node || node.isText) return;
63331
+ const currentMarks = node.attrs.marksAsAttrs || [];
63332
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
63333
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
63334
+ if (isEqual) return;
63335
+ const newAttrs = {
63336
+ ...node.attrs,
63337
+ marksAsAttrs: newMarks
63338
+ };
63339
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
63340
+ dispatch(tr);
63341
+ }, 0);
63342
+ };
63343
+ const getMarksFromNeighbors = (currentPos, view) => {
63344
+ const $pos = view.state.doc.resolve(currentPos);
63345
+ const styles = {};
63346
+ const marks = [];
63347
+ const before = $pos.nodeBefore;
63348
+ if (before) {
63349
+ Object.assign(styles, processMarks(before.marks));
63350
+ marks.push(...before.marks);
63351
+ }
63352
+ const after = $pos.nodeAfter;
63353
+ if (after) {
63354
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
63355
+ marks.push(...after.marks);
63356
+ }
63357
+ return {
63358
+ styles,
63359
+ marks
63360
+ };
63361
+ };
63362
+ const processMarks = (marks) => {
63363
+ const styles = {};
63364
+ marks.forEach((mark) => {
63365
+ const { type: type2, attrs } = mark;
63366
+ switch (type2.name) {
63367
+ case "textStyle":
63368
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
63369
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
63370
+ if (attrs.color) styles["color"] = attrs.color;
63371
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
63372
+ break;
63373
+ case "bold":
63374
+ styles["font-weight"] = "bold";
63375
+ break;
63376
+ case "italic":
63377
+ styles["font-style"] = "italic";
63378
+ break;
63379
+ case "underline":
63380
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
63381
+ break;
63382
+ case "strike":
63383
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
63384
+ break;
63385
+ default:
63386
+ if (attrs?.style) {
63387
+ Object.entries(attrs.style).forEach(([key2, value]) => {
63388
+ styles[key2] = value;
63389
+ });
63390
+ }
63391
+ break;
63392
+ }
63393
+ });
63394
+ return styles;
63395
+ };
63396
+ const ShapeContainer = Node$1.create({
63397
+ name: "shapeContainer",
63398
+ group: "block",
63399
+ content: "block+",
62876
63400
  isolating: true,
62877
- atom: false,
62878
- // false - has editable content.
62879
- draggable: true,
62880
63401
  addOptions() {
62881
63402
  return {
62882
63403
  htmlAttributes: {
62883
- class: structuredContentClass,
62884
- "aria-label": "Structured content block node"
63404
+ class: "sd-editor-shape-container",
63405
+ "aria-label": "Shape container node"
62885
63406
  }
62886
63407
  };
62887
63408
  },
62888
63409
  addAttributes() {
62889
63410
  return {
62890
- id: {
63411
+ fillcolor: {
63412
+ renderDOM: (attrs) => {
63413
+ if (!attrs.fillcolor) return {};
63414
+ return {
63415
+ style: `background-color: ${attrs.fillcolor}`
63416
+ };
63417
+ }
63418
+ },
63419
+ sdBlockId: {
62891
63420
  default: null,
62892
- parseDOM: (elem) => elem.getAttribute("data-id"),
63421
+ keepOnSplit: false,
63422
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62893
63423
  renderDOM: (attrs) => {
62894
- if (!attrs.id) return {};
62895
- return { "data-id": attrs.id };
63424
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62896
63425
  }
62897
63426
  },
62898
- sdtPr: {
63427
+ style: {
63428
+ renderDOM: (attrs) => {
63429
+ if (!attrs.style) return {};
63430
+ return {
63431
+ style: attrs.style
63432
+ };
63433
+ }
63434
+ },
63435
+ wrapAttributes: {
63436
+ rendered: false
63437
+ },
63438
+ attributes: {
62899
63439
  rendered: false
62900
63440
  }
62901
63441
  };
62902
63442
  },
62903
63443
  parseDOM() {
62904
- return [{ tag: "div[data-structured-content-block]" }];
63444
+ return [
63445
+ {
63446
+ tag: `div[data-type="${this.name}"]`
63447
+ }
63448
+ ];
62905
63449
  },
62906
63450
  renderDOM({ htmlAttributes }) {
62907
63451
  return [
62908
63452
  "div",
62909
- Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
62910
- "data-structured-content-block": ""
62911
- }),
63453
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
62912
63454
  0
62913
63455
  ];
62914
- },
62915
- addNodeView() {
62916
- return (props) => {
62917
- return new StructuredContentBlockView({ ...props });
62918
- };
62919
63456
  }
62920
63457
  });
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",
63458
+ const ShapeTextbox = Node$1.create({
63459
+ name: "shapeTextbox",
63060
63460
  group: "block",
63061
- content: "block*",
63062
- atom: true,
63461
+ content: "paragraph* block*",
63063
63462
  isolating: true,
63064
63463
  addOptions() {
63065
63464
  return {
63066
63465
  htmlAttributes: {
63067
- class: "sd-document-section-block",
63068
- "aria-label": "Structured content block"
63466
+ class: "sd-editor-shape-textbox",
63467
+ "aria-label": "Shape textbox node"
63069
63468
  }
63070
63469
  };
63071
63470
  },
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
63471
  addAttributes() {
63084
63472
  return {
63085
- id: {},
63086
63473
  sdBlockId: {
63087
63474
  default: null,
63088
63475
  keepOnSplit: false,
@@ -63091,212 +63478,131 @@ const DocumentSection = Node$1.create({
63091
63478
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
63092
63479
  }
63093
63480
  },
63094
- title: {},
63095
- description: {},
63096
- sectionType: {},
63097
- isLocked: { default: false }
63481
+ attributes: {
63482
+ rendered: false
63483
+ }
63098
63484
  };
63099
63485
  },
63100
- addNodeView() {
63101
- return ({ node, editor, getPos, decorations }) => {
63102
- return new DocumentSectionView(node, getPos, decorations, editor);
63486
+ parseDOM() {
63487
+ return [
63488
+ {
63489
+ tag: `div[data-type="${this.name}"]`
63490
+ }
63491
+ ];
63492
+ },
63493
+ renderDOM({ htmlAttributes }) {
63494
+ return [
63495
+ "div",
63496
+ Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
63497
+ 0
63498
+ ];
63499
+ }
63500
+ });
63501
+ const ContentBlock = Node$1.create({
63502
+ name: "contentBlock",
63503
+ group: "inline",
63504
+ content: "",
63505
+ isolating: true,
63506
+ atom: true,
63507
+ inline: true,
63508
+ addOptions() {
63509
+ return {
63510
+ htmlAttributes: {
63511
+ contenteditable: false
63512
+ }
63103
63513
  };
63104
63514
  },
63105
- addCommands() {
63515
+ addAttributes() {
63106
63516
  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);
63517
+ horizontalRule: {
63518
+ default: false,
63519
+ renderDOM: ({ horizontalRule }) => {
63520
+ if (!horizontalRule) return {};
63521
+ return { "data-horizontal-rule": "true" };
63188
63522
  }
63189
- return true;
63190
63523
  },
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);
63524
+ size: {
63525
+ default: null,
63526
+ renderDOM: ({ size: size2 }) => {
63527
+ if (!size2) return {};
63528
+ let style2 = "";
63529
+ if (size2.top) style2 += `top: ${size2.top}px; `;
63530
+ if (size2.left) style2 += `left: ${size2.left}px; `;
63531
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
63532
+ if (size2.height)
63533
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
63534
+ return { style: style2 };
63214
63535
  }
63215
- return true;
63216
63536
  },
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);
63537
+ background: {
63538
+ default: null,
63539
+ renderDOM: (attrs) => {
63540
+ if (!attrs.background) return {};
63541
+ return {
63542
+ style: `background-color: ${attrs.background}`
63543
+ };
63235
63544
  }
63236
- return true;
63237
63545
  },
63546
+ drawingContent: {
63547
+ rendered: false
63548
+ },
63549
+ attributes: {
63550
+ rendered: false
63551
+ }
63552
+ };
63553
+ },
63554
+ parseDOM() {
63555
+ return [
63556
+ {
63557
+ tag: `div[data-type="${this.name}"]`
63558
+ }
63559
+ ];
63560
+ },
63561
+ renderDOM({ htmlAttributes }) {
63562
+ return ["div", Attribute2.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
63563
+ },
63564
+ addCommands() {
63565
+ return {
63238
63566
  /**
63239
- * Lock section against edits
63567
+ * Insert a horizontal rule
63240
63568
  * @category Command
63241
- * @param {number} id - Section to lock
63242
63569
  * @example
63243
- * editor.commands.lockSectionById(123)
63570
+ * editor.commands.insertHorizontalRule()
63571
+ * @note Creates a visual separator between content sections
63244
63572
  */
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;
63573
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
63574
+ return commands2.insertContent({
63575
+ type: this.name,
63576
+ attrs: {
63577
+ horizontalRule: true,
63578
+ size: { width: "100%", height: 2 },
63579
+ background: "#e5e7eb"
63580
+ }
63581
+ });
63255
63582
  },
63256
63583
  /**
63257
- * Modify section attributes or content
63584
+ * Insert a content block
63258
63585
  * @category Command
63259
- * @param {SectionUpdate} options - Changes to apply
63586
+ * @param {ContentBlockConfig} config - Block configuration
63260
63587
  * @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' }
63588
+ * // Insert a spacer block
63589
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
63590
+ *
63591
+ * @example
63592
+ * // Insert a colored divider
63593
+ * editor.commands.insertContentBlock({
63594
+ * size: { width: '50%', height: 3 },
63595
+ * background: '#3b82f6'
63267
63596
  * })
63597
+ * @note Used for spacing, dividers, and special inline content
63268
63598
  */
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;
63599
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
63600
+ return commands2.insertContent({
63601
+ type: this.name,
63602
+ attrs: config2
63603
+ });
63293
63604
  }
63294
63605
  };
63295
- },
63296
- addHelpers() {
63297
- return {
63298
- ...SectionHelpers
63299
- };
63300
63606
  }
63301
63607
  });
63302
63608
  const { findChildren } = helpers;
@@ -70146,6 +70452,7 @@ const getStarterExtensions = () => {
70146
70452
  Search,
70147
70453
  StructuredContent,
70148
70454
  StructuredContentBlock,
70455
+ StructuredContentCommands,
70149
70456
  DocumentSection,
70150
70457
  NodeResizer,
70151
70458
  CustomSelection,