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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/chunks/{PdfViewer-HN-tp5RN.es.js → PdfViewer-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-DyWe33pN.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BeVpZc19.cjs → index-BbGPYtNy.cjs} +2 -2
  4. package/dist/chunks/{index-ir6efMuz.es.js → index-DWKEKmiB.es.js} +2 -2
  5. package/dist/chunks/{super-editor.es-BwqYS285.es.js → super-editor.es-BVxfhpAJ.es.js} +1581 -1274
  6. package/dist/chunks/{super-editor.es-CKfdmK-8.cjs → super-editor.es-BoUJEZaF.cjs} +1581 -1274
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/style.css +1 -0
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-C-yWLpFM.js} +150 -105
  11. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CFqh_xBx.js → editor-BBnC1DzI.js} +1436 -1172
  13. package/dist/super-editor/chunks/{toolbar-DdfyWgZF.js → toolbar-QJANo61B.js} +2 -2
  14. package/dist/super-editor/converter.es.js +1 -1
  15. package/dist/super-editor/docx-zipper.es.js +2 -2
  16. package/dist/super-editor/editor.es.js +3 -3
  17. package/dist/super-editor/file-zipper.es.js +1 -1
  18. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  19. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  20. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  21. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  22. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  23. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  27. package/dist/super-editor/style.css +1 -0
  28. package/dist/super-editor/super-editor.es.js +7 -7
  29. package/dist/super-editor/toolbar.es.js +2 -2
  30. package/dist/super-editor.cjs +1 -1
  31. package/dist/super-editor.es.js +1 -1
  32. package/dist/superdoc.cjs +2 -2
  33. package/dist/superdoc.es.js +2 -2
  34. package/dist/superdoc.umd.js +1581 -1274
  35. package/dist/superdoc.umd.js.map +1 -1
  36. package/package.json +1 -1
@@ -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,7 +12107,7 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
12107
12107
  if (emitParams) editor.emit("commentsUpdate", emitParams);
12108
12108
  return newTrackedChanges;
12109
12109
  };
12110
- const getTrackedChangeText = ({ state, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
12110
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
12111
12111
  let trackedChangeText = "";
12112
12112
  let deletionText = "";
12113
12113
  if (trackedChangeType === TrackInsertMarkName) {
@@ -12149,10 +12149,8 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
12149
12149
  if (hasMatchingId) nodesWithMark.push(node2);
12150
12150
  });
12151
12151
  const { deletionText, trackedChangeText } = getTrackedChangeText({
12152
- state: newEditorState,
12153
12152
  nodes: nodesWithMark.length ? nodesWithMark : [node],
12154
12153
  mark: trackedMark,
12155
- marks,
12156
12154
  trackedChangeType,
12157
12155
  isDeletionInsertion
12158
12156
  });
@@ -16967,258 +16965,785 @@ const SlashMenu = Extension.create({
16967
16965
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
16968
16966
  }
16969
16967
  });
16970
- const Document = Node$1.create({
16971
- name: "doc",
16972
- topNode: true,
16973
- content: "block+",
16974
- parseDOM() {
16975
- return [{ tag: "doc" }];
16976
- },
16977
- renderDOM() {
16978
- return ["doc", 0];
16979
- },
16980
- addAttributes() {
16981
- return {
16982
- attributes: {
16983
- rendered: false,
16984
- "aria-label": "Document node"
16985
- }
16986
- };
16987
- },
16988
- addCommands() {
16989
- return {
16990
- /**
16991
- * Get document statistics
16992
- * @category Command
16993
- * @example
16994
- * // Get word and character count
16995
- * const stats = editor.commands.getDocumentStats()
16996
- * console.log(`${stats.words} words, ${stats.characters} characters`)
16997
- * @note Returns word count, character count, and paragraph count
16998
- */
16999
- getDocumentStats: () => ({ editor }) => {
17000
- const text = editor.getText();
17001
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
17002
- const characters = text.length;
17003
- const paragraphs = editor.state.doc.content.childCount;
17004
- return {
17005
- words,
17006
- characters,
17007
- paragraphs
17008
- };
17009
- },
17010
- /**
17011
- * Clear entire document
17012
- * @category Command
17013
- * @example
17014
- * editor.commands.clearDocument()
17015
- * @note Replaces all content with an empty paragraph
17016
- */
17017
- clearDocument: () => ({ commands: commands2 }) => {
17018
- 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
+ );
17019
17059
  }
17020
- };
17060
+ }
17061
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
17062
+ return false;
17063
+ }
17064
+ return true;
17021
17065
  }
17022
- });
17023
- const Text = Node$1.create({
17024
- name: "text",
17025
- group: "inline",
17026
- inline: true,
17027
- addOptions() {
17028
- 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;
17029
17073
  }
17030
- });
17031
- const splitRun = () => (props) => {
17032
- const { state, view, tr } = props;
17033
- const { $from, empty: empty2 } = state.selection;
17034
- if (!empty2) return false;
17035
- if ($from.parent.type.name !== "run") return false;
17036
- 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);
17037
17131
  view.dispatch(transaction);
17038
- });
17039
- if (handled) {
17040
- tr.setMeta("preventDispatch", true);
17041
17132
  }
17042
- return handled;
17043
- };
17044
- const Run = OxmlNode.create({
17045
- name: "run",
17046
- oXmlName: "w:r",
17047
- 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",
17048
17179
  inline: true,
17049
17180
  content: "inline*",
17050
- selectable: false,
17051
- childToAttributes: ["runProperties"],
17181
+ isolating: true,
17182
+ atom: false,
17183
+ // false - has editable content.
17184
+ draggable: true,
17052
17185
  addOptions() {
17053
17186
  return {
17054
17187
  htmlAttributes: {
17055
- "data-run": "1"
17188
+ class: structuredContentClass$1,
17189
+ "aria-label": "Structured content node"
17056
17190
  }
17057
17191
  };
17058
17192
  },
17059
17193
  addAttributes() {
17060
17194
  return {
17061
- runProperties: {
17195
+ id: {
17062
17196
  default: null,
17063
- rendered: false,
17064
- keepOnSplit: true
17197
+ parseDOM: (elem) => elem.getAttribute("data-id"),
17198
+ renderDOM: (attrs) => {
17199
+ if (!attrs.id) return {};
17200
+ return { "data-id": attrs.id };
17201
+ }
17065
17202
  },
17066
- rsidR: {
17203
+ tag: {
17067
17204
  default: null,
17068
- rendered: false,
17069
- keepOnSplit: true
17205
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
17206
+ renderDOM: (attrs) => {
17207
+ if (!attrs.tag) return {};
17208
+ return { "data-tag": attrs.tag };
17209
+ }
17070
17210
  },
17071
- rsidRPr: {
17211
+ alias: {
17072
17212
  default: null,
17073
- rendered: false,
17074
- keepOnSplit: true
17213
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17214
+ renderDOM: (attrs) => {
17215
+ if (!attrs.alias) return {};
17216
+ return { "data-alias": attrs.alias };
17217
+ }
17075
17218
  },
17076
- rsidDel: {
17077
- default: null,
17078
- rendered: false,
17079
- keepOnSplit: true
17219
+ sdtPr: {
17220
+ rendered: false
17080
17221
  }
17081
17222
  };
17082
17223
  },
17083
- addCommands() {
17084
- return {
17085
- splitRun
17086
- };
17087
- },
17088
17224
  parseDOM() {
17089
- return [{ tag: "span[data-run]" }];
17225
+ return [{ tag: "span[data-structured-content]" }];
17090
17226
  },
17091
17227
  renderDOM({ htmlAttributes }) {
17092
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17093
- 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
+ };
17094
17240
  }
17095
17241
  });
17096
- const inputRegex$1 = /^\s*([-+*])\s$/;
17097
- const BulletList = Node$1.create({
17098
- name: "bulletList",
17099
- group: "block list",
17100
- selectable: false,
17101
- content() {
17102
- return `${this.options.itemTypeName}+`;
17103
- },
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,
17104
17292
  addOptions() {
17105
17293
  return {
17106
- itemTypeName: "listItem",
17107
17294
  htmlAttributes: {
17108
- "aria-label": "Bullet list node"
17109
- },
17110
- keepMarks: true,
17111
- keepAttributes: false
17295
+ class: structuredContentClass,
17296
+ "aria-label": "Structured content block node"
17297
+ }
17112
17298
  };
17113
17299
  },
17114
- parseDOM() {
17115
- return [{ tag: "ul" }];
17116
- },
17117
- renderDOM({ htmlAttributes }) {
17118
- const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17119
- return ["ul", attributes, 0];
17120
- },
17121
17300
  addAttributes() {
17122
17301
  return {
17123
- "list-style-type": {
17124
- default: "bullet",
17125
- 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
+ }
17126
17309
  },
17127
- listId: {
17128
- 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
+ }
17129
17317
  },
17130
- sdBlockId: {
17318
+ alias: {
17131
17319
  default: null,
17132
- keepOnSplit: false,
17133
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17320
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17134
17321
  renderDOM: (attrs) => {
17135
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17322
+ if (!attrs.alias) return {};
17323
+ return { "data-alias": attrs.alias };
17136
17324
  }
17137
17325
  },
17138
- attributes: {
17139
- rendered: false,
17140
- keepOnSplit: true
17326
+ sdtPr: {
17327
+ rendered: false
17141
17328
  }
17142
17329
  };
17143
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",
17144
17384
  addCommands() {
17145
17385
  return {
17146
17386
  /**
17147
- * Toggle a bullet list at the current selection
17387
+ * Inserts a structured content inline at selection.
17148
17388
  * @category Command
17149
- * @example
17150
- * // Toggle bullet list on selected text
17151
- * editor.commands.toggleBulletList()
17152
- * @note Converts selected paragraphs to list items or removes list formatting
17389
+ * @param {StructuredContentInlineInsert} options
17153
17390
  */
17154
- toggleBulletList: () => (params2) => {
17155
- return toggleList(this.type)(params2);
17156
- }
17157
- };
17158
- },
17159
- addShortcuts() {
17160
- return {
17161
- "Mod-Shift-8": () => {
17162
- return this.editor.commands.toggleBulletList();
17163
- }
17164
- };
17165
- },
17166
- addInputRules() {
17167
- return [
17168
- new InputRule({
17169
- match: inputRegex$1,
17170
- handler: ({ state, range }) => {
17171
- const $pos = state.selection.$from;
17172
- const listItemType = state.schema.nodes.listItem;
17173
- for (let depth = $pos.depth; depth >= 0; depth--) {
17174
- if ($pos.node(depth).type === listItemType) {
17175
- return null;
17176
- }
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);
17177
17399
  }
17178
- const { tr } = state;
17179
- tr.delete(range.from, range.to);
17180
- ListHelpers.createNewList({
17181
- listType: this.type,
17182
- tr,
17183
- editor: this.editor
17184
- });
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);
17185
17422
  }
17186
- })
17187
- ];
17188
- }
17189
- });
17190
- const inputRegex = /^(\d+)\.\s$/;
17191
- const OrderedList = Node$1.create({
17192
- name: "orderedList",
17193
- group: "block list",
17194
- selectable: false,
17195
- content() {
17196
- return `${this.options.itemTypeName}+`;
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
+ };
17197
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,
17198
17725
  addOptions() {
17199
17726
  return {
17200
- itemTypeName: "listItem",
17201
17727
  htmlAttributes: {
17202
- "aria-label": "Ordered list node"
17203
- },
17204
- keepMarks: true,
17205
- keepAttributes: false,
17206
- listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
17728
+ class: "sd-document-section-block",
17729
+ "aria-label": "Structured content block"
17730
+ }
17207
17731
  };
17208
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
+ },
17209
17744
  addAttributes() {
17210
17745
  return {
17211
- order: {
17212
- default: 1,
17213
- parseDOM: (element) => {
17214
- return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
17215
- },
17216
- renderDOM: (attrs) => {
17217
- return {
17218
- start: attrs.order
17219
- };
17220
- }
17221
- },
17746
+ id: {},
17222
17747
  sdBlockId: {
17223
17748
  default: null,
17224
17749
  keepOnSplit: false,
@@ -17227,121 +17752,589 @@ const OrderedList = Node$1.create({
17227
17752
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17228
17753
  }
17229
17754
  },
17230
- syncId: {
17231
- default: null,
17232
- parseDOM: (elem) => elem.getAttribute("data-sync-id"),
17233
- renderDOM: (attrs) => {
17234
- if (!attrs.syncId) return {};
17235
- return {
17236
- "data-sync-id": attrs.syncId
17237
- };
17238
- }
17239
- // rendered: false,
17240
- },
17241
- listId: {
17242
- keepOnSplit: true,
17243
- parseDOM: (elem) => elem.getAttribute("data-list-id"),
17244
- renderDOM: (attrs) => {
17245
- if (!attrs.listId) return {};
17246
- return {
17247
- "data-list-id": attrs.listId
17248
- };
17249
- }
17250
- },
17251
- "list-style-type": {
17252
- default: "decimal",
17253
- rendered: false
17254
- },
17255
- attributes: {
17256
- rendered: false,
17257
- keepOnSplit: true
17258
- }
17755
+ title: {},
17756
+ description: {},
17757
+ sectionType: {},
17758
+ isLocked: { default: false }
17259
17759
  };
17260
17760
  },
17261
- parseDOM() {
17262
- return [{ tag: "ol" }];
17263
- },
17264
- renderDOM({ htmlAttributes }) {
17265
- const { start: start2, ...restAttributes } = htmlAttributes;
17266
- return start2 === 1 ? ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
17761
+ addNodeView() {
17762
+ return ({ node, editor, getPos, decorations }) => {
17763
+ return new DocumentSectionView(node, getPos, decorations, editor);
17764
+ };
17267
17765
  },
17268
17766
  addCommands() {
17269
17767
  return {
17270
17768
  /**
17271
- * Toggle ordered list formatting
17769
+ * Create a lockable content section
17272
17770
  * @category Command
17771
+ * @param {SectionCreate} [options={}] - Section configuration
17273
17772
  * @example
17274
- * editor.commands.toggleOrderedList()
17275
- * @note Converts selection to ordered list or back to paragraphs
17773
+ * editor.commands.createDocumentSection({
17774
+ * id: 1,
17775
+ * title: 'Terms & Conditions',
17776
+ * isLocked: true,
17777
+ * html: '<p>Legal content...</p>'
17778
+ * })
17276
17779
  */
17277
- toggleOrderedList: () => (params2) => {
17278
- return toggleList(this.type)(params2);
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;
17279
17851
  },
17280
17852
  /**
17281
- * Restart list node numbering
17853
+ * Remove section wrapper at cursor, preserving its content
17282
17854
  * @category Command
17283
- * @param {Array} followingNodes - Nodes to restart
17284
- * @param {number} pos - Starting position
17285
17855
  * @example
17286
- * editor.commands.restartListNodes(nodes, position)
17287
- * @note Resets list numbering for specified nodes
17856
+ * editor.commands.removeSectionAtSelection()
17857
+ * @note Content stays in document, only section wrapper is removed
17288
17858
  */
17289
- restartListNodes: (followingNodes, pos) => ({ tr }) => {
17290
- let currentNodePos = pos;
17291
- const nodes = followingNodes.map((node) => {
17292
- const resultNode = {
17293
- node,
17294
- pos: currentNodePos
17295
- };
17296
- currentNodePos += node.nodeSize;
17297
- return resultNode;
17298
- });
17299
- nodes.forEach((item) => {
17300
- const { pos: pos2 } = item;
17301
- const newPos = tr.mapping.map(pos2);
17302
- tr.setNodeMarkup(newPos, void 0, {});
17303
- });
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
+ }
17304
17876
  return true;
17305
17877
  },
17306
17878
  /**
17307
- * Update ordered list style type based on nesting level
17879
+ * Delete section and all its content
17308
17880
  * @category Command
17881
+ * @param {number} id - Section to delete
17309
17882
  * @example
17310
- * editor.commands.updateOrderedListStyleType()
17311
- * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
17883
+ * editor.commands.removeSectionById(123)
17312
17884
  */
17313
- updateOrderedListStyleType: () => ({ dispatch, tr }) => {
17314
- let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
17315
- if (!list) {
17316
- return true;
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);
17317
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);
17318
17949
  if (dispatch) {
17319
- let listLevel = (list.depth - 1) / 2;
17320
- let listStyleTypes = this.options.listStyleTypes;
17321
- let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
17322
- let currentListStyle = list.node.attrs["list-style-type"];
17323
- let nodeAtPos = tr.doc.nodeAt(list.pos);
17324
- if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
17325
- tr.setNodeMarkup(list.pos, void 0, {
17326
- ...list.node.attrs,
17327
- ...{
17328
- "list-style-type": listStyle
17329
- }
17330
- });
17331
- }
17950
+ tr.setMeta("documentSection", { action: "update", id, attrs });
17951
+ dispatch(tr);
17332
17952
  }
17333
17953
  return true;
17334
17954
  }
17335
17955
  };
17336
17956
  },
17337
- addShortcuts() {
17957
+ addHelpers() {
17338
17958
  return {
17339
- "Mod-Shift-7": () => {
17340
- return this.editor.commands.toggleOrderedList();
17341
- }
17959
+ ...SectionHelpers
17342
17960
  };
17343
- },
17344
- addInputRules() {
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
+ })
18180
+ ];
18181
+ }
18182
+ });
18183
+ const inputRegex = /^(\d+)\.\s$/;
18184
+ const OrderedList = Node$1.create({
18185
+ name: "orderedList",
18186
+ group: "block list",
18187
+ selectable: false,
18188
+ content() {
18189
+ return `${this.options.itemTypeName}+`;
18190
+ },
18191
+ addOptions() {
18192
+ return {
18193
+ itemTypeName: "listItem",
18194
+ htmlAttributes: {
18195
+ "aria-label": "Ordered list node"
18196
+ },
18197
+ keepMarks: true,
18198
+ keepAttributes: false,
18199
+ listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
18200
+ };
18201
+ },
18202
+ addAttributes() {
18203
+ return {
18204
+ order: {
18205
+ default: 1,
18206
+ parseDOM: (element) => {
18207
+ return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
18208
+ },
18209
+ renderDOM: (attrs) => {
18210
+ return {
18211
+ start: attrs.order
18212
+ };
18213
+ }
18214
+ },
18215
+ sdBlockId: {
18216
+ default: null,
18217
+ keepOnSplit: false,
18218
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
18219
+ renderDOM: (attrs) => {
18220
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
18221
+ }
18222
+ },
18223
+ syncId: {
18224
+ default: null,
18225
+ parseDOM: (elem) => elem.getAttribute("data-sync-id"),
18226
+ renderDOM: (attrs) => {
18227
+ if (!attrs.syncId) return {};
18228
+ return {
18229
+ "data-sync-id": attrs.syncId
18230
+ };
18231
+ }
18232
+ // rendered: false,
18233
+ },
18234
+ listId: {
18235
+ keepOnSplit: true,
18236
+ parseDOM: (elem) => elem.getAttribute("data-list-id"),
18237
+ renderDOM: (attrs) => {
18238
+ if (!attrs.listId) return {};
18239
+ return {
18240
+ "data-list-id": attrs.listId
18241
+ };
18242
+ }
18243
+ },
18244
+ "list-style-type": {
18245
+ default: "decimal",
18246
+ rendered: false
18247
+ },
18248
+ attributes: {
18249
+ rendered: false,
18250
+ keepOnSplit: true
18251
+ }
18252
+ };
18253
+ },
18254
+ parseDOM() {
18255
+ return [{ tag: "ol" }];
18256
+ },
18257
+ renderDOM({ htmlAttributes }) {
18258
+ const { start: start2, ...restAttributes } = htmlAttributes;
18259
+ return start2 === 1 ? ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
18260
+ },
18261
+ addCommands() {
18262
+ return {
18263
+ /**
18264
+ * Toggle ordered list formatting
18265
+ * @category Command
18266
+ * @example
18267
+ * editor.commands.toggleOrderedList()
18268
+ * @note Converts selection to ordered list or back to paragraphs
18269
+ */
18270
+ toggleOrderedList: () => (params2) => {
18271
+ return toggleList(this.type)(params2);
18272
+ },
18273
+ /**
18274
+ * Restart list node numbering
18275
+ * @category Command
18276
+ * @param {Array} followingNodes - Nodes to restart
18277
+ * @param {number} pos - Starting position
18278
+ * @example
18279
+ * editor.commands.restartListNodes(nodes, position)
18280
+ * @note Resets list numbering for specified nodes
18281
+ */
18282
+ restartListNodes: (followingNodes, pos) => ({ tr }) => {
18283
+ let currentNodePos = pos;
18284
+ const nodes = followingNodes.map((node) => {
18285
+ const resultNode = {
18286
+ node,
18287
+ pos: currentNodePos
18288
+ };
18289
+ currentNodePos += node.nodeSize;
18290
+ return resultNode;
18291
+ });
18292
+ nodes.forEach((item) => {
18293
+ const { pos: pos2 } = item;
18294
+ const newPos = tr.mapping.map(pos2);
18295
+ tr.setNodeMarkup(newPos, void 0, {});
18296
+ });
18297
+ return true;
18298
+ },
18299
+ /**
18300
+ * Update ordered list style type based on nesting level
18301
+ * @category Command
18302
+ * @example
18303
+ * editor.commands.updateOrderedListStyleType()
18304
+ * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
18305
+ */
18306
+ updateOrderedListStyleType: () => ({ dispatch, tr }) => {
18307
+ let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
18308
+ if (!list) {
18309
+ return true;
18310
+ }
18311
+ if (dispatch) {
18312
+ let listLevel = (list.depth - 1) / 2;
18313
+ let listStyleTypes = this.options.listStyleTypes;
18314
+ let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
18315
+ let currentListStyle = list.node.attrs["list-style-type"];
18316
+ let nodeAtPos = tr.doc.nodeAt(list.pos);
18317
+ if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
18318
+ tr.setNodeMarkup(list.pos, void 0, {
18319
+ ...list.node.attrs,
18320
+ ...{
18321
+ "list-style-type": listStyle
18322
+ }
18323
+ });
18324
+ }
18325
+ }
18326
+ return true;
18327
+ }
18328
+ };
18329
+ },
18330
+ addShortcuts() {
18331
+ return {
18332
+ "Mod-Shift-7": () => {
18333
+ return this.editor.commands.toggleOrderedList();
18334
+ }
18335
+ };
18336
+ },
18337
+ addInputRules() {
17345
18338
  return [
17346
18339
  new InputRule({
17347
18340
  match: inputRegex,
@@ -18549,7 +19542,7 @@ class ListItemNodeView {
18549
19542
  this.decorations = decorations;
18550
19543
  this.view = editor.view;
18551
19544
  this.getPos = getPos;
18552
- __privateMethod(this, _ListItemNodeView_instances, init_fn2).call(this);
19545
+ __privateMethod(this, _ListItemNodeView_instances, init_fn3).call(this);
18553
19546
  activeListItemNodeViews.add(this);
18554
19547
  }
18555
19548
  refreshIndentStyling() {
@@ -18610,7 +19603,7 @@ class ListItemNodeView {
18610
19603
  }
18611
19604
  }
18612
19605
  _ListItemNodeView_instances = new WeakSet();
18613
- init_fn2 = function() {
19606
+ init_fn3 = function() {
18614
19607
  const { attrs } = this.node;
18615
19608
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
18616
19609
  let orderMarker = "";
@@ -25958,745 +26951,96 @@ const processMarks = (marks) => {
25958
26951
  styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
25959
26952
  break;
25960
26953
  case "strike":
25961
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
25962
- break;
25963
- default:
25964
- if (attrs?.style) {
25965
- Object.entries(attrs.style).forEach(([key2, value]) => {
25966
- styles[key2] = value;
25967
- });
25968
- }
25969
- break;
25970
- }
25971
- });
25972
- return styles;
25973
- };
25974
- const ShapeContainer = Node$1.create({
25975
- name: "shapeContainer",
25976
- group: "block",
25977
- content: "block+",
25978
- isolating: true,
25979
- addOptions() {
25980
- return {
25981
- htmlAttributes: {
25982
- class: "sd-editor-shape-container",
25983
- "aria-label": "Shape container node"
25984
- }
25985
- };
25986
- },
25987
- addAttributes() {
25988
- return {
25989
- fillcolor: {
25990
- renderDOM: (attrs) => {
25991
- if (!attrs.fillcolor) return {};
25992
- return {
25993
- style: `background-color: ${attrs.fillcolor}`
25994
- };
25995
- }
25996
- },
25997
- sdBlockId: {
25998
- default: null,
25999
- keepOnSplit: false,
26000
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26001
- renderDOM: (attrs) => {
26002
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26003
- }
26004
- },
26005
- style: {
26006
- renderDOM: (attrs) => {
26007
- if (!attrs.style) return {};
26008
- return {
26009
- style: attrs.style
26010
- };
26011
- }
26012
- },
26013
- wrapAttributes: {
26014
- rendered: false
26015
- },
26016
- attributes: {
26017
- rendered: false
26018
- }
26019
- };
26020
- },
26021
- parseDOM() {
26022
- return [
26023
- {
26024
- tag: `div[data-type="${this.name}"]`
26025
- }
26026
- ];
26027
- },
26028
- renderDOM({ htmlAttributes }) {
26029
- return [
26030
- "div",
26031
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26032
- 0
26033
- ];
26034
- }
26035
- });
26036
- const ShapeTextbox = Node$1.create({
26037
- name: "shapeTextbox",
26038
- group: "block",
26039
- content: "paragraph* block*",
26040
- isolating: true,
26041
- addOptions() {
26042
- return {
26043
- htmlAttributes: {
26044
- class: "sd-editor-shape-textbox",
26045
- "aria-label": "Shape textbox node"
26046
- }
26047
- };
26048
- },
26049
- addAttributes() {
26050
- return {
26051
- sdBlockId: {
26052
- default: null,
26053
- keepOnSplit: false,
26054
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26055
- renderDOM: (attrs) => {
26056
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26057
- }
26058
- },
26059
- attributes: {
26060
- rendered: false
26061
- }
26062
- };
26063
- },
26064
- parseDOM() {
26065
- return [
26066
- {
26067
- tag: `div[data-type="${this.name}"]`
26068
- }
26069
- ];
26070
- },
26071
- renderDOM({ htmlAttributes }) {
26072
- return [
26073
- "div",
26074
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26075
- 0
26076
- ];
26077
- }
26078
- });
26079
- const ContentBlock = Node$1.create({
26080
- name: "contentBlock",
26081
- group: "inline",
26082
- content: "",
26083
- isolating: true,
26084
- atom: true,
26085
- inline: true,
26086
- addOptions() {
26087
- return {
26088
- htmlAttributes: {
26089
- contenteditable: false
26090
- }
26091
- };
26092
- },
26093
- addAttributes() {
26094
- return {
26095
- horizontalRule: {
26096
- default: false,
26097
- renderDOM: ({ horizontalRule }) => {
26098
- if (!horizontalRule) return {};
26099
- return { "data-horizontal-rule": "true" };
26100
- }
26101
- },
26102
- size: {
26103
- default: null,
26104
- renderDOM: ({ size }) => {
26105
- if (!size) return {};
26106
- let style = "";
26107
- if (size.top) style += `top: ${size.top}px; `;
26108
- if (size.left) style += `left: ${size.left}px; `;
26109
- if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
26110
- if (size.height)
26111
- style += `height: ${size.height.toString().endsWith("%") ? size.height : `${size.height}px`}; `;
26112
- return { style };
26113
- }
26114
- },
26115
- background: {
26116
- default: null,
26117
- renderDOM: (attrs) => {
26118
- if (!attrs.background) return {};
26119
- return {
26120
- style: `background-color: ${attrs.background}`
26121
- };
26122
- }
26123
- },
26124
- drawingContent: {
26125
- rendered: false
26126
- },
26127
- attributes: {
26128
- rendered: false
26129
- }
26130
- };
26131
- },
26132
- parseDOM() {
26133
- return [
26134
- {
26135
- tag: `div[data-type="${this.name}"]`
26136
- }
26137
- ];
26138
- },
26139
- renderDOM({ htmlAttributes }) {
26140
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
26141
- },
26142
- addCommands() {
26143
- return {
26144
- /**
26145
- * Insert a horizontal rule
26146
- * @category Command
26147
- * @example
26148
- * editor.commands.insertHorizontalRule()
26149
- * @note Creates a visual separator between content sections
26150
- */
26151
- insertHorizontalRule: () => ({ commands: commands2 }) => {
26152
- return commands2.insertContent({
26153
- type: this.name,
26154
- attrs: {
26155
- horizontalRule: true,
26156
- size: { width: "100%", height: 2 },
26157
- background: "#e5e7eb"
26158
- }
26159
- });
26160
- },
26161
- /**
26162
- * Insert a content block
26163
- * @category Command
26164
- * @param {ContentBlockConfig} config - Block configuration
26165
- * @example
26166
- * // Insert a spacer block
26167
- * editor.commands.insertContentBlock({ size: { height: 20 } })
26168
- *
26169
- * @example
26170
- * // Insert a colored divider
26171
- * editor.commands.insertContentBlock({
26172
- * size: { width: '50%', height: 3 },
26173
- * background: '#3b82f6'
26174
- * })
26175
- * @note Used for spacing, dividers, and special inline content
26176
- */
26177
- insertContentBlock: (config) => ({ commands: commands2 }) => {
26178
- return commands2.insertContent({
26179
- type: this.name,
26180
- attrs: config
26181
- });
26182
- }
26183
- };
26184
- }
26185
- });
26186
- class StructuredContentViewBase {
26187
- constructor(props) {
26188
- __publicField(this, "node");
26189
- __publicField(this, "view");
26190
- __publicField(this, "getPos");
26191
- __publicField(this, "decorations");
26192
- __publicField(this, "innerDecorations");
26193
- __publicField(this, "editor");
26194
- __publicField(this, "extension");
26195
- __publicField(this, "htmlAttributes");
26196
- __publicField(this, "root");
26197
- __publicField(this, "isDragging", false);
26198
- this.node = props.node;
26199
- this.view = props.editor.view;
26200
- this.getPos = props.getPos;
26201
- this.decorations = props.decorations;
26202
- this.innerDecorations = props.innerDecorations;
26203
- this.editor = props.editor;
26204
- this.extension = props.extension;
26205
- this.htmlAttributes = props.htmlAttributes;
26206
- this.mount(props);
26207
- }
26208
- mount() {
26209
- return;
26210
- }
26211
- get dom() {
26212
- return this.root;
26213
- }
26214
- get contentDOM() {
26215
- return null;
26216
- }
26217
- update(node, decorations, innerDecorations) {
26218
- if (node.type !== this.node.type) {
26219
- return false;
26220
- }
26221
- this.node = node;
26222
- this.decorations = decorations;
26223
- this.innerDecorations = innerDecorations;
26224
- this.updateHTMLAttributes();
26225
- return true;
26226
- }
26227
- stopEvent(event) {
26228
- if (!this.dom) return false;
26229
- const target = event.target;
26230
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
26231
- if (!isInElement) return false;
26232
- const isDragEvent = event.type.startsWith("drag");
26233
- const isDropEvent = event.type === "drop";
26234
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
26235
- if (isInput && !isDropEvent && !isDragEvent) return true;
26236
- const { isEditable } = this.editor;
26237
- const { isDragging } = this;
26238
- const isDraggable = !!this.node.type.spec.draggable;
26239
- const isSelectable = NodeSelection.isSelectable(this.node);
26240
- const isCopyEvent = event.type === "copy";
26241
- const isPasteEvent = event.type === "paste";
26242
- const isCutEvent = event.type === "cut";
26243
- const isClickEvent = event.type === "mousedown";
26244
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
26245
- event.preventDefault();
26246
- }
26247
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
26248
- event.preventDefault();
26249
- return false;
26250
- }
26251
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
26252
- const dragHandle = target.closest("[data-drag-handle]");
26253
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
26254
- if (isValidDragHandle) {
26255
- this.isDragging = true;
26256
- document.addEventListener(
26257
- "dragend",
26258
- () => {
26259
- this.isDragging = false;
26260
- },
26261
- { once: true }
26262
- );
26263
- document.addEventListener(
26264
- "drop",
26265
- () => {
26266
- this.isDragging = false;
26267
- },
26268
- { once: true }
26269
- );
26270
- document.addEventListener(
26271
- "mouseup",
26272
- () => {
26273
- this.isDragging = false;
26274
- },
26275
- { once: true }
26276
- );
26277
- }
26278
- }
26279
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
26280
- return false;
26281
- }
26282
- return true;
26283
- }
26284
- ignoreMutation(mutation) {
26285
- if (!this.dom || !this.contentDOM) return true;
26286
- if (this.node.isLeaf || this.node.isAtom) return true;
26287
- if (mutation.type === "selection") return false;
26288
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
26289
- if (this.contentDOM.contains(mutation.target)) return false;
26290
- return true;
26291
- }
26292
- destroy() {
26293
- this.dom.remove();
26294
- this.contentDOM?.remove();
26295
- }
26296
- updateAttributes(attrs) {
26297
- const pos = this.getPos();
26298
- if (typeof pos !== "number") {
26299
- return;
26300
- }
26301
- return this.view.dispatch(
26302
- this.view.state.tr.setNodeMarkup(pos, void 0, {
26303
- ...this.node.attrs,
26304
- ...attrs
26305
- })
26306
- );
26307
- }
26308
- updateHTMLAttributes() {
26309
- const { extensionService } = this.editor;
26310
- const { attributes } = extensionService;
26311
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
26312
- this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
26313
- }
26314
- createDragHandle() {
26315
- const dragHandle = document.createElement("span");
26316
- dragHandle.classList.add("sd-structured-content-draggable");
26317
- dragHandle.draggable = true;
26318
- dragHandle.contentEditable = "false";
26319
- dragHandle.dataset.dragHandle = "";
26320
- const textElement = document.createElement("span");
26321
- textElement.textContent = "Structured content";
26322
- dragHandle.append(textElement);
26323
- return dragHandle;
26324
- }
26325
- onDragStart(event) {
26326
- const { view } = this.editor;
26327
- const target = event.target;
26328
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
26329
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
26330
- return;
26331
- }
26332
- let x = 0;
26333
- let y = 0;
26334
- if (this.dom !== dragHandle) {
26335
- const domBox = this.dom.getBoundingClientRect();
26336
- const handleBox = dragHandle.getBoundingClientRect();
26337
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
26338
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
26339
- x = handleBox.x - domBox.x + offsetX;
26340
- y = handleBox.y - domBox.y + offsetY;
26341
- }
26342
- event.dataTransfer?.setDragImage(this.dom, x, y);
26343
- const pos = this.getPos();
26344
- if (typeof pos !== "number") {
26345
- return;
26346
- }
26347
- const selection = NodeSelection.create(view.state.doc, pos);
26348
- const transaction = view.state.tr.setSelection(selection);
26349
- view.dispatch(transaction);
26350
- }
26351
- }
26352
- class StructuredContentInlineView extends StructuredContentViewBase {
26353
- constructor(props) {
26354
- super(props);
26355
- }
26356
- mount() {
26357
- this.buildView();
26358
- }
26359
- get contentDOM() {
26360
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
26361
- return contentElement || null;
26362
- }
26363
- createElement() {
26364
- const element = document.createElement("span");
26365
- element.classList.add(structuredContentClass$1);
26366
- element.setAttribute("data-structured-content", "");
26367
- const contentElement = document.createElement("span");
26368
- contentElement.classList.add(structuredContentInnerClass$1);
26369
- element.append(contentElement);
26370
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26371
- updateDOMAttributes(element, { ...domAttrs });
26372
- return { element, contentElement };
26373
- }
26374
- buildView() {
26375
- const { element } = this.createElement();
26376
- const dragHandle = this.createDragHandle();
26377
- element.prepend(dragHandle);
26378
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26379
- this.root = element;
26380
- }
26381
- updateView() {
26382
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26383
- updateDOMAttributes(this.dom, { ...domAttrs });
26384
- }
26385
- update(node, decorations, innerDecorations) {
26386
- const result = super.update(node, decorations, innerDecorations);
26387
- if (!result) return false;
26388
- this.updateView();
26389
- return true;
26390
- }
26391
- }
26392
- const structuredContentClass$1 = "sd-structured-content";
26393
- const structuredContentInnerClass$1 = "sd-structured-content__content";
26394
- const StructuredContent = Node$1.create({
26395
- name: "structuredContent",
26396
- group: "inline structuredContent",
26397
- inline: true,
26398
- content: "inline*",
26399
- isolating: true,
26400
- atom: false,
26401
- // false - has editable content.
26402
- draggable: true,
26403
- addOptions() {
26404
- return {
26405
- htmlAttributes: {
26406
- class: structuredContentClass$1,
26407
- "aria-label": "Structured content node"
26408
- }
26409
- };
26410
- },
26411
- addAttributes() {
26412
- return {
26413
- id: {
26414
- default: null,
26415
- parseDOM: (elem) => elem.getAttribute("data-id"),
26416
- renderDOM: (attrs) => {
26417
- if (!attrs.id) return {};
26418
- return { "data-id": attrs.id };
26419
- }
26420
- },
26421
- sdtPr: {
26422
- rendered: false
26423
- }
26424
- };
26425
- },
26426
- parseDOM() {
26427
- return [{ tag: "span[data-structured-content]" }];
26428
- },
26429
- renderDOM({ htmlAttributes }) {
26430
- return [
26431
- "span",
26432
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26433
- "data-structured-content": ""
26434
- }),
26435
- 0
26436
- ];
26437
- },
26438
- addNodeView() {
26439
- return (props) => {
26440
- return new StructuredContentInlineView({ ...props });
26441
- };
26442
- }
26443
- });
26444
- class StructuredContentBlockView extends StructuredContentViewBase {
26445
- constructor(props) {
26446
- super(props);
26447
- }
26448
- mount() {
26449
- this.buildView();
26450
- }
26451
- get contentDOM() {
26452
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
26453
- return contentElement || null;
26454
- }
26455
- createElement() {
26456
- const element = document.createElement("div");
26457
- element.classList.add(structuredContentClass);
26458
- element.setAttribute("data-structured-content-block", "");
26459
- const contentElement = document.createElement("div");
26460
- contentElement.classList.add(structuredContentInnerClass);
26461
- element.append(contentElement);
26462
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26463
- updateDOMAttributes(element, { ...domAttrs });
26464
- return { element, contentElement };
26465
- }
26466
- buildView() {
26467
- const { element } = this.createElement();
26468
- const dragHandle = this.createDragHandle();
26469
- element.prepend(dragHandle);
26470
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26471
- this.root = element;
26472
- }
26473
- updateView() {
26474
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26475
- updateDOMAttributes(this.dom, { ...domAttrs });
26476
- }
26477
- update(node, decorations, innerDecorations) {
26478
- const result = super.update(node, decorations, innerDecorations);
26479
- if (!result) return false;
26480
- this.updateView();
26481
- return true;
26482
- }
26483
- }
26484
- const structuredContentClass = "sd-structured-content-block";
26485
- const structuredContentInnerClass = "sd-structured-content-block__content";
26486
- const StructuredContentBlock = Node$1.create({
26487
- name: "structuredContentBlock",
26488
- group: "block structuredContent",
26489
- content: "block*",
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+",
26490
26971
  isolating: true,
26491
- atom: false,
26492
- // false - has editable content.
26493
- draggable: true,
26494
26972
  addOptions() {
26495
26973
  return {
26496
26974
  htmlAttributes: {
26497
- class: structuredContentClass,
26498
- "aria-label": "Structured content block node"
26975
+ class: "sd-editor-shape-container",
26976
+ "aria-label": "Shape container node"
26499
26977
  }
26500
26978
  };
26501
26979
  },
26502
26980
  addAttributes() {
26503
26981
  return {
26504
- id: {
26982
+ fillcolor: {
26983
+ renderDOM: (attrs) => {
26984
+ if (!attrs.fillcolor) return {};
26985
+ return {
26986
+ style: `background-color: ${attrs.fillcolor}`
26987
+ };
26988
+ }
26989
+ },
26990
+ sdBlockId: {
26505
26991
  default: null,
26506
- parseDOM: (elem) => elem.getAttribute("data-id"),
26992
+ keepOnSplit: false,
26993
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26507
26994
  renderDOM: (attrs) => {
26508
- if (!attrs.id) return {};
26509
- return { "data-id": attrs.id };
26995
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26510
26996
  }
26511
26997
  },
26512
- 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: {
26513
27010
  rendered: false
26514
27011
  }
26515
27012
  };
26516
27013
  },
26517
27014
  parseDOM() {
26518
- return [{ tag: "div[data-structured-content-block]" }];
27015
+ return [
27016
+ {
27017
+ tag: `div[data-type="${this.name}"]`
27018
+ }
27019
+ ];
26519
27020
  },
26520
27021
  renderDOM({ htmlAttributes }) {
26521
27022
  return [
26522
27023
  "div",
26523
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26524
- "data-structured-content-block": ""
26525
- }),
27024
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26526
27025
  0
26527
27026
  ];
26528
- },
26529
- addNodeView() {
26530
- return (props) => {
26531
- return new StructuredContentBlockView({ ...props });
26532
- };
26533
27027
  }
26534
27028
  });
26535
- class DocumentSectionView {
26536
- constructor(node, getPos, decorations, editor) {
26537
- __privateAdd(this, _DocumentSectionView_instances);
26538
- this.node = node;
26539
- this.editor = editor;
26540
- this.decorations = decorations;
26541
- this.view = editor.view;
26542
- this.getPos = getPos;
26543
- __privateMethod(this, _DocumentSectionView_instances, init_fn3).call(this);
26544
- }
26545
- }
26546
- _DocumentSectionView_instances = new WeakSet();
26547
- init_fn3 = function() {
26548
- const { attrs } = this.node;
26549
- const { id, title, description } = attrs;
26550
- this.dom = document.createElement("div");
26551
- this.dom.className = "sd-document-section-block";
26552
- this.dom.setAttribute("data-id", id);
26553
- this.dom.setAttribute("data-title", title);
26554
- this.dom.setAttribute("data-description", description);
26555
- this.dom.setAttribute("aria-label", "Document section");
26556
- __privateMethod(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
26557
- this.contentDOM = document.createElement("div");
26558
- this.contentDOM.className = "sd-document-section-block-content";
26559
- this.contentDOM.setAttribute("contenteditable", "true");
26560
- this.dom.appendChild(this.contentDOM);
26561
- };
26562
- addToolTip_fn = function() {
26563
- const { title } = this.node.attrs;
26564
- this.infoDiv = document.createElement("div");
26565
- this.infoDiv.className = "sd-document-section-block-info";
26566
- const textSpan = document.createElement("span");
26567
- textSpan.textContent = title || "Document section";
26568
- this.infoDiv.appendChild(textSpan);
26569
- this.infoDiv.setAttribute("contenteditable", "false");
26570
- this.dom.appendChild(this.infoDiv);
26571
- };
26572
- const getAllSections = (editor) => {
26573
- if (!editor) return [];
26574
- const type = editor.schema.nodes.documentSection;
26575
- if (!type) return [];
26576
- const sections = [];
26577
- const { state } = editor;
26578
- state.doc.descendants((node, pos) => {
26579
- if (node.type.name === type.name) {
26580
- sections.push({ node, pos });
26581
- }
26582
- });
26583
- return sections;
26584
- };
26585
- const exportSectionsToHTML = (editor) => {
26586
- const sections = getAllSections(editor);
26587
- const processedSections = /* @__PURE__ */ new Set();
26588
- const result = [];
26589
- sections.forEach(({ node }) => {
26590
- const { attrs } = node;
26591
- const { id, title, description } = attrs;
26592
- if (processedSections.has(id)) return;
26593
- processedSections.add(id);
26594
- const html = getHTMLFromNode(node, editor);
26595
- result.push({
26596
- id,
26597
- title,
26598
- description,
26599
- html
26600
- });
26601
- });
26602
- return result;
26603
- };
26604
- const getHTMLFromNode = (node, editor) => {
26605
- const tempDocument = document.implementation.createHTMLDocument();
26606
- const container = tempDocument.createElement("div");
26607
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
26608
- container.appendChild(fragment);
26609
- let html = container.innerHTML;
26610
- return html;
26611
- };
26612
- const exportSectionsToJSON = (editor) => {
26613
- const sections = getAllSections(editor);
26614
- const processedSections = /* @__PURE__ */ new Set();
26615
- const result = [];
26616
- sections.forEach(({ node }) => {
26617
- const { attrs } = node;
26618
- const { id, title, description } = attrs;
26619
- if (processedSections.has(id)) return;
26620
- processedSections.add(id);
26621
- result.push({
26622
- id,
26623
- title,
26624
- description,
26625
- content: node.toJSON()
26626
- });
26627
- });
26628
- return result;
26629
- };
26630
- const getLinkedSectionEditor = (id, options, editor) => {
26631
- const sections = getAllSections(editor);
26632
- const section = sections.find((s) => s.node.attrs.id === id);
26633
- if (!section) return null;
26634
- const child = editor.createChildEditor({
26635
- ...options,
26636
- onUpdate: ({ editor: childEditor, transaction }) => {
26637
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
26638
- if (isFromtLinkedParent) return;
26639
- const updatedContent = childEditor.state.doc.content;
26640
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26641
- if (!sectionNode) return;
26642
- const { pos, node } = sectionNode;
26643
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
26644
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
26645
- tr.setMeta("fromLinkedChild", true);
26646
- editor.view.dispatch(tr);
26647
- }
26648
- });
26649
- editor.on("update", ({ transaction }) => {
26650
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
26651
- if (isFromLinkedChild) return;
26652
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26653
- if (!sectionNode) return;
26654
- const sectionContent = sectionNode.node.content;
26655
- const json = {
26656
- type: "doc",
26657
- content: sectionContent.content.map((node) => node.toJSON())
26658
- };
26659
- const childTr = child.state.tr;
26660
- childTr.setMeta("fromLinkedParent", true);
26661
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
26662
- child.view.dispatch(childTr);
26663
- });
26664
- return child;
26665
- };
26666
- const SectionHelpers = {
26667
- getAllSections,
26668
- exportSectionsToHTML,
26669
- exportSectionsToJSON,
26670
- getLinkedSectionEditor
26671
- };
26672
- const DocumentSection = Node$1.create({
26673
- name: "documentSection",
27029
+ const ShapeTextbox = Node$1.create({
27030
+ name: "shapeTextbox",
26674
27031
  group: "block",
26675
- content: "block*",
26676
- atom: true,
27032
+ content: "paragraph* block*",
26677
27033
  isolating: true,
26678
27034
  addOptions() {
26679
27035
  return {
26680
27036
  htmlAttributes: {
26681
- class: "sd-document-section-block",
26682
- "aria-label": "Structured content block"
27037
+ class: "sd-editor-shape-textbox",
27038
+ "aria-label": "Shape textbox node"
26683
27039
  }
26684
27040
  };
26685
27041
  },
26686
- parseDOM() {
26687
- return [
26688
- {
26689
- tag: "div.sd-document-section-block",
26690
- priority: 60
26691
- }
26692
- ];
26693
- },
26694
- renderDOM({ htmlAttributes }) {
26695
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
26696
- },
26697
27042
  addAttributes() {
26698
27043
  return {
26699
- id: {},
26700
27044
  sdBlockId: {
26701
27045
  default: null,
26702
27046
  keepOnSplit: false,
@@ -26705,212 +27049,131 @@ const DocumentSection = Node$1.create({
26705
27049
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26706
27050
  }
26707
27051
  },
26708
- title: {},
26709
- description: {},
26710
- sectionType: {},
26711
- isLocked: { default: false }
27052
+ attributes: {
27053
+ rendered: false
27054
+ }
26712
27055
  };
26713
27056
  },
26714
- addNodeView() {
26715
- return ({ node, editor, getPos, decorations }) => {
26716
- 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
+ }
26717
27084
  };
26718
27085
  },
26719
- addCommands() {
27086
+ addAttributes() {
26720
27087
  return {
26721
- /**
26722
- * Create a lockable content section
26723
- * @category Command
26724
- * @param {SectionCreate} [options={}] - Section configuration
26725
- * @example
26726
- * editor.commands.createDocumentSection({
26727
- * id: 1,
26728
- * title: 'Terms & Conditions',
26729
- * isLocked: true,
26730
- * html: '<p>Legal content...</p>'
26731
- * })
26732
- */
26733
- createDocumentSection: (options = {}) => ({ tr, state, dispatch, editor }) => {
26734
- const { selection } = state;
26735
- let { from: from2, to } = selection;
26736
- let content = selection.content().content;
26737
- const { html: optionsHTML, json: optionsJSON } = options;
26738
- if (optionsHTML) {
26739
- const html = htmlHandler(optionsHTML, this.editor);
26740
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
26741
- content = doc2.content;
26742
- }
26743
- if (optionsJSON) {
26744
- content = this.editor.schema.nodeFromJSON(optionsJSON);
26745
- }
26746
- if (!content?.content?.length) {
26747
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
26748
- }
26749
- if (!options.id) {
26750
- const allSections = SectionHelpers.getAllSections(editor);
26751
- options.id = allSections.length + 1;
26752
- }
26753
- if (!options.title) {
26754
- options.title = "Document section";
26755
- }
26756
- const node = this.type.createAndFill(options, content);
26757
- if (!node) return false;
26758
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
26759
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
26760
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
26761
- from2 = insertPos2;
26762
- to = insertPos2;
26763
- }
26764
- tr.replaceRangeWith(from2, to, node);
26765
- const nodeEnd = from2 + node.nodeSize;
26766
- let shouldInsertParagraph = true;
26767
- let insertPos = nodeEnd;
26768
- if (nodeEnd >= tr.doc.content.size) {
26769
- insertPos = tr.doc.content.size;
26770
- if (insertPos > 0) {
26771
- const $endPos = tr.doc.resolve(insertPos);
26772
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
26773
- shouldInsertParagraph = false;
26774
- }
26775
- }
26776
- }
26777
- if (shouldInsertParagraph) {
26778
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
26779
- tr.insert(insertPos, emptyParagraph);
26780
- }
26781
- if (dispatch) {
26782
- tr.setMeta("documentSection", { action: "create" });
26783
- dispatch(tr);
26784
- setTimeout(() => {
26785
- try {
26786
- const currentState = editor.state;
26787
- const docSize = currentState.doc.content.size;
26788
- let targetPos = from2 + node.nodeSize;
26789
- if (shouldInsertParagraph) {
26790
- targetPos += 1;
26791
- }
26792
- targetPos = Math.min(targetPos, docSize);
26793
- if (targetPos < docSize && targetPos > 0) {
26794
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
26795
- const newTr = currentState.tr.setSelection(newSelection);
26796
- editor.view.dispatch(newTr);
26797
- }
26798
- } catch (e) {
26799
- console.warn("Could not set delayed selection:", e);
26800
- }
26801
- }, 0);
27088
+ horizontalRule: {
27089
+ default: false,
27090
+ renderDOM: ({ horizontalRule }) => {
27091
+ if (!horizontalRule) return {};
27092
+ return { "data-horizontal-rule": "true" };
26802
27093
  }
26803
- return true;
26804
27094
  },
26805
- /**
26806
- * Remove section wrapper at cursor, preserving its content
26807
- * @category Command
26808
- * @example
26809
- * editor.commands.removeSectionAtSelection()
26810
- * @note Content stays in document, only section wrapper is removed
26811
- */
26812
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
26813
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
26814
- if (!sdtNode) return false;
26815
- const { node, pos } = sdtNode;
26816
- const nodeStart = pos;
26817
- const nodeEnd = nodeStart + node.nodeSize;
26818
- const contentToPreserve = node.content;
26819
- tr.delete(nodeStart, nodeEnd);
26820
- if (contentToPreserve.size > 0) {
26821
- tr.insert(nodeStart, contentToPreserve);
26822
- }
26823
- const newPos = Math.min(nodeStart, tr.doc.content.size);
26824
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
26825
- if (dispatch) {
26826
- tr.setMeta("documentSection", { action: "delete" });
26827
- 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 };
26828
27106
  }
26829
- return true;
26830
27107
  },
26831
- /**
26832
- * Delete section and all its content
26833
- * @category Command
26834
- * @param {number} id - Section to delete
26835
- * @example
26836
- * editor.commands.removeSectionById(123)
26837
- */
26838
- removeSectionById: (id) => ({ tr, dispatch }) => {
26839
- const sections = SectionHelpers.getAllSections(this.editor);
26840
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
26841
- if (!sectionToRemove) return false;
26842
- const { pos, node } = sectionToRemove;
26843
- const nodeStart = pos;
26844
- const nodeEnd = nodeStart + node.nodeSize;
26845
- tr.delete(nodeStart, nodeEnd);
26846
- if (dispatch) {
26847
- tr.setMeta("documentSection", { action: "delete", id });
26848
- dispatch(tr);
27108
+ background: {
27109
+ default: null,
27110
+ renderDOM: (attrs) => {
27111
+ if (!attrs.background) return {};
27112
+ return {
27113
+ style: `background-color: ${attrs.background}`
27114
+ };
26849
27115
  }
26850
- return true;
26851
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 {
26852
27137
  /**
26853
- * Lock section against edits
27138
+ * Insert a horizontal rule
26854
27139
  * @category Command
26855
- * @param {number} id - Section to lock
26856
27140
  * @example
26857
- * editor.commands.lockSectionById(123)
27141
+ * editor.commands.insertHorizontalRule()
27142
+ * @note Creates a visual separator between content sections
26858
27143
  */
26859
- lockSectionById: (id) => ({ tr, dispatch }) => {
26860
- const sections = SectionHelpers.getAllSections(this.editor);
26861
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
26862
- if (!sectionToLock) return false;
26863
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
26864
- if (dispatch) {
26865
- tr.setMeta("documentSection", { action: "lock", id });
26866
- dispatch(tr);
26867
- }
26868
- 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
+ });
26869
27153
  },
26870
27154
  /**
26871
- * Modify section attributes or content
27155
+ * Insert a content block
26872
27156
  * @category Command
26873
- * @param {SectionUpdate} options - Changes to apply
27157
+ * @param {ContentBlockConfig} config - Block configuration
26874
27158
  * @example
26875
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
26876
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
26877
- * editor.commands.updateSectionById({
26878
- * id: 123,
26879
- * html: '<p>Updated</p>',
26880
- * 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'
26881
27167
  * })
27168
+ * @note Used for spacing, dividers, and special inline content
26882
27169
  */
26883
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
26884
- const sections = SectionHelpers.getAllSections(editor || this.editor);
26885
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
26886
- if (!sectionToUpdate) return false;
26887
- const { pos, node } = sectionToUpdate;
26888
- let newContent = null;
26889
- if (html) {
26890
- const htmlDoc = htmlHandler(html, editor || this.editor);
26891
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
26892
- newContent = doc2.content;
26893
- }
26894
- if (json) {
26895
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
26896
- }
26897
- if (!newContent) {
26898
- newContent = node.content;
26899
- }
26900
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
26901
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
26902
- if (dispatch) {
26903
- tr.setMeta("documentSection", { action: "update", id, attrs });
26904
- dispatch(tr);
26905
- }
26906
- return true;
27170
+ insertContentBlock: (config) => ({ commands: commands2 }) => {
27171
+ return commands2.insertContent({
27172
+ type: this.name,
27173
+ attrs: config
27174
+ });
26907
27175
  }
26908
27176
  };
26909
- },
26910
- addHelpers() {
26911
- return {
26912
- ...SectionHelpers
26913
- };
26914
27177
  }
26915
27178
  });
26916
27179
  const { findChildren } = helpers;
@@ -33760,6 +34023,7 @@ const getStarterExtensions = () => {
33760
34023
  Search,
33761
34024
  StructuredContent,
33762
34025
  StructuredContentBlock,
34026
+ StructuredContentCommands,
33763
34027
  DocumentSection,
33764
34028
  NodeResizer,
33765
34029
  CustomSelection,