@harbour-enterprises/superdoc 0.21.0 → 0.22.0-next.10

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 (59) hide show
  1. package/dist/chunks/{PdfViewer-D3zo7tPo.es.js → PdfViewer-CJdQmuIm.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-OZDJ7gwT.cjs → PdfViewer-DE1NR4Ve.cjs} +1 -1
  3. package/dist/chunks/{index-MzW5BVNd.es.js → index-B9sHxXr_.es.js} +55 -27
  4. package/dist/chunks/{index-CfYf4T_z.cjs → index-nfoifSpX.cjs} +55 -27
  5. package/dist/chunks/{super-editor.es-U-GVCd_F.cjs → super-editor.es-DAP-fnHo.cjs} +3469 -2438
  6. package/dist/chunks/{super-editor.es-Bntob7Wd.es.js → super-editor.es-_iVPQ8J8.es.js} +3469 -2438
  7. package/dist/core/SuperDoc.d.ts +5 -0
  8. package/dist/core/SuperDoc.d.ts.map +1 -1
  9. package/dist/core/types/index.d.ts +12 -4
  10. package/dist/core/types/index.d.ts.map +1 -1
  11. package/dist/stores/comments-store.d.ts +4 -1
  12. package/dist/stores/comments-store.d.ts.map +1 -1
  13. package/dist/style.css +53 -44
  14. package/dist/super-editor/ai-writer.es.js +2 -2
  15. package/dist/super-editor/chunks/{converter-3xnF_NHq.js → converter-DK1NMJZB.js} +1148 -748
  16. package/dist/super-editor/chunks/{docx-zipper-CZdELYi-.js → docx-zipper-CmK8TyNb.js} +73 -12
  17. package/dist/super-editor/chunks/{editor-BqYH4kDD.js → editor-YR4uV-dp.js} +1917 -1606
  18. package/dist/super-editor/chunks/{toolbar-TkaE2kKM.js → toolbar-DzJyRvb0.js} +2 -2
  19. package/dist/super-editor/converter.es.js +1 -1
  20. package/dist/super-editor/docx-zipper.es.js +2 -2
  21. package/dist/super-editor/editor.es.js +3 -3
  22. package/dist/super-editor/file-zipper.es.js +1 -1
  23. package/dist/super-editor/src/components/slash-menu/menuItems.d.ts +5 -1
  24. package/dist/super-editor/src/components/slash-menu/tests/testHelpers.d.ts +466 -0
  25. package/dist/super-editor/src/components/slash-menu/utils.d.ts +9 -2
  26. package/dist/super-editor/src/core/DocxZipper.d.ts +1 -1
  27. package/dist/super-editor/src/core/commands/__tests__/schemaWithLists.d.ts +2 -0
  28. package/dist/super-editor/src/core/commands/__tests__/testHelpers.d.ts +4 -0
  29. package/dist/super-editor/src/core/commands/__tests__/testSchema.d.ts +2 -0
  30. package/dist/super-editor/src/core/commands/tests/commandTestUtils.d.ts +7 -0
  31. package/dist/super-editor/src/core/commands/tests/test-schema.d.ts +2 -0
  32. package/dist/super-editor/src/core/super-converter/SuperConverter.d.ts +1 -13
  33. package/dist/super-editor/src/core/super-converter/exporter.d.ts +1 -0
  34. package/dist/super-editor/src/core/super-converter/helpers/tableFallbackHelpers.d.ts +24 -0
  35. package/dist/super-editor/src/core/super-converter/v3/handlers/mc/altermateContent/alternate-content-translator.d.ts +6 -0
  36. package/dist/super-editor/src/core/super-converter/v3/handlers/mc/altermateContent/index.d.ts +1 -0
  37. package/dist/super-editor/src/extensions/custom-selection/custom-selection.d.ts +5 -0
  38. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  39. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  40. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  41. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +7 -0
  42. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +7 -0
  43. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +7 -0
  44. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +8 -0
  45. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  46. package/dist/super-editor/src/tests/helpers/helpers.d.ts +1 -0
  47. package/dist/super-editor/src/utils/contextmenu-helpers.d.ts +24 -0
  48. package/dist/super-editor/style.css +9 -0
  49. package/dist/super-editor/super-editor.es.js +454 -154
  50. package/dist/super-editor/toolbar.es.js +2 -2
  51. package/dist/super-editor.cjs +1 -1
  52. package/dist/super-editor.es.js +1 -1
  53. package/dist/superdoc.cjs +2 -2
  54. package/dist/superdoc.es.js +2 -2
  55. package/dist/superdoc.umd.js +3528 -2469
  56. package/dist/superdoc.umd.js.map +1 -1
  57. package/package.json +1 -1
  58. package/dist/super-editor/src/extensions/run-item/index.d.ts +0 -1
  59. package/dist/super-editor/src/extensions/run-item/run-item.d.ts +0 -26
@@ -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-3xnF_NHq.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 InputRule, ak as kebabCase, al as findParentNodeClosestToPos, am as getListItemStyleDefinitions, an as docxNumberigHelpers, ao as parseIndentElement, ap as combineIndents, aq as SelectionRange, ar as Transform, as as isInTable$1, at as generateDocxRandomId, au as insertNewRelationship } from "./converter-DK1NMJZB.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-CZdELYi-.js";
17
+ import { D as DocxZipper } from "./docx-zipper-CmK8TyNb.js";
18
18
  var GOOD_LEAF_SIZE = 200;
19
19
  var RopeSequence = function RopeSequence2() {
20
20
  };
@@ -11675,9 +11675,11 @@ const toggleHeaderFooterEditMode = ({ editor, focusedSectionEditor, isEditMode,
11675
11675
  item.editor.view.dom.setAttribute("documentmode", documentMode);
11676
11676
  });
11677
11677
  if (isEditMode) {
11678
- const pm = document.querySelector(".ProseMirror");
11679
- pm.classList.add("header-footer-edit");
11680
- pm.setAttribute("aria-readonly", true);
11678
+ const pm = editor.view?.dom || editor.options.element?.querySelector?.(".ProseMirror");
11679
+ if (pm) {
11680
+ pm.classList.add("header-footer-edit");
11681
+ pm.setAttribute("aria-readonly", true);
11682
+ }
11681
11683
  }
11682
11684
  if (focusedSectionEditor) {
11683
11685
  focusedSectionEditor.view.focus();
@@ -12105,28 +12107,25 @@ const handleTrackedChangeTransaction = (trackedChangeMeta, trackedChanges, newEd
12105
12107
  if (emitParams) editor.emit("commentsUpdate", emitParams);
12106
12108
  return newTrackedChanges;
12107
12109
  };
12108
- const getTrackedChangeText = ({ state, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
12110
+ const getTrackedChangeText = ({ state, nodes, mark, marks, trackedChangeType, isDeletionInsertion }) => {
12109
12111
  let trackedChangeText = "";
12110
12112
  let deletionText = "";
12111
12113
  if (trackedChangeType === TrackInsertMarkName) {
12112
- trackedChangeText = node?.text ?? "";
12114
+ trackedChangeText = nodes.reduce((acc, node) => {
12115
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
12116
+ acc += node?.text || node?.textContent || "";
12117
+ return acc;
12118
+ }, "");
12113
12119
  }
12114
12120
  if (trackedChangeType === TrackFormatMarkName) {
12115
12121
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
12116
12122
  }
12117
12123
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
12118
- deletionText = node?.text ?? "";
12119
- if (isDeletionInsertion) {
12120
- let { id } = marks.deletionMark.attrs;
12121
- let deletionNode = findNode$1(state.doc, (node2) => {
12122
- const { marks: marks2 = [] } = node2;
12123
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
12124
- if (!changeMarks.length) return false;
12125
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
12126
- if (hasMatchingId) return true;
12127
- });
12128
- deletionText = deletionNode?.node.text ?? "";
12129
- }
12124
+ deletionText = nodes.reduce((acc, node) => {
12125
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
12126
+ acc += node?.text || node?.textContent || "";
12127
+ return acc;
12128
+ }, "");
12130
12129
  }
12131
12130
  return {
12132
12131
  deletionText,
@@ -12141,18 +12140,17 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
12141
12140
  const id = attrs.id;
12142
12141
  const node = nodes[0];
12143
12142
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
12144
- let existingNode;
12143
+ let nodesWithMark = [];
12145
12144
  newEditorState.doc.descendants((node2) => {
12146
12145
  const { marks: marks2 = [] } = node2;
12147
12146
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
12148
12147
  if (!changeMarks.length) return;
12149
12148
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
12150
- if (hasMatchingId) existingNode = node2;
12151
- if (existingNode) return false;
12149
+ if (hasMatchingId) nodesWithMark.push(node2);
12152
12150
  });
12153
12151
  const { deletionText, trackedChangeText } = getTrackedChangeText({
12154
12152
  state: newEditorState,
12155
- node: existingNode || node,
12153
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
12156
12154
  mark: trackedMark,
12157
12155
  marks,
12158
12156
  trackedChangeType,
@@ -12182,14 +12180,6 @@ const createOrUpdateTrackedChangeComment = ({ event, marks, deletionNodes, nodes
12182
12180
  else if (event === "update") params2.event = comments_module_events.UPDATE;
12183
12181
  return params2;
12184
12182
  };
12185
- function findNode$1(node, predicate) {
12186
- let found = null;
12187
- node.descendants((node2, pos) => {
12188
- if (predicate(node2)) found = { node: node2, pos };
12189
- if (found) return false;
12190
- });
12191
- return found;
12192
- }
12193
12183
  function findRangeById(doc2, id) {
12194
12184
  let from2 = null, to = null;
12195
12185
  doc2.descendants((node, pos) => {
@@ -12707,6 +12697,7 @@ const generateTableIfNecessary = ({ tableNode, annotationValues, tr, state }) =>
12707
12697
  const mappedRowStart = tr.mapping.map(absoluteRowStart);
12708
12698
  const rowEnd = mappedRowStart + rowNode.nodeSize;
12709
12699
  tr.replaceWith(mappedRowStart, rowEnd, Fragment.from(newRows));
12700
+ tr.setMeta("tableGeneration", true);
12710
12701
  } catch (error) {
12711
12702
  console.error("Error during row generation:", error);
12712
12703
  throw error;
@@ -13111,7 +13102,7 @@ function findFieldAnnotationsBetween(from2, to, doc2) {
13111
13102
  }
13112
13103
  function findRemovedFieldAnnotations(tr) {
13113
13104
  let removedNodes = [];
13114
- if (!tr.steps.length || tr.meta && !Object.keys(tr.meta).every((meta) => ["inputType", "uiEvent", "paste"].includes(meta)) || ["historyUndo", "historyRedo"].includes(tr.getMeta("inputType")) || ["drop"].includes(tr.getMeta("uiEvent")) || tr.getMeta("fieldAnnotationUpdate") === true) {
13105
+ if (!tr.steps.length || tr.meta && !Object.keys(tr.meta).every((meta) => ["inputType", "uiEvent", "paste"].includes(meta)) || ["historyUndo", "historyRedo"].includes(tr.getMeta("inputType")) || ["drop"].includes(tr.getMeta("uiEvent")) || tr.getMeta("fieldAnnotationUpdate") === true || tr.getMeta("tableGeneration") === true) {
13115
13106
  return removedNodes;
13116
13107
  }
13117
13108
  const hasDeletion = transactionDeletedAnything(tr);
@@ -14332,7 +14323,7 @@ const _Editor = class _Editor extends EventEmitter {
14332
14323
  setDocumentMode(documentMode) {
14333
14324
  let cleanedMode = documentMode?.toLowerCase() || "editing";
14334
14325
  if (!this.extensionService || !this.state) return;
14335
- const pm = document.querySelector(".ProseMirror");
14326
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
14336
14327
  if (this.options.role === "viewer") cleanedMode = "viewing";
14337
14328
  if (this.options.role === "suggester" && cleanedMode === "editing") cleanedMode = "suggesting";
14338
14329
  if (cleanedMode === "viewing") {
@@ -14562,6 +14553,8 @@ const _Editor = class _Editor extends EventEmitter {
14562
14553
  element.style.isolation = "isolate";
14563
14554
  proseMirror.style.outline = "none";
14564
14555
  proseMirror.style.border = "none";
14556
+ element.style.backgroundColor = "#fff";
14557
+ proseMirror.style.backgroundColor = "#fff";
14565
14558
  const { typeface, fontSizePt, fontFamilyCss } = this.converter.getDocumentDefaultStyles() ?? {};
14566
14559
  const resolvedFontFamily = fontFamilyCss || typeface;
14567
14560
  if (resolvedFontFamily) {
@@ -14818,7 +14811,8 @@ const _Editor = class _Editor extends EventEmitter {
14818
14811
  files: this.options.content
14819
14812
  },
14820
14813
  media,
14821
- true
14814
+ true,
14815
+ updatedDocs
14822
14816
  );
14823
14817
  return updatedDocs;
14824
14818
  }
@@ -14884,7 +14878,7 @@ const _Editor = class _Editor extends EventEmitter {
14884
14878
  * @returns {Object | void} Migration results
14885
14879
  */
14886
14880
  processCollaborationMigrations() {
14887
- console.debug("[checkVersionMigrations] Current editor version", "0.20.2");
14881
+ console.debug("[checkVersionMigrations] Current editor version", "0.21.0");
14888
14882
  if (!this.options.ydoc) return;
14889
14883
  const metaMap = this.options.ydoc.getMap("meta");
14890
14884
  let docVersion = metaMap.get("version");
@@ -15378,9 +15372,11 @@ createView_fn = function(element) {
15378
15372
  isEditMode: false,
15379
15373
  documentMode: this.options.documentMode
15380
15374
  });
15381
- const pm = document.querySelector(".ProseMirror");
15382
- pm.classList.remove("header-footer-edit");
15383
- pm.setAttribute("aria-readonly", false);
15375
+ const pm = this.view?.dom || this.options.element?.querySelector?.(".ProseMirror");
15376
+ if (pm) {
15377
+ pm.classList.remove("header-footer-edit");
15378
+ pm.setAttribute("aria-readonly", false);
15379
+ }
15384
15380
  }
15385
15381
  setWordSelection(view, pos);
15386
15382
  }
@@ -16971,583 +16967,1605 @@ const SlashMenu = Extension.create({
16971
16967
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
16972
16968
  }
16973
16969
  });
16974
- const Document = Node$1.create({
16975
- name: "doc",
16976
- topNode: true,
16977
- content: "block+",
16978
- parseDOM() {
16979
- return [{ tag: "doc" }];
16980
- },
16981
- renderDOM() {
16982
- return ["doc", 0];
16983
- },
16984
- addAttributes() {
16985
- return {
16986
- attributes: {
16987
- rendered: false,
16988
- "aria-label": "Document node"
16989
- }
16990
- };
16991
- },
16992
- addCommands() {
16993
- return {
16994
- /**
16995
- * Get document statistics
16996
- * @category Command
16997
- * @example
16998
- * // Get word and character count
16999
- * const stats = editor.commands.getDocumentStats()
17000
- * console.log(`${stats.words} words, ${stats.characters} characters`)
17001
- * @note Returns word count, character count, and paragraph count
17002
- */
17003
- getDocumentStats: () => ({ editor }) => {
17004
- const text = editor.getText();
17005
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
17006
- const characters = text.length;
17007
- const paragraphs = editor.state.doc.content.childCount;
17008
- return {
17009
- words,
17010
- characters,
17011
- paragraphs
17012
- };
17013
- },
17014
- /**
17015
- * Clear entire document
17016
- * @category Command
17017
- * @example
17018
- * editor.commands.clearDocument()
17019
- * @note Replaces all content with an empty paragraph
17020
- */
17021
- clearDocument: () => ({ commands: commands2 }) => {
17022
- return commands2.setContent("<p></p>");
16970
+ class StructuredContentViewBase {
16971
+ constructor(props) {
16972
+ __publicField(this, "node");
16973
+ __publicField(this, "view");
16974
+ __publicField(this, "getPos");
16975
+ __publicField(this, "decorations");
16976
+ __publicField(this, "innerDecorations");
16977
+ __publicField(this, "editor");
16978
+ __publicField(this, "extension");
16979
+ __publicField(this, "htmlAttributes");
16980
+ __publicField(this, "root");
16981
+ __publicField(this, "isDragging", false);
16982
+ this.node = props.node;
16983
+ this.view = props.editor.view;
16984
+ this.getPos = props.getPos;
16985
+ this.decorations = props.decorations;
16986
+ this.innerDecorations = props.innerDecorations;
16987
+ this.editor = props.editor;
16988
+ this.extension = props.extension;
16989
+ this.htmlAttributes = props.htmlAttributes;
16990
+ this.mount(props);
16991
+ }
16992
+ mount() {
16993
+ return;
16994
+ }
16995
+ get dom() {
16996
+ return this.root;
16997
+ }
16998
+ get contentDOM() {
16999
+ return null;
17000
+ }
17001
+ update(node, decorations, innerDecorations) {
17002
+ if (node.type !== this.node.type) {
17003
+ return false;
17004
+ }
17005
+ this.node = node;
17006
+ this.decorations = decorations;
17007
+ this.innerDecorations = innerDecorations;
17008
+ this.updateHTMLAttributes();
17009
+ return true;
17010
+ }
17011
+ stopEvent(event) {
17012
+ if (!this.dom) return false;
17013
+ const target = event.target;
17014
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
17015
+ if (!isInElement) return false;
17016
+ const isDragEvent = event.type.startsWith("drag");
17017
+ const isDropEvent = event.type === "drop";
17018
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
17019
+ if (isInput && !isDropEvent && !isDragEvent) return true;
17020
+ const { isEditable } = this.editor;
17021
+ const { isDragging } = this;
17022
+ const isDraggable = !!this.node.type.spec.draggable;
17023
+ const isSelectable = NodeSelection.isSelectable(this.node);
17024
+ const isCopyEvent = event.type === "copy";
17025
+ const isPasteEvent = event.type === "paste";
17026
+ const isCutEvent = event.type === "cut";
17027
+ const isClickEvent = event.type === "mousedown";
17028
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
17029
+ event.preventDefault();
17030
+ }
17031
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
17032
+ event.preventDefault();
17033
+ return false;
17034
+ }
17035
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
17036
+ const dragHandle = target.closest("[data-drag-handle]");
17037
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
17038
+ if (isValidDragHandle) {
17039
+ this.isDragging = true;
17040
+ document.addEventListener(
17041
+ "dragend",
17042
+ () => {
17043
+ this.isDragging = false;
17044
+ },
17045
+ { once: true }
17046
+ );
17047
+ document.addEventListener(
17048
+ "drop",
17049
+ () => {
17050
+ this.isDragging = false;
17051
+ },
17052
+ { once: true }
17053
+ );
17054
+ document.addEventListener(
17055
+ "mouseup",
17056
+ () => {
17057
+ this.isDragging = false;
17058
+ },
17059
+ { once: true }
17060
+ );
17023
17061
  }
17024
- };
17062
+ }
17063
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
17064
+ return false;
17065
+ }
17066
+ return true;
17025
17067
  }
17026
- });
17027
- const Text = Node$1.create({
17028
- name: "text",
17029
- group: "inline",
17030
- inline: true,
17031
- addOptions() {
17032
- return {};
17068
+ ignoreMutation(mutation) {
17069
+ if (!this.dom || !this.contentDOM) return true;
17070
+ if (this.node.isLeaf || this.node.isAtom) return true;
17071
+ if (mutation.type === "selection") return false;
17072
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
17073
+ if (this.contentDOM.contains(mutation.target)) return false;
17074
+ return true;
17033
17075
  }
17034
- });
17035
- const splitRun = () => (props) => {
17036
- const { state, view, tr } = props;
17037
- const { $from, empty: empty2 } = state.selection;
17038
- if (!empty2) return false;
17039
- if ($from.parent.type.name !== "run") return false;
17040
- const handled = splitBlock(state, (transaction) => {
17076
+ destroy() {
17077
+ this.dom.remove();
17078
+ this.contentDOM?.remove();
17079
+ }
17080
+ updateAttributes(attrs) {
17081
+ const pos = this.getPos();
17082
+ if (typeof pos !== "number") {
17083
+ return;
17084
+ }
17085
+ return this.view.dispatch(
17086
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
17087
+ ...this.node.attrs,
17088
+ ...attrs
17089
+ })
17090
+ );
17091
+ }
17092
+ updateHTMLAttributes() {
17093
+ const { extensionService } = this.editor;
17094
+ const { attributes } = extensionService;
17095
+ const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
17096
+ this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
17097
+ }
17098
+ createDragHandle() {
17099
+ const dragHandle = document.createElement("span");
17100
+ dragHandle.classList.add("sd-structured-content-draggable");
17101
+ dragHandle.draggable = true;
17102
+ dragHandle.contentEditable = "false";
17103
+ dragHandle.dataset.dragHandle = "";
17104
+ const textElement = document.createElement("span");
17105
+ textElement.textContent = this.node.attrs.alias || "Structured content";
17106
+ dragHandle.append(textElement);
17107
+ return dragHandle;
17108
+ }
17109
+ onDragStart(event) {
17110
+ const { view } = this.editor;
17111
+ const target = event.target;
17112
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
17113
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
17114
+ return;
17115
+ }
17116
+ let x = 0;
17117
+ let y = 0;
17118
+ if (this.dom !== dragHandle) {
17119
+ const domBox = this.dom.getBoundingClientRect();
17120
+ const handleBox = dragHandle.getBoundingClientRect();
17121
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
17122
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
17123
+ x = handleBox.x - domBox.x + offsetX;
17124
+ y = handleBox.y - domBox.y + offsetY;
17125
+ }
17126
+ event.dataTransfer?.setDragImage(this.dom, x, y);
17127
+ const pos = this.getPos();
17128
+ if (typeof pos !== "number") {
17129
+ return;
17130
+ }
17131
+ const selection = NodeSelection.create(view.state.doc, pos);
17132
+ const transaction = view.state.tr.setSelection(selection);
17041
17133
  view.dispatch(transaction);
17042
- });
17043
- if (handled) {
17044
- tr.setMeta("preventDispatch", true);
17045
17134
  }
17046
- return handled;
17047
- };
17048
- const Run = OxmlNode.create({
17049
- name: "run",
17050
- oXmlName: "w:r",
17051
- group: "inline",
17135
+ }
17136
+ class StructuredContentInlineView extends StructuredContentViewBase {
17137
+ constructor(props) {
17138
+ super(props);
17139
+ }
17140
+ mount() {
17141
+ this.buildView();
17142
+ }
17143
+ get contentDOM() {
17144
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
17145
+ return contentElement || null;
17146
+ }
17147
+ createElement() {
17148
+ const element = document.createElement("span");
17149
+ element.classList.add(structuredContentClass$1);
17150
+ element.setAttribute("data-structured-content", "");
17151
+ const contentElement = document.createElement("span");
17152
+ contentElement.classList.add(structuredContentInnerClass$1);
17153
+ element.append(contentElement);
17154
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17155
+ updateDOMAttributes(element, { ...domAttrs });
17156
+ return { element, contentElement };
17157
+ }
17158
+ buildView() {
17159
+ const { element } = this.createElement();
17160
+ const dragHandle = this.createDragHandle();
17161
+ element.prepend(dragHandle);
17162
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
17163
+ this.root = element;
17164
+ }
17165
+ updateView() {
17166
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17167
+ updateDOMAttributes(this.dom, { ...domAttrs });
17168
+ }
17169
+ update(node, decorations, innerDecorations) {
17170
+ const result = super.update(node, decorations, innerDecorations);
17171
+ if (!result) return false;
17172
+ this.updateView();
17173
+ return true;
17174
+ }
17175
+ }
17176
+ const structuredContentClass$1 = "sd-structured-content";
17177
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
17178
+ const StructuredContent = Node$1.create({
17179
+ name: "structuredContent",
17180
+ group: "inline structuredContent",
17052
17181
  inline: true,
17053
17182
  content: "inline*",
17054
- selectable: false,
17055
- childToAttributes: ["runProperties"],
17183
+ isolating: true,
17184
+ atom: false,
17185
+ // false - has editable content.
17186
+ draggable: true,
17056
17187
  addOptions() {
17057
17188
  return {
17058
17189
  htmlAttributes: {
17059
- "data-run": "1"
17190
+ class: structuredContentClass$1,
17191
+ "aria-label": "Structured content node"
17060
17192
  }
17061
17193
  };
17062
17194
  },
17063
17195
  addAttributes() {
17064
17196
  return {
17065
- runProperties: {
17197
+ id: {
17066
17198
  default: null,
17067
- rendered: false,
17068
- keepOnSplit: true
17199
+ parseDOM: (elem) => elem.getAttribute("data-id"),
17200
+ renderDOM: (attrs) => {
17201
+ if (!attrs.id) return {};
17202
+ return { "data-id": attrs.id };
17203
+ }
17069
17204
  },
17070
- rsidR: {
17205
+ tag: {
17071
17206
  default: null,
17072
- rendered: false,
17073
- keepOnSplit: true
17207
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
17208
+ renderDOM: (attrs) => {
17209
+ if (!attrs.tag) return {};
17210
+ return { "data-tag": attrs.tag };
17211
+ }
17074
17212
  },
17075
- rsidRPr: {
17213
+ alias: {
17076
17214
  default: null,
17077
- rendered: false,
17078
- keepOnSplit: true
17215
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17216
+ renderDOM: (attrs) => {
17217
+ if (!attrs.alias) return {};
17218
+ return { "data-alias": attrs.alias };
17219
+ }
17079
17220
  },
17080
- rsidDel: {
17081
- default: null,
17082
- rendered: false,
17083
- keepOnSplit: true
17221
+ sdtPr: {
17222
+ rendered: false
17084
17223
  }
17085
17224
  };
17086
17225
  },
17087
- addCommands() {
17088
- return {
17089
- splitRun
17090
- };
17091
- },
17092
17226
  parseDOM() {
17093
- return [{ tag: "span[data-run]" }];
17227
+ return [{ tag: "span[data-structured-content]" }];
17094
17228
  },
17095
17229
  renderDOM({ htmlAttributes }) {
17096
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17097
- return ["span", base2, 0];
17230
+ return [
17231
+ "span",
17232
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
17233
+ "data-structured-content": ""
17234
+ }),
17235
+ 0
17236
+ ];
17237
+ },
17238
+ addNodeView() {
17239
+ return (props) => {
17240
+ return new StructuredContentInlineView({ ...props });
17241
+ };
17098
17242
  }
17099
17243
  });
17100
- const inputRegex$1 = /^\s*([-+*])\s$/;
17101
- const BulletList = Node$1.create({
17102
- name: "bulletList",
17103
- group: "block list",
17104
- selectable: false,
17105
- content() {
17106
- return `${this.options.itemTypeName}+`;
17107
- },
17244
+ class StructuredContentBlockView extends StructuredContentViewBase {
17245
+ constructor(props) {
17246
+ super(props);
17247
+ }
17248
+ mount() {
17249
+ this.buildView();
17250
+ }
17251
+ get contentDOM() {
17252
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
17253
+ return contentElement || null;
17254
+ }
17255
+ createElement() {
17256
+ const element = document.createElement("div");
17257
+ element.classList.add(structuredContentClass);
17258
+ element.setAttribute("data-structured-content-block", "");
17259
+ const contentElement = document.createElement("div");
17260
+ contentElement.classList.add(structuredContentInnerClass);
17261
+ element.append(contentElement);
17262
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17263
+ updateDOMAttributes(element, { ...domAttrs });
17264
+ return { element, contentElement };
17265
+ }
17266
+ buildView() {
17267
+ const { element } = this.createElement();
17268
+ const dragHandle = this.createDragHandle();
17269
+ element.prepend(dragHandle);
17270
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
17271
+ this.root = element;
17272
+ }
17273
+ updateView() {
17274
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
17275
+ updateDOMAttributes(this.dom, { ...domAttrs });
17276
+ }
17277
+ update(node, decorations, innerDecorations) {
17278
+ const result = super.update(node, decorations, innerDecorations);
17279
+ if (!result) return false;
17280
+ this.updateView();
17281
+ return true;
17282
+ }
17283
+ }
17284
+ const structuredContentClass = "sd-structured-content-block";
17285
+ const structuredContentInnerClass = "sd-structured-content-block__content";
17286
+ const StructuredContentBlock = Node$1.create({
17287
+ name: "structuredContentBlock",
17288
+ group: "block structuredContent",
17289
+ content: "block*",
17290
+ isolating: true,
17291
+ atom: false,
17292
+ // false - has editable content.
17293
+ draggable: true,
17108
17294
  addOptions() {
17109
17295
  return {
17110
- itemTypeName: "listItem",
17111
17296
  htmlAttributes: {
17112
- "aria-label": "Bullet list node"
17113
- },
17114
- keepMarks: true,
17115
- keepAttributes: false
17297
+ class: structuredContentClass,
17298
+ "aria-label": "Structured content block node"
17299
+ }
17116
17300
  };
17117
17301
  },
17118
- parseDOM() {
17119
- return [{ tag: "ul" }];
17120
- },
17121
- renderDOM({ htmlAttributes }) {
17122
- const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
17123
- return ["ul", attributes, 0];
17124
- },
17125
17302
  addAttributes() {
17126
17303
  return {
17127
- "list-style-type": {
17128
- default: "bullet",
17129
- rendered: false
17304
+ id: {
17305
+ default: null,
17306
+ parseDOM: (elem) => elem.getAttribute("data-id"),
17307
+ renderDOM: (attrs) => {
17308
+ if (!attrs.id) return {};
17309
+ return { "data-id": attrs.id };
17310
+ }
17130
17311
  },
17131
- listId: {
17132
- rendered: false
17312
+ tag: {
17313
+ default: null,
17314
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
17315
+ renderDOM: (attrs) => {
17316
+ if (!attrs.tag) return {};
17317
+ return { "data-tag": attrs.tag };
17318
+ }
17133
17319
  },
17134
- sdBlockId: {
17320
+ alias: {
17135
17321
  default: null,
17136
- keepOnSplit: false,
17137
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17322
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
17138
17323
  renderDOM: (attrs) => {
17139
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17324
+ if (!attrs.alias) return {};
17325
+ return { "data-alias": attrs.alias };
17140
17326
  }
17141
17327
  },
17142
- attributes: {
17143
- rendered: false,
17144
- keepOnSplit: true
17328
+ sdtPr: {
17329
+ rendered: false
17145
17330
  }
17146
17331
  };
17147
17332
  },
17333
+ parseDOM() {
17334
+ return [{ tag: "div[data-structured-content-block]" }];
17335
+ },
17336
+ renderDOM({ htmlAttributes }) {
17337
+ return [
17338
+ "div",
17339
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
17340
+ "data-structured-content-block": ""
17341
+ }),
17342
+ 0
17343
+ ];
17344
+ },
17345
+ addNodeView() {
17346
+ return (props) => {
17347
+ return new StructuredContentBlockView({ ...props });
17348
+ };
17349
+ }
17350
+ });
17351
+ function getStructuredContentTagsById(idOrIds, state) {
17352
+ const result = findChildren$5(state.doc, (node) => {
17353
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
17354
+ if (Array.isArray(idOrIds)) {
17355
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
17356
+ } else {
17357
+ return isStructuredContent && node.attrs.id === idOrIds;
17358
+ }
17359
+ });
17360
+ return result;
17361
+ }
17362
+ function getStructuredContentTags(state) {
17363
+ const result = findChildren$5(state.doc, (node) => {
17364
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
17365
+ });
17366
+ return result;
17367
+ }
17368
+ function getStructuredContentInlineTags(state) {
17369
+ const result = findChildren$5(state.doc, (node) => node.type.name === "structuredContent");
17370
+ return result;
17371
+ }
17372
+ function getStructuredContentBlockTags(state) {
17373
+ const result = findChildren$5(state.doc, (node) => node.type.name === "structuredContentBlock");
17374
+ return result;
17375
+ }
17376
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
17377
+ __proto__: null,
17378
+ getStructuredContentBlockTags,
17379
+ getStructuredContentInlineTags,
17380
+ getStructuredContentTags,
17381
+ getStructuredContentTagsById
17382
+ }, Symbol.toStringTag, { value: "Module" }));
17383
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
17384
+ const StructuredContentCommands = Extension.create({
17385
+ name: "structuredContentCommands",
17148
17386
  addCommands() {
17149
17387
  return {
17150
17388
  /**
17151
- * Toggle a bullet list at the current selection
17389
+ * Inserts a structured content inline at selection.
17152
17390
  * @category Command
17153
- * @example
17154
- * // Toggle bullet list on selected text
17155
- * editor.commands.toggleBulletList()
17156
- * @note Converts selected paragraphs to list items or removes list formatting
17391
+ * @param {StructuredContentInlineInsert} options
17157
17392
  */
17158
- toggleBulletList: () => (params2) => {
17159
- return toggleList(this.type)(params2);
17160
- }
17161
- };
17162
- },
17163
- addShortcuts() {
17164
- return {
17165
- "Mod-Shift-8": () => {
17166
- return this.editor.commands.toggleBulletList();
17167
- }
17168
- };
17169
- },
17170
- addInputRules() {
17171
- return [
17172
- new InputRule({
17173
- match: inputRegex$1,
17174
- handler: ({ state, range }) => {
17175
- const $pos = state.selection.$from;
17176
- const listItemType = state.schema.nodes.listItem;
17177
- for (let depth = $pos.depth; depth >= 0; depth--) {
17178
- if ($pos.node(depth).type === listItemType) {
17179
- return null;
17180
- }
17393
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state, tr }) => {
17394
+ const { schema } = editor;
17395
+ let { from: from2, to } = state.selection;
17396
+ if (dispatch) {
17397
+ const selectionText = state.doc.textBetween(from2, to);
17398
+ let content = null;
17399
+ if (selectionText) {
17400
+ content = schema.text(selectionText);
17181
17401
  }
17182
- const { tr } = state;
17183
- tr.delete(range.from, range.to);
17184
- ListHelpers.createNewList({
17185
- listType: this.type,
17186
- tr,
17187
- editor: this.editor
17188
- });
17189
- }
17190
- })
17191
- ];
17192
- }
17193
- });
17194
- const inputRegex = /^(\d+)\.\s$/;
17195
- const OrderedList = Node$1.create({
17196
- name: "orderedList",
17197
- group: "block list",
17198
- selectable: false,
17199
- content() {
17200
- return `${this.options.itemTypeName}+`;
17201
- },
17202
- addOptions() {
17203
- return {
17204
- itemTypeName: "listItem",
17205
- htmlAttributes: {
17206
- "aria-label": "Ordered list node"
17207
- },
17208
- keepMarks: true,
17209
- keepAttributes: false,
17210
- listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
17211
- };
17212
- },
17213
- addAttributes() {
17214
- return {
17215
- order: {
17216
- default: 1,
17217
- parseDOM: (element) => {
17218
- return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
17219
- },
17220
- renderDOM: (attrs) => {
17221
- return {
17222
- start: attrs.order
17402
+ if (options.text) {
17403
+ content = schema.text(options.text);
17404
+ }
17405
+ if (options.json) {
17406
+ content = schema.nodeFromJSON(options.json);
17407
+ }
17408
+ if (!content) {
17409
+ content = schema.text(" ");
17410
+ }
17411
+ const attrs = {
17412
+ ...options.attrs,
17413
+ id: options.attrs?.id || randomId(),
17414
+ tag: "inline_text_sdt",
17415
+ alias: options.attrs?.alias || "Structured content"
17223
17416
  };
17417
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
17418
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state.selection);
17419
+ if (parent) {
17420
+ const insertPos = parent.pos + parent.node.nodeSize;
17421
+ from2 = to = insertPos;
17422
+ }
17423
+ tr.replaceWith(from2, to, node);
17224
17424
  }
17425
+ return true;
17225
17426
  },
17226
- sdBlockId: {
17227
- default: null,
17228
- keepOnSplit: false,
17229
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17230
- renderDOM: (attrs) => {
17231
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17232
- }
17233
- },
17234
- syncId: {
17235
- default: null,
17236
- parseDOM: (elem) => elem.getAttribute("data-sync-id"),
17237
- renderDOM: (attrs) => {
17238
- if (!attrs.syncId) return {};
17239
- return {
17240
- "data-sync-id": attrs.syncId
17427
+ /**
17428
+ * Inserts a structured content block at selection.
17429
+ * @category Command
17430
+ * @param {StructuredContentBlockInsert} options
17431
+ */
17432
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state, tr }) => {
17433
+ const { schema } = editor;
17434
+ let { from: from2, to } = state.selection;
17435
+ if (dispatch) {
17436
+ const selectionContent = state.selection.content();
17437
+ let content = null;
17438
+ if (selectionContent.size) {
17439
+ content = selectionContent.content;
17440
+ }
17441
+ if (options.html) {
17442
+ const html = htmlHandler(options.html, editor);
17443
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
17444
+ content = doc2.content;
17445
+ }
17446
+ if (options.json) {
17447
+ content = schema.nodeFromJSON(options.json);
17448
+ }
17449
+ if (!content) {
17450
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
17451
+ }
17452
+ const attrs = {
17453
+ ...options.attrs,
17454
+ id: options.attrs?.id || randomId(),
17455
+ tag: "block_table_sdt",
17456
+ alias: options.attrs?.alias || "Structured content"
17241
17457
  };
17458
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
17459
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state.selection);
17460
+ if (parent) {
17461
+ const insertPos = parent.pos + parent.node.nodeSize;
17462
+ from2 = to = insertPos;
17463
+ }
17464
+ tr.replaceRangeWith(from2, to, node);
17242
17465
  }
17243
- // rendered: false,
17466
+ return true;
17244
17467
  },
17245
- listId: {
17246
- keepOnSplit: true,
17247
- parseDOM: (elem) => elem.getAttribute("data-list-id"),
17248
- renderDOM: (attrs) => {
17249
- if (!attrs.listId) return {};
17250
- return {
17251
- "data-list-id": attrs.listId
17252
- };
17468
+ /**
17469
+ * Updates a structured content attributes or content.
17470
+ * If the updated node does not match the schema, it will not be updated.
17471
+ * @category Command
17472
+ * @param {string} id
17473
+ * @param {StructuredContentUpdate} options
17474
+ */
17475
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state, tr }) => {
17476
+ const structuredContentTags = getStructuredContentTagsById(id, state);
17477
+ if (!structuredContentTags.length) {
17478
+ return true;
17253
17479
  }
17480
+ const { schema } = editor;
17481
+ if (dispatch) {
17482
+ const structuredContent = structuredContentTags[0];
17483
+ const { pos, node } = structuredContent;
17484
+ const posFrom = pos;
17485
+ const posTo = pos + node.nodeSize;
17486
+ let content = null;
17487
+ if (options.text) {
17488
+ content = schema.text(options.text);
17489
+ }
17490
+ if (options.html) {
17491
+ const html = htmlHandler(options.html, editor);
17492
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
17493
+ content = doc2.content;
17494
+ }
17495
+ if (options.json) {
17496
+ content = schema.nodeFromJSON(options.json);
17497
+ }
17498
+ if (!content) {
17499
+ content = node.content;
17500
+ }
17501
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
17502
+ try {
17503
+ updatedNode.check();
17504
+ } catch {
17505
+ console.error("Updated node does not conform to the schema");
17506
+ return false;
17507
+ }
17508
+ tr.replaceWith(posFrom, posTo, updatedNode);
17509
+ }
17510
+ return true;
17254
17511
  },
17255
- "list-style-type": {
17256
- default: "decimal",
17257
- rendered: false
17258
- },
17259
- attributes: {
17260
- rendered: false,
17261
- keepOnSplit: true
17262
- }
17263
- };
17264
- },
17265
- parseDOM() {
17266
- return [{ tag: "ol" }];
17267
- },
17268
- renderDOM({ htmlAttributes }) {
17269
- const { start: start2, ...restAttributes } = htmlAttributes;
17270
- return start2 === 1 ? ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
17271
- },
17272
- addCommands() {
17273
- return {
17274
17512
  /**
17275
- * Toggle ordered list formatting
17513
+ * Removes a structured content.
17276
17514
  * @category Command
17277
- * @example
17278
- * editor.commands.toggleOrderedList()
17279
- * @note Converts selection to ordered list or back to paragraphs
17515
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
17280
17516
  */
17281
- toggleOrderedList: () => (params2) => {
17282
- return toggleList(this.type)(params2);
17517
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
17518
+ if (!structuredContentTags.length) {
17519
+ return true;
17520
+ }
17521
+ if (dispatch) {
17522
+ structuredContentTags.forEach((structuredContent) => {
17523
+ const { pos, node } = structuredContent;
17524
+ const posFrom = tr.mapping.map(pos);
17525
+ const posTo = tr.mapping.map(pos + node.nodeSize);
17526
+ const currentNode = tr.doc.nodeAt(posFrom);
17527
+ if (currentNode && node.eq(currentNode)) {
17528
+ tr.delete(posFrom, posTo);
17529
+ }
17530
+ });
17531
+ }
17532
+ return true;
17283
17533
  },
17284
17534
  /**
17285
- * Restart list node numbering
17535
+ * Removes a structured content by ID.
17286
17536
  * @category Command
17287
- * @param {Array} followingNodes - Nodes to restart
17288
- * @param {number} pos - Starting position
17289
- * @example
17290
- * editor.commands.restartListNodes(nodes, position)
17291
- * @note Resets list numbering for specified nodes
17537
+ * @param {string | string[]} idOrIds
17292
17538
  */
17293
- restartListNodes: (followingNodes, pos) => ({ tr }) => {
17294
- let currentNodePos = pos;
17295
- const nodes = followingNodes.map((node) => {
17296
- const resultNode = {
17297
- node,
17298
- pos: currentNodePos
17299
- };
17300
- currentNodePos += node.nodeSize;
17301
- return resultNode;
17302
- });
17303
- nodes.forEach((item) => {
17304
- const { pos: pos2 } = item;
17305
- const newPos = tr.mapping.map(pos2);
17306
- tr.setNodeMarkup(newPos, void 0, {});
17307
- });
17539
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state, tr }) => {
17540
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state);
17541
+ if (!structuredContentTags.length) {
17542
+ return true;
17543
+ }
17544
+ if (dispatch) {
17545
+ structuredContentTags.forEach((structuredContent) => {
17546
+ const { pos, node } = structuredContent;
17547
+ const posFrom = tr.mapping.map(pos);
17548
+ const posTo = tr.mapping.map(pos + node.nodeSize);
17549
+ const currentNode = tr.doc.nodeAt(posFrom);
17550
+ if (currentNode && node.eq(currentNode)) {
17551
+ tr.delete(posFrom, posTo);
17552
+ }
17553
+ });
17554
+ }
17308
17555
  return true;
17309
17556
  },
17310
17557
  /**
17311
- * Update ordered list style type based on nesting level
17558
+ * Removes a structured content at cursor, preserving its content.
17312
17559
  * @category Command
17313
- * @example
17314
- * editor.commands.updateOrderedListStyleType()
17315
- * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
17316
17560
  */
17317
- updateOrderedListStyleType: () => ({ dispatch, tr }) => {
17318
- let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
17319
- if (!list) {
17561
+ deleteStructuredContentAtSelection: () => ({ editor, dispatch, state, tr }) => {
17562
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
17563
+ const structuredContent = findParentNode(predicate)(state.selection);
17564
+ if (!structuredContent) {
17320
17565
  return true;
17321
17566
  }
17322
17567
  if (dispatch) {
17323
- let listLevel = (list.depth - 1) / 2;
17324
- let listStyleTypes = this.options.listStyleTypes;
17325
- let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
17326
- let currentListStyle = list.node.attrs["list-style-type"];
17327
- let nodeAtPos = tr.doc.nodeAt(list.pos);
17328
- if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
17329
- tr.setNodeMarkup(list.pos, void 0, {
17330
- ...list.node.attrs,
17331
- ...{
17332
- "list-style-type": listStyle
17333
- }
17334
- });
17335
- }
17568
+ const { node, pos } = structuredContent;
17569
+ const posFrom = pos;
17570
+ const posTo = posFrom + node.nodeSize;
17571
+ const content = node.content;
17572
+ tr.replaceWith(posFrom, posTo, content);
17336
17573
  }
17337
17574
  return true;
17338
17575
  }
17339
17576
  };
17340
17577
  },
17341
- addShortcuts() {
17578
+ addHelpers() {
17342
17579
  return {
17343
- "Mod-Shift-7": () => {
17344
- return this.editor.commands.toggleOrderedList();
17345
- }
17580
+ ...structuredContentHelpers
17346
17581
  };
17347
- },
17348
- addInputRules() {
17349
- return [
17350
- new InputRule({
17351
- match: inputRegex,
17352
- handler: ({ state, range }) => {
17353
- const $pos = state.selection.$from;
17354
- const listItemType = state.schema.nodes.listItem;
17355
- for (let depth = $pos.depth; depth >= 0; depth--) {
17356
- if ($pos.node(depth).type === listItemType) {
17357
- return null;
17358
- }
17359
- }
17360
- const { tr } = state;
17361
- tr.delete(range.from, range.to);
17362
- ListHelpers.createNewList({
17363
- listType: this.type,
17364
- tr,
17365
- editor: this.editor
17366
- });
17367
- }
17368
- })
17369
- ];
17370
17582
  }
17371
17583
  });
17372
- const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
17373
- const handler = listIndexMap[listNumberingType];
17374
- return handler ? handler(listLevel, lvlText, customFormat) : null;
17375
- };
17376
- const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
17377
- const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
17378
- const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
17379
- const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
17380
- const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
17381
- const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
17382
- const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
17383
- const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
17384
- const listIndexMap = {
17385
- decimal: handleDecimal,
17386
- lowerRoman: handleLowerRoman,
17387
- upperRoman: handleRoman,
17388
- lowerLetter: handleLowerAlpha,
17389
- upperLetter: handleAlpha,
17390
- ordinal: handleOrdinal,
17391
- custom: handleCustom,
17392
- japaneseCounting: handleJapaneseCounting
17393
- };
17394
- const createNumbering = (values, lvlText) => {
17395
- return values.reduce((acc, value, index2) => {
17396
- return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
17397
- }, lvlText);
17584
+ const randomId = () => {
17585
+ return Math.floor(Math.random() * 4294967295).toString();
17398
17586
  };
17399
- const generateNumbering = (path, lvlText, formatter) => {
17400
- const formattedValues = path.map(formatter);
17401
- return createNumbering(formattedValues, lvlText);
17402
- };
17403
- const ordinalFormatter = (level) => {
17404
- const suffixes = ["th", "st", "nd", "rd"];
17405
- const value = level % 100;
17406
- const suffix = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
17407
- const p = level + suffix;
17408
- return p;
17587
+ class DocumentSectionView {
17588
+ constructor(node, getPos, decorations, editor) {
17589
+ __privateAdd(this, _DocumentSectionView_instances);
17590
+ this.node = node;
17591
+ this.editor = editor;
17592
+ this.decorations = decorations;
17593
+ this.view = editor.view;
17594
+ this.getPos = getPos;
17595
+ __privateMethod(this, _DocumentSectionView_instances, init_fn2).call(this);
17596
+ }
17597
+ }
17598
+ _DocumentSectionView_instances = new WeakSet();
17599
+ init_fn2 = function() {
17600
+ const { attrs } = this.node;
17601
+ const { id, title, description } = attrs;
17602
+ this.dom = document.createElement("div");
17603
+ this.dom.className = "sd-document-section-block";
17604
+ this.dom.setAttribute("data-id", id);
17605
+ this.dom.setAttribute("data-title", title);
17606
+ this.dom.setAttribute("data-description", description);
17607
+ this.dom.setAttribute("aria-label", "Document section");
17608
+ __privateMethod(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
17609
+ this.contentDOM = document.createElement("div");
17610
+ this.contentDOM.className = "sd-document-section-block-content";
17611
+ this.contentDOM.setAttribute("contenteditable", "true");
17612
+ this.dom.appendChild(this.contentDOM);
17409
17613
  };
17410
- const generateFromCustom = (path, lvlText, customFormat) => {
17411
- if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
17412
- const match = customFormat.match(/(\d+)/);
17413
- if (!match) throw new Error("Invalid format string: no numeric pattern found");
17414
- const sample = match[1];
17415
- const digitCount = sample.length;
17416
- const index2 = path.pop();
17417
- return String(index2).padStart(digitCount, "0");
17614
+ addToolTip_fn = function() {
17615
+ const { title } = this.node.attrs;
17616
+ this.infoDiv = document.createElement("div");
17617
+ this.infoDiv.className = "sd-document-section-block-info";
17618
+ const textSpan = document.createElement("span");
17619
+ textSpan.textContent = title || "Document section";
17620
+ this.infoDiv.appendChild(textSpan);
17621
+ this.infoDiv.setAttribute("contenteditable", "false");
17622
+ this.dom.appendChild(this.infoDiv);
17418
17623
  };
17419
- const intToRoman = (num) => {
17420
- const romanNumeralMap = [
17421
- { value: 1e3, numeral: "M" },
17422
- { value: 900, numeral: "CM" },
17423
- { value: 500, numeral: "D" },
17424
- { value: 400, numeral: "CD" },
17425
- { value: 100, numeral: "C" },
17426
- { value: 90, numeral: "XC" },
17427
- { value: 50, numeral: "L" },
17428
- { value: 40, numeral: "XL" },
17429
- { value: 10, numeral: "X" },
17430
- { value: 9, numeral: "IX" },
17431
- { value: 5, numeral: "V" },
17432
- { value: 4, numeral: "IV" },
17433
- { value: 1, numeral: "I" }
17434
- ];
17435
- let result = "";
17436
- for (const { value, numeral } of romanNumeralMap) {
17437
- while (num >= value) {
17438
- result += numeral;
17439
- num -= value;
17624
+ const getAllSections = (editor) => {
17625
+ if (!editor) return [];
17626
+ const type = editor.schema.nodes.documentSection;
17627
+ if (!type) return [];
17628
+ const sections = [];
17629
+ const { state } = editor;
17630
+ state.doc.descendants((node, pos) => {
17631
+ if (node.type.name === type.name) {
17632
+ sections.push({ node, pos });
17440
17633
  }
17441
- }
17442
- return result;
17634
+ });
17635
+ return sections;
17443
17636
  };
17444
- const intToAlpha = (num) => {
17445
- let result = "";
17446
- const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
17447
- while (num > 0) {
17448
- let index2 = (num - 1) % 26;
17449
- result = alphabet[index2] + result;
17450
- num = Math.floor((num - 1) / 26);
17451
- }
17637
+ const exportSectionsToHTML = (editor) => {
17638
+ const sections = getAllSections(editor);
17639
+ const processedSections = /* @__PURE__ */ new Set();
17640
+ const result = [];
17641
+ sections.forEach(({ node }) => {
17642
+ const { attrs } = node;
17643
+ const { id, title, description } = attrs;
17644
+ if (processedSections.has(id)) return;
17645
+ processedSections.add(id);
17646
+ const html = getHTMLFromNode(node, editor);
17647
+ result.push({
17648
+ id,
17649
+ title,
17650
+ description,
17651
+ html
17652
+ });
17653
+ });
17452
17654
  return result;
17453
17655
  };
17454
- const intToJapaneseCounting = (num) => {
17455
- const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
17456
- const units = ["", "十", "百", "千"];
17457
- if (num === 0) return "零";
17458
- if (num < 10) return digits[num];
17459
- let result = "";
17460
- let tempNum = num;
17461
- let unitIndex = 0;
17462
- while (tempNum > 0) {
17463
- const digit = tempNum % 10;
17464
- if (digit !== 0) {
17465
- const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
17466
- result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
17467
- } else if (result && tempNum > 0) {
17468
- if (!result.startsWith("零") && tempNum % 100 !== 0) {
17469
- result = "零" + result;
17470
- }
17471
- }
17472
- tempNum = Math.floor(tempNum / 10);
17473
- unitIndex++;
17474
- if (unitIndex > 3) break;
17475
- }
17476
- if (num >= 10 && num < 20) {
17477
- result = result.replace(/^一十/, "十");
17478
- }
17479
- return result;
17656
+ const getHTMLFromNode = (node, editor) => {
17657
+ const tempDocument = document.implementation.createHTMLDocument();
17658
+ const container = tempDocument.createElement("div");
17659
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
17660
+ container.appendChild(fragment);
17661
+ let html = container.innerHTML;
17662
+ return html;
17480
17663
  };
17481
- const CustomSelectionPluginKey = new PluginKey("CustomSelection");
17482
- const handleClickOutside = (event, editor) => {
17483
- const editorElem = editor?.options?.element;
17484
- if (!editorElem) return;
17485
- const isInsideEditor = editorElem?.contains(event.target);
17486
- if (!isInsideEditor) {
17487
- editor.setOptions({
17488
- focusTarget: event.target
17489
- });
17490
- } else {
17491
- editor.setOptions({
17492
- focusTarget: null
17664
+ const exportSectionsToJSON = (editor) => {
17665
+ const sections = getAllSections(editor);
17666
+ const processedSections = /* @__PURE__ */ new Set();
17667
+ const result = [];
17668
+ sections.forEach(({ node }) => {
17669
+ const { attrs } = node;
17670
+ const { id, title, description } = attrs;
17671
+ if (processedSections.has(id)) return;
17672
+ processedSections.add(id);
17673
+ result.push({
17674
+ id,
17675
+ title,
17676
+ description,
17677
+ content: node.toJSON()
17493
17678
  });
17494
- }
17679
+ });
17680
+ return result;
17495
17681
  };
17496
- function getFocusMeta(tr) {
17497
- return tr.getMeta(CustomSelectionPluginKey);
17498
- }
17499
- function setFocusMeta(tr, value) {
17500
- return tr.setMeta(CustomSelectionPluginKey, value);
17501
- }
17502
- function getFocusState(state) {
17503
- return CustomSelectionPluginKey.getState(state);
17504
- }
17505
- const isToolbarInput = (target) => {
17506
- return !!target?.closest(".button-text-input") || target?.classList?.contains("button-text-input");
17682
+ const getLinkedSectionEditor = (id, options, editor) => {
17683
+ const sections = getAllSections(editor);
17684
+ const section = sections.find((s) => s.node.attrs.id === id);
17685
+ if (!section) return null;
17686
+ const child = editor.createChildEditor({
17687
+ ...options,
17688
+ onUpdate: ({ editor: childEditor, transaction }) => {
17689
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
17690
+ if (isFromtLinkedParent) return;
17691
+ const updatedContent = childEditor.state.doc.content;
17692
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
17693
+ if (!sectionNode) return;
17694
+ const { pos, node } = sectionNode;
17695
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
17696
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
17697
+ tr.setMeta("fromLinkedChild", true);
17698
+ editor.view.dispatch(tr);
17699
+ }
17700
+ });
17701
+ editor.on("update", ({ transaction }) => {
17702
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
17703
+ if (isFromLinkedChild) return;
17704
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
17705
+ if (!sectionNode) return;
17706
+ const sectionContent = sectionNode.node.content;
17707
+ const json = {
17708
+ type: "doc",
17709
+ content: sectionContent.content.map((node) => node.toJSON())
17710
+ };
17711
+ const childTr = child.state.tr;
17712
+ childTr.setMeta("fromLinkedParent", true);
17713
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
17714
+ child.view.dispatch(childTr);
17715
+ });
17716
+ return child;
17507
17717
  };
17508
- const isToolbarButton = (target) => {
17509
- return !!target?.closest(".toolbar-button") || target?.classList?.contains("toolbar-button");
17718
+ const SectionHelpers = {
17719
+ getAllSections,
17720
+ exportSectionsToHTML,
17721
+ exportSectionsToJSON,
17722
+ getLinkedSectionEditor
17510
17723
  };
17511
- const CustomSelection = Extension.create({
17512
- name: "customSelection",
17513
- addPmPlugins() {
17514
- const editor = this.editor;
17515
- const customSelectionPlugin = new Plugin({
17516
- key: CustomSelectionPluginKey,
17517
- state: {
17518
- init: () => ({
17519
- focused: false,
17520
- preservedSelection: null,
17521
- showVisualSelection: false
17522
- }),
17523
- apply: (tr, value) => {
17524
- const meta = getFocusMeta(tr);
17525
- if (meta !== void 0) {
17526
- return { ...value, ...meta };
17527
- }
17528
- return value;
17724
+ const DocumentSection = Node$1.create({
17725
+ name: "documentSection",
17726
+ group: "block",
17727
+ content: "block*",
17728
+ atom: true,
17729
+ isolating: true,
17730
+ addOptions() {
17731
+ return {
17732
+ htmlAttributes: {
17733
+ class: "sd-document-section-block",
17734
+ "aria-label": "Structured content block"
17735
+ }
17736
+ };
17737
+ },
17738
+ parseDOM() {
17739
+ return [
17740
+ {
17741
+ tag: "div.sd-document-section-block",
17742
+ priority: 60
17743
+ }
17744
+ ];
17745
+ },
17746
+ renderDOM({ htmlAttributes }) {
17747
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
17748
+ },
17749
+ addAttributes() {
17750
+ return {
17751
+ id: {},
17752
+ sdBlockId: {
17753
+ default: null,
17754
+ keepOnSplit: false,
17755
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
17756
+ renderDOM: (attrs) => {
17757
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
17529
17758
  }
17530
17759
  },
17531
- view: () => {
17532
- const clickHandler = (event) => handleClickOutside(event, editor);
17533
- document?.addEventListener("mousedown", clickHandler);
17534
- return {
17535
- destroy: () => {
17536
- document?.removeEventListener("mousedown", clickHandler);
17537
- }
17538
- };
17539
- },
17540
- props: {
17541
- handleDOMEvents: {
17542
- contextmenu: (view, event) => {
17543
- event.preventDefault();
17544
- const { selection } = view.state;
17545
- if (!selection.empty) {
17760
+ title: {},
17761
+ description: {},
17762
+ sectionType: {},
17763
+ isLocked: { default: false }
17764
+ };
17765
+ },
17766
+ addNodeView() {
17767
+ return ({ node, editor, getPos, decorations }) => {
17768
+ return new DocumentSectionView(node, getPos, decorations, editor);
17769
+ };
17770
+ },
17771
+ addCommands() {
17772
+ return {
17773
+ /**
17774
+ * Create a lockable content section
17775
+ * @category Command
17776
+ * @param {SectionCreate} [options={}] - Section configuration
17777
+ * @example
17778
+ * editor.commands.createDocumentSection({
17779
+ * id: 1,
17780
+ * title: 'Terms & Conditions',
17781
+ * isLocked: true,
17782
+ * html: '<p>Legal content...</p>'
17783
+ * })
17784
+ */
17785
+ createDocumentSection: (options = {}) => ({ tr, state, dispatch, editor }) => {
17786
+ const { selection } = state;
17787
+ let { from: from2, to } = selection;
17788
+ let content = selection.content().content;
17789
+ const { html: optionsHTML, json: optionsJSON } = options;
17790
+ if (optionsHTML) {
17791
+ const html = htmlHandler(optionsHTML, this.editor);
17792
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
17793
+ content = doc2.content;
17794
+ }
17795
+ if (optionsJSON) {
17796
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
17797
+ }
17798
+ if (!content?.content?.length) {
17799
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
17800
+ }
17801
+ if (!options.id) {
17802
+ const allSections = SectionHelpers.getAllSections(editor);
17803
+ options.id = allSections.length + 1;
17804
+ }
17805
+ if (!options.title) {
17806
+ options.title = "Document section";
17807
+ }
17808
+ const node = this.type.createAndFill(options, content);
17809
+ if (!node) return false;
17810
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
17811
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
17812
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
17813
+ from2 = insertPos2;
17814
+ to = insertPos2;
17815
+ }
17816
+ tr.replaceRangeWith(from2, to, node);
17817
+ const nodeEnd = from2 + node.nodeSize;
17818
+ let shouldInsertParagraph = true;
17819
+ let insertPos = nodeEnd;
17820
+ if (nodeEnd >= tr.doc.content.size) {
17821
+ insertPos = tr.doc.content.size;
17822
+ if (insertPos > 0) {
17823
+ const $endPos = tr.doc.resolve(insertPos);
17824
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
17825
+ shouldInsertParagraph = false;
17826
+ }
17827
+ }
17828
+ }
17829
+ if (shouldInsertParagraph) {
17830
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
17831
+ tr.insert(insertPos, emptyParagraph);
17832
+ }
17833
+ if (dispatch) {
17834
+ tr.setMeta("documentSection", { action: "create" });
17835
+ dispatch(tr);
17836
+ setTimeout(() => {
17837
+ try {
17838
+ const currentState = editor.state;
17839
+ const docSize = currentState.doc.content.size;
17840
+ let targetPos = from2 + node.nodeSize;
17841
+ if (shouldInsertParagraph) {
17842
+ targetPos += 1;
17843
+ }
17844
+ targetPos = Math.min(targetPos, docSize);
17845
+ if (targetPos < docSize && targetPos > 0) {
17846
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
17847
+ const newTr = currentState.tr.setSelection(newSelection);
17848
+ editor.view.dispatch(newTr);
17849
+ }
17850
+ } catch (e) {
17851
+ console.warn("Could not set delayed selection:", e);
17852
+ }
17853
+ }, 0);
17854
+ }
17855
+ return true;
17856
+ },
17857
+ /**
17858
+ * Remove section wrapper at cursor, preserving its content
17859
+ * @category Command
17860
+ * @example
17861
+ * editor.commands.removeSectionAtSelection()
17862
+ * @note Content stays in document, only section wrapper is removed
17863
+ */
17864
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
17865
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
17866
+ if (!sdtNode) return false;
17867
+ const { node, pos } = sdtNode;
17868
+ const nodeStart = pos;
17869
+ const nodeEnd = nodeStart + node.nodeSize;
17870
+ const contentToPreserve = node.content;
17871
+ tr.delete(nodeStart, nodeEnd);
17872
+ if (contentToPreserve.size > 0) {
17873
+ tr.insert(nodeStart, contentToPreserve);
17874
+ }
17875
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
17876
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
17877
+ if (dispatch) {
17878
+ tr.setMeta("documentSection", { action: "delete" });
17879
+ dispatch(tr);
17880
+ }
17881
+ return true;
17882
+ },
17883
+ /**
17884
+ * Delete section and all its content
17885
+ * @category Command
17886
+ * @param {number} id - Section to delete
17887
+ * @example
17888
+ * editor.commands.removeSectionById(123)
17889
+ */
17890
+ removeSectionById: (id) => ({ tr, dispatch }) => {
17891
+ const sections = SectionHelpers.getAllSections(this.editor);
17892
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
17893
+ if (!sectionToRemove) return false;
17894
+ const { pos, node } = sectionToRemove;
17895
+ const nodeStart = pos;
17896
+ const nodeEnd = nodeStart + node.nodeSize;
17897
+ tr.delete(nodeStart, nodeEnd);
17898
+ if (dispatch) {
17899
+ tr.setMeta("documentSection", { action: "delete", id });
17900
+ dispatch(tr);
17901
+ }
17902
+ return true;
17903
+ },
17904
+ /**
17905
+ * Lock section against edits
17906
+ * @category Command
17907
+ * @param {number} id - Section to lock
17908
+ * @example
17909
+ * editor.commands.lockSectionById(123)
17910
+ */
17911
+ lockSectionById: (id) => ({ tr, dispatch }) => {
17912
+ const sections = SectionHelpers.getAllSections(this.editor);
17913
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
17914
+ if (!sectionToLock) return false;
17915
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
17916
+ if (dispatch) {
17917
+ tr.setMeta("documentSection", { action: "lock", id });
17918
+ dispatch(tr);
17919
+ }
17920
+ return true;
17921
+ },
17922
+ /**
17923
+ * Modify section attributes or content
17924
+ * @category Command
17925
+ * @param {SectionUpdate} options - Changes to apply
17926
+ * @example
17927
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
17928
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
17929
+ * editor.commands.updateSectionById({
17930
+ * id: 123,
17931
+ * html: '<p>Updated</p>',
17932
+ * attrs: { title: 'New Title' }
17933
+ * })
17934
+ */
17935
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
17936
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
17937
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
17938
+ if (!sectionToUpdate) return false;
17939
+ const { pos, node } = sectionToUpdate;
17940
+ let newContent = null;
17941
+ if (html) {
17942
+ const htmlDoc = htmlHandler(html, editor || this.editor);
17943
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
17944
+ newContent = doc2.content;
17945
+ }
17946
+ if (json) {
17947
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
17948
+ }
17949
+ if (!newContent) {
17950
+ newContent = node.content;
17951
+ }
17952
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
17953
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
17954
+ if (dispatch) {
17955
+ tr.setMeta("documentSection", { action: "update", id, attrs });
17956
+ dispatch(tr);
17957
+ }
17958
+ return true;
17959
+ }
17960
+ };
17961
+ },
17962
+ addHelpers() {
17963
+ return {
17964
+ ...SectionHelpers
17965
+ };
17966
+ }
17967
+ });
17968
+ const Document = Node$1.create({
17969
+ name: "doc",
17970
+ topNode: true,
17971
+ content: "block+",
17972
+ parseDOM() {
17973
+ return [{ tag: "doc" }];
17974
+ },
17975
+ renderDOM() {
17976
+ return ["doc", 0];
17977
+ },
17978
+ addAttributes() {
17979
+ return {
17980
+ attributes: {
17981
+ rendered: false,
17982
+ "aria-label": "Document node"
17983
+ }
17984
+ };
17985
+ },
17986
+ addCommands() {
17987
+ return {
17988
+ /**
17989
+ * Get document statistics
17990
+ * @category Command
17991
+ * @example
17992
+ * // Get word and character count
17993
+ * const stats = editor.commands.getDocumentStats()
17994
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
17995
+ * @note Returns word count, character count, and paragraph count
17996
+ */
17997
+ getDocumentStats: () => ({ editor }) => {
17998
+ const text = editor.getText();
17999
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
18000
+ const characters = text.length;
18001
+ const paragraphs = editor.state.doc.content.childCount;
18002
+ return {
18003
+ words,
18004
+ characters,
18005
+ paragraphs
18006
+ };
18007
+ },
18008
+ /**
18009
+ * Clear entire document
18010
+ * @category Command
18011
+ * @example
18012
+ * editor.commands.clearDocument()
18013
+ * @note Replaces all content with an empty paragraph
18014
+ */
18015
+ clearDocument: () => ({ commands: commands2 }) => {
18016
+ return commands2.setContent("<p></p>");
18017
+ }
18018
+ };
18019
+ }
18020
+ });
18021
+ const Text = Node$1.create({
18022
+ name: "text",
18023
+ group: "inline",
18024
+ inline: true,
18025
+ addOptions() {
18026
+ return {};
18027
+ }
18028
+ });
18029
+ const splitRun = () => (props) => {
18030
+ const { state, view, tr } = props;
18031
+ const { $from, empty: empty2 } = state.selection;
18032
+ if (!empty2) return false;
18033
+ if ($from.parent.type.name !== "run") return false;
18034
+ const handled = splitBlock(state, (transaction) => {
18035
+ view.dispatch(transaction);
18036
+ });
18037
+ if (handled) {
18038
+ tr.setMeta("preventDispatch", true);
18039
+ }
18040
+ return handled;
18041
+ };
18042
+ const Run = OxmlNode.create({
18043
+ name: "run",
18044
+ oXmlName: "w:r",
18045
+ group: "inline",
18046
+ inline: true,
18047
+ content: "inline*",
18048
+ selectable: false,
18049
+ childToAttributes: ["runProperties"],
18050
+ addOptions() {
18051
+ return {
18052
+ htmlAttributes: {
18053
+ "data-run": "1"
18054
+ }
18055
+ };
18056
+ },
18057
+ addAttributes() {
18058
+ return {
18059
+ runProperties: {
18060
+ default: null,
18061
+ rendered: false,
18062
+ keepOnSplit: true
18063
+ },
18064
+ rsidR: {
18065
+ default: null,
18066
+ rendered: false,
18067
+ keepOnSplit: true
18068
+ },
18069
+ rsidRPr: {
18070
+ default: null,
18071
+ rendered: false,
18072
+ keepOnSplit: true
18073
+ },
18074
+ rsidDel: {
18075
+ default: null,
18076
+ rendered: false,
18077
+ keepOnSplit: true
18078
+ }
18079
+ };
18080
+ },
18081
+ addCommands() {
18082
+ return {
18083
+ splitRun
18084
+ };
18085
+ },
18086
+ parseDOM() {
18087
+ return [{ tag: "span[data-run]" }];
18088
+ },
18089
+ renderDOM({ htmlAttributes }) {
18090
+ const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
18091
+ return ["span", base2, 0];
18092
+ }
18093
+ });
18094
+ const inputRegex$1 = /^\s*([-+*])\s$/;
18095
+ const BulletList = Node$1.create({
18096
+ name: "bulletList",
18097
+ group: "block list",
18098
+ selectable: false,
18099
+ content() {
18100
+ return `${this.options.itemTypeName}+`;
18101
+ },
18102
+ addOptions() {
18103
+ return {
18104
+ itemTypeName: "listItem",
18105
+ htmlAttributes: {
18106
+ "aria-label": "Bullet list node"
18107
+ },
18108
+ keepMarks: true,
18109
+ keepAttributes: false
18110
+ };
18111
+ },
18112
+ parseDOM() {
18113
+ return [{ tag: "ul" }];
18114
+ },
18115
+ renderDOM({ htmlAttributes }) {
18116
+ const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
18117
+ return ["ul", attributes, 0];
18118
+ },
18119
+ addAttributes() {
18120
+ return {
18121
+ "list-style-type": {
18122
+ default: "bullet",
18123
+ rendered: false
18124
+ },
18125
+ listId: {
18126
+ rendered: false
18127
+ },
18128
+ sdBlockId: {
18129
+ default: null,
18130
+ keepOnSplit: false,
18131
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
18132
+ renderDOM: (attrs) => {
18133
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
18134
+ }
18135
+ },
18136
+ attributes: {
18137
+ rendered: false,
18138
+ keepOnSplit: true
18139
+ }
18140
+ };
18141
+ },
18142
+ addCommands() {
18143
+ return {
18144
+ /**
18145
+ * Toggle a bullet list at the current selection
18146
+ * @category Command
18147
+ * @example
18148
+ * // Toggle bullet list on selected text
18149
+ * editor.commands.toggleBulletList()
18150
+ * @note Converts selected paragraphs to list items or removes list formatting
18151
+ */
18152
+ toggleBulletList: () => (params2) => {
18153
+ return toggleList(this.type)(params2);
18154
+ }
18155
+ };
18156
+ },
18157
+ addShortcuts() {
18158
+ return {
18159
+ "Mod-Shift-8": () => {
18160
+ return this.editor.commands.toggleBulletList();
18161
+ }
18162
+ };
18163
+ },
18164
+ addInputRules() {
18165
+ return [
18166
+ new InputRule({
18167
+ match: inputRegex$1,
18168
+ handler: ({ state, range }) => {
18169
+ const $pos = state.selection.$from;
18170
+ const listItemType = state.schema.nodes.listItem;
18171
+ for (let depth = $pos.depth; depth >= 0; depth--) {
18172
+ if ($pos.node(depth).type === listItemType) {
18173
+ return null;
18174
+ }
18175
+ }
18176
+ const { tr } = state;
18177
+ tr.delete(range.from, range.to);
18178
+ ListHelpers.createNewList({
18179
+ listType: this.type,
18180
+ tr,
18181
+ editor: this.editor
18182
+ });
18183
+ }
18184
+ })
18185
+ ];
18186
+ }
18187
+ });
18188
+ const inputRegex = /^(\d+)\.\s$/;
18189
+ const OrderedList = Node$1.create({
18190
+ name: "orderedList",
18191
+ group: "block list",
18192
+ selectable: false,
18193
+ content() {
18194
+ return `${this.options.itemTypeName}+`;
18195
+ },
18196
+ addOptions() {
18197
+ return {
18198
+ itemTypeName: "listItem",
18199
+ htmlAttributes: {
18200
+ "aria-label": "Ordered list node"
18201
+ },
18202
+ keepMarks: true,
18203
+ keepAttributes: false,
18204
+ listStyleTypes: ["decimal", "lowerAlpha", "lowerRoman"]
18205
+ };
18206
+ },
18207
+ addAttributes() {
18208
+ return {
18209
+ order: {
18210
+ default: 1,
18211
+ parseDOM: (element) => {
18212
+ return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1;
18213
+ },
18214
+ renderDOM: (attrs) => {
18215
+ return {
18216
+ start: attrs.order
18217
+ };
18218
+ }
18219
+ },
18220
+ sdBlockId: {
18221
+ default: null,
18222
+ keepOnSplit: false,
18223
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
18224
+ renderDOM: (attrs) => {
18225
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
18226
+ }
18227
+ },
18228
+ syncId: {
18229
+ default: null,
18230
+ parseDOM: (elem) => elem.getAttribute("data-sync-id"),
18231
+ renderDOM: (attrs) => {
18232
+ if (!attrs.syncId) return {};
18233
+ return {
18234
+ "data-sync-id": attrs.syncId
18235
+ };
18236
+ }
18237
+ // rendered: false,
18238
+ },
18239
+ listId: {
18240
+ keepOnSplit: true,
18241
+ parseDOM: (elem) => elem.getAttribute("data-list-id"),
18242
+ renderDOM: (attrs) => {
18243
+ if (!attrs.listId) return {};
18244
+ return {
18245
+ "data-list-id": attrs.listId
18246
+ };
18247
+ }
18248
+ },
18249
+ "list-style-type": {
18250
+ default: "decimal",
18251
+ rendered: false
18252
+ },
18253
+ attributes: {
18254
+ rendered: false,
18255
+ keepOnSplit: true
18256
+ }
18257
+ };
18258
+ },
18259
+ parseDOM() {
18260
+ return [{ tag: "ol" }];
18261
+ },
18262
+ renderDOM({ htmlAttributes }) {
18263
+ const { start: start2, ...restAttributes } = htmlAttributes;
18264
+ return start2 === 1 ? ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, restAttributes), 0] : ["ol", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
18265
+ },
18266
+ addCommands() {
18267
+ return {
18268
+ /**
18269
+ * Toggle ordered list formatting
18270
+ * @category Command
18271
+ * @example
18272
+ * editor.commands.toggleOrderedList()
18273
+ * @note Converts selection to ordered list or back to paragraphs
18274
+ */
18275
+ toggleOrderedList: () => (params2) => {
18276
+ return toggleList(this.type)(params2);
18277
+ },
18278
+ /**
18279
+ * Restart list node numbering
18280
+ * @category Command
18281
+ * @param {Array} followingNodes - Nodes to restart
18282
+ * @param {number} pos - Starting position
18283
+ * @example
18284
+ * editor.commands.restartListNodes(nodes, position)
18285
+ * @note Resets list numbering for specified nodes
18286
+ */
18287
+ restartListNodes: (followingNodes, pos) => ({ tr }) => {
18288
+ let currentNodePos = pos;
18289
+ const nodes = followingNodes.map((node) => {
18290
+ const resultNode = {
18291
+ node,
18292
+ pos: currentNodePos
18293
+ };
18294
+ currentNodePos += node.nodeSize;
18295
+ return resultNode;
18296
+ });
18297
+ nodes.forEach((item) => {
18298
+ const { pos: pos2 } = item;
18299
+ const newPos = tr.mapping.map(pos2);
18300
+ tr.setNodeMarkup(newPos, void 0, {});
18301
+ });
18302
+ return true;
18303
+ },
18304
+ /**
18305
+ * Update ordered list style type based on nesting level
18306
+ * @category Command
18307
+ * @example
18308
+ * editor.commands.updateOrderedListStyleType()
18309
+ * @note Cycles through decimal -> lowerAlpha -> lowerRoman based on depth
18310
+ */
18311
+ updateOrderedListStyleType: () => ({ dispatch, tr }) => {
18312
+ let list = findParentNode((node) => node.type.name === this.name)(tr.selection);
18313
+ if (!list) {
18314
+ return true;
18315
+ }
18316
+ if (dispatch) {
18317
+ let listLevel = (list.depth - 1) / 2;
18318
+ let listStyleTypes = this.options.listStyleTypes;
18319
+ let listStyle = listStyleTypes[listLevel % listStyleTypes.length];
18320
+ let currentListStyle = list.node.attrs["list-style-type"];
18321
+ let nodeAtPos = tr.doc.nodeAt(list.pos);
18322
+ if (currentListStyle !== listStyle && nodeAtPos.eq(list.node)) {
18323
+ tr.setNodeMarkup(list.pos, void 0, {
18324
+ ...list.node.attrs,
18325
+ ...{
18326
+ "list-style-type": listStyle
18327
+ }
18328
+ });
18329
+ }
18330
+ }
18331
+ return true;
18332
+ }
18333
+ };
18334
+ },
18335
+ addShortcuts() {
18336
+ return {
18337
+ "Mod-Shift-7": () => {
18338
+ return this.editor.commands.toggleOrderedList();
18339
+ }
18340
+ };
18341
+ },
18342
+ addInputRules() {
18343
+ return [
18344
+ new InputRule({
18345
+ match: inputRegex,
18346
+ handler: ({ state, range }) => {
18347
+ const $pos = state.selection.$from;
18348
+ const listItemType = state.schema.nodes.listItem;
18349
+ for (let depth = $pos.depth; depth >= 0; depth--) {
18350
+ if ($pos.node(depth).type === listItemType) {
18351
+ return null;
18352
+ }
18353
+ }
18354
+ const { tr } = state;
18355
+ tr.delete(range.from, range.to);
18356
+ ListHelpers.createNewList({
18357
+ listType: this.type,
18358
+ tr,
18359
+ editor: this.editor
18360
+ });
18361
+ }
18362
+ })
18363
+ ];
18364
+ }
18365
+ });
18366
+ const generateOrderedListIndex = ({ listLevel, lvlText, listNumberingType, customFormat }) => {
18367
+ const handler = listIndexMap[listNumberingType];
18368
+ return handler ? handler(listLevel, lvlText, customFormat) : null;
18369
+ };
18370
+ const handleDecimal = (path, lvlText) => generateNumbering(path, lvlText, String);
18371
+ const handleRoman = (path, lvlText) => generateNumbering(path, lvlText, intToRoman);
18372
+ const handleLowerRoman = (path, lvlText) => handleRoman(path, lvlText).toLowerCase();
18373
+ const handleLowerAlpha = (path, lvlText) => handleAlpha(path, lvlText).toLowerCase();
18374
+ const handleAlpha = (path, lvlText) => generateNumbering(path, lvlText, (p) => intToAlpha(p));
18375
+ const handleOrdinal = (path, lvlText) => generateNumbering(path, lvlText, ordinalFormatter);
18376
+ const handleCustom = (path, lvlText, customFormat) => generateFromCustom(path, lvlText, customFormat);
18377
+ const handleJapaneseCounting = (path, lvlText) => generateNumbering(path, lvlText, intToJapaneseCounting);
18378
+ const listIndexMap = {
18379
+ decimal: handleDecimal,
18380
+ lowerRoman: handleLowerRoman,
18381
+ upperRoman: handleRoman,
18382
+ lowerLetter: handleLowerAlpha,
18383
+ upperLetter: handleAlpha,
18384
+ ordinal: handleOrdinal,
18385
+ custom: handleCustom,
18386
+ japaneseCounting: handleJapaneseCounting
18387
+ };
18388
+ const createNumbering = (values, lvlText) => {
18389
+ return values.reduce((acc, value, index2) => {
18390
+ return value > 9 ? acc.replace(/^0/, "").replace(`%${index2 + 1}`, value) : acc.replace(`%${index2 + 1}`, value);
18391
+ }, lvlText);
18392
+ };
18393
+ const generateNumbering = (path, lvlText, formatter) => {
18394
+ const formattedValues = path.map(formatter);
18395
+ return createNumbering(formattedValues, lvlText);
18396
+ };
18397
+ const ordinalFormatter = (level) => {
18398
+ const suffixes = ["th", "st", "nd", "rd"];
18399
+ const value = level % 100;
18400
+ const suffix = suffixes[(value - 20) % 10] || suffixes[value] || suffixes[0];
18401
+ const p = level + suffix;
18402
+ return p;
18403
+ };
18404
+ const generateFromCustom = (path, lvlText, customFormat) => {
18405
+ if (customFormat !== "001, 002, 003, ...") return generateNumbering(path, lvlText, String);
18406
+ const match = customFormat.match(/(\d+)/);
18407
+ if (!match) throw new Error("Invalid format string: no numeric pattern found");
18408
+ const sample = match[1];
18409
+ const digitCount = sample.length;
18410
+ const index2 = path.pop();
18411
+ return String(index2).padStart(digitCount, "0");
18412
+ };
18413
+ const intToRoman = (num) => {
18414
+ const romanNumeralMap = [
18415
+ { value: 1e3, numeral: "M" },
18416
+ { value: 900, numeral: "CM" },
18417
+ { value: 500, numeral: "D" },
18418
+ { value: 400, numeral: "CD" },
18419
+ { value: 100, numeral: "C" },
18420
+ { value: 90, numeral: "XC" },
18421
+ { value: 50, numeral: "L" },
18422
+ { value: 40, numeral: "XL" },
18423
+ { value: 10, numeral: "X" },
18424
+ { value: 9, numeral: "IX" },
18425
+ { value: 5, numeral: "V" },
18426
+ { value: 4, numeral: "IV" },
18427
+ { value: 1, numeral: "I" }
18428
+ ];
18429
+ let result = "";
18430
+ for (const { value, numeral } of romanNumeralMap) {
18431
+ while (num >= value) {
18432
+ result += numeral;
18433
+ num -= value;
18434
+ }
18435
+ }
18436
+ return result;
18437
+ };
18438
+ const intToAlpha = (num) => {
18439
+ let result = "";
18440
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
18441
+ while (num > 0) {
18442
+ let index2 = (num - 1) % 26;
18443
+ result = alphabet[index2] + result;
18444
+ num = Math.floor((num - 1) / 26);
18445
+ }
18446
+ return result;
18447
+ };
18448
+ const intToJapaneseCounting = (num) => {
18449
+ const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
18450
+ const units = ["", "十", "百", "千"];
18451
+ if (num === 0) return "零";
18452
+ if (num < 10) return digits[num];
18453
+ let result = "";
18454
+ let tempNum = num;
18455
+ let unitIndex = 0;
18456
+ while (tempNum > 0) {
18457
+ const digit = tempNum % 10;
18458
+ if (digit !== 0) {
18459
+ const digitStr = digit === 1 && unitIndex > 0 ? "" : digits[digit];
18460
+ result = digitStr + (unitIndex > 0 ? units[unitIndex] : "") + result;
18461
+ } else if (result && tempNum > 0) {
18462
+ if (!result.startsWith("零") && tempNum % 100 !== 0) {
18463
+ result = "零" + result;
18464
+ }
18465
+ }
18466
+ tempNum = Math.floor(tempNum / 10);
18467
+ unitIndex++;
18468
+ if (unitIndex > 3) break;
18469
+ }
18470
+ if (num >= 10 && num < 20) {
18471
+ result = result.replace(/^一十/, "十");
18472
+ }
18473
+ return result;
18474
+ };
18475
+ const isKeyboardInvocation = (event) => {
18476
+ return event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
18477
+ };
18478
+ const prefersNativeMenu = (event) => {
18479
+ if (!event) return false;
18480
+ if (event.ctrlKey || event.metaKey) {
18481
+ return true;
18482
+ }
18483
+ return isKeyboardInvocation(event);
18484
+ };
18485
+ const shouldAllowNativeContextMenu = (event) => {
18486
+ return prefersNativeMenu(event);
18487
+ };
18488
+ const shouldBypassContextMenu = shouldAllowNativeContextMenu;
18489
+ const DEFAULT_SELECTION_STATE = Object.freeze({
18490
+ focused: false,
18491
+ preservedSelection: null,
18492
+ showVisualSelection: false,
18493
+ skipFocusReset: false
18494
+ });
18495
+ const normalizeSelectionState = (state = {}) => ({
18496
+ ...DEFAULT_SELECTION_STATE,
18497
+ ...state
18498
+ });
18499
+ const CustomSelectionPluginKey = new PluginKey("CustomSelection");
18500
+ const handleClickOutside = (event, editor) => {
18501
+ const editorElem = editor?.options?.element;
18502
+ if (!editorElem) return;
18503
+ const isInsideEditor = editorElem?.contains(event.target);
18504
+ if (!isInsideEditor) {
18505
+ editor.setOptions({
18506
+ focusTarget: event.target
18507
+ });
18508
+ } else {
18509
+ editor.setOptions({
18510
+ focusTarget: null
18511
+ });
18512
+ }
18513
+ };
18514
+ function getFocusMeta(tr) {
18515
+ return tr.getMeta(CustomSelectionPluginKey);
18516
+ }
18517
+ function setFocusMeta(tr, value) {
18518
+ return tr.setMeta(CustomSelectionPluginKey, value);
18519
+ }
18520
+ function getFocusState(state) {
18521
+ return CustomSelectionPluginKey.getState(state);
18522
+ }
18523
+ const isToolbarInput = (target) => {
18524
+ return !!target?.closest(".button-text-input") || target?.classList?.contains("button-text-input");
18525
+ };
18526
+ const isToolbarButton = (target) => {
18527
+ return !!target?.closest(".toolbar-button") || target?.classList?.contains("toolbar-button");
18528
+ };
18529
+ const CustomSelection = Extension.create({
18530
+ name: "customSelection",
18531
+ addPmPlugins() {
18532
+ const editor = this.editor;
18533
+ const customSelectionPlugin = new Plugin({
18534
+ key: CustomSelectionPluginKey,
18535
+ state: {
18536
+ init: () => ({ ...DEFAULT_SELECTION_STATE }),
18537
+ apply: (tr, value) => {
18538
+ const meta = getFocusMeta(tr);
18539
+ if (meta !== void 0) {
18540
+ return { ...value, ...meta };
18541
+ }
18542
+ return value;
18543
+ }
18544
+ },
18545
+ view: () => {
18546
+ const clickHandler = (event) => handleClickOutside(event, editor);
18547
+ document?.addEventListener("mousedown", clickHandler);
18548
+ return {
18549
+ destroy: () => {
18550
+ document?.removeEventListener("mousedown", clickHandler);
18551
+ }
18552
+ };
18553
+ },
18554
+ props: {
18555
+ handleDOMEvents: {
18556
+ contextmenu: (view, event) => {
18557
+ if (shouldAllowNativeContextMenu(event)) {
18558
+ return false;
18559
+ }
18560
+ event.preventDefault();
18561
+ const { selection } = view.state;
18562
+ if (!selection.empty) {
17546
18563
  view.dispatch(
17547
18564
  setFocusMeta(view.state.tr, {
17548
18565
  focused: true,
17549
18566
  preservedSelection: selection,
17550
- showVisualSelection: true
18567
+ showVisualSelection: true,
18568
+ skipFocusReset: true
17551
18569
  })
17552
18570
  );
17553
18571
  }
@@ -17558,6 +18576,9 @@ const CustomSelection = Extension.create({
17558
18576
  },
17559
18577
  mousedown: (view, event) => {
17560
18578
  if (event.button === 2) {
18579
+ if (shouldAllowNativeContextMenu(event)) {
18580
+ return false;
18581
+ }
17561
18582
  event.preventDefault();
17562
18583
  const { selection: selection2 } = view.state;
17563
18584
  if (!selection2.empty) {
@@ -17565,7 +18586,8 @@ const CustomSelection = Extension.create({
17565
18586
  setFocusMeta(view.state.tr, {
17566
18587
  focused: true,
17567
18588
  preservedSelection: selection2,
17568
- showVisualSelection: true
18589
+ showVisualSelection: true,
18590
+ skipFocusReset: true
17569
18591
  })
17570
18592
  );
17571
18593
  this.editor.setOptions({
@@ -17588,7 +18610,8 @@ const CustomSelection = Extension.create({
17588
18610
  setFocusMeta(view.state.tr, {
17589
18611
  focused: true,
17590
18612
  preservedSelection: selection,
17591
- showVisualSelection: true
18613
+ showVisualSelection: true,
18614
+ skipFocusReset: false
17592
18615
  })
17593
18616
  );
17594
18617
  this.editor.setOptions({
@@ -17606,7 +18629,8 @@ const CustomSelection = Extension.create({
17606
18629
  setFocusMeta(view.state.tr, {
17607
18630
  focused: true,
17608
18631
  preservedSelection: selection,
17609
- showVisualSelection: true
18632
+ showVisualSelection: true,
18633
+ skipFocusReset: false
17610
18634
  })
17611
18635
  );
17612
18636
  }
@@ -17617,7 +18641,8 @@ const CustomSelection = Extension.create({
17617
18641
  setFocusMeta(view.state.tr, {
17618
18642
  focused: false,
17619
18643
  preservedSelection: null,
17620
- showVisualSelection: false
18644
+ showVisualSelection: false,
18645
+ skipFocusReset: false
17621
18646
  })
17622
18647
  );
17623
18648
  if (!selection.empty && !this.editor.options.element?.contains(target)) {
@@ -17634,12 +18659,20 @@ const CustomSelection = Extension.create({
17634
18659
  const isElement2 = target instanceof Element;
17635
18660
  const isToolbarBtn = isElement2 && isToolbarButton(target);
17636
18661
  const isToolbarInp = isElement2 && isToolbarInput(target);
18662
+ const focusState = getFocusState(view.state);
18663
+ if (focusState?.skipFocusReset) {
18664
+ view.dispatch(
18665
+ setFocusMeta(view.state.tr, normalizeSelectionState({ ...focusState, skipFocusReset: false }))
18666
+ );
18667
+ return false;
18668
+ }
17637
18669
  if (!isToolbarBtn && !isToolbarInp) {
17638
18670
  view.dispatch(
17639
18671
  setFocusMeta(view.state.tr, {
17640
18672
  focused: false,
17641
18673
  preservedSelection: null,
17642
- showVisualSelection: false
18674
+ showVisualSelection: false,
18675
+ skipFocusReset: false
17643
18676
  })
17644
18677
  );
17645
18678
  }
@@ -17650,12 +18683,16 @@ const CustomSelection = Extension.create({
17650
18683
  const isToolbarBtn = isElement2 && isToolbarButton(target);
17651
18684
  const isToolbarInp = isElement2 && isToolbarInput(target);
17652
18685
  const state = getFocusState(view.state);
18686
+ if (state?.skipFocusReset) {
18687
+ return false;
18688
+ }
17653
18689
  if (isToolbarBtn || isToolbarInp) {
17654
18690
  view.dispatch(
17655
18691
  setFocusMeta(view.state.tr, {
17656
18692
  focused: true,
17657
18693
  preservedSelection: state.preservedSelection || view.state.selection,
17658
- showVisualSelection: true
18694
+ showVisualSelection: true,
18695
+ skipFocusReset: false
17659
18696
  })
17660
18697
  );
17661
18698
  } else {
@@ -17663,7 +18700,8 @@ const CustomSelection = Extension.create({
17663
18700
  setFocusMeta(view.state.tr, {
17664
18701
  focused: false,
17665
18702
  preservedSelection: null,
17666
- showVisualSelection: false
18703
+ showVisualSelection: false,
18704
+ skipFocusReset: false
17667
18705
  })
17668
18706
  );
17669
18707
  }
@@ -18509,7 +19547,7 @@ class ListItemNodeView {
18509
19547
  this.decorations = decorations;
18510
19548
  this.view = editor.view;
18511
19549
  this.getPos = getPos;
18512
- __privateMethod(this, _ListItemNodeView_instances, init_fn2).call(this);
19550
+ __privateMethod(this, _ListItemNodeView_instances, init_fn3).call(this);
18513
19551
  activeListItemNodeViews.add(this);
18514
19552
  }
18515
19553
  refreshIndentStyling() {
@@ -18570,7 +19608,7 @@ class ListItemNodeView {
18570
19608
  }
18571
19609
  }
18572
19610
  _ListItemNodeView_instances = new WeakSet();
18573
- init_fn2 = function() {
19611
+ init_fn3 = function() {
18574
19612
  const { attrs } = this.node;
18575
19613
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
18576
19614
  let orderMarker = "";
@@ -25679,984 +26717,335 @@ const PageNumber = Node$1.create({
25679
26717
  }
25680
26718
  };
25681
26719
  },
25682
- addAttributes() {
25683
- return {
25684
- marksAsAttrs: {
25685
- default: null,
25686
- rendered: false
25687
- }
25688
- };
25689
- },
25690
- addNodeView() {
25691
- return ({ node, editor, getPos, decorations }) => {
25692
- const htmlAttributes = this.options.htmlAttributes;
25693
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
25694
- };
25695
- },
25696
- parseDOM() {
25697
- return [{ tag: 'span[data-id="auto-page-number"' }];
25698
- },
25699
- renderDOM({ htmlAttributes }) {
25700
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
25701
- },
25702
- addCommands() {
25703
- return {
25704
- /**
25705
- * Insert an automatic page number
25706
- * @category Command
25707
- * @returns {Function} Command function
25708
- * @example
25709
- * editor.commands.addAutoPageNumber()
25710
- * @note Only works in header/footer contexts
25711
- */
25712
- addAutoPageNumber: () => ({ tr, dispatch, state, editor }) => {
25713
- const { options } = editor;
25714
- if (!options.isHeaderOrFooter) return false;
25715
- const { schema } = state;
25716
- const pageNumberType = schema?.nodes?.["page-number"];
25717
- if (!pageNumberType) return false;
25718
- const pageNumberNodeJSON = { type: "page-number" };
25719
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
25720
- if (dispatch) {
25721
- tr.replaceSelectionWith(pageNumberNode, false);
25722
- tr.setMeta("forceUpdatePagination", true);
25723
- }
25724
- return true;
25725
- }
25726
- };
25727
- },
25728
- addShortcuts() {
25729
- return {
25730
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
25731
- };
25732
- }
25733
- });
25734
- const TotalPageCount = Node$1.create({
25735
- name: "total-page-number",
25736
- group: "inline",
25737
- inline: true,
25738
- atom: true,
25739
- draggable: false,
25740
- selectable: false,
25741
- content: "text*",
25742
- addOptions() {
25743
- return {
25744
- htmlAttributes: {
25745
- contenteditable: false,
25746
- "data-id": "auto-total-pages",
25747
- "aria-label": "Total page count node",
25748
- class: "sd-editor-auto-total-pages"
25749
- }
25750
- };
25751
- },
25752
- addAttributes() {
25753
- return {
25754
- marksAsAttrs: {
25755
- default: null,
25756
- rendered: false
25757
- }
25758
- };
25759
- },
25760
- addNodeView() {
25761
- return ({ node, editor, getPos, decorations }) => {
25762
- const htmlAttributes = this.options.htmlAttributes;
25763
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
25764
- };
25765
- },
25766
- parseDOM() {
25767
- return [{ tag: 'span[data-id="auto-total-pages"' }];
25768
- },
25769
- renderDOM({ htmlAttributes }) {
25770
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
25771
- },
25772
- addCommands() {
25773
- return {
25774
- /**
25775
- * Insert total page count
25776
- * @category Command
25777
- * @returns {Function} Command function
25778
- * @example
25779
- * editor.commands.addTotalPageCount()
25780
- * @note Only works in header/footer contexts
25781
- */
25782
- addTotalPageCount: () => ({ tr, dispatch, state, editor }) => {
25783
- const { options } = editor;
25784
- if (!options.isHeaderOrFooter) return false;
25785
- const { schema } = state;
25786
- const pageNumberType = schema.nodes?.["total-page-number"];
25787
- if (!pageNumberType) return false;
25788
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
25789
- const pageNumberNode = {
25790
- type: "total-page-number",
25791
- content: [{ type: "text", text: String(currentPages) }]
25792
- };
25793
- const pageNode = schema.nodeFromJSON(pageNumberNode);
25794
- if (dispatch) {
25795
- tr.replaceSelectionWith(pageNode, false);
25796
- }
25797
- return true;
25798
- }
25799
- };
25800
- },
25801
- addShortcuts() {
25802
- return {
25803
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
25804
- };
25805
- }
25806
- });
25807
- const getNodeAttributes = (nodeName, editor) => {
25808
- switch (nodeName) {
25809
- case "page-number":
25810
- return {
25811
- text: editor.options.currentPageNumber || "1",
25812
- className: "sd-editor-auto-page-number",
25813
- dataId: "auto-page-number",
25814
- ariaLabel: "Page number node"
25815
- };
25816
- case "total-page-number":
25817
- return {
25818
- text: editor.options.parentEditor?.currentTotalPages || "1",
25819
- className: "sd-editor-auto-total-pages",
25820
- dataId: "auto-total-pages",
25821
- ariaLabel: "Total page count node"
25822
- };
25823
- default:
25824
- return {};
25825
- }
25826
- };
25827
- class AutoPageNumberNodeView {
25828
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
25829
- __privateAdd(this, _AutoPageNumberNodeView_instances);
25830
- this.node = node;
25831
- this.editor = editor;
25832
- this.view = editor.view;
25833
- this.getPos = getPos;
25834
- this.editor = editor;
25835
- this.dom = __privateMethod(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
25836
- }
25837
- update(node) {
25838
- const incomingType = node?.type?.name;
25839
- const currentType = this.node?.type?.name;
25840
- if (!incomingType || incomingType !== currentType) return false;
25841
- this.node = node;
25842
- return true;
25843
- }
25844
- }
25845
- _AutoPageNumberNodeView_instances = new WeakSet();
25846
- renderDom_fn = function(node, htmlAttributes) {
25847
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
25848
- const content = document.createTextNode(String(attrs.text));
25849
- const nodeContent = document.createElement("span");
25850
- nodeContent.className = attrs.className;
25851
- nodeContent.setAttribute("data-id", attrs.dataId);
25852
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
25853
- const currentPos = this.getPos();
25854
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
25855
- __privateMethod(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
25856
- Object.assign(nodeContent.style, styles);
25857
- nodeContent.appendChild(content);
25858
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
25859
- if (value) nodeContent.setAttribute(key2, value);
25860
- });
25861
- return nodeContent;
25862
- };
25863
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
25864
- setTimeout(() => {
25865
- const { state } = this.editor;
25866
- const { dispatch } = this.view;
25867
- const node = state.doc.nodeAt(pos);
25868
- if (!node || node.isText) return;
25869
- const currentMarks = node.attrs.marksAsAttrs || [];
25870
- const newMarks = marks.map((m) => ({ type: m.type.name, attrs: m.attrs }));
25871
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
25872
- if (isEqual) return;
25873
- const newAttrs = {
25874
- ...node.attrs,
25875
- marksAsAttrs: newMarks
25876
- };
25877
- const tr = state.tr.setNodeMarkup(pos, void 0, newAttrs);
25878
- dispatch(tr);
25879
- }, 0);
25880
- };
25881
- const getMarksFromNeighbors = (currentPos, view) => {
25882
- const $pos = view.state.doc.resolve(currentPos);
25883
- const styles = {};
25884
- const marks = [];
25885
- const before = $pos.nodeBefore;
25886
- if (before) {
25887
- Object.assign(styles, processMarks(before.marks));
25888
- marks.push(...before.marks);
25889
- }
25890
- const after = $pos.nodeAfter;
25891
- if (after) {
25892
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
25893
- marks.push(...after.marks);
25894
- }
25895
- return {
25896
- styles,
25897
- marks
25898
- };
25899
- };
25900
- const processMarks = (marks) => {
25901
- const styles = {};
25902
- marks.forEach((mark) => {
25903
- const { type, attrs } = mark;
25904
- switch (type.name) {
25905
- case "textStyle":
25906
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
25907
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
25908
- if (attrs.color) styles["color"] = attrs.color;
25909
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
25910
- break;
25911
- case "bold":
25912
- styles["font-weight"] = "bold";
25913
- break;
25914
- case "italic":
25915
- styles["font-style"] = "italic";
25916
- break;
25917
- case "underline":
25918
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
25919
- break;
25920
- case "strike":
25921
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
25922
- break;
25923
- default:
25924
- if (attrs?.style) {
25925
- Object.entries(attrs.style).forEach(([key2, value]) => {
25926
- styles[key2] = value;
25927
- });
25928
- }
25929
- break;
25930
- }
25931
- });
25932
- return styles;
25933
- };
25934
- const ShapeContainer = Node$1.create({
25935
- name: "shapeContainer",
25936
- group: "block",
25937
- content: "block+",
25938
- isolating: true,
25939
- addOptions() {
25940
- return {
25941
- htmlAttributes: {
25942
- class: "sd-editor-shape-container",
25943
- "aria-label": "Shape container node"
25944
- }
25945
- };
25946
- },
25947
- addAttributes() {
25948
- return {
25949
- fillcolor: {
25950
- renderDOM: (attrs) => {
25951
- if (!attrs.fillcolor) return {};
25952
- return {
25953
- style: `background-color: ${attrs.fillcolor}`
25954
- };
25955
- }
25956
- },
25957
- sdBlockId: {
25958
- default: null,
25959
- keepOnSplit: false,
25960
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
25961
- renderDOM: (attrs) => {
25962
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
25963
- }
25964
- },
25965
- style: {
25966
- renderDOM: (attrs) => {
25967
- if (!attrs.style) return {};
25968
- return {
25969
- style: attrs.style
25970
- };
25971
- }
25972
- },
25973
- wrapAttributes: {
25974
- rendered: false
25975
- },
25976
- attributes: {
25977
- rendered: false
25978
- }
25979
- };
25980
- },
25981
- parseDOM() {
25982
- return [
25983
- {
25984
- tag: `div[data-type="${this.name}"]`
25985
- }
25986
- ];
25987
- },
25988
- renderDOM({ htmlAttributes }) {
25989
- return [
25990
- "div",
25991
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
25992
- 0
25993
- ];
25994
- }
25995
- });
25996
- const ShapeTextbox = Node$1.create({
25997
- name: "shapeTextbox",
25998
- group: "block",
25999
- content: "paragraph* block*",
26000
- isolating: true,
26001
- addOptions() {
26002
- return {
26003
- htmlAttributes: {
26004
- class: "sd-editor-shape-textbox",
26005
- "aria-label": "Shape textbox node"
26006
- }
26007
- };
26008
- },
26009
- addAttributes() {
26010
- return {
26011
- sdBlockId: {
26012
- default: null,
26013
- keepOnSplit: false,
26014
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26015
- renderDOM: (attrs) => {
26016
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26017
- }
26018
- },
26019
- attributes: {
26020
- rendered: false
26021
- }
26022
- };
26023
- },
26024
- parseDOM() {
26025
- return [
26026
- {
26027
- tag: `div[data-type="${this.name}"]`
26028
- }
26029
- ];
26030
- },
26031
- renderDOM({ htmlAttributes }) {
26032
- return [
26033
- "div",
26034
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26035
- 0
26036
- ];
26037
- }
26038
- });
26039
- const ContentBlock = Node$1.create({
26040
- name: "contentBlock",
26041
- group: "inline",
26042
- content: "",
26043
- isolating: true,
26044
- atom: true,
26045
- inline: true,
26046
- addOptions() {
26047
- return {
26048
- htmlAttributes: {
26049
- contenteditable: false
26050
- }
26051
- };
26052
- },
26053
- addAttributes() {
26054
- return {
26055
- horizontalRule: {
26056
- default: false,
26057
- renderDOM: ({ horizontalRule }) => {
26058
- if (!horizontalRule) return {};
26059
- return { "data-horizontal-rule": "true" };
26060
- }
26061
- },
26062
- size: {
26063
- default: null,
26064
- renderDOM: ({ size }) => {
26065
- if (!size) return {};
26066
- let style = "";
26067
- if (size.top) style += `top: ${size.top}px; `;
26068
- if (size.left) style += `left: ${size.left}px; `;
26069
- if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
26070
- if (size.height)
26071
- style += `height: ${size.height.toString().endsWith("%") ? size.height : `${size.height}px`}; `;
26072
- return { style };
26073
- }
26074
- },
26075
- background: {
26076
- default: null,
26077
- renderDOM: (attrs) => {
26078
- if (!attrs.background) return {};
26079
- return {
26080
- style: `background-color: ${attrs.background}`
26081
- };
26082
- }
26083
- },
26084
- drawingContent: {
26085
- rendered: false
26086
- },
26087
- attributes: {
26720
+ addAttributes() {
26721
+ return {
26722
+ marksAsAttrs: {
26723
+ default: null,
26088
26724
  rendered: false
26089
26725
  }
26090
26726
  };
26091
26727
  },
26728
+ addNodeView() {
26729
+ return ({ node, editor, getPos, decorations }) => {
26730
+ const htmlAttributes = this.options.htmlAttributes;
26731
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
26732
+ };
26733
+ },
26092
26734
  parseDOM() {
26093
- return [
26094
- {
26095
- tag: `div[data-type="${this.name}"]`
26096
- }
26097
- ];
26735
+ return [{ tag: 'span[data-id="auto-page-number"' }];
26098
26736
  },
26099
26737
  renderDOM({ htmlAttributes }) {
26100
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
26738
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
26101
26739
  },
26102
26740
  addCommands() {
26103
26741
  return {
26104
26742
  /**
26105
- * Insert a horizontal rule
26106
- * @category Command
26107
- * @example
26108
- * editor.commands.insertHorizontalRule()
26109
- * @note Creates a visual separator between content sections
26110
- */
26111
- insertHorizontalRule: () => ({ commands: commands2 }) => {
26112
- return commands2.insertContent({
26113
- type: this.name,
26114
- attrs: {
26115
- horizontalRule: true,
26116
- size: { width: "100%", height: 2 },
26117
- background: "#e5e7eb"
26118
- }
26119
- });
26120
- },
26121
- /**
26122
- * Insert a content block
26743
+ * Insert an automatic page number
26123
26744
  * @category Command
26124
- * @param {ContentBlockConfig} config - Block configuration
26125
- * @example
26126
- * // Insert a spacer block
26127
- * editor.commands.insertContentBlock({ size: { height: 20 } })
26128
- *
26745
+ * @returns {Function} Command function
26129
26746
  * @example
26130
- * // Insert a colored divider
26131
- * editor.commands.insertContentBlock({
26132
- * size: { width: '50%', height: 3 },
26133
- * background: '#3b82f6'
26134
- * })
26135
- * @note Used for spacing, dividers, and special inline content
26747
+ * editor.commands.addAutoPageNumber()
26748
+ * @note Only works in header/footer contexts
26136
26749
  */
26137
- insertContentBlock: (config) => ({ commands: commands2 }) => {
26138
- return commands2.insertContent({
26139
- type: this.name,
26140
- attrs: config
26141
- });
26750
+ addAutoPageNumber: () => ({ tr, dispatch, state, editor }) => {
26751
+ const { options } = editor;
26752
+ if (!options.isHeaderOrFooter) return false;
26753
+ const { schema } = state;
26754
+ const pageNumberType = schema?.nodes?.["page-number"];
26755
+ if (!pageNumberType) return false;
26756
+ const pageNumberNodeJSON = { type: "page-number" };
26757
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
26758
+ if (dispatch) {
26759
+ tr.replaceSelectionWith(pageNumberNode, false);
26760
+ tr.setMeta("forceUpdatePagination", true);
26761
+ }
26762
+ return true;
26142
26763
  }
26143
26764
  };
26765
+ },
26766
+ addShortcuts() {
26767
+ return {
26768
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
26769
+ };
26144
26770
  }
26145
26771
  });
26146
- class StructuredContentViewBase {
26147
- constructor(props) {
26148
- __publicField(this, "node");
26149
- __publicField(this, "view");
26150
- __publicField(this, "getPos");
26151
- __publicField(this, "decorations");
26152
- __publicField(this, "innerDecorations");
26153
- __publicField(this, "editor");
26154
- __publicField(this, "extension");
26155
- __publicField(this, "htmlAttributes");
26156
- __publicField(this, "root");
26157
- __publicField(this, "isDragging", false);
26158
- this.node = props.node;
26159
- this.view = props.editor.view;
26160
- this.getPos = props.getPos;
26161
- this.decorations = props.decorations;
26162
- this.innerDecorations = props.innerDecorations;
26163
- this.editor = props.editor;
26164
- this.extension = props.extension;
26165
- this.htmlAttributes = props.htmlAttributes;
26166
- this.mount(props);
26167
- }
26168
- mount() {
26169
- return;
26170
- }
26171
- get dom() {
26172
- return this.root;
26173
- }
26174
- get contentDOM() {
26175
- return null;
26176
- }
26177
- update(node, decorations, innerDecorations) {
26178
- if (node.type !== this.node.type) {
26179
- return false;
26180
- }
26181
- this.node = node;
26182
- this.decorations = decorations;
26183
- this.innerDecorations = innerDecorations;
26184
- this.updateHTMLAttributes();
26185
- return true;
26186
- }
26187
- stopEvent(event) {
26188
- if (!this.dom) return false;
26189
- const target = event.target;
26190
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
26191
- if (!isInElement) return false;
26192
- const isDragEvent = event.type.startsWith("drag");
26193
- const isDropEvent = event.type === "drop";
26194
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
26195
- if (isInput && !isDropEvent && !isDragEvent) return true;
26196
- const { isEditable } = this.editor;
26197
- const { isDragging } = this;
26198
- const isDraggable = !!this.node.type.spec.draggable;
26199
- const isSelectable = NodeSelection.isSelectable(this.node);
26200
- const isCopyEvent = event.type === "copy";
26201
- const isPasteEvent = event.type === "paste";
26202
- const isCutEvent = event.type === "cut";
26203
- const isClickEvent = event.type === "mousedown";
26204
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
26205
- event.preventDefault();
26206
- }
26207
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
26208
- event.preventDefault();
26209
- return false;
26210
- }
26211
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
26212
- const dragHandle = target.closest("[data-drag-handle]");
26213
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
26214
- if (isValidDragHandle) {
26215
- this.isDragging = true;
26216
- document.addEventListener(
26217
- "dragend",
26218
- () => {
26219
- this.isDragging = false;
26220
- },
26221
- { once: true }
26222
- );
26223
- document.addEventListener(
26224
- "drop",
26225
- () => {
26226
- this.isDragging = false;
26227
- },
26228
- { once: true }
26229
- );
26230
- document.addEventListener(
26231
- "mouseup",
26232
- () => {
26233
- this.isDragging = false;
26234
- },
26235
- { once: true }
26236
- );
26237
- }
26238
- }
26239
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
26240
- return false;
26241
- }
26242
- return true;
26243
- }
26244
- ignoreMutation(mutation) {
26245
- if (!this.dom || !this.contentDOM) return true;
26246
- if (this.node.isLeaf || this.node.isAtom) return true;
26247
- if (mutation.type === "selection") return false;
26248
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
26249
- if (this.contentDOM.contains(mutation.target)) return false;
26250
- return true;
26251
- }
26252
- destroy() {
26253
- this.dom.remove();
26254
- this.contentDOM?.remove();
26255
- }
26256
- updateAttributes(attrs) {
26257
- const pos = this.getPos();
26258
- if (typeof pos !== "number") {
26259
- return;
26260
- }
26261
- return this.view.dispatch(
26262
- this.view.state.tr.setNodeMarkup(pos, void 0, {
26263
- ...this.node.attrs,
26264
- ...attrs
26265
- })
26266
- );
26267
- }
26268
- updateHTMLAttributes() {
26269
- const { extensionService } = this.editor;
26270
- const { attributes } = extensionService;
26271
- const extensionAttrs = attributes.filter((i) => i.type === this.node.type.name);
26272
- this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
26273
- }
26274
- createDragHandle() {
26275
- const dragHandle = document.createElement("span");
26276
- dragHandle.classList.add("sd-structured-content-draggable");
26277
- dragHandle.draggable = true;
26278
- dragHandle.contentEditable = "false";
26279
- dragHandle.dataset.dragHandle = "";
26280
- const textElement = document.createElement("span");
26281
- textElement.textContent = "Structured content";
26282
- dragHandle.append(textElement);
26283
- return dragHandle;
26284
- }
26285
- onDragStart(event) {
26286
- const { view } = this.editor;
26287
- const target = event.target;
26288
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
26289
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
26290
- return;
26291
- }
26292
- let x = 0;
26293
- let y = 0;
26294
- if (this.dom !== dragHandle) {
26295
- const domBox = this.dom.getBoundingClientRect();
26296
- const handleBox = dragHandle.getBoundingClientRect();
26297
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
26298
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
26299
- x = handleBox.x - domBox.x + offsetX;
26300
- y = handleBox.y - domBox.y + offsetY;
26301
- }
26302
- event.dataTransfer?.setDragImage(this.dom, x, y);
26303
- const pos = this.getPos();
26304
- if (typeof pos !== "number") {
26305
- return;
26306
- }
26307
- const selection = NodeSelection.create(view.state.doc, pos);
26308
- const transaction = view.state.tr.setSelection(selection);
26309
- view.dispatch(transaction);
26310
- }
26311
- }
26312
- class StructuredContentInlineView extends StructuredContentViewBase {
26313
- constructor(props) {
26314
- super(props);
26315
- }
26316
- mount() {
26317
- this.buildView();
26318
- }
26319
- get contentDOM() {
26320
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
26321
- return contentElement || null;
26322
- }
26323
- createElement() {
26324
- const element = document.createElement("span");
26325
- element.classList.add(structuredContentClass$1);
26326
- element.setAttribute("data-structured-content", "");
26327
- const contentElement = document.createElement("span");
26328
- contentElement.classList.add(structuredContentInnerClass$1);
26329
- element.append(contentElement);
26330
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26331
- updateDOMAttributes(element, { ...domAttrs });
26332
- return { element, contentElement };
26333
- }
26334
- buildView() {
26335
- const { element } = this.createElement();
26336
- const dragHandle = this.createDragHandle();
26337
- element.prepend(dragHandle);
26338
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26339
- this.root = element;
26340
- }
26341
- updateView() {
26342
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26343
- updateDOMAttributes(this.dom, { ...domAttrs });
26344
- }
26345
- update(node, decorations, innerDecorations) {
26346
- const result = super.update(node, decorations, innerDecorations);
26347
- if (!result) return false;
26348
- this.updateView();
26349
- return true;
26350
- }
26351
- }
26352
- const structuredContentClass$1 = "sd-structured-content";
26353
- const structuredContentInnerClass$1 = "sd-structured-content__content";
26354
- const StructuredContent = Node$1.create({
26355
- name: "structuredContent",
26356
- group: "inline structuredContent",
26772
+ const TotalPageCount = Node$1.create({
26773
+ name: "total-page-number",
26774
+ group: "inline",
26357
26775
  inline: true,
26358
- content: "inline*",
26359
- isolating: true,
26360
- atom: false,
26361
- // false - has editable content.
26362
- draggable: true,
26776
+ atom: true,
26777
+ draggable: false,
26778
+ selectable: false,
26779
+ content: "text*",
26363
26780
  addOptions() {
26364
26781
  return {
26365
26782
  htmlAttributes: {
26366
- class: structuredContentClass$1,
26367
- "aria-label": "Structured content node"
26783
+ contenteditable: false,
26784
+ "data-id": "auto-total-pages",
26785
+ "aria-label": "Total page count node",
26786
+ class: "sd-editor-auto-total-pages"
26368
26787
  }
26369
26788
  };
26370
26789
  },
26371
26790
  addAttributes() {
26372
26791
  return {
26373
- id: {
26792
+ marksAsAttrs: {
26374
26793
  default: null,
26375
- parseDOM: (elem) => elem.getAttribute("data-id"),
26376
- renderDOM: (attrs) => {
26377
- if (!attrs.id) return {};
26378
- return { "data-id": attrs.id };
26379
- }
26380
- },
26381
- sdtPr: {
26382
26794
  rendered: false
26383
26795
  }
26384
26796
  };
26385
26797
  },
26798
+ addNodeView() {
26799
+ return ({ node, editor, getPos, decorations }) => {
26800
+ const htmlAttributes = this.options.htmlAttributes;
26801
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
26802
+ };
26803
+ },
26386
26804
  parseDOM() {
26387
- return [{ tag: "span[data-structured-content]" }];
26805
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
26388
26806
  },
26389
26807
  renderDOM({ htmlAttributes }) {
26390
- return [
26391
- "span",
26392
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26393
- "data-structured-content": ""
26394
- }),
26395
- 0
26396
- ];
26808
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
26397
26809
  },
26398
- addNodeView() {
26399
- return (props) => {
26400
- return new StructuredContentInlineView({ ...props });
26810
+ addCommands() {
26811
+ return {
26812
+ /**
26813
+ * Insert total page count
26814
+ * @category Command
26815
+ * @returns {Function} Command function
26816
+ * @example
26817
+ * editor.commands.addTotalPageCount()
26818
+ * @note Only works in header/footer contexts
26819
+ */
26820
+ addTotalPageCount: () => ({ tr, dispatch, state, editor }) => {
26821
+ const { options } = editor;
26822
+ if (!options.isHeaderOrFooter) return false;
26823
+ const { schema } = state;
26824
+ const pageNumberType = schema.nodes?.["total-page-number"];
26825
+ if (!pageNumberType) return false;
26826
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
26827
+ const pageNumberNode = {
26828
+ type: "total-page-number",
26829
+ content: [{ type: "text", text: String(currentPages) }]
26830
+ };
26831
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
26832
+ if (dispatch) {
26833
+ tr.replaceSelectionWith(pageNode, false);
26834
+ }
26835
+ return true;
26836
+ }
26837
+ };
26838
+ },
26839
+ addShortcuts() {
26840
+ return {
26841
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
26401
26842
  };
26402
26843
  }
26403
26844
  });
26404
- class StructuredContentBlockView extends StructuredContentViewBase {
26405
- constructor(props) {
26406
- super(props);
26407
- }
26408
- mount() {
26409
- this.buildView();
26410
- }
26411
- get contentDOM() {
26412
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
26413
- return contentElement || null;
26414
- }
26415
- createElement() {
26416
- const element = document.createElement("div");
26417
- element.classList.add(structuredContentClass);
26418
- element.setAttribute("data-structured-content-block", "");
26419
- const contentElement = document.createElement("div");
26420
- contentElement.classList.add(structuredContentInnerClass);
26421
- element.append(contentElement);
26422
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26423
- updateDOMAttributes(element, { ...domAttrs });
26424
- return { element, contentElement };
26425
- }
26426
- buildView() {
26427
- const { element } = this.createElement();
26428
- const dragHandle = this.createDragHandle();
26429
- element.prepend(dragHandle);
26430
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
26431
- this.root = element;
26845
+ const getNodeAttributes = (nodeName, editor) => {
26846
+ switch (nodeName) {
26847
+ case "page-number":
26848
+ return {
26849
+ text: editor.options.currentPageNumber || "1",
26850
+ className: "sd-editor-auto-page-number",
26851
+ dataId: "auto-page-number",
26852
+ ariaLabel: "Page number node"
26853
+ };
26854
+ case "total-page-number":
26855
+ return {
26856
+ text: editor.options.parentEditor?.currentTotalPages || "1",
26857
+ className: "sd-editor-auto-total-pages",
26858
+ dataId: "auto-total-pages",
26859
+ ariaLabel: "Total page count node"
26860
+ };
26861
+ default:
26862
+ return {};
26432
26863
  }
26433
- updateView() {
26434
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
26435
- updateDOMAttributes(this.dom, { ...domAttrs });
26864
+ };
26865
+ class AutoPageNumberNodeView {
26866
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
26867
+ __privateAdd(this, _AutoPageNumberNodeView_instances);
26868
+ this.node = node;
26869
+ this.editor = editor;
26870
+ this.view = editor.view;
26871
+ this.getPos = getPos;
26872
+ this.editor = editor;
26873
+ this.dom = __privateMethod(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
26436
26874
  }
26437
- update(node, decorations, innerDecorations) {
26438
- const result = super.update(node, decorations, innerDecorations);
26439
- if (!result) return false;
26440
- this.updateView();
26875
+ update(node) {
26876
+ const incomingType = node?.type?.name;
26877
+ const currentType = this.node?.type?.name;
26878
+ if (!incomingType || incomingType !== currentType) return false;
26879
+ this.node = node;
26441
26880
  return true;
26442
26881
  }
26443
26882
  }
26444
- const structuredContentClass = "sd-structured-content-block";
26445
- const structuredContentInnerClass = "sd-structured-content-block__content";
26446
- const StructuredContentBlock = Node$1.create({
26447
- name: "structuredContentBlock",
26448
- group: "block structuredContent",
26449
- content: "block*",
26883
+ _AutoPageNumberNodeView_instances = new WeakSet();
26884
+ renderDom_fn = function(node, htmlAttributes) {
26885
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
26886
+ const content = document.createTextNode(String(attrs.text));
26887
+ const nodeContent = document.createElement("span");
26888
+ nodeContent.className = attrs.className;
26889
+ nodeContent.setAttribute("data-id", attrs.dataId);
26890
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
26891
+ const currentPos = this.getPos();
26892
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
26893
+ __privateMethod(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
26894
+ Object.assign(nodeContent.style, styles);
26895
+ nodeContent.appendChild(content);
26896
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
26897
+ if (value) nodeContent.setAttribute(key2, value);
26898
+ });
26899
+ return nodeContent;
26900
+ };
26901
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
26902
+ setTimeout(() => {
26903
+ const { state } = this.editor;
26904
+ const { dispatch } = this.view;
26905
+ const node = state.doc.nodeAt(pos);
26906
+ if (!node || node.isText) return;
26907
+ const currentMarks = node.attrs.marksAsAttrs || [];
26908
+ const newMarks = marks.map((m) => ({ type: m.type.name, attrs: m.attrs }));
26909
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
26910
+ if (isEqual) return;
26911
+ const newAttrs = {
26912
+ ...node.attrs,
26913
+ marksAsAttrs: newMarks
26914
+ };
26915
+ const tr = state.tr.setNodeMarkup(pos, void 0, newAttrs);
26916
+ dispatch(tr);
26917
+ }, 0);
26918
+ };
26919
+ const getMarksFromNeighbors = (currentPos, view) => {
26920
+ const $pos = view.state.doc.resolve(currentPos);
26921
+ const styles = {};
26922
+ const marks = [];
26923
+ const before = $pos.nodeBefore;
26924
+ if (before) {
26925
+ Object.assign(styles, processMarks(before.marks));
26926
+ marks.push(...before.marks);
26927
+ }
26928
+ const after = $pos.nodeAfter;
26929
+ if (after) {
26930
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
26931
+ marks.push(...after.marks);
26932
+ }
26933
+ return {
26934
+ styles,
26935
+ marks
26936
+ };
26937
+ };
26938
+ const processMarks = (marks) => {
26939
+ const styles = {};
26940
+ marks.forEach((mark) => {
26941
+ const { type, attrs } = mark;
26942
+ switch (type.name) {
26943
+ case "textStyle":
26944
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
26945
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
26946
+ if (attrs.color) styles["color"] = attrs.color;
26947
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
26948
+ break;
26949
+ case "bold":
26950
+ styles["font-weight"] = "bold";
26951
+ break;
26952
+ case "italic":
26953
+ styles["font-style"] = "italic";
26954
+ break;
26955
+ case "underline":
26956
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
26957
+ break;
26958
+ case "strike":
26959
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
26960
+ break;
26961
+ default:
26962
+ if (attrs?.style) {
26963
+ Object.entries(attrs.style).forEach(([key2, value]) => {
26964
+ styles[key2] = value;
26965
+ });
26966
+ }
26967
+ break;
26968
+ }
26969
+ });
26970
+ return styles;
26971
+ };
26972
+ const ShapeContainer = Node$1.create({
26973
+ name: "shapeContainer",
26974
+ group: "block",
26975
+ content: "block+",
26450
26976
  isolating: true,
26451
- atom: false,
26452
- // false - has editable content.
26453
- draggable: true,
26454
26977
  addOptions() {
26455
26978
  return {
26456
26979
  htmlAttributes: {
26457
- class: structuredContentClass,
26458
- "aria-label": "Structured content block node"
26980
+ class: "sd-editor-shape-container",
26981
+ "aria-label": "Shape container node"
26459
26982
  }
26460
26983
  };
26461
26984
  },
26462
26985
  addAttributes() {
26463
26986
  return {
26464
- id: {
26987
+ fillcolor: {
26988
+ renderDOM: (attrs) => {
26989
+ if (!attrs.fillcolor) return {};
26990
+ return {
26991
+ style: `background-color: ${attrs.fillcolor}`
26992
+ };
26993
+ }
26994
+ },
26995
+ sdBlockId: {
26465
26996
  default: null,
26466
- parseDOM: (elem) => elem.getAttribute("data-id"),
26997
+ keepOnSplit: false,
26998
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
26467
26999
  renderDOM: (attrs) => {
26468
- if (!attrs.id) return {};
26469
- return { "data-id": attrs.id };
27000
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26470
27001
  }
26471
27002
  },
26472
- sdtPr: {
27003
+ style: {
27004
+ renderDOM: (attrs) => {
27005
+ if (!attrs.style) return {};
27006
+ return {
27007
+ style: attrs.style
27008
+ };
27009
+ }
27010
+ },
27011
+ wrapAttributes: {
27012
+ rendered: false
27013
+ },
27014
+ attributes: {
26473
27015
  rendered: false
26474
27016
  }
26475
27017
  };
26476
27018
  },
26477
27019
  parseDOM() {
26478
- return [{ tag: "div[data-structured-content-block]" }];
27020
+ return [
27021
+ {
27022
+ tag: `div[data-type="${this.name}"]`
27023
+ }
27024
+ ];
26479
27025
  },
26480
27026
  renderDOM({ htmlAttributes }) {
26481
27027
  return [
26482
27028
  "div",
26483
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
26484
- "data-structured-content-block": ""
26485
- }),
27029
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
26486
27030
  0
26487
27031
  ];
26488
- },
26489
- addNodeView() {
26490
- return (props) => {
26491
- return new StructuredContentBlockView({ ...props });
26492
- };
26493
27032
  }
26494
27033
  });
26495
- class DocumentSectionView {
26496
- constructor(node, getPos, decorations, editor) {
26497
- __privateAdd(this, _DocumentSectionView_instances);
26498
- this.node = node;
26499
- this.editor = editor;
26500
- this.decorations = decorations;
26501
- this.view = editor.view;
26502
- this.getPos = getPos;
26503
- __privateMethod(this, _DocumentSectionView_instances, init_fn3).call(this);
26504
- }
26505
- }
26506
- _DocumentSectionView_instances = new WeakSet();
26507
- init_fn3 = function() {
26508
- const { attrs } = this.node;
26509
- const { id, title, description } = attrs;
26510
- this.dom = document.createElement("div");
26511
- this.dom.className = "sd-document-section-block";
26512
- this.dom.setAttribute("data-id", id);
26513
- this.dom.setAttribute("data-title", title);
26514
- this.dom.setAttribute("data-description", description);
26515
- this.dom.setAttribute("aria-label", "Document section");
26516
- __privateMethod(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
26517
- this.contentDOM = document.createElement("div");
26518
- this.contentDOM.className = "sd-document-section-block-content";
26519
- this.contentDOM.setAttribute("contenteditable", "true");
26520
- this.dom.appendChild(this.contentDOM);
26521
- };
26522
- addToolTip_fn = function() {
26523
- const { title } = this.node.attrs;
26524
- this.infoDiv = document.createElement("div");
26525
- this.infoDiv.className = "sd-document-section-block-info";
26526
- const textSpan = document.createElement("span");
26527
- textSpan.textContent = title || "Document section";
26528
- this.infoDiv.appendChild(textSpan);
26529
- this.infoDiv.setAttribute("contenteditable", "false");
26530
- this.dom.appendChild(this.infoDiv);
26531
- };
26532
- const getAllSections = (editor) => {
26533
- if (!editor) return [];
26534
- const type = editor.schema.nodes.documentSection;
26535
- if (!type) return [];
26536
- const sections = [];
26537
- const { state } = editor;
26538
- state.doc.descendants((node, pos) => {
26539
- if (node.type.name === type.name) {
26540
- sections.push({ node, pos });
26541
- }
26542
- });
26543
- return sections;
26544
- };
26545
- const exportSectionsToHTML = (editor) => {
26546
- const sections = getAllSections(editor);
26547
- const processedSections = /* @__PURE__ */ new Set();
26548
- const result = [];
26549
- sections.forEach(({ node }) => {
26550
- const { attrs } = node;
26551
- const { id, title, description } = attrs;
26552
- if (processedSections.has(id)) return;
26553
- processedSections.add(id);
26554
- const html = getHTMLFromNode(node, editor);
26555
- result.push({
26556
- id,
26557
- title,
26558
- description,
26559
- html
26560
- });
26561
- });
26562
- return result;
26563
- };
26564
- const getHTMLFromNode = (node, editor) => {
26565
- const tempDocument = document.implementation.createHTMLDocument();
26566
- const container = tempDocument.createElement("div");
26567
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
26568
- container.appendChild(fragment);
26569
- let html = container.innerHTML;
26570
- return html;
26571
- };
26572
- const exportSectionsToJSON = (editor) => {
26573
- const sections = getAllSections(editor);
26574
- const processedSections = /* @__PURE__ */ new Set();
26575
- const result = [];
26576
- sections.forEach(({ node }) => {
26577
- const { attrs } = node;
26578
- const { id, title, description } = attrs;
26579
- if (processedSections.has(id)) return;
26580
- processedSections.add(id);
26581
- result.push({
26582
- id,
26583
- title,
26584
- description,
26585
- content: node.toJSON()
26586
- });
26587
- });
26588
- return result;
26589
- };
26590
- const getLinkedSectionEditor = (id, options, editor) => {
26591
- const sections = getAllSections(editor);
26592
- const section = sections.find((s) => s.node.attrs.id === id);
26593
- if (!section) return null;
26594
- const child = editor.createChildEditor({
26595
- ...options,
26596
- onUpdate: ({ editor: childEditor, transaction }) => {
26597
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
26598
- if (isFromtLinkedParent) return;
26599
- const updatedContent = childEditor.state.doc.content;
26600
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26601
- if (!sectionNode) return;
26602
- const { pos, node } = sectionNode;
26603
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
26604
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
26605
- tr.setMeta("fromLinkedChild", true);
26606
- editor.view.dispatch(tr);
26607
- }
26608
- });
26609
- editor.on("update", ({ transaction }) => {
26610
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
26611
- if (isFromLinkedChild) return;
26612
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
26613
- if (!sectionNode) return;
26614
- const sectionContent = sectionNode.node.content;
26615
- const json = {
26616
- type: "doc",
26617
- content: sectionContent.content.map((node) => node.toJSON())
26618
- };
26619
- const childTr = child.state.tr;
26620
- childTr.setMeta("fromLinkedParent", true);
26621
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
26622
- child.view.dispatch(childTr);
26623
- });
26624
- return child;
26625
- };
26626
- const SectionHelpers = {
26627
- getAllSections,
26628
- exportSectionsToHTML,
26629
- exportSectionsToJSON,
26630
- getLinkedSectionEditor
26631
- };
26632
- const DocumentSection = Node$1.create({
26633
- name: "documentSection",
27034
+ const ShapeTextbox = Node$1.create({
27035
+ name: "shapeTextbox",
26634
27036
  group: "block",
26635
- content: "block*",
26636
- atom: true,
27037
+ content: "paragraph* block*",
26637
27038
  isolating: true,
26638
27039
  addOptions() {
26639
27040
  return {
26640
27041
  htmlAttributes: {
26641
- class: "sd-document-section-block",
26642
- "aria-label": "Structured content block"
27042
+ class: "sd-editor-shape-textbox",
27043
+ "aria-label": "Shape textbox node"
26643
27044
  }
26644
27045
  };
26645
27046
  },
26646
- parseDOM() {
26647
- return [
26648
- {
26649
- tag: "div.sd-document-section-block",
26650
- priority: 60
26651
- }
26652
- ];
26653
- },
26654
- renderDOM({ htmlAttributes }) {
26655
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
26656
- },
26657
27047
  addAttributes() {
26658
27048
  return {
26659
- id: {},
26660
27049
  sdBlockId: {
26661
27050
  default: null,
26662
27051
  keepOnSplit: false,
@@ -26665,212 +27054,131 @@ const DocumentSection = Node$1.create({
26665
27054
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
26666
27055
  }
26667
27056
  },
26668
- title: {},
26669
- description: {},
26670
- sectionType: {},
26671
- isLocked: { default: false }
27057
+ attributes: {
27058
+ rendered: false
27059
+ }
26672
27060
  };
26673
27061
  },
26674
- addNodeView() {
26675
- return ({ node, editor, getPos, decorations }) => {
26676
- return new DocumentSectionView(node, getPos, decorations, editor);
27062
+ parseDOM() {
27063
+ return [
27064
+ {
27065
+ tag: `div[data-type="${this.name}"]`
27066
+ }
27067
+ ];
27068
+ },
27069
+ renderDOM({ htmlAttributes }) {
27070
+ return [
27071
+ "div",
27072
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
27073
+ 0
27074
+ ];
27075
+ }
27076
+ });
27077
+ const ContentBlock = Node$1.create({
27078
+ name: "contentBlock",
27079
+ group: "inline",
27080
+ content: "",
27081
+ isolating: true,
27082
+ atom: true,
27083
+ inline: true,
27084
+ addOptions() {
27085
+ return {
27086
+ htmlAttributes: {
27087
+ contenteditable: false
27088
+ }
26677
27089
  };
26678
27090
  },
26679
- addCommands() {
27091
+ addAttributes() {
26680
27092
  return {
26681
- /**
26682
- * Create a lockable content section
26683
- * @category Command
26684
- * @param {SectionCreate} [options={}] - Section configuration
26685
- * @example
26686
- * editor.commands.createDocumentSection({
26687
- * id: 1,
26688
- * title: 'Terms & Conditions',
26689
- * isLocked: true,
26690
- * html: '<p>Legal content...</p>'
26691
- * })
26692
- */
26693
- createDocumentSection: (options = {}) => ({ tr, state, dispatch, editor }) => {
26694
- const { selection } = state;
26695
- let { from: from2, to } = selection;
26696
- let content = selection.content().content;
26697
- const { html: optionsHTML, json: optionsJSON } = options;
26698
- if (optionsHTML) {
26699
- const html = htmlHandler(optionsHTML, this.editor);
26700
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
26701
- content = doc2.content;
26702
- }
26703
- if (optionsJSON) {
26704
- content = this.editor.schema.nodeFromJSON(optionsJSON);
26705
- }
26706
- if (!content?.content?.length) {
26707
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
26708
- }
26709
- if (!options.id) {
26710
- const allSections = SectionHelpers.getAllSections(editor);
26711
- options.id = allSections.length + 1;
26712
- }
26713
- if (!options.title) {
26714
- options.title = "Document section";
26715
- }
26716
- const node = this.type.createAndFill(options, content);
26717
- if (!node) return false;
26718
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
26719
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
26720
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
26721
- from2 = insertPos2;
26722
- to = insertPos2;
26723
- }
26724
- tr.replaceRangeWith(from2, to, node);
26725
- const nodeEnd = from2 + node.nodeSize;
26726
- let shouldInsertParagraph = true;
26727
- let insertPos = nodeEnd;
26728
- if (nodeEnd >= tr.doc.content.size) {
26729
- insertPos = tr.doc.content.size;
26730
- if (insertPos > 0) {
26731
- const $endPos = tr.doc.resolve(insertPos);
26732
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
26733
- shouldInsertParagraph = false;
26734
- }
26735
- }
26736
- }
26737
- if (shouldInsertParagraph) {
26738
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
26739
- tr.insert(insertPos, emptyParagraph);
26740
- }
26741
- if (dispatch) {
26742
- tr.setMeta("documentSection", { action: "create" });
26743
- dispatch(tr);
26744
- setTimeout(() => {
26745
- try {
26746
- const currentState = editor.state;
26747
- const docSize = currentState.doc.content.size;
26748
- let targetPos = from2 + node.nodeSize;
26749
- if (shouldInsertParagraph) {
26750
- targetPos += 1;
26751
- }
26752
- targetPos = Math.min(targetPos, docSize);
26753
- if (targetPos < docSize && targetPos > 0) {
26754
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
26755
- const newTr = currentState.tr.setSelection(newSelection);
26756
- editor.view.dispatch(newTr);
26757
- }
26758
- } catch (e) {
26759
- console.warn("Could not set delayed selection:", e);
26760
- }
26761
- }, 0);
27093
+ horizontalRule: {
27094
+ default: false,
27095
+ renderDOM: ({ horizontalRule }) => {
27096
+ if (!horizontalRule) return {};
27097
+ return { "data-horizontal-rule": "true" };
26762
27098
  }
26763
- return true;
26764
27099
  },
26765
- /**
26766
- * Remove section wrapper at cursor, preserving its content
26767
- * @category Command
26768
- * @example
26769
- * editor.commands.removeSectionAtSelection()
26770
- * @note Content stays in document, only section wrapper is removed
26771
- */
26772
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
26773
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
26774
- if (!sdtNode) return false;
26775
- const { node, pos } = sdtNode;
26776
- const nodeStart = pos;
26777
- const nodeEnd = nodeStart + node.nodeSize;
26778
- const contentToPreserve = node.content;
26779
- tr.delete(nodeStart, nodeEnd);
26780
- if (contentToPreserve.size > 0) {
26781
- tr.insert(nodeStart, contentToPreserve);
26782
- }
26783
- const newPos = Math.min(nodeStart, tr.doc.content.size);
26784
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
26785
- if (dispatch) {
26786
- tr.setMeta("documentSection", { action: "delete" });
26787
- dispatch(tr);
27100
+ size: {
27101
+ default: null,
27102
+ renderDOM: ({ size }) => {
27103
+ if (!size) return {};
27104
+ let style = "";
27105
+ if (size.top) style += `top: ${size.top}px; `;
27106
+ if (size.left) style += `left: ${size.left}px; `;
27107
+ if (size.width) style += `width: ${size.width.toString().endsWith("%") ? size.width : `${size.width}px`}; `;
27108
+ if (size.height)
27109
+ style += `height: ${size.height.toString().endsWith("%") ? size.height : `${size.height}px`}; `;
27110
+ return { style };
26788
27111
  }
26789
- return true;
26790
27112
  },
26791
- /**
26792
- * Delete section and all its content
26793
- * @category Command
26794
- * @param {number} id - Section to delete
26795
- * @example
26796
- * editor.commands.removeSectionById(123)
26797
- */
26798
- removeSectionById: (id) => ({ tr, dispatch }) => {
26799
- const sections = SectionHelpers.getAllSections(this.editor);
26800
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
26801
- if (!sectionToRemove) return false;
26802
- const { pos, node } = sectionToRemove;
26803
- const nodeStart = pos;
26804
- const nodeEnd = nodeStart + node.nodeSize;
26805
- tr.delete(nodeStart, nodeEnd);
26806
- if (dispatch) {
26807
- tr.setMeta("documentSection", { action: "delete", id });
26808
- dispatch(tr);
27113
+ background: {
27114
+ default: null,
27115
+ renderDOM: (attrs) => {
27116
+ if (!attrs.background) return {};
27117
+ return {
27118
+ style: `background-color: ${attrs.background}`
27119
+ };
26809
27120
  }
26810
- return true;
26811
27121
  },
27122
+ drawingContent: {
27123
+ rendered: false
27124
+ },
27125
+ attributes: {
27126
+ rendered: false
27127
+ }
27128
+ };
27129
+ },
27130
+ parseDOM() {
27131
+ return [
27132
+ {
27133
+ tag: `div[data-type="${this.name}"]`
27134
+ }
27135
+ ];
27136
+ },
27137
+ renderDOM({ htmlAttributes }) {
27138
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
27139
+ },
27140
+ addCommands() {
27141
+ return {
26812
27142
  /**
26813
- * Lock section against edits
27143
+ * Insert a horizontal rule
26814
27144
  * @category Command
26815
- * @param {number} id - Section to lock
26816
27145
  * @example
26817
- * editor.commands.lockSectionById(123)
27146
+ * editor.commands.insertHorizontalRule()
27147
+ * @note Creates a visual separator between content sections
26818
27148
  */
26819
- lockSectionById: (id) => ({ tr, dispatch }) => {
26820
- const sections = SectionHelpers.getAllSections(this.editor);
26821
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
26822
- if (!sectionToLock) return false;
26823
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
26824
- if (dispatch) {
26825
- tr.setMeta("documentSection", { action: "lock", id });
26826
- dispatch(tr);
26827
- }
26828
- return true;
27149
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
27150
+ return commands2.insertContent({
27151
+ type: this.name,
27152
+ attrs: {
27153
+ horizontalRule: true,
27154
+ size: { width: "100%", height: 2 },
27155
+ background: "#e5e7eb"
27156
+ }
27157
+ });
26829
27158
  },
26830
27159
  /**
26831
- * Modify section attributes or content
27160
+ * Insert a content block
26832
27161
  * @category Command
26833
- * @param {SectionUpdate} options - Changes to apply
27162
+ * @param {ContentBlockConfig} config - Block configuration
26834
27163
  * @example
26835
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
26836
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
26837
- * editor.commands.updateSectionById({
26838
- * id: 123,
26839
- * html: '<p>Updated</p>',
26840
- * attrs: { title: 'New Title' }
27164
+ * // Insert a spacer block
27165
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
27166
+ *
27167
+ * @example
27168
+ * // Insert a colored divider
27169
+ * editor.commands.insertContentBlock({
27170
+ * size: { width: '50%', height: 3 },
27171
+ * background: '#3b82f6'
26841
27172
  * })
27173
+ * @note Used for spacing, dividers, and special inline content
26842
27174
  */
26843
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
26844
- const sections = SectionHelpers.getAllSections(editor || this.editor);
26845
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
26846
- if (!sectionToUpdate) return false;
26847
- const { pos, node } = sectionToUpdate;
26848
- let newContent = null;
26849
- if (html) {
26850
- const htmlDoc = htmlHandler(html, editor || this.editor);
26851
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
26852
- newContent = doc2.content;
26853
- }
26854
- if (json) {
26855
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
26856
- }
26857
- if (!newContent) {
26858
- newContent = node.content;
26859
- }
26860
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
26861
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
26862
- if (dispatch) {
26863
- tr.setMeta("documentSection", { action: "update", id, attrs });
26864
- dispatch(tr);
26865
- }
26866
- return true;
27175
+ insertContentBlock: (config) => ({ commands: commands2 }) => {
27176
+ return commands2.insertContent({
27177
+ type: this.name,
27178
+ attrs: config
27179
+ });
26867
27180
  }
26868
27181
  };
26869
- },
26870
- addHelpers() {
26871
- return {
26872
- ...SectionHelpers
26873
- };
26874
27182
  }
26875
27183
  });
26876
27184
  const { findChildren } = helpers;
@@ -33438,7 +33746,8 @@ const nodeResizer = (nodeNames = ["image"], editor) => {
33438
33746
  const prevSelection = prevState.selection;
33439
33747
  if (selection.from !== prevSelection.from || selection.to !== prevSelection.to) {
33440
33748
  setTimeout(() => {
33441
- const selectedResizableWrapper = document.querySelector(".sd-editor-resizable-wrapper");
33749
+ const searchRoot = editorView?.dom;
33750
+ const selectedResizableWrapper = searchRoot?.querySelector(".sd-editor-resizable-wrapper");
33442
33751
  if (selectedResizableWrapper) {
33443
33752
  showResizeHandles(view2, selectedResizableWrapper);
33444
33753
  } else {
@@ -33719,6 +34028,7 @@ const getStarterExtensions = () => {
33719
34028
  Search,
33720
34029
  StructuredContent,
33721
34030
  StructuredContentBlock,
34031
+ StructuredContentCommands,
33722
34032
  DocumentSection,
33723
34033
  NodeResizer,
33724
34034
  CustomSelection,
@@ -33752,6 +34062,7 @@ export {
33752
34062
  SectionHelpers as o,
33753
34063
  getAllowedImageDimensions as p,
33754
34064
  replaceSelectionWithImagePlaceholder as r,
34065
+ shouldBypassContextMenu as s,
33755
34066
  useHighContrastMode as u,
33756
34067
  yUndoPluginKey as y
33757
34068
  };