@harbour-enterprises/superdoc 0.22.0-next.8 → 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 (38) hide show
  1. package/dist/chunks/{PdfViewer-C1tow4Hk.es.js → PdfViewer-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-BBQm50V3.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BkpP-c9A.cjs → index-BbGPYtNy.cjs} +16 -9
  4. package/dist/chunks/{index-CJUwCOxd.es.js → index-DWKEKmiB.es.js} +16 -9
  5. package/dist/chunks/{super-editor.es-Bg0-F_x5.es.js → super-editor.es-BVxfhpAJ.es.js} +1562 -1267
  6. package/dist/chunks/{super-editor.es-D6u8E2n4.cjs → super-editor.es-BoUJEZaF.cjs} +1562 -1267
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/stores/comments-store.d.ts +4 -1
  9. package/dist/stores/comments-store.d.ts.map +1 -1
  10. package/dist/style.css +45 -44
  11. package/dist/super-editor/ai-writer.es.js +2 -2
  12. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-C-yWLpFM.js} +150 -105
  13. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  14. package/dist/super-editor/chunks/{editor-M2L6bDyC.js → editor-BBnC1DzI.js} +1532 -1280
  15. package/dist/super-editor/chunks/{toolbar-DsPK5x8a.js → toolbar-QJANo61B.js} +2 -2
  16. package/dist/super-editor/converter.es.js +1 -1
  17. package/dist/super-editor/docx-zipper.es.js +2 -2
  18. package/dist/super-editor/editor.es.js +3 -3
  19. package/dist/super-editor/file-zipper.es.js +1 -1
  20. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  21. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  22. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  23. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  27. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  28. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  29. package/dist/super-editor/style.css +1 -0
  30. package/dist/super-editor/super-editor.es.js +7 -7
  31. package/dist/super-editor/toolbar.es.js +2 -2
  32. package/dist/super-editor.cjs +1 -1
  33. package/dist/super-editor.es.js +1 -1
  34. package/dist/superdoc.cjs +2 -2
  35. package/dist/superdoc.es.js +2 -2
  36. package/dist/superdoc.umd.js +1573 -1271
  37. package/dist/superdoc.umd.js.map +1 -1
  38. package/package.json +1 -1
@@ -9,12 +9,12 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
9
9
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
10
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
11
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
- 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;
12
+ 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;
13
13
  import * as Y from "yjs";
14
14
  import { UndoManager, Item as Item$1, ContentType, Text as Text$1, XmlElement, encodeStateAsUpdate } from "yjs";
15
- import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as ReplaceStep, E as NodeRange, G as findWrapping, L as ListHelpers, H as findParentNode, I as isMacOS, J as isIOS, K as getSchemaTypeByName, O as inputRulesPlugin, Q as TrackDeleteMarkName, U as TrackInsertMarkName, V as v4, W as TrackFormatMarkName, X as comments_module_events, Y as findMark, Z as objectIncludes, _ as AddMarkStep, $ as RemoveMarkStep, a0 as twipsToLines, a1 as pixelsToTwips, a2 as helpers, a3 as posToDOMRect, a4 as CommandService, a5 as SuperConverter, a6 as createDocument, a7 as createDocFromMarkdown, a8 as createDocFromHTML, a9 as EditorState, aa as hasSomeParentWithClass, ab as isActive, ac as unflattenListsInHtml, ad as parseSizeUnit, ae as minMax, af as getLineHeightValueString, ag as InputRule, ah as kebabCase, ai as findParentNodeClosestToPos, aj as getListItemStyleDefinitions, ak as docxNumberigHelpers, al as parseIndentElement, am as combineIndents, an as SelectionRange, ao as Transform, ap as isInTable$1, aq as generateDocxRandomId, ar as insertNewRelationship, as as updateDOMAttributes, at as htmlHandler } from "./converter-BgedUNCW.js";
15
+ import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as ReplaceStep, E as NodeRange, G as findWrapping, L as ListHelpers, H as findParentNode, I as isMacOS, J as isIOS, K as getSchemaTypeByName, O as inputRulesPlugin, Q as TrackDeleteMarkName, U as TrackInsertMarkName, V as v4, W as TrackFormatMarkName, X as comments_module_events, Y as findMark, Z as objectIncludes, _ as AddMarkStep, $ as RemoveMarkStep, a0 as twipsToLines, a1 as pixelsToTwips, a2 as helpers, a3 as posToDOMRect, a4 as CommandService, a5 as SuperConverter, a6 as createDocument, a7 as createDocFromMarkdown, a8 as createDocFromHTML, a9 as EditorState, aa as hasSomeParentWithClass, ab as isActive, ac as unflattenListsInHtml, ad as parseSizeUnit, ae as minMax, af as getLineHeightValueString, ag as updateDOMAttributes, ah as findChildren$5, ai as htmlHandler, aj as generateRandomSigned32BitIntStrId, ak as InputRule, al as kebabCase, am as findParentNodeClosestToPos, an as getListItemStyleDefinitions, ao as docxNumberigHelpers, ap as parseIndentElement, aq as combineIndents, ar as SelectionRange, as as Transform, at as isInTable$1, au as generateDocxRandomId, av as insertNewRelationship } from "./converter-C-yWLpFM.js";
16
16
  import { ref, computed, createElementBlock, openBlock, withModifiers, Fragment as Fragment$1, renderList, normalizeClass, createCommentVNode, toDisplayString, createElementVNode, createApp } from "vue";
17
- import { D as DocxZipper } from "./docx-zipper-ByLK3trM.js";
17
+ import { D as DocxZipper } from "./docx-zipper-CmGlSUQM.js";
18
18
  var GOOD_LEAF_SIZE = 200;
19
19
  var RopeSequence = function RopeSequence2() {
20
20
  };
@@ -12107,28 +12107,25 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
12107
12107
  if (emitParams) editor.emit("commentsUpdate", emitParams);
12108
12108
  return newTrackedChanges;
12109
12109
  };
12110
- const getTrackedChangeText = ({ state, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
12110
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
12111
12111
  let trackedChangeText = "";
12112
12112
  let deletionText = "";
12113
12113
  if (trackedChangeType === TrackInsertMarkName) {
12114
- trackedChangeText = node?.text ?? "";
12114
+ trackedChangeText = nodes.reduce((acc, node) => {
12115
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
12116
+ acc += node?.text || node?.textContent || "";
12117
+ return acc;
12118
+ }, "");
12115
12119
  }
12116
12120
  if (trackedChangeType === TrackFormatMarkName) {
12117
12121
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
12118
12122
  }
12119
12123
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
12120
- deletionText = node?.text ?? "";
12121
- if (isDeletionInsertion) {
12122
- let { id } = marks.deletionMark.attrs;
12123
- let deletionNode = findNode$1(state.doc, (node2) => {
12124
- const { marks: marks2 = [] } = node2;
12125
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
12126
- if (!changeMarks.length) return false;
12127
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
12128
- if (hasMatchingId) return true;
12129
- });
12130
- deletionText = deletionNode?.node.text ?? "";
12131
- }
12124
+ deletionText = nodes.reduce((acc, node) => {
12125
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
12126
+ acc += node?.text || node?.textContent || "";
12127
+ return acc;
12128
+ }, "");
12132
12129
  }
12133
12130
  return {
12134
12131
  deletionText,
@@ -12143,20 +12140,17 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
12143
12140
  const id = attrs.id;
12144
12141
  const node = nodes[0];
12145
12142
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
12146
- let existingNode;
12143
+ let nodesWithMark = [];
12147
12144
  newEditorState.doc.descendants((node2) => {
12148
12145
  const { marks: marks2 = [] } = node2;
12149
12146
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
12150
12147
  if (!changeMarks.length) return;
12151
12148
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
12152
- if (hasMatchingId) existingNode = node2;
12153
- if (existingNode) return false;
12149
+ if (hasMatchingId) nodesWithMark.push(node2);
12154
12150
  });
12155
12151
  const { deletionText, trackedChangeText } = getTrackedChangeText({
12156
- state: newEditorState,
12157
- node: existingNode || node,
12152
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
12158
12153
  mark: trackedMark,
12159
- marks,
12160
12154
  trackedChangeType,
12161
12155
  isDeletionInsertion
12162
12156
  });
@@ -12184,14 +12178,6 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
12184
12178
  else if (event === "update") params2.event = comments_module_events.UPDATE;
12185
12179
  return params2;
12186
12180
  };
12187
- function findNode$1(node, predicate) {
12188
- let found = null;
12189
- node.descendants((node2, pos) => {
12190
- if (predicate(node2)) found = { node: node2, pos };
12191
- if (found) return false;
12192
- });
12193
- return found;
12194
- }
12195
12181
  function findRangeById(doc2, id) {
12196
12182
  let from2 = null, to = null;
12197
12183
  doc2.descendants((node, pos) => {
@@ -16979,223 +16965,1218 @@ const SlashMenu = Extension.create({
16979
16965
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
16980
16966
  }
16981
16967
  });
16982
- const Document = Node$1.create({
16983
- name: "doc",
16984
- topNode: true,
16985
- content: "block+",
16986
- parseDOM() {
16987
- return [{ tag: "doc" }];
16988
- },
16989
- renderDOM() {
16990
- return ["doc", 0];
16991
- },
16992
- addAttributes() {
16993
- return {
16994
- attributes: {
16995
- rendered: false,
16996
- "aria-label": "Document node"
16997
- }
16998
- };
16999
- },
17000
- addCommands() {
17001
- return {
17002
- /**
17003
- * Get document statistics
17004
- * @category Command
17005
- * @example
17006
- * // Get word and character count
17007
- * const stats = editor.commands.getDocumentStats()
17008
- * console.log(`${stats.words} words, ${stats.characters} characters`)
17009
- * @note Returns word count, character count, and paragraph count
17010
- */
17011
- getDocumentStats: () => ({ editor }) => {
17012
- const text = editor.getText();
17013
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
17014
- const characters = text.length;
17015
- const paragraphs = editor.state.doc.content.childCount;
17016
- return {
17017
- words,
17018
- characters,
17019
- paragraphs
17020
- };
17021
- },
17022
- /**
17023
- * Clear entire document
17024
- * @category Command
17025
- * @example
17026
- * editor.commands.clearDocument()
17027
- * @note Replaces all content with an empty paragraph
17028
- */
17029
- clearDocument: () => ({ commands: commands2 }) => {
17030
- return commands2.setContent("<p></p>");
16968
+ class StructuredContentViewBase {
16969
+ constructor(props) {
16970
+ __publicField(this, "node");
16971
+ __publicField(this, "view");
16972
+ __publicField(this, "getPos");
16973
+ __publicField(this, "decorations");
16974
+ __publicField(this, "innerDecorations");
16975
+ __publicField(this, "editor");
16976
+ __publicField(this, "extension");
16977
+ __publicField(this, "htmlAttributes");
16978
+ __publicField(this, "root");
16979
+ __publicField(this, "isDragging", false);
16980
+ this.node = props.node;
16981
+ this.view = props.editor.view;
16982
+ this.getPos = props.getPos;
16983
+ this.decorations = props.decorations;
16984
+ this.innerDecorations = props.innerDecorations;
16985
+ this.editor = props.editor;
16986
+ this.extension = props.extension;
16987
+ this.htmlAttributes = props.htmlAttributes;
16988
+ this.mount(props);
16989
+ }
16990
+ mount() {
16991
+ return;
16992
+ }
16993
+ get dom() {
16994
+ return this.root;
16995
+ }
16996
+ get contentDOM() {
16997
+ return null;
16998
+ }
16999
+ update(node, decorations, innerDecorations) {
17000
+ if (node.type !== this.node.type) {
17001
+ return false;
17002
+ }
17003
+ this.node = node;
17004
+ this.decorations = decorations;
17005
+ this.innerDecorations = innerDecorations;
17006
+ this.updateHTMLAttributes();
17007
+ return true;
17008
+ }
17009
+ stopEvent(event) {
17010
+ if (!this.dom) return false;
17011
+ const target = event.target;
17012
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
17013
+ if (!isInElement) return false;
17014
+ const isDragEvent = event.type.startsWith("drag");
17015
+ const isDropEvent = event.type === "drop";
17016
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
17017
+ if (isInput && !isDropEvent && !isDragEvent) return true;
17018
+ const { isEditable } = this.editor;
17019
+ const { isDragging } = this;
17020
+ const isDraggable = !!this.node.type.spec.draggable;
17021
+ const isSelectable = NodeSelection.isSelectable(this.node);
17022
+ const isCopyEvent = event.type === "copy";
17023
+ const isPasteEvent = event.type === "paste";
17024
+ const isCutEvent = event.type === "cut";
17025
+ const isClickEvent = event.type === "mousedown";
17026
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
17027
+ event.preventDefault();
17028
+ }
17029
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
17030
+ event.preventDefault();
17031
+ return false;
17032
+ }
17033
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
17034
+ const dragHandle = target.closest("[data-drag-handle]");
17035
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
17036
+ if (isValidDragHandle) {
17037
+ this.isDragging = true;
17038
+ document.addEventListener(
17039
+ "dragend",
17040
+ () => {
17041
+ this.isDragging = false;
17042
+ },
17043
+ { once: true }
17044
+ );
17045
+ document.addEventListener(
17046
+ "drop",
17047
+ () => {
17048
+ this.isDragging = false;
17049
+ },
17050
+ { once: true }
17051
+ );
17052
+ document.addEventListener(
17053
+ "mouseup",
17054
+ () => {
17055
+ this.isDragging = false;
17056
+ },
17057
+ { once: true }
17058
+ );
17031
17059
  }
17032
- };
17060
+ }
17061
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
17062
+ return false;
17063
+ }
17064
+ return true;
17033
17065
  }
17034
- });
17035
- const Text = Node$1.create({
17036
- name: "text",
17037
- group: "inline",
17038
- inline: true,
17039
- addOptions() {
17040
- return {};
17066
+ ignoreMutation(mutation) {
17067
+ if (!this.dom || !this.contentDOM) return true;
17068
+ if (this.node.isLeaf || this.node.isAtom) return true;
17069
+ if (mutation.type === "selection") return false;
17070
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
17071
+ if (this.contentDOM.contains(mutation.target)) return false;
17072
+ return true;
17041
17073
  }
17042
- });
17043
- const splitRun = () => (props) => {
17044
- const { state, view, tr } = props;
17045
- const { $from, empty: empty2 } = state.selection;
17046
- if (!empty2) return false;
17047
- if ($from.parent.type.name !== "run") return false;
17048
- const handled = splitBlock(state, (transaction) => {
17074
+ destroy() {
17075
+ this.dom.remove();
17076
+ this.contentDOM?.remove();
17077
+ }
17078
+ updateAttributes(attrs) {
17079
+ const pos = this.getPos();
17080
+ if (typeof pos !== "number") {
17081
+ return;
17082
+ }
17083
+ return this.view.dispatch(
17084
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
17085
+ ...this.node.attrs,
17086
+ ...attrs
17087
+ })
17088
+ );
17089
+ }
17090
+ updateHTMLAttributes() {
17091
+ const { extensionService } = this.editor;
17092
+ const { attributes } = extensionService;
17093
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
17094
+ this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
17095
+ }
17096
+ createDragHandle() {
17097
+ const dragHandle = document.createElement("span");
17098
+ dragHandle.classList.add("sd-structured-content-draggable");
17099
+ dragHandle.draggable = true;
17100
+ dragHandle.contentEditable = "false";
17101
+ dragHandle.dataset.dragHandle = "";
17102
+ const textElement = document.createElement("span");
17103
+ textElement.textContent = this.node.attrs.alias || "Structured content";
17104
+ dragHandle.append(textElement);
17105
+ return dragHandle;
17106
+ }
17107
+ onDragStart(event) {
17108
+ const { view } = this.editor;
17109
+ const target = event.target;
17110
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
17111
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
17112
+ return;
17113
+ }
17114
+ let x = 0;
17115
+ let y = 0;
17116
+ if (this.dom !== dragHandle) {
17117
+ const domBox = this.dom.getBoundingClientRect();
17118
+ const handleBox = dragHandle.getBoundingClientRect();
17119
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
17120
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
17121
+ x = handleBox.x - domBox.x + offsetX;
17122
+ y = handleBox.y - domBox.y + offsetY;
17123
+ }
17124
+ event.dataTransfer?.setDragImage(this.dom, x, y);
17125
+ const pos = this.getPos();
17126
+ if (typeof pos !== "number") {
17127
+ return;
17128
+ }
17129
+ const selection = NodeSelection.create(view.state.doc, pos);
17130
+ const transaction = view.state.tr.setSelection(selection);
17049
17131
  view.dispatch(transaction);
17050
- });
17051
- if (handled) {
17052
- tr.setMeta("preventDispatch", true);
17053
17132
  }
17054
- return handled;
17055
- };
17056
- const Run = OxmlNode.create({
17057
- name: "run",
17058
- oXmlName: "w:r",
17059
- group: "inline",
17133
+ }
17134
+ class StructuredContentInlineView extends StructuredContentViewBase {
17135
+ constructor(props) {
17136
+ super(props);
17137
+ }
17138
+ mount() {
17139
+ this.buildView();
17140
+ }
17141
+ get contentDOM() {
17142
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
17143
+ return contentElement || null;
17144
+ }
17145
+ createElement() {
17146
+ const element = document.createElement("span");
17147
+ element.classList.add(structuredContentClass$1);
17148
+ element.setAttribute("data-structured-content", "");
17149
+ const contentElement = document.createElement("span");
17150
+ contentElement.classList.add(structuredContentInnerClass$1);
17151
+ element.append(contentElement);
17152
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17153
+ updateDOMAttributes(element, { ...domAttrs });
17154
+ return { element, contentElement };
17155
+ }
17156
+ buildView() {
17157
+ const { element } = this.createElement();
17158
+ const dragHandle = this.createDragHandle();
17159
+ element.prepend(dragHandle);
17160
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
17161
+ this.root = element;
17162
+ }
17163
+ updateView() {
17164
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17165
+ updateDOMAttributes(this.dom, { ...domAttrs });
17166
+ }
17167
+ update(node, decorations, innerDecorations) {
17168
+ const result = super.update(node, decorations, innerDecorations);
17169
+ if (!result) return false;
17170
+ this.updateView();
17171
+ return true;
17172
+ }
17173
+ }
17174
+ const structuredContentClass$1 = "sd-structured-content";
17175
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
17176
+ const StructuredContent = Node$1.create({
17177
+ name: "structuredContent",
17178
+ group: "inline structuredContent",
17060
17179
  inline: true,
17061
17180
  content: "inline*",
17062
- selectable: false,
17063
- childToAttributes: ["runProperties"],
17181
+ isolating: true,
17182
+ atom: false,
17183
+ // false - has editable content.
17184
+ draggable: true,
17064
17185
  addOptions() {
17065
17186
  return {
17066
17187
  htmlAttributes: {
17067
- "data-run": "1"
17188
+ class: structuredContentClass$1,
17189
+ "aria-label": "Structured content node"
17068
17190
  }
17069
17191
  };
17070
17192
  },
17071
17193
  addAttributes() {
17072
17194
  return {
17073
- runProperties: {
17195
+ id: {
17074
17196
  default: null,
17075
- rendered: false,
17076
- keepOnSplit: true
17197
+ parseDOM: (elem) => elem.getAttribute("data-id"),
17198
+ renderDOM: (attrs) => {
17199
+ if (!attrs.id) return {};
17200
+ return { "data-id": attrs.id };
17201
+ }
17077
17202
  },
17078
- rsidR: {
17203
+ tag: {
17079
17204
  default: null,
17080
- rendered: false,
17081
- keepOnSplit: true
17205
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
17206
+ renderDOM: (attrs) => {
17207
+ if (!attrs.tag) return {};
17208
+ return { "data-tag": attrs.tag };
17209
+ }
17082
17210
  },
17083
- rsidRPr: {
17211
+ alias: {
17084
17212
  default: null,
17085
- rendered: false,
17086
- keepOnSplit: true
17213
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17214
+ renderDOM: (attrs) => {
17215
+ if (!attrs.alias) return {};
17216
+ return { "data-alias": attrs.alias };
17217
+ }
17087
17218
  },
17088
- rsidDel: {
17089
- default: null,
17090
- rendered: false,
17091
- keepOnSplit: true
17219
+ sdtPr: {
17220
+ rendered: false
17092
17221
  }
17093
17222
  };
17094
17223
  },
17095
- addCommands() {
17096
- return {
17097
- splitRun
17098
- };
17099
- },
17100
17224
  parseDOM() {
17101
- return [{ tag: "span[data-run]" }];
17225
+ return [{ tag: "span[data-structured-content]" }];
17102
17226
  },
17103
17227
  renderDOM({ htmlAttributes }) {
17104
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17105
- return ["span", base2, 0];
17228
+ return [
17229
+ "span",
17230
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
17231
+ "data-structured-content": ""
17232
+ }),
17233
+ 0
17234
+ ];
17235
+ },
17236
+ addNodeView() {
17237
+ return (props) => {
17238
+ return new StructuredContentInlineView({ ...props });
17239
+ };
17106
17240
  }
17107
17241
  });
17108
- const inputRegex$1 = /^\s*([-+*])\s$/;
17109
- const BulletList = Node$1.create({
17110
- name: "bulletList",
17111
- group: "block list",
17112
- selectable: false,
17113
- content() {
17114
- return `${this.options.itemTypeName}+`;
17115
- },
17242
+ class StructuredContentBlockView extends StructuredContentViewBase {
17243
+ constructor(props) {
17244
+ super(props);
17245
+ }
17246
+ mount() {
17247
+ this.buildView();
17248
+ }
17249
+ get contentDOM() {
17250
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
17251
+ return contentElement || null;
17252
+ }
17253
+ createElement() {
17254
+ const element = document.createElement("div");
17255
+ element.classList.add(structuredContentClass);
17256
+ element.setAttribute("data-structured-content-block", "");
17257
+ const contentElement = document.createElement("div");
17258
+ contentElement.classList.add(structuredContentInnerClass);
17259
+ element.append(contentElement);
17260
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17261
+ updateDOMAttributes(element, { ...domAttrs });
17262
+ return { element, contentElement };
17263
+ }
17264
+ buildView() {
17265
+ const { element } = this.createElement();
17266
+ const dragHandle = this.createDragHandle();
17267
+ element.prepend(dragHandle);
17268
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
17269
+ this.root = element;
17270
+ }
17271
+ updateView() {
17272
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17273
+ updateDOMAttributes(this.dom, { ...domAttrs });
17274
+ }
17275
+ update(node, decorations, innerDecorations) {
17276
+ const result = super.update(node, decorations, innerDecorations);
17277
+ if (!result) return false;
17278
+ this.updateView();
17279
+ return true;
17280
+ }
17281
+ }
17282
+ const structuredContentClass = "sd-structured-content-block";
17283
+ const structuredContentInnerClass = "sd-structured-content-block__content";
17284
+ const StructuredContentBlock = Node$1.create({
17285
+ name: "structuredContentBlock",
17286
+ group: "block structuredContent",
17287
+ content: "block*",
17288
+ isolating: true,
17289
+ atom: false,
17290
+ // false - has editable content.
17291
+ draggable: true,
17116
17292
  addOptions() {
17117
17293
  return {
17118
- itemTypeName: "listItem",
17119
17294
  htmlAttributes: {
17120
- "aria-label": "Bullet list node"
17121
- },
17122
- keepMarks: true,
17123
- keepAttributes: false
17295
+ class: structuredContentClass,
17296
+ "aria-label": "Structured content block node"
17297
+ }
17124
17298
  };
17125
17299
  },
17126
- parseDOM() {
17127
- return [{ tag: "ul" }];
17128
- },
17129
- renderDOM({ htmlAttributes }) {
17130
- const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17131
- return ["ul", attributes, 0];
17132
- },
17133
17300
  addAttributes() {
17134
17301
  return {
17135
- "list-style-type": {
17136
- default: "bullet",
17137
- rendered: false
17302
+ id: {
17303
+ default: null,
17304
+ parseDOM: (elem) => elem.getAttribute("data-id"),
17305
+ renderDOM: (attrs) => {
17306
+ if (!attrs.id) return {};
17307
+ return { "data-id": attrs.id };
17308
+ }
17138
17309
  },
17139
- listId: {
17140
- rendered: false
17310
+ tag: {
17311
+ default: null,
17312
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
17313
+ renderDOM: (attrs) => {
17314
+ if (!attrs.tag) return {};
17315
+ return { "data-tag": attrs.tag };
17316
+ }
17141
17317
  },
17142
- sdBlockId: {
17318
+ alias: {
17143
17319
  default: null,
17144
- keepOnSplit: false,
17145
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17320
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17146
17321
  renderDOM: (attrs) => {
17147
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17322
+ if (!attrs.alias) return {};
17323
+ return { "data-alias": attrs.alias };
17148
17324
  }
17149
17325
  },
17150
- attributes: {
17151
- rendered: false,
17152
- keepOnSplit: true
17326
+ sdtPr: {
17327
+ rendered: false
17153
17328
  }
17154
17329
  };
17155
17330
  },
17331
+ parseDOM() {
17332
+ return [{ tag: "div[data-structured-content-block]" }];
17333
+ },
17334
+ renderDOM({ htmlAttributes }) {
17335
+ return [
17336
+ "div",
17337
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
17338
+ "data-structured-content-block": ""
17339
+ }),
17340
+ 0
17341
+ ];
17342
+ },
17343
+ addNodeView() {
17344
+ return (props) => {
17345
+ return new StructuredContentBlockView({ ...props });
17346
+ };
17347
+ }
17348
+ });
17349
+ function getStructuredContentTagsById(idOrIds, state) {
17350
+ const result = findChildren$5(state.doc, (node) => {
17351
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
17352
+ if (Array.isArray(idOrIds)) {
17353
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
17354
+ } else {
17355
+ return isStructuredContent && node.attrs.id === idOrIds;
17356
+ }
17357
+ });
17358
+ return result;
17359
+ }
17360
+ function getStructuredContentTags(state) {
17361
+ const result = findChildren$5(state.doc, (node) => {
17362
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
17363
+ });
17364
+ return result;
17365
+ }
17366
+ function getStructuredContentInlineTags(state) {
17367
+ const result = findChildren$5(state.doc, (node) => node.type.name === "structuredContent");
17368
+ return result;
17369
+ }
17370
+ function getStructuredContentBlockTags(state) {
17371
+ const result = findChildren$5(state.doc, (node) => node.type.name === "structuredContentBlock");
17372
+ return result;
17373
+ }
17374
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
17375
+ __proto__: null,
17376
+ getStructuredContentBlockTags,
17377
+ getStructuredContentInlineTags,
17378
+ getStructuredContentTags,
17379
+ getStructuredContentTagsById
17380
+ }, Symbol.toStringTag, { value: "Module" }));
17381
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
17382
+ const StructuredContentCommands = Extension.create({
17383
+ name: "structuredContentCommands",
17156
17384
  addCommands() {
17157
17385
  return {
17158
17386
  /**
17159
- * Toggle a bullet list at the current selection
17387
+ * Inserts a structured content inline at selection.
17160
17388
  * @category Command
17161
- * @example
17162
- * // Toggle bullet list on selected text
17163
- * editor.commands.toggleBulletList()
17164
- * @note Converts selected paragraphs to list items or removes list formatting
17389
+ * @param {StructuredContentInlineInsert} options
17165
17390
  */
17166
- toggleBulletList: () => (params2) => {
17167
- return toggleList(this.type)(params2);
17168
- }
17169
- };
17170
- },
17171
- addShortcuts() {
17172
- return {
17173
- "Mod-Shift-8": () => {
17174
- return this.editor.commands.toggleBulletList();
17175
- }
17176
- };
17177
- },
17178
- addInputRules() {
17179
- return [
17180
- new InputRule({
17181
- match: inputRegex$1,
17182
- handler: ({ state, range }) => {
17183
- const $pos = state.selection.$from;
17184
- const listItemType = state.schema.nodes.listItem;
17185
- for (let depth = $pos.depth; depth >= 0; depth--) {
17186
- if ($pos.node(depth).type === listItemType) {
17187
- return null;
17188
- }
17391
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state, tr }) => {
17392
+ const { schema } = editor;
17393
+ let { from: from2, to } = state.selection;
17394
+ if (dispatch) {
17395
+ const selectionText = state.doc.textBetween(from2, to);
17396
+ let content = null;
17397
+ if (selectionText) {
17398
+ content = schema.text(selectionText);
17189
17399
  }
17190
- const { tr } = state;
17191
- tr.delete(range.from, range.to);
17192
- ListHelpers.createNewList({
17193
- listType: this.type,
17194
- tr,
17195
- editor: this.editor
17196
- });
17197
- }
17198
- })
17400
+ if (options.text) {
17401
+ content = schema.text(options.text);
17402
+ }
17403
+ if (options.json) {
17404
+ content = schema.nodeFromJSON(options.json);
17405
+ }
17406
+ if (!content) {
17407
+ content = schema.text(" ");
17408
+ }
17409
+ const attrs = {
17410
+ ...options.attrs,
17411
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
17412
+ tag: "inline_text_sdt",
17413
+ alias: options.attrs?.alias || "Structured content"
17414
+ };
17415
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
17416
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state.selection);
17417
+ if (parent) {
17418
+ const insertPos = parent.pos + parent.node.nodeSize;
17419
+ from2 = to = insertPos;
17420
+ }
17421
+ tr.replaceWith(from2, to, node);
17422
+ }
17423
+ return true;
17424
+ },
17425
+ /**
17426
+ * Inserts a structured content block at selection.
17427
+ * @category Command
17428
+ * @param {StructuredContentBlockInsert} options
17429
+ */
17430
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state, tr }) => {
17431
+ const { schema } = editor;
17432
+ let { from: from2, to } = state.selection;
17433
+ if (dispatch) {
17434
+ const selectionContent = state.selection.content();
17435
+ let content = null;
17436
+ if (selectionContent.size) {
17437
+ content = selectionContent.content;
17438
+ }
17439
+ if (options.html) {
17440
+ const html = htmlHandler(options.html, editor);
17441
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
17442
+ content = doc2.content;
17443
+ }
17444
+ if (options.json) {
17445
+ content = schema.nodeFromJSON(options.json);
17446
+ }
17447
+ if (!content) {
17448
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
17449
+ }
17450
+ const attrs = {
17451
+ ...options.attrs,
17452
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
17453
+ tag: "block_table_sdt",
17454
+ alias: options.attrs?.alias || "Structured content"
17455
+ };
17456
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
17457
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state.selection);
17458
+ if (parent) {
17459
+ const insertPos = parent.pos + parent.node.nodeSize;
17460
+ from2 = to = insertPos;
17461
+ }
17462
+ tr.replaceRangeWith(from2, to, node);
17463
+ }
17464
+ return true;
17465
+ },
17466
+ /**
17467
+ * Updates a structured content attributes or content.
17468
+ * If the updated node does not match the schema, it will not be updated.
17469
+ * @category Command
17470
+ * @param {string} id
17471
+ * @param {StructuredContentUpdate} options
17472
+ */
17473
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state, tr }) => {
17474
+ const structuredContentTags = getStructuredContentTagsById(id, state);
17475
+ if (!structuredContentTags.length) {
17476
+ return true;
17477
+ }
17478
+ const { schema } = editor;
17479
+ if (dispatch) {
17480
+ const structuredContent = structuredContentTags[0];
17481
+ const { pos, node } = structuredContent;
17482
+ const posFrom = pos;
17483
+ const posTo = pos + node.nodeSize;
17484
+ let content = null;
17485
+ if (options.text) {
17486
+ content = schema.text(options.text);
17487
+ }
17488
+ if (options.html) {
17489
+ const html = htmlHandler(options.html, editor);
17490
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
17491
+ content = doc2.content;
17492
+ }
17493
+ if (options.json) {
17494
+ content = schema.nodeFromJSON(options.json);
17495
+ }
17496
+ if (!content) {
17497
+ content = node.content;
17498
+ }
17499
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
17500
+ try {
17501
+ updatedNode.check();
17502
+ } catch {
17503
+ console.error("Updated node does not conform to the schema");
17504
+ return false;
17505
+ }
17506
+ tr.replaceWith(posFrom, posTo, updatedNode);
17507
+ }
17508
+ return true;
17509
+ },
17510
+ /**
17511
+ * Removes a structured content.
17512
+ * @category Command
17513
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
17514
+ */
17515
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
17516
+ if (!structuredContentTags.length) {
17517
+ return true;
17518
+ }
17519
+ if (dispatch) {
17520
+ structuredContentTags.forEach((structuredContent) => {
17521
+ const { pos, node } = structuredContent;
17522
+ const posFrom = tr.mapping.map(pos);
17523
+ const posTo = tr.mapping.map(pos + node.nodeSize);
17524
+ const currentNode = tr.doc.nodeAt(posFrom);
17525
+ if (currentNode && node.eq(currentNode)) {
17526
+ tr.delete(posFrom, posTo);
17527
+ }
17528
+ });
17529
+ }
17530
+ return true;
17531
+ },
17532
+ /**
17533
+ * Removes a structured content by ID.
17534
+ * @category Command
17535
+ * @param {string | string[]} idOrIds
17536
+ */
17537
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state, tr }) => {
17538
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state);
17539
+ if (!structuredContentTags.length) {
17540
+ return true;
17541
+ }
17542
+ if (dispatch) {
17543
+ structuredContentTags.forEach((structuredContent) => {
17544
+ const { pos, node } = structuredContent;
17545
+ const posFrom = tr.mapping.map(pos);
17546
+ const posTo = tr.mapping.map(pos + node.nodeSize);
17547
+ const currentNode = tr.doc.nodeAt(posFrom);
17548
+ if (currentNode && node.eq(currentNode)) {
17549
+ tr.delete(posFrom, posTo);
17550
+ }
17551
+ });
17552
+ }
17553
+ return true;
17554
+ },
17555
+ /**
17556
+ * Removes a structured content at cursor, preserving its content.
17557
+ * @category Command
17558
+ */
17559
+ deleteStructuredContentAtSelection: () => ({ dispatch, state, tr }) => {
17560
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
17561
+ const structuredContent = findParentNode(predicate)(state.selection);
17562
+ if (!structuredContent) {
17563
+ return true;
17564
+ }
17565
+ if (dispatch) {
17566
+ const { node, pos } = structuredContent;
17567
+ const posFrom = pos;
17568
+ const posTo = posFrom + node.nodeSize;
17569
+ const content = node.content;
17570
+ tr.replaceWith(posFrom, posTo, content);
17571
+ }
17572
+ return true;
17573
+ }
17574
+ };
17575
+ },
17576
+ addHelpers() {
17577
+ return {
17578
+ ...structuredContentHelpers
17579
+ };
17580
+ }
17581
+ });
17582
+ class DocumentSectionView {
17583
+ constructor(node, getPos, decorations, editor) {
17584
+ __privateAdd(this, _DocumentSectionView_instances);
17585
+ this.node = node;
17586
+ this.editor = editor;
17587
+ this.decorations = decorations;
17588
+ this.view = editor.view;
17589
+ this.getPos = getPos;
17590
+ __privateMethod(this, _DocumentSectionView_instances, init_fn2).call(this);
17591
+ }
17592
+ }
17593
+ _DocumentSectionView_instances = new WeakSet();
17594
+ init_fn2 = function() {
17595
+ const { attrs } = this.node;
17596
+ const { id, title, description } = attrs;
17597
+ this.dom = document.createElement("div");
17598
+ this.dom.className = "sd-document-section-block";
17599
+ this.dom.setAttribute("data-id", id);
17600
+ this.dom.setAttribute("data-title", title);
17601
+ this.dom.setAttribute("data-description", description);
17602
+ this.dom.setAttribute("aria-label", "Document section");
17603
+ __privateMethod(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
17604
+ this.contentDOM = document.createElement("div");
17605
+ this.contentDOM.className = "sd-document-section-block-content";
17606
+ this.contentDOM.setAttribute("contenteditable", "true");
17607
+ this.dom.appendChild(this.contentDOM);
17608
+ };
17609
+ addToolTip_fn = function() {
17610
+ const { title } = this.node.attrs;
17611
+ this.infoDiv = document.createElement("div");
17612
+ this.infoDiv.className = "sd-document-section-block-info";
17613
+ const textSpan = document.createElement("span");
17614
+ textSpan.textContent = title || "Document section";
17615
+ this.infoDiv.appendChild(textSpan);
17616
+ this.infoDiv.setAttribute("contenteditable", "false");
17617
+ this.dom.appendChild(this.infoDiv);
17618
+ };
17619
+ const getAllSections = (editor) => {
17620
+ if (!editor) return [];
17621
+ const type = editor.schema.nodes.documentSection;
17622
+ if (!type) return [];
17623
+ const sections = [];
17624
+ const { state } = editor;
17625
+ state.doc.descendants((node, pos) => {
17626
+ if (node.type.name === type.name) {
17627
+ sections.push({ node, pos });
17628
+ }
17629
+ });
17630
+ return sections;
17631
+ };
17632
+ const exportSectionsToHTML = (editor) => {
17633
+ const sections = getAllSections(editor);
17634
+ const processedSections = /* @__PURE__ */ new Set();
17635
+ const result = [];
17636
+ sections.forEach(({ node }) => {
17637
+ const { attrs } = node;
17638
+ const { id, title, description } = attrs;
17639
+ if (processedSections.has(id)) return;
17640
+ processedSections.add(id);
17641
+ const html = getHTMLFromNode(node, editor);
17642
+ result.push({
17643
+ id,
17644
+ title,
17645
+ description,
17646
+ html
17647
+ });
17648
+ });
17649
+ return result;
17650
+ };
17651
+ const getHTMLFromNode = (node, editor) => {
17652
+ const tempDocument = document.implementation.createHTMLDocument();
17653
+ const container = tempDocument.createElement("div");
17654
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
17655
+ container.appendChild(fragment);
17656
+ let html = container.innerHTML;
17657
+ return html;
17658
+ };
17659
+ const exportSectionsToJSON = (editor) => {
17660
+ const sections = getAllSections(editor);
17661
+ const processedSections = /* @__PURE__ */ new Set();
17662
+ const result = [];
17663
+ sections.forEach(({ node }) => {
17664
+ const { attrs } = node;
17665
+ const { id, title, description } = attrs;
17666
+ if (processedSections.has(id)) return;
17667
+ processedSections.add(id);
17668
+ result.push({
17669
+ id,
17670
+ title,
17671
+ description,
17672
+ content: node.toJSON()
17673
+ });
17674
+ });
17675
+ return result;
17676
+ };
17677
+ const getLinkedSectionEditor = (id, options, editor) => {
17678
+ const sections = getAllSections(editor);
17679
+ const section = sections.find((s) => s.node.attrs.id === id);
17680
+ if (!section) return null;
17681
+ const child = editor.createChildEditor({
17682
+ ...options,
17683
+ onUpdate: ({ editor: childEditor, transaction }) => {
17684
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
17685
+ if (isFromtLinkedParent) return;
17686
+ const updatedContent = childEditor.state.doc.content;
17687
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
17688
+ if (!sectionNode) return;
17689
+ const { pos, node } = sectionNode;
17690
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
17691
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
17692
+ tr.setMeta("fromLinkedChild", true);
17693
+ editor.view.dispatch(tr);
17694
+ }
17695
+ });
17696
+ editor.on("update", ({ transaction }) => {
17697
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
17698
+ if (isFromLinkedChild) return;
17699
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
17700
+ if (!sectionNode) return;
17701
+ const sectionContent = sectionNode.node.content;
17702
+ const json = {
17703
+ type: "doc",
17704
+ content: sectionContent.content.map((node) => node.toJSON())
17705
+ };
17706
+ const childTr = child.state.tr;
17707
+ childTr.setMeta("fromLinkedParent", true);
17708
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
17709
+ child.view.dispatch(childTr);
17710
+ });
17711
+ return child;
17712
+ };
17713
+ const SectionHelpers = {
17714
+ getAllSections,
17715
+ exportSectionsToHTML,
17716
+ exportSectionsToJSON,
17717
+ getLinkedSectionEditor
17718
+ };
17719
+ const DocumentSection = Node$1.create({
17720
+ name: "documentSection",
17721
+ group: "block",
17722
+ content: "block*",
17723
+ atom: true,
17724
+ isolating: true,
17725
+ addOptions() {
17726
+ return {
17727
+ htmlAttributes: {
17728
+ class: "sd-document-section-block",
17729
+ "aria-label": "Structured content block"
17730
+ }
17731
+ };
17732
+ },
17733
+ parseDOM() {
17734
+ return [
17735
+ {
17736
+ tag: "div.sd-document-section-block",
17737
+ priority: 60
17738
+ }
17739
+ ];
17740
+ },
17741
+ renderDOM({ htmlAttributes }) {
17742
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
17743
+ },
17744
+ addAttributes() {
17745
+ return {
17746
+ id: {},
17747
+ sdBlockId: {
17748
+ default: null,
17749
+ keepOnSplit: false,
17750
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17751
+ renderDOM: (attrs) => {
17752
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17753
+ }
17754
+ },
17755
+ title: {},
17756
+ description: {},
17757
+ sectionType: {},
17758
+ isLocked: { default: false }
17759
+ };
17760
+ },
17761
+ addNodeView() {
17762
+ return ({ node, editor, getPos, decorations }) => {
17763
+ return new DocumentSectionView(node, getPos, decorations, editor);
17764
+ };
17765
+ },
17766
+ addCommands() {
17767
+ return {
17768
+ /**
17769
+ * Create a lockable content section
17770
+ * @category Command
17771
+ * @param {SectionCreate} [options={}] - Section configuration
17772
+ * @example
17773
+ * editor.commands.createDocumentSection({
17774
+ * id: 1,
17775
+ * title: 'Terms & Conditions',
17776
+ * isLocked: true,
17777
+ * html: '<p>Legal content...</p>'
17778
+ * })
17779
+ */
17780
+ createDocumentSection: (options = {}) => ({ tr, state, dispatch, editor }) => {
17781
+ const { selection } = state;
17782
+ let { from: from2, to } = selection;
17783
+ let content = selection.content().content;
17784
+ const { html: optionsHTML, json: optionsJSON } = options;
17785
+ if (optionsHTML) {
17786
+ const html = htmlHandler(optionsHTML, this.editor);
17787
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
17788
+ content = doc2.content;
17789
+ }
17790
+ if (optionsJSON) {
17791
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
17792
+ }
17793
+ if (!content?.content?.length) {
17794
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
17795
+ }
17796
+ if (!options.id) {
17797
+ const allSections = SectionHelpers.getAllSections(editor);
17798
+ options.id = allSections.length + 1;
17799
+ }
17800
+ if (!options.title) {
17801
+ options.title = "Document section";
17802
+ }
17803
+ const node = this.type.createAndFill(options, content);
17804
+ if (!node) return false;
17805
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
17806
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
17807
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
17808
+ from2 = insertPos2;
17809
+ to = insertPos2;
17810
+ }
17811
+ tr.replaceRangeWith(from2, to, node);
17812
+ const nodeEnd = from2 + node.nodeSize;
17813
+ let shouldInsertParagraph = true;
17814
+ let insertPos = nodeEnd;
17815
+ if (nodeEnd >= tr.doc.content.size) {
17816
+ insertPos = tr.doc.content.size;
17817
+ if (insertPos > 0) {
17818
+ const $endPos = tr.doc.resolve(insertPos);
17819
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
17820
+ shouldInsertParagraph = false;
17821
+ }
17822
+ }
17823
+ }
17824
+ if (shouldInsertParagraph) {
17825
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
17826
+ tr.insert(insertPos, emptyParagraph);
17827
+ }
17828
+ if (dispatch) {
17829
+ tr.setMeta("documentSection", { action: "create" });
17830
+ dispatch(tr);
17831
+ setTimeout(() => {
17832
+ try {
17833
+ const currentState = editor.state;
17834
+ const docSize = currentState.doc.content.size;
17835
+ let targetPos = from2 + node.nodeSize;
17836
+ if (shouldInsertParagraph) {
17837
+ targetPos += 1;
17838
+ }
17839
+ targetPos = Math.min(targetPos, docSize);
17840
+ if (targetPos < docSize && targetPos > 0) {
17841
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
17842
+ const newTr = currentState.tr.setSelection(newSelection);
17843
+ editor.view.dispatch(newTr);
17844
+ }
17845
+ } catch (e) {
17846
+ console.warn("Could not set delayed selection:", e);
17847
+ }
17848
+ }, 0);
17849
+ }
17850
+ return true;
17851
+ },
17852
+ /**
17853
+ * Remove section wrapper at cursor, preserving its content
17854
+ * @category Command
17855
+ * @example
17856
+ * editor.commands.removeSectionAtSelection()
17857
+ * @note Content stays in document, only section wrapper is removed
17858
+ */
17859
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
17860
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
17861
+ if (!sdtNode) return false;
17862
+ const { node, pos } = sdtNode;
17863
+ const nodeStart = pos;
17864
+ const nodeEnd = nodeStart + node.nodeSize;
17865
+ const contentToPreserve = node.content;
17866
+ tr.delete(nodeStart, nodeEnd);
17867
+ if (contentToPreserve.size > 0) {
17868
+ tr.insert(nodeStart, contentToPreserve);
17869
+ }
17870
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
17871
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
17872
+ if (dispatch) {
17873
+ tr.setMeta("documentSection", { action: "delete" });
17874
+ dispatch(tr);
17875
+ }
17876
+ return true;
17877
+ },
17878
+ /**
17879
+ * Delete section and all its content
17880
+ * @category Command
17881
+ * @param {number} id - Section to delete
17882
+ * @example
17883
+ * editor.commands.removeSectionById(123)
17884
+ */
17885
+ removeSectionById: (id) => ({ tr, dispatch }) => {
17886
+ const sections = SectionHelpers.getAllSections(this.editor);
17887
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
17888
+ if (!sectionToRemove) return false;
17889
+ const { pos, node } = sectionToRemove;
17890
+ const nodeStart = pos;
17891
+ const nodeEnd = nodeStart + node.nodeSize;
17892
+ tr.delete(nodeStart, nodeEnd);
17893
+ if (dispatch) {
17894
+ tr.setMeta("documentSection", { action: "delete", id });
17895
+ dispatch(tr);
17896
+ }
17897
+ return true;
17898
+ },
17899
+ /**
17900
+ * Lock section against edits
17901
+ * @category Command
17902
+ * @param {number} id - Section to lock
17903
+ * @example
17904
+ * editor.commands.lockSectionById(123)
17905
+ */
17906
+ lockSectionById: (id) => ({ tr, dispatch }) => {
17907
+ const sections = SectionHelpers.getAllSections(this.editor);
17908
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
17909
+ if (!sectionToLock) return false;
17910
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
17911
+ if (dispatch) {
17912
+ tr.setMeta("documentSection", { action: "lock", id });
17913
+ dispatch(tr);
17914
+ }
17915
+ return true;
17916
+ },
17917
+ /**
17918
+ * Modify section attributes or content
17919
+ * @category Command
17920
+ * @param {SectionUpdate} options - Changes to apply
17921
+ * @example
17922
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
17923
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
17924
+ * editor.commands.updateSectionById({
17925
+ * id: 123,
17926
+ * html: '<p>Updated</p>',
17927
+ * attrs: { title: 'New Title' }
17928
+ * })
17929
+ */
17930
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
17931
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
17932
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
17933
+ if (!sectionToUpdate) return false;
17934
+ const { pos, node } = sectionToUpdate;
17935
+ let newContent = null;
17936
+ if (html) {
17937
+ const htmlDoc = htmlHandler(html, editor || this.editor);
17938
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
17939
+ newContent = doc2.content;
17940
+ }
17941
+ if (json) {
17942
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
17943
+ }
17944
+ if (!newContent) {
17945
+ newContent = node.content;
17946
+ }
17947
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
17948
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
17949
+ if (dispatch) {
17950
+ tr.setMeta("documentSection", { action: "update", id, attrs });
17951
+ dispatch(tr);
17952
+ }
17953
+ return true;
17954
+ }
17955
+ };
17956
+ },
17957
+ addHelpers() {
17958
+ return {
17959
+ ...SectionHelpers
17960
+ };
17961
+ }
17962
+ });
17963
+ const Document = Node$1.create({
17964
+ name: "doc",
17965
+ topNode: true,
17966
+ content: "block+",
17967
+ parseDOM() {
17968
+ return [{ tag: "doc" }];
17969
+ },
17970
+ renderDOM() {
17971
+ return ["doc", 0];
17972
+ },
17973
+ addAttributes() {
17974
+ return {
17975
+ attributes: {
17976
+ rendered: false,
17977
+ "aria-label": "Document node"
17978
+ }
17979
+ };
17980
+ },
17981
+ addCommands() {
17982
+ return {
17983
+ /**
17984
+ * Get document statistics
17985
+ * @category Command
17986
+ * @example
17987
+ * // Get word and character count
17988
+ * const stats = editor.commands.getDocumentStats()
17989
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
17990
+ * @note Returns word count, character count, and paragraph count
17991
+ */
17992
+ getDocumentStats: () => ({ editor }) => {
17993
+ const text = editor.getText();
17994
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
17995
+ const characters = text.length;
17996
+ const paragraphs = editor.state.doc.content.childCount;
17997
+ return {
17998
+ words,
17999
+ characters,
18000
+ paragraphs
18001
+ };
18002
+ },
18003
+ /**
18004
+ * Clear entire document
18005
+ * @category Command
18006
+ * @example
18007
+ * editor.commands.clearDocument()
18008
+ * @note Replaces all content with an empty paragraph
18009
+ */
18010
+ clearDocument: () => ({ commands: commands2 }) => {
18011
+ return commands2.setContent("<p></p>");
18012
+ }
18013
+ };
18014
+ }
18015
+ });
18016
+ const Text = Node$1.create({
18017
+ name: "text",
18018
+ group: "inline",
18019
+ inline: true,
18020
+ addOptions() {
18021
+ return {};
18022
+ }
18023
+ });
18024
+ const splitRun = () => (props) => {
18025
+ const { state, view, tr } = props;
18026
+ const { $from, empty: empty2 } = state.selection;
18027
+ if (!empty2) return false;
18028
+ if ($from.parent.type.name !== "run") return false;
18029
+ const handled = splitBlock(state, (transaction) => {
18030
+ view.dispatch(transaction);
18031
+ });
18032
+ if (handled) {
18033
+ tr.setMeta("preventDispatch", true);
18034
+ }
18035
+ return handled;
18036
+ };
18037
+ const Run = OxmlNode.create({
18038
+ name: "run",
18039
+ oXmlName: "w:r",
18040
+ group: "inline",
18041
+ inline: true,
18042
+ content: "inline*",
18043
+ selectable: false,
18044
+ childToAttributes: ["runProperties"],
18045
+ addOptions() {
18046
+ return {
18047
+ htmlAttributes: {
18048
+ "data-run": "1"
18049
+ }
18050
+ };
18051
+ },
18052
+ addAttributes() {
18053
+ return {
18054
+ runProperties: {
18055
+ default: null,
18056
+ rendered: false,
18057
+ keepOnSplit: true
18058
+ },
18059
+ rsidR: {
18060
+ default: null,
18061
+ rendered: false,
18062
+ keepOnSplit: true
18063
+ },
18064
+ rsidRPr: {
18065
+ default: null,
18066
+ rendered: false,
18067
+ keepOnSplit: true
18068
+ },
18069
+ rsidDel: {
18070
+ default: null,
18071
+ rendered: false,
18072
+ keepOnSplit: true
18073
+ }
18074
+ };
18075
+ },
18076
+ addCommands() {
18077
+ return {
18078
+ splitRun
18079
+ };
18080
+ },
18081
+ parseDOM() {
18082
+ return [{ tag: "span[data-run]" }];
18083
+ },
18084
+ renderDOM({ htmlAttributes }) {
18085
+ const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
18086
+ return ["span", base2, 0];
18087
+ }
18088
+ });
18089
+ const inputRegex$1 = /^\s*([-+*])\s$/;
18090
+ const BulletList = Node$1.create({
18091
+ name: "bulletList",
18092
+ group: "block list",
18093
+ selectable: false,
18094
+ content() {
18095
+ return `${this.options.itemTypeName}+`;
18096
+ },
18097
+ addOptions() {
18098
+ return {
18099
+ itemTypeName: "listItem",
18100
+ htmlAttributes: {
18101
+ "aria-label": "Bullet list node"
18102
+ },
18103
+ keepMarks: true,
18104
+ keepAttributes: false
18105
+ };
18106
+ },
18107
+ parseDOM() {
18108
+ return [{ tag: "ul" }];
18109
+ },
18110
+ renderDOM({ htmlAttributes }) {
18111
+ const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
18112
+ return ["ul", attributes, 0];
18113
+ },
18114
+ addAttributes() {
18115
+ return {
18116
+ "list-style-type": {
18117
+ default: "bullet",
18118
+ rendered: false
18119
+ },
18120
+ listId: {
18121
+ rendered: false
18122
+ },
18123
+ sdBlockId: {
18124
+ default: null,
18125
+ keepOnSplit: false,
18126
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
18127
+ renderDOM: (attrs) => {
18128
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
18129
+ }
18130
+ },
18131
+ attributes: {
18132
+ rendered: false,
18133
+ keepOnSplit: true
18134
+ }
18135
+ };
18136
+ },
18137
+ addCommands() {
18138
+ return {
18139
+ /**
18140
+ * Toggle a bullet list at the current selection
18141
+ * @category Command
18142
+ * @example
18143
+ * // Toggle bullet list on selected text
18144
+ * editor.commands.toggleBulletList()
18145
+ * @note Converts selected paragraphs to list items or removes list formatting
18146
+ */
18147
+ toggleBulletList: () => (params2) => {
18148
+ return toggleList(this.type)(params2);
18149
+ }
18150
+ };
18151
+ },
18152
+ addShortcuts() {
18153
+ return {
18154
+ "Mod-Shift-8": () => {
18155
+ return this.editor.commands.toggleBulletList();
18156
+ }
18157
+ };
18158
+ },
18159
+ addInputRules() {
18160
+ return [
18161
+ new InputRule({
18162
+ match: inputRegex$1,
18163
+ handler: ({ state, range }) => {
18164
+ const $pos = state.selection.$from;
18165
+ const listItemType = state.schema.nodes.listItem;
18166
+ for (let depth = $pos.depth; depth >= 0; depth--) {
18167
+ if ($pos.node(depth).type === listItemType) {
18168
+ return null;
18169
+ }
18170
+ }
18171
+ const { tr } = state;
18172
+ tr.delete(range.from, range.to);
18173
+ ListHelpers.createNewList({
18174
+ listType: this.type,
18175
+ tr,
18176
+ editor: this.editor
18177
+ });
18178
+ }
18179
+ })
17199
18180
  ];
17200
18181
  }
17201
18182
  });
@@ -18561,7 +19542,7 @@ class ListItemNodeView {
18561
19542
  this.decorations = decorations;
18562
19543
  this.view = editor.view;
18563
19544
  this.getPos = getPos;
18564
- __privateMethod(this, _ListItemNodeView_instances, init_fn2).call(this);
19545
+ __privateMethod(this, _ListItemNodeView_instances, init_fn3).call(this);
18565
19546
  activeListItemNodeViews.add(this);
18566
19547
  }
18567
19548
  refreshIndentStyling() {
@@ -18622,7 +19603,7 @@ class ListItemNodeView {
18622
19603
  }
18623
19604
  }
18624
19605
  _ListItemNodeView_instances = new WeakSet();
18625
- init_fn2 = function() {
19606
+ init_fn3 = function() {
18626
19607
  const { attrs } = this.node;
18627
19608
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
18628
19609
  let orderMarker = "";
@@ -25731,984 +26712,335 @@ const PageNumber = Node$1.create({
25731
26712
  }
25732
26713
  };
25733
26714
  },
25734
- addAttributes() {
25735
- return {
25736
- marksAsAttrs: {
25737
- default: null,
25738
- rendered: false
25739
- }
25740
- };
25741
- },
25742
- addNodeView() {
25743
- return ({ node, editor, getPos, decorations }) => {
25744
- const htmlAttributes = this.options.htmlAttributes;
25745
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
25746
- };
25747
- },
25748
- parseDOM() {
25749
- return [{ tag: 'span[data-id="auto-page-number"' }];
25750
- },
25751
- renderDOM({ htmlAttributes }) {
25752
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
25753
- },
25754
- addCommands() {
25755
- return {
25756
- /**
25757
- * Insert an automatic page number
25758
- * @category Command
25759
- * @returns {Function} Command function
25760
- * @example
25761
- * editor.commands.addAutoPageNumber()
25762
- * @note Only works in header/footer contexts
25763
- */
25764
- addAutoPageNumber: () => ({ tr, dispatch, state, editor }) => {
25765
- const { options } = editor;
25766
- if (!options.isHeaderOrFooter) return false;
25767
- const { schema } = state;
25768
- const pageNumberType = schema?.nodes?.["page-number"];
25769
- if (!pageNumberType) return false;
25770
- const pageNumberNodeJSON = { type: "page-number" };
25771
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
25772
- if (dispatch) {
25773
- tr.replaceSelectionWith(pageNumberNode, false);
25774
- tr.setMeta("forceUpdatePagination", true);
25775
- }
25776
- return true;
25777
- }
25778
- };
25779
- },
25780
- addShortcuts() {
25781
- return {
25782
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
25783
- };
25784
- }
25785
- });
25786
- const TotalPageCount = Node$1.create({
25787
- name: "total-page-number",
25788
- group: "inline",
25789
- inline: true,
25790
- atom: true,
25791
- draggable: false,
25792
- selectable: false,
25793
- content: "text*",
25794
- addOptions() {
25795
- return {
25796
- htmlAttributes: {
25797
- contenteditable: false,
25798
- "data-id": "auto-total-pages",
25799
- "aria-label": "Total page count node",
25800
- class: "sd-editor-auto-total-pages"
25801
- }
25802
- };
25803
- },
25804
- addAttributes() {
25805
- return {
25806
- marksAsAttrs: {
25807
- default: null,
25808
- rendered: false
25809
- }
25810
- };
25811
- },
25812
- addNodeView() {
25813
- return ({ node, editor, getPos, decorations }) => {
25814
- const htmlAttributes = this.options.htmlAttributes;
25815
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
25816
- };
25817
- },
25818
- parseDOM() {
25819
- return [{ tag: 'span[data-id="auto-total-pages"' }];
25820
- },
25821
- renderDOM({ htmlAttributes }) {
25822
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
25823
- },
25824
- addCommands() {
25825
- return {
25826
- /**
25827
- * Insert total page count
25828
- * @category Command
25829
- * @returns {Function} Command function
25830
- * @example
25831
- * editor.commands.addTotalPageCount()
25832
- * @note Only works in header/footer contexts
25833
- */
25834
- addTotalPageCount: () => ({ tr, dispatch, state, editor }) => {
25835
- const { options } = editor;
25836
- if (!options.isHeaderOrFooter) return false;
25837
- const { schema } = state;
25838
- const pageNumberType = schema.nodes?.["total-page-number"];
25839
- if (!pageNumberType) return false;
25840
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
25841
- const pageNumberNode = {
25842
- type: "total-page-number",
25843
- content: [{ type: "text", text: String(currentPages) }]
25844
- };
25845
- const pageNode = schema.nodeFromJSON(pageNumberNode);
25846
- if (dispatch) {
25847
- tr.replaceSelectionWith(pageNode, false);
25848
- }
25849
- return true;
25850
- }
25851
- };
25852
- },
25853
- addShortcuts() {
25854
- return {
25855
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
25856
- };
25857
- }
25858
- });
25859
- const getNodeAttributes = (nodeName, editor) => {
25860
- switch (nodeName) {
25861
- case "page-number":
25862
- return {
25863
- text: editor.options.currentPageNumber || "1",
25864
- className: "sd-editor-auto-page-number",
25865
- dataId: "auto-page-number",
25866
- ariaLabel: "Page number node"
25867
- };
25868
- case "total-page-number":
25869
- return {
25870
- text: editor.options.parentEditor?.currentTotalPages || "1",
25871
- className: "sd-editor-auto-total-pages",
25872
- dataId: "auto-total-pages",
25873
- ariaLabel: "Total page count node"
25874
- };
25875
- default:
25876
- return {};
25877
- }
25878
- };
25879
- class AutoPageNumberNodeView {
25880
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
25881
- __privateAdd(this, _AutoPageNumberNodeView_instances);
25882
- this.node = node;
25883
- this.editor = editor;
25884
- this.view = editor.view;
25885
- this.getPos = getPos;
25886
- this.editor = editor;
25887
- this.dom = __privateMethod(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
25888
- }
25889
- update(node) {
25890
- const incomingType = node?.type?.name;
25891
- const currentType = this.node?.type?.name;
25892
- if (!incomingType || incomingType !== currentType) return false;
25893
- this.node = node;
25894
- return true;
25895
- }
25896
- }
25897
- _AutoPageNumberNodeView_instances = new WeakSet();
25898
- renderDom_fn = function(node, htmlAttributes) {
25899
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
25900
- const content = document.createTextNode(String(attrs.text));
25901
- const nodeContent = document.createElement("span");
25902
- nodeContent.className = attrs.className;
25903
- nodeContent.setAttribute("data-id", attrs.dataId);
25904
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
25905
- const currentPos = this.getPos();
25906
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
25907
- __privateMethod(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
25908
- Object.assign(nodeContent.style, styles);
25909
- nodeContent.appendChild(content);
25910
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
25911
- if (value) nodeContent.setAttribute(key2, value);
25912
- });
25913
- return nodeContent;
25914
- };
25915
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
25916
- setTimeout(() => {
25917
- const { state } = this.editor;
25918
- const { dispatch } = this.view;
25919
- const node = state.doc.nodeAt(pos);
25920
- if (!node || node.isText) return;
25921
- const currentMarks = node.attrs.marksAsAttrs || [];
25922
- const newMarks = marks.map((m) => ({ type: m.type.name, attrs: m.attrs }));
25923
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
25924
- if (isEqual) return;
25925
- const newAttrs = {
25926
- ...node.attrs,
25927
- marksAsAttrs: newMarks
25928
- };
25929
- const tr = state.tr.setNodeMarkup(pos, void 0, newAttrs);
25930
- dispatch(tr);
25931
- }, 0);
25932
- };
25933
- const getMarksFromNeighbors = (currentPos, view) => {
25934
- const $pos = view.state.doc.resolve(currentPos);
25935
- const styles = {};
25936
- const marks = [];
25937
- const before = $pos.nodeBefore;
25938
- if (before) {
25939
- Object.assign(styles, processMarks(before.marks));
25940
- marks.push(...before.marks);
25941
- }
25942
- const after = $pos.nodeAfter;
25943
- if (after) {
25944
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
25945
- marks.push(...after.marks);
25946
- }
25947
- return {
25948
- styles,
25949
- marks
25950
- };
25951
- };
25952
- const processMarks = (marks) => {
25953
- const styles = {};
25954
- marks.forEach((mark) => {
25955
- const { type, attrs } = mark;
25956
- switch (type.name) {
25957
- case "textStyle":
25958
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
25959
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
25960
- if (attrs.color) styles["color"] = attrs.color;
25961
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
25962
- break;
25963
- case "bold":
25964
- styles["font-weight"] = "bold";
25965
- break;
25966
- case "italic":
25967
- styles["font-style"] = "italic";
25968
- break;
25969
- case "underline":
25970
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
25971
- break;
25972
- case "strike":
25973
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
25974
- break;
25975
- default:
25976
- if (attrs?.style) {
25977
- Object.entries(attrs.style).forEach(([key2, value]) => {
25978
- styles[key2] = value;
25979
- });
25980
- }
25981
- break;
25982
- }
25983
- });
25984
- return styles;
25985
- };
25986
- const ShapeContainer = Node$1.create({
25987
- name: "shapeContainer",
25988
- group: "block",
25989
- content: "block+",
25990
- isolating: true,
25991
- addOptions() {
25992
- return {
25993
- htmlAttributes: {
25994
- class: "sd-editor-shape-container",
25995
- "aria-label": "Shape container node"
25996
- }
25997
- };
25998
- },
25999
- addAttributes() {
26000
- return {
26001
- fillcolor: {
26002
- renderDOM: (attrs) => {
26003
- if (!attrs.fillcolor) return {};
26004
- return {
26005
- style: `background-color: ${attrs.fillcolor}`
26006
- };
26007
- }
26008
- },
26009
- sdBlockId: {
26010
- default: null,
26011
- keepOnSplit: false,
26012
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26013
- renderDOM: (attrs) => {
26014
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26015
- }
26016
- },
26017
- style: {
26018
- renderDOM: (attrs) => {
26019
- if (!attrs.style) return {};
26020
- return {
26021
- style: attrs.style
26022
- };
26023
- }
26024
- },
26025
- wrapAttributes: {
26026
- rendered: false
26027
- },
26028
- attributes: {
26029
- rendered: false
26030
- }
26031
- };
26032
- },
26033
- parseDOM() {
26034
- return [
26035
- {
26036
- tag: `div[data-type="${this.name}"]`
26037
- }
26038
- ];
26039
- },
26040
- renderDOM({ htmlAttributes }) {
26041
- return [
26042
- "div",
26043
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26044
- 0
26045
- ];
26046
- }
26047
- });
26048
- const ShapeTextbox = Node$1.create({
26049
- name: "shapeTextbox",
26050
- group: "block",
26051
- content: "paragraph* block*",
26052
- isolating: true,
26053
- addOptions() {
26054
- return {
26055
- htmlAttributes: {
26056
- class: "sd-editor-shape-textbox",
26057
- "aria-label": "Shape textbox node"
26058
- }
26059
- };
26060
- },
26061
- addAttributes() {
26062
- return {
26063
- sdBlockId: {
26064
- default: null,
26065
- keepOnSplit: false,
26066
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26067
- renderDOM: (attrs) => {
26068
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26069
- }
26070
- },
26071
- attributes: {
26072
- rendered: false
26073
- }
26074
- };
26075
- },
26076
- parseDOM() {
26077
- return [
26078
- {
26079
- tag: `div[data-type="${this.name}"]`
26080
- }
26081
- ];
26082
- },
26083
- renderDOM({ htmlAttributes }) {
26084
- return [
26085
- "div",
26086
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26087
- 0
26088
- ];
26089
- }
26090
- });
26091
- const ContentBlock = Node$1.create({
26092
- name: "contentBlock",
26093
- group: "inline",
26094
- content: "",
26095
- isolating: true,
26096
- atom: true,
26097
- inline: true,
26098
- addOptions() {
26099
- return {
26100
- htmlAttributes: {
26101
- contenteditable: false
26102
- }
26103
- };
26104
- },
26105
- addAttributes() {
26106
- return {
26107
- horizontalRule: {
26108
- default: false,
26109
- renderDOM: ({ horizontalRule }) => {
26110
- if (!horizontalRule) return {};
26111
- return { "data-horizontal-rule": "true" };
26112
- }
26113
- },
26114
- size: {
26115
- default: null,
26116
- renderDOM: ({ size }) => {
26117
- if (!size) return {};
26118
- let style = "";
26119
- if (size.top) style += `top: ${size.top}px; `;
26120
- if (size.left) style += `left: ${size.left}px; `;
26121
- if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
26122
- if (size.height)
26123
- style += `height: ${size.height.toString().endsWith("%") ? size.height : `${size.height}px`}; `;
26124
- return { style };
26125
- }
26126
- },
26127
- background: {
26128
- default: null,
26129
- renderDOM: (attrs) => {
26130
- if (!attrs.background) return {};
26131
- return {
26132
- style: `background-color: ${attrs.background}`
26133
- };
26134
- }
26135
- },
26136
- drawingContent: {
26137
- rendered: false
26138
- },
26139
- attributes: {
26715
+ addAttributes() {
26716
+ return {
26717
+ marksAsAttrs: {
26718
+ default: null,
26140
26719
  rendered: false
26141
26720
  }
26142
26721
  };
26143
26722
  },
26723
+ addNodeView() {
26724
+ return ({ node, editor, getPos, decorations }) => {
26725
+ const htmlAttributes = this.options.htmlAttributes;
26726
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
26727
+ };
26728
+ },
26144
26729
  parseDOM() {
26145
- return [
26146
- {
26147
- tag: `div[data-type="${this.name}"]`
26148
- }
26149
- ];
26730
+ return [{ tag: 'span[data-id="auto-page-number"' }];
26150
26731
  },
26151
26732
  renderDOM({ htmlAttributes }) {
26152
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
26733
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
26153
26734
  },
26154
26735
  addCommands() {
26155
26736
  return {
26156
26737
  /**
26157
- * Insert a horizontal rule
26158
- * @category Command
26159
- * @example
26160
- * editor.commands.insertHorizontalRule()
26161
- * @note Creates a visual separator between content sections
26162
- */
26163
- insertHorizontalRule: () => ({ commands: commands2 }) => {
26164
- return commands2.insertContent({
26165
- type: this.name,
26166
- attrs: {
26167
- horizontalRule: true,
26168
- size: { width: "100%", height: 2 },
26169
- background: "#e5e7eb"
26170
- }
26171
- });
26172
- },
26173
- /**
26174
- * Insert a content block
26738
+ * Insert an automatic page number
26175
26739
  * @category Command
26176
- * @param {ContentBlockConfig} config - Block configuration
26177
- * @example
26178
- * // Insert a spacer block
26179
- * editor.commands.insertContentBlock({ size: { height: 20 } })
26180
- *
26740
+ * @returns {Function} Command function
26181
26741
  * @example
26182
- * // Insert a colored divider
26183
- * editor.commands.insertContentBlock({
26184
- * size: { width: '50%', height: 3 },
26185
- * background: '#3b82f6'
26186
- * })
26187
- * @note Used for spacing, dividers, and special inline content
26742
+ * editor.commands.addAutoPageNumber()
26743
+ * @note Only works in header/footer contexts
26188
26744
  */
26189
- insertContentBlock: (config) => ({ commands: commands2 }) => {
26190
- return commands2.insertContent({
26191
- type: this.name,
26192
- attrs: config
26193
- });
26745
+ addAutoPageNumber: () => ({ tr, dispatch, state, editor }) => {
26746
+ const { options } = editor;
26747
+ if (!options.isHeaderOrFooter) return false;
26748
+ const { schema } = state;
26749
+ const pageNumberType = schema?.nodes?.["page-number"];
26750
+ if (!pageNumberType) return false;
26751
+ const pageNumberNodeJSON = { type: "page-number" };
26752
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
26753
+ if (dispatch) {
26754
+ tr.replaceSelectionWith(pageNumberNode, false);
26755
+ tr.setMeta("forceUpdatePagination", true);
26756
+ }
26757
+ return true;
26194
26758
  }
26195
26759
  };
26760
+ },
26761
+ addShortcuts() {
26762
+ return {
26763
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
26764
+ };
26196
26765
  }
26197
26766
  });
26198
- class StructuredContentViewBase {
26199
- constructor(props) {
26200
- __publicField(this, "node");
26201
- __publicField(this, "view");
26202
- __publicField(this, "getPos");
26203
- __publicField(this, "decorations");
26204
- __publicField(this, "innerDecorations");
26205
- __publicField(this, "editor");
26206
- __publicField(this, "extension");
26207
- __publicField(this, "htmlAttributes");
26208
- __publicField(this, "root");
26209
- __publicField(this, "isDragging", false);
26210
- this.node = props.node;
26211
- this.view = props.editor.view;
26212
- this.getPos = props.getPos;
26213
- this.decorations = props.decorations;
26214
- this.innerDecorations = props.innerDecorations;
26215
- this.editor = props.editor;
26216
- this.extension = props.extension;
26217
- this.htmlAttributes = props.htmlAttributes;
26218
- this.mount(props);
26219
- }
26220
- mount() {
26221
- return;
26222
- }
26223
- get dom() {
26224
- return this.root;
26225
- }
26226
- get contentDOM() {
26227
- return null;
26228
- }
26229
- update(node, decorations, innerDecorations) {
26230
- if (node.type !== this.node.type) {
26231
- return false;
26232
- }
26233
- this.node = node;
26234
- this.decorations = decorations;
26235
- this.innerDecorations = innerDecorations;
26236
- this.updateHTMLAttributes();
26237
- return true;
26238
- }
26239
- stopEvent(event) {
26240
- if (!this.dom) return false;
26241
- const target = event.target;
26242
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
26243
- if (!isInElement) return false;
26244
- const isDragEvent = event.type.startsWith("drag");
26245
- const isDropEvent = event.type === "drop";
26246
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
26247
- if (isInput && !isDropEvent && !isDragEvent) return true;
26248
- const { isEditable } = this.editor;
26249
- const { isDragging } = this;
26250
- const isDraggable = !!this.node.type.spec.draggable;
26251
- const isSelectable = NodeSelection.isSelectable(this.node);
26252
- const isCopyEvent = event.type === "copy";
26253
- const isPasteEvent = event.type === "paste";
26254
- const isCutEvent = event.type === "cut";
26255
- const isClickEvent = event.type === "mousedown";
26256
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
26257
- event.preventDefault();
26258
- }
26259
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
26260
- event.preventDefault();
26261
- return false;
26262
- }
26263
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
26264
- const dragHandle = target.closest("[data-drag-handle]");
26265
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
26266
- if (isValidDragHandle) {
26267
- this.isDragging = true;
26268
- document.addEventListener(
26269
- "dragend",
26270
- () => {
26271
- this.isDragging = false;
26272
- },
26273
- { once: true }
26274
- );
26275
- document.addEventListener(
26276
- "drop",
26277
- () => {
26278
- this.isDragging = false;
26279
- },
26280
- { once: true }
26281
- );
26282
- document.addEventListener(
26283
- "mouseup",
26284
- () => {
26285
- this.isDragging = false;
26286
- },
26287
- { once: true }
26288
- );
26289
- }
26290
- }
26291
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
26292
- return false;
26293
- }
26294
- return true;
26295
- }
26296
- ignoreMutation(mutation) {
26297
- if (!this.dom || !this.contentDOM) return true;
26298
- if (this.node.isLeaf || this.node.isAtom) return true;
26299
- if (mutation.type === "selection") return false;
26300
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
26301
- if (this.contentDOM.contains(mutation.target)) return false;
26302
- return true;
26303
- }
26304
- destroy() {
26305
- this.dom.remove();
26306
- this.contentDOM?.remove();
26307
- }
26308
- updateAttributes(attrs) {
26309
- const pos = this.getPos();
26310
- if (typeof pos !== "number") {
26311
- return;
26312
- }
26313
- return this.view.dispatch(
26314
- this.view.state.tr.setNodeMarkup(pos, void 0, {
26315
- ...this.node.attrs,
26316
- ...attrs
26317
- })
26318
- );
26319
- }
26320
- updateHTMLAttributes() {
26321
- const { extensionService } = this.editor;
26322
- const { attributes } = extensionService;
26323
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
26324
- this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
26325
- }
26326
- createDragHandle() {
26327
- const dragHandle = document.createElement("span");
26328
- dragHandle.classList.add("sd-structured-content-draggable");
26329
- dragHandle.draggable = true;
26330
- dragHandle.contentEditable = "false";
26331
- dragHandle.dataset.dragHandle = "";
26332
- const textElement = document.createElement("span");
26333
- textElement.textContent = "Structured content";
26334
- dragHandle.append(textElement);
26335
- return dragHandle;
26336
- }
26337
- onDragStart(event) {
26338
- const { view } = this.editor;
26339
- const target = event.target;
26340
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
26341
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
26342
- return;
26343
- }
26344
- let x = 0;
26345
- let y = 0;
26346
- if (this.dom !== dragHandle) {
26347
- const domBox = this.dom.getBoundingClientRect();
26348
- const handleBox = dragHandle.getBoundingClientRect();
26349
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
26350
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
26351
- x = handleBox.x - domBox.x + offsetX;
26352
- y = handleBox.y - domBox.y + offsetY;
26353
- }
26354
- event.dataTransfer?.setDragImage(this.dom, x, y);
26355
- const pos = this.getPos();
26356
- if (typeof pos !== "number") {
26357
- return;
26358
- }
26359
- const selection = NodeSelection.create(view.state.doc, pos);
26360
- const transaction = view.state.tr.setSelection(selection);
26361
- view.dispatch(transaction);
26362
- }
26363
- }
26364
- class StructuredContentInlineView extends StructuredContentViewBase {
26365
- constructor(props) {
26366
- super(props);
26367
- }
26368
- mount() {
26369
- this.buildView();
26370
- }
26371
- get contentDOM() {
26372
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
26373
- return contentElement || null;
26374
- }
26375
- createElement() {
26376
- const element = document.createElement("span");
26377
- element.classList.add(structuredContentClass$1);
26378
- element.setAttribute("data-structured-content", "");
26379
- const contentElement = document.createElement("span");
26380
- contentElement.classList.add(structuredContentInnerClass$1);
26381
- element.append(contentElement);
26382
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26383
- updateDOMAttributes(element, { ...domAttrs });
26384
- return { element, contentElement };
26385
- }
26386
- buildView() {
26387
- const { element } = this.createElement();
26388
- const dragHandle = this.createDragHandle();
26389
- element.prepend(dragHandle);
26390
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26391
- this.root = element;
26392
- }
26393
- updateView() {
26394
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26395
- updateDOMAttributes(this.dom, { ...domAttrs });
26396
- }
26397
- update(node, decorations, innerDecorations) {
26398
- const result = super.update(node, decorations, innerDecorations);
26399
- if (!result) return false;
26400
- this.updateView();
26401
- return true;
26402
- }
26403
- }
26404
- const structuredContentClass$1 = "sd-structured-content";
26405
- const structuredContentInnerClass$1 = "sd-structured-content__content";
26406
- const StructuredContent = Node$1.create({
26407
- name: "structuredContent",
26408
- group: "inline structuredContent",
26767
+ const TotalPageCount = Node$1.create({
26768
+ name: "total-page-number",
26769
+ group: "inline",
26409
26770
  inline: true,
26410
- content: "inline*",
26411
- isolating: true,
26412
- atom: false,
26413
- // false - has editable content.
26414
- draggable: true,
26771
+ atom: true,
26772
+ draggable: false,
26773
+ selectable: false,
26774
+ content: "text*",
26415
26775
  addOptions() {
26416
26776
  return {
26417
26777
  htmlAttributes: {
26418
- class: structuredContentClass$1,
26419
- "aria-label": "Structured content node"
26778
+ contenteditable: false,
26779
+ "data-id": "auto-total-pages",
26780
+ "aria-label": "Total page count node",
26781
+ class: "sd-editor-auto-total-pages"
26420
26782
  }
26421
26783
  };
26422
26784
  },
26423
26785
  addAttributes() {
26424
26786
  return {
26425
- id: {
26787
+ marksAsAttrs: {
26426
26788
  default: null,
26427
- parseDOM: (elem) => elem.getAttribute("data-id"),
26428
- renderDOM: (attrs) => {
26429
- if (!attrs.id) return {};
26430
- return { "data-id": attrs.id };
26431
- }
26432
- },
26433
- sdtPr: {
26434
26789
  rendered: false
26435
26790
  }
26436
26791
  };
26437
26792
  },
26793
+ addNodeView() {
26794
+ return ({ node, editor, getPos, decorations }) => {
26795
+ const htmlAttributes = this.options.htmlAttributes;
26796
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
26797
+ };
26798
+ },
26438
26799
  parseDOM() {
26439
- return [{ tag: "span[data-structured-content]" }];
26800
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
26440
26801
  },
26441
26802
  renderDOM({ htmlAttributes }) {
26442
- return [
26443
- "span",
26444
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26445
- "data-structured-content": ""
26446
- }),
26447
- 0
26448
- ];
26803
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
26449
26804
  },
26450
- addNodeView() {
26451
- return (props) => {
26452
- return new StructuredContentInlineView({ ...props });
26805
+ addCommands() {
26806
+ return {
26807
+ /**
26808
+ * Insert total page count
26809
+ * @category Command
26810
+ * @returns {Function} Command function
26811
+ * @example
26812
+ * editor.commands.addTotalPageCount()
26813
+ * @note Only works in header/footer contexts
26814
+ */
26815
+ addTotalPageCount: () => ({ tr, dispatch, state, editor }) => {
26816
+ const { options } = editor;
26817
+ if (!options.isHeaderOrFooter) return false;
26818
+ const { schema } = state;
26819
+ const pageNumberType = schema.nodes?.["total-page-number"];
26820
+ if (!pageNumberType) return false;
26821
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
26822
+ const pageNumberNode = {
26823
+ type: "total-page-number",
26824
+ content: [{ type: "text", text: String(currentPages) }]
26825
+ };
26826
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
26827
+ if (dispatch) {
26828
+ tr.replaceSelectionWith(pageNode, false);
26829
+ }
26830
+ return true;
26831
+ }
26832
+ };
26833
+ },
26834
+ addShortcuts() {
26835
+ return {
26836
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
26453
26837
  };
26454
26838
  }
26455
26839
  });
26456
- class StructuredContentBlockView extends StructuredContentViewBase {
26457
- constructor(props) {
26458
- super(props);
26459
- }
26460
- mount() {
26461
- this.buildView();
26462
- }
26463
- get contentDOM() {
26464
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
26465
- return contentElement || null;
26466
- }
26467
- createElement() {
26468
- const element = document.createElement("div");
26469
- element.classList.add(structuredContentClass);
26470
- element.setAttribute("data-structured-content-block", "");
26471
- const contentElement = document.createElement("div");
26472
- contentElement.classList.add(structuredContentInnerClass);
26473
- element.append(contentElement);
26474
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26475
- updateDOMAttributes(element, { ...domAttrs });
26476
- return { element, contentElement };
26477
- }
26478
- buildView() {
26479
- const { element } = this.createElement();
26480
- const dragHandle = this.createDragHandle();
26481
- element.prepend(dragHandle);
26482
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26483
- this.root = element;
26840
+ const getNodeAttributes = (nodeName, editor) => {
26841
+ switch (nodeName) {
26842
+ case "page-number":
26843
+ return {
26844
+ text: editor.options.currentPageNumber || "1",
26845
+ className: "sd-editor-auto-page-number",
26846
+ dataId: "auto-page-number",
26847
+ ariaLabel: "Page number node"
26848
+ };
26849
+ case "total-page-number":
26850
+ return {
26851
+ text: editor.options.parentEditor?.currentTotalPages || "1",
26852
+ className: "sd-editor-auto-total-pages",
26853
+ dataId: "auto-total-pages",
26854
+ ariaLabel: "Total page count node"
26855
+ };
26856
+ default:
26857
+ return {};
26484
26858
  }
26485
- updateView() {
26486
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26487
- updateDOMAttributes(this.dom, { ...domAttrs });
26859
+ };
26860
+ class AutoPageNumberNodeView {
26861
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
26862
+ __privateAdd(this, _AutoPageNumberNodeView_instances);
26863
+ this.node = node;
26864
+ this.editor = editor;
26865
+ this.view = editor.view;
26866
+ this.getPos = getPos;
26867
+ this.editor = editor;
26868
+ this.dom = __privateMethod(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
26488
26869
  }
26489
- update(node, decorations, innerDecorations) {
26490
- const result = super.update(node, decorations, innerDecorations);
26491
- if (!result) return false;
26492
- this.updateView();
26870
+ update(node) {
26871
+ const incomingType = node?.type?.name;
26872
+ const currentType = this.node?.type?.name;
26873
+ if (!incomingType || incomingType !== currentType) return false;
26874
+ this.node = node;
26493
26875
  return true;
26494
26876
  }
26495
26877
  }
26496
- const structuredContentClass = "sd-structured-content-block";
26497
- const structuredContentInnerClass = "sd-structured-content-block__content";
26498
- const StructuredContentBlock = Node$1.create({
26499
- name: "structuredContentBlock",
26500
- group: "block structuredContent",
26501
- content: "block*",
26878
+ _AutoPageNumberNodeView_instances = new WeakSet();
26879
+ renderDom_fn = function(node, htmlAttributes) {
26880
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
26881
+ const content = document.createTextNode(String(attrs.text));
26882
+ const nodeContent = document.createElement("span");
26883
+ nodeContent.className = attrs.className;
26884
+ nodeContent.setAttribute("data-id", attrs.dataId);
26885
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
26886
+ const currentPos = this.getPos();
26887
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
26888
+ __privateMethod(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
26889
+ Object.assign(nodeContent.style, styles);
26890
+ nodeContent.appendChild(content);
26891
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
26892
+ if (value) nodeContent.setAttribute(key2, value);
26893
+ });
26894
+ return nodeContent;
26895
+ };
26896
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
26897
+ setTimeout(() => {
26898
+ const { state } = this.editor;
26899
+ const { dispatch } = this.view;
26900
+ const node = state.doc.nodeAt(pos);
26901
+ if (!node || node.isText) return;
26902
+ const currentMarks = node.attrs.marksAsAttrs || [];
26903
+ const newMarks = marks.map((m) => ({ type: m.type.name, attrs: m.attrs }));
26904
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
26905
+ if (isEqual) return;
26906
+ const newAttrs = {
26907
+ ...node.attrs,
26908
+ marksAsAttrs: newMarks
26909
+ };
26910
+ const tr = state.tr.setNodeMarkup(pos, void 0, newAttrs);
26911
+ dispatch(tr);
26912
+ }, 0);
26913
+ };
26914
+ const getMarksFromNeighbors = (currentPos, view) => {
26915
+ const $pos = view.state.doc.resolve(currentPos);
26916
+ const styles = {};
26917
+ const marks = [];
26918
+ const before = $pos.nodeBefore;
26919
+ if (before) {
26920
+ Object.assign(styles, processMarks(before.marks));
26921
+ marks.push(...before.marks);
26922
+ }
26923
+ const after = $pos.nodeAfter;
26924
+ if (after) {
26925
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
26926
+ marks.push(...after.marks);
26927
+ }
26928
+ return {
26929
+ styles,
26930
+ marks
26931
+ };
26932
+ };
26933
+ const processMarks = (marks) => {
26934
+ const styles = {};
26935
+ marks.forEach((mark) => {
26936
+ const { type, attrs } = mark;
26937
+ switch (type.name) {
26938
+ case "textStyle":
26939
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
26940
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
26941
+ if (attrs.color) styles["color"] = attrs.color;
26942
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
26943
+ break;
26944
+ case "bold":
26945
+ styles["font-weight"] = "bold";
26946
+ break;
26947
+ case "italic":
26948
+ styles["font-style"] = "italic";
26949
+ break;
26950
+ case "underline":
26951
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
26952
+ break;
26953
+ case "strike":
26954
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
26955
+ break;
26956
+ default:
26957
+ if (attrs?.style) {
26958
+ Object.entries(attrs.style).forEach(([key2, value]) => {
26959
+ styles[key2] = value;
26960
+ });
26961
+ }
26962
+ break;
26963
+ }
26964
+ });
26965
+ return styles;
26966
+ };
26967
+ const ShapeContainer = Node$1.create({
26968
+ name: "shapeContainer",
26969
+ group: "block",
26970
+ content: "block+",
26502
26971
  isolating: true,
26503
- atom: false,
26504
- // false - has editable content.
26505
- draggable: true,
26506
26972
  addOptions() {
26507
26973
  return {
26508
26974
  htmlAttributes: {
26509
- class: structuredContentClass,
26510
- "aria-label": "Structured content block node"
26975
+ class: "sd-editor-shape-container",
26976
+ "aria-label": "Shape container node"
26511
26977
  }
26512
26978
  };
26513
26979
  },
26514
26980
  addAttributes() {
26515
26981
  return {
26516
- id: {
26982
+ fillcolor: {
26983
+ renderDOM: (attrs) => {
26984
+ if (!attrs.fillcolor) return {};
26985
+ return {
26986
+ style: `background-color: ${attrs.fillcolor}`
26987
+ };
26988
+ }
26989
+ },
26990
+ sdBlockId: {
26517
26991
  default: null,
26518
- parseDOM: (elem) => elem.getAttribute("data-id"),
26992
+ keepOnSplit: false,
26993
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26519
26994
  renderDOM: (attrs) => {
26520
- if (!attrs.id) return {};
26521
- return { "data-id": attrs.id };
26995
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26522
26996
  }
26523
26997
  },
26524
- sdtPr: {
26998
+ style: {
26999
+ renderDOM: (attrs) => {
27000
+ if (!attrs.style) return {};
27001
+ return {
27002
+ style: attrs.style
27003
+ };
27004
+ }
27005
+ },
27006
+ wrapAttributes: {
27007
+ rendered: false
27008
+ },
27009
+ attributes: {
26525
27010
  rendered: false
26526
27011
  }
26527
27012
  };
26528
27013
  },
26529
27014
  parseDOM() {
26530
- return [{ tag: "div[data-structured-content-block]" }];
27015
+ return [
27016
+ {
27017
+ tag: `div[data-type="${this.name}"]`
27018
+ }
27019
+ ];
26531
27020
  },
26532
27021
  renderDOM({ htmlAttributes }) {
26533
27022
  return [
26534
27023
  "div",
26535
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26536
- "data-structured-content-block": ""
26537
- }),
27024
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26538
27025
  0
26539
27026
  ];
26540
- },
26541
- addNodeView() {
26542
- return (props) => {
26543
- return new StructuredContentBlockView({ ...props });
26544
- };
26545
27027
  }
26546
27028
  });
26547
- class DocumentSectionView {
26548
- constructor(node, getPos, decorations, editor) {
26549
- __privateAdd(this, _DocumentSectionView_instances);
26550
- this.node = node;
26551
- this.editor = editor;
26552
- this.decorations = decorations;
26553
- this.view = editor.view;
26554
- this.getPos = getPos;
26555
- __privateMethod(this, _DocumentSectionView_instances, init_fn3).call(this);
26556
- }
26557
- }
26558
- _DocumentSectionView_instances = new WeakSet();
26559
- init_fn3 = function() {
26560
- const { attrs } = this.node;
26561
- const { id, title, description } = attrs;
26562
- this.dom = document.createElement("div");
26563
- this.dom.className = "sd-document-section-block";
26564
- this.dom.setAttribute("data-id", id);
26565
- this.dom.setAttribute("data-title", title);
26566
- this.dom.setAttribute("data-description", description);
26567
- this.dom.setAttribute("aria-label", "Document section");
26568
- __privateMethod(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
26569
- this.contentDOM = document.createElement("div");
26570
- this.contentDOM.className = "sd-document-section-block-content";
26571
- this.contentDOM.setAttribute("contenteditable", "true");
26572
- this.dom.appendChild(this.contentDOM);
26573
- };
26574
- addToolTip_fn = function() {
26575
- const { title } = this.node.attrs;
26576
- this.infoDiv = document.createElement("div");
26577
- this.infoDiv.className = "sd-document-section-block-info";
26578
- const textSpan = document.createElement("span");
26579
- textSpan.textContent = title || "Document section";
26580
- this.infoDiv.appendChild(textSpan);
26581
- this.infoDiv.setAttribute("contenteditable", "false");
26582
- this.dom.appendChild(this.infoDiv);
26583
- };
26584
- const getAllSections = (editor) => {
26585
- if (!editor) return [];
26586
- const type = editor.schema.nodes.documentSection;
26587
- if (!type) return [];
26588
- const sections = [];
26589
- const { state } = editor;
26590
- state.doc.descendants((node, pos) => {
26591
- if (node.type.name === type.name) {
26592
- sections.push({ node, pos });
26593
- }
26594
- });
26595
- return sections;
26596
- };
26597
- const exportSectionsToHTML = (editor) => {
26598
- const sections = getAllSections(editor);
26599
- const processedSections = /* @__PURE__ */ new Set();
26600
- const result = [];
26601
- sections.forEach(({ node }) => {
26602
- const { attrs } = node;
26603
- const { id, title, description } = attrs;
26604
- if (processedSections.has(id)) return;
26605
- processedSections.add(id);
26606
- const html = getHTMLFromNode(node, editor);
26607
- result.push({
26608
- id,
26609
- title,
26610
- description,
26611
- html
26612
- });
26613
- });
26614
- return result;
26615
- };
26616
- const getHTMLFromNode = (node, editor) => {
26617
- const tempDocument = document.implementation.createHTMLDocument();
26618
- const container = tempDocument.createElement("div");
26619
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
26620
- container.appendChild(fragment);
26621
- let html = container.innerHTML;
26622
- return html;
26623
- };
26624
- const exportSectionsToJSON = (editor) => {
26625
- const sections = getAllSections(editor);
26626
- const processedSections = /* @__PURE__ */ new Set();
26627
- const result = [];
26628
- sections.forEach(({ node }) => {
26629
- const { attrs } = node;
26630
- const { id, title, description } = attrs;
26631
- if (processedSections.has(id)) return;
26632
- processedSections.add(id);
26633
- result.push({
26634
- id,
26635
- title,
26636
- description,
26637
- content: node.toJSON()
26638
- });
26639
- });
26640
- return result;
26641
- };
26642
- const getLinkedSectionEditor = (id, options, editor) => {
26643
- const sections = getAllSections(editor);
26644
- const section = sections.find((s) => s.node.attrs.id === id);
26645
- if (!section) return null;
26646
- const child = editor.createChildEditor({
26647
- ...options,
26648
- onUpdate: ({ editor: childEditor, transaction }) => {
26649
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
26650
- if (isFromtLinkedParent) return;
26651
- const updatedContent = childEditor.state.doc.content;
26652
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26653
- if (!sectionNode) return;
26654
- const { pos, node } = sectionNode;
26655
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
26656
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
26657
- tr.setMeta("fromLinkedChild", true);
26658
- editor.view.dispatch(tr);
26659
- }
26660
- });
26661
- editor.on("update", ({ transaction }) => {
26662
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
26663
- if (isFromLinkedChild) return;
26664
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26665
- if (!sectionNode) return;
26666
- const sectionContent = sectionNode.node.content;
26667
- const json = {
26668
- type: "doc",
26669
- content: sectionContent.content.map((node) => node.toJSON())
26670
- };
26671
- const childTr = child.state.tr;
26672
- childTr.setMeta("fromLinkedParent", true);
26673
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
26674
- child.view.dispatch(childTr);
26675
- });
26676
- return child;
26677
- };
26678
- const SectionHelpers = {
26679
- getAllSections,
26680
- exportSectionsToHTML,
26681
- exportSectionsToJSON,
26682
- getLinkedSectionEditor
26683
- };
26684
- const DocumentSection = Node$1.create({
26685
- name: "documentSection",
27029
+ const ShapeTextbox = Node$1.create({
27030
+ name: "shapeTextbox",
26686
27031
  group: "block",
26687
- content: "block*",
26688
- atom: true,
27032
+ content: "paragraph* block*",
26689
27033
  isolating: true,
26690
27034
  addOptions() {
26691
27035
  return {
26692
27036
  htmlAttributes: {
26693
- class: "sd-document-section-block",
26694
- "aria-label": "Structured content block"
27037
+ class: "sd-editor-shape-textbox",
27038
+ "aria-label": "Shape textbox node"
26695
27039
  }
26696
27040
  };
26697
27041
  },
26698
- parseDOM() {
26699
- return [
26700
- {
26701
- tag: "div.sd-document-section-block",
26702
- priority: 60
26703
- }
26704
- ];
26705
- },
26706
- renderDOM({ htmlAttributes }) {
26707
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
26708
- },
26709
27042
  addAttributes() {
26710
27043
  return {
26711
- id: {},
26712
27044
  sdBlockId: {
26713
27045
  default: null,
26714
27046
  keepOnSplit: false,
@@ -26717,212 +27049,131 @@ const DocumentSection = Node$1.create({
26717
27049
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26718
27050
  }
26719
27051
  },
26720
- title: {},
26721
- description: {},
26722
- sectionType: {},
26723
- isLocked: { default: false }
27052
+ attributes: {
27053
+ rendered: false
27054
+ }
26724
27055
  };
26725
27056
  },
26726
- addNodeView() {
26727
- return ({ node, editor, getPos, decorations }) => {
26728
- return new DocumentSectionView(node, getPos, decorations, editor);
27057
+ parseDOM() {
27058
+ return [
27059
+ {
27060
+ tag: `div[data-type="${this.name}"]`
27061
+ }
27062
+ ];
27063
+ },
27064
+ renderDOM({ htmlAttributes }) {
27065
+ return [
27066
+ "div",
27067
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
27068
+ 0
27069
+ ];
27070
+ }
27071
+ });
27072
+ const ContentBlock = Node$1.create({
27073
+ name: "contentBlock",
27074
+ group: "inline",
27075
+ content: "",
27076
+ isolating: true,
27077
+ atom: true,
27078
+ inline: true,
27079
+ addOptions() {
27080
+ return {
27081
+ htmlAttributes: {
27082
+ contenteditable: false
27083
+ }
26729
27084
  };
26730
27085
  },
26731
- addCommands() {
27086
+ addAttributes() {
26732
27087
  return {
26733
- /**
26734
- * Create a lockable content section
26735
- * @category Command
26736
- * @param {SectionCreate} [options={}] - Section configuration
26737
- * @example
26738
- * editor.commands.createDocumentSection({
26739
- * id: 1,
26740
- * title: 'Terms & Conditions',
26741
- * isLocked: true,
26742
- * html: '<p>Legal content...</p>'
26743
- * })
26744
- */
26745
- createDocumentSection: (options = {}) => ({ tr, state, dispatch, editor }) => {
26746
- const { selection } = state;
26747
- let { from: from2, to } = selection;
26748
- let content = selection.content().content;
26749
- const { html: optionsHTML, json: optionsJSON } = options;
26750
- if (optionsHTML) {
26751
- const html = htmlHandler(optionsHTML, this.editor);
26752
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
26753
- content = doc2.content;
26754
- }
26755
- if (optionsJSON) {
26756
- content = this.editor.schema.nodeFromJSON(optionsJSON);
26757
- }
26758
- if (!content?.content?.length) {
26759
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
26760
- }
26761
- if (!options.id) {
26762
- const allSections = SectionHelpers.getAllSections(editor);
26763
- options.id = allSections.length + 1;
26764
- }
26765
- if (!options.title) {
26766
- options.title = "Document section";
26767
- }
26768
- const node = this.type.createAndFill(options, content);
26769
- if (!node) return false;
26770
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
26771
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
26772
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
26773
- from2 = insertPos2;
26774
- to = insertPos2;
26775
- }
26776
- tr.replaceRangeWith(from2, to, node);
26777
- const nodeEnd = from2 + node.nodeSize;
26778
- let shouldInsertParagraph = true;
26779
- let insertPos = nodeEnd;
26780
- if (nodeEnd >= tr.doc.content.size) {
26781
- insertPos = tr.doc.content.size;
26782
- if (insertPos > 0) {
26783
- const $endPos = tr.doc.resolve(insertPos);
26784
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
26785
- shouldInsertParagraph = false;
26786
- }
26787
- }
26788
- }
26789
- if (shouldInsertParagraph) {
26790
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
26791
- tr.insert(insertPos, emptyParagraph);
26792
- }
26793
- if (dispatch) {
26794
- tr.setMeta("documentSection", { action: "create" });
26795
- dispatch(tr);
26796
- setTimeout(() => {
26797
- try {
26798
- const currentState = editor.state;
26799
- const docSize = currentState.doc.content.size;
26800
- let targetPos = from2 + node.nodeSize;
26801
- if (shouldInsertParagraph) {
26802
- targetPos += 1;
26803
- }
26804
- targetPos = Math.min(targetPos, docSize);
26805
- if (targetPos < docSize && targetPos > 0) {
26806
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
26807
- const newTr = currentState.tr.setSelection(newSelection);
26808
- editor.view.dispatch(newTr);
26809
- }
26810
- } catch (e) {
26811
- console.warn("Could not set delayed selection:", e);
26812
- }
26813
- }, 0);
27088
+ horizontalRule: {
27089
+ default: false,
27090
+ renderDOM: ({ horizontalRule }) => {
27091
+ if (!horizontalRule) return {};
27092
+ return { "data-horizontal-rule": "true" };
26814
27093
  }
26815
- return true;
26816
27094
  },
26817
- /**
26818
- * Remove section wrapper at cursor, preserving its content
26819
- * @category Command
26820
- * @example
26821
- * editor.commands.removeSectionAtSelection()
26822
- * @note Content stays in document, only section wrapper is removed
26823
- */
26824
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
26825
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
26826
- if (!sdtNode) return false;
26827
- const { node, pos } = sdtNode;
26828
- const nodeStart = pos;
26829
- const nodeEnd = nodeStart + node.nodeSize;
26830
- const contentToPreserve = node.content;
26831
- tr.delete(nodeStart, nodeEnd);
26832
- if (contentToPreserve.size > 0) {
26833
- tr.insert(nodeStart, contentToPreserve);
26834
- }
26835
- const newPos = Math.min(nodeStart, tr.doc.content.size);
26836
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
26837
- if (dispatch) {
26838
- tr.setMeta("documentSection", { action: "delete" });
26839
- dispatch(tr);
27095
+ size: {
27096
+ default: null,
27097
+ renderDOM: ({ size }) => {
27098
+ if (!size) return {};
27099
+ let style = "";
27100
+ if (size.top) style += `top: ${size.top}px; `;
27101
+ if (size.left) style += `left: ${size.left}px; `;
27102
+ if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
27103
+ if (size.height)
27104
+ style += `height: ${size.height.toString().endsWith("%") ? size.height : `${size.height}px`}; `;
27105
+ return { style };
26840
27106
  }
26841
- return true;
26842
27107
  },
26843
- /**
26844
- * Delete section and all its content
26845
- * @category Command
26846
- * @param {number} id - Section to delete
26847
- * @example
26848
- * editor.commands.removeSectionById(123)
26849
- */
26850
- removeSectionById: (id) => ({ tr, dispatch }) => {
26851
- const sections = SectionHelpers.getAllSections(this.editor);
26852
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
26853
- if (!sectionToRemove) return false;
26854
- const { pos, node } = sectionToRemove;
26855
- const nodeStart = pos;
26856
- const nodeEnd = nodeStart + node.nodeSize;
26857
- tr.delete(nodeStart, nodeEnd);
26858
- if (dispatch) {
26859
- tr.setMeta("documentSection", { action: "delete", id });
26860
- dispatch(tr);
27108
+ background: {
27109
+ default: null,
27110
+ renderDOM: (attrs) => {
27111
+ if (!attrs.background) return {};
27112
+ return {
27113
+ style: `background-color: ${attrs.background}`
27114
+ };
26861
27115
  }
26862
- return true;
26863
27116
  },
27117
+ drawingContent: {
27118
+ rendered: false
27119
+ },
27120
+ attributes: {
27121
+ rendered: false
27122
+ }
27123
+ };
27124
+ },
27125
+ parseDOM() {
27126
+ return [
27127
+ {
27128
+ tag: `div[data-type="${this.name}"]`
27129
+ }
27130
+ ];
27131
+ },
27132
+ renderDOM({ htmlAttributes }) {
27133
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
27134
+ },
27135
+ addCommands() {
27136
+ return {
26864
27137
  /**
26865
- * Lock section against edits
27138
+ * Insert a horizontal rule
26866
27139
  * @category Command
26867
- * @param {number} id - Section to lock
26868
27140
  * @example
26869
- * editor.commands.lockSectionById(123)
27141
+ * editor.commands.insertHorizontalRule()
27142
+ * @note Creates a visual separator between content sections
26870
27143
  */
26871
- lockSectionById: (id) => ({ tr, dispatch }) => {
26872
- const sections = SectionHelpers.getAllSections(this.editor);
26873
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
26874
- if (!sectionToLock) return false;
26875
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
26876
- if (dispatch) {
26877
- tr.setMeta("documentSection", { action: "lock", id });
26878
- dispatch(tr);
26879
- }
26880
- return true;
27144
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
27145
+ return commands2.insertContent({
27146
+ type: this.name,
27147
+ attrs: {
27148
+ horizontalRule: true,
27149
+ size: { width: "100%", height: 2 },
27150
+ background: "#e5e7eb"
27151
+ }
27152
+ });
26881
27153
  },
26882
27154
  /**
26883
- * Modify section attributes or content
27155
+ * Insert a content block
26884
27156
  * @category Command
26885
- * @param {SectionUpdate} options - Changes to apply
27157
+ * @param {ContentBlockConfig} config - Block configuration
26886
27158
  * @example
26887
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
26888
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
26889
- * editor.commands.updateSectionById({
26890
- * id: 123,
26891
- * html: '<p>Updated</p>',
26892
- * attrs: { title: 'New Title' }
27159
+ * // Insert a spacer block
27160
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
27161
+ *
27162
+ * @example
27163
+ * // Insert a colored divider
27164
+ * editor.commands.insertContentBlock({
27165
+ * size: { width: '50%', height: 3 },
27166
+ * background: '#3b82f6'
26893
27167
  * })
27168
+ * @note Used for spacing, dividers, and special inline content
26894
27169
  */
26895
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
26896
- const sections = SectionHelpers.getAllSections(editor || this.editor);
26897
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
26898
- if (!sectionToUpdate) return false;
26899
- const { pos, node } = sectionToUpdate;
26900
- let newContent = null;
26901
- if (html) {
26902
- const htmlDoc = htmlHandler(html, editor || this.editor);
26903
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
26904
- newContent = doc2.content;
26905
- }
26906
- if (json) {
26907
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
26908
- }
26909
- if (!newContent) {
26910
- newContent = node.content;
26911
- }
26912
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
26913
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
26914
- if (dispatch) {
26915
- tr.setMeta("documentSection", { action: "update", id, attrs });
26916
- dispatch(tr);
26917
- }
26918
- return true;
27170
+ insertContentBlock: (config) => ({ commands: commands2 }) => {
27171
+ return commands2.insertContent({
27172
+ type: this.name,
27173
+ attrs: config
27174
+ });
26919
27175
  }
26920
27176
  };
26921
- },
26922
- addHelpers() {
26923
- return {
26924
- ...SectionHelpers
26925
- };
26926
27177
  }
26927
27178
  });
26928
27179
  const { findChildren } = helpers;
@@ -33772,6 +34023,7 @@ const getStarterExtensions = () => {
33772
34023
  Search,
33773
34024
  StructuredContent,
33774
34025
  StructuredContentBlock,
34026
+ StructuredContentCommands,
33775
34027
  DocumentSection,
33776
34028
  NodeResizer,
33777
34029
  CustomSelection,