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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/chunks/{PdfViewer-C1tow4Hk.es.js → PdfViewer-BNWaI4WI.es.js} +1 -1
  2. package/dist/chunks/{PdfViewer-BBQm50V3.cjs → PdfViewer-DpkgwUPi.cjs} +1 -1
  3. package/dist/chunks/{index-BkpP-c9A.cjs → index-BbGPYtNy.cjs} +16 -9
  4. package/dist/chunks/{index-CJUwCOxd.es.js → index-DWKEKmiB.es.js} +16 -9
  5. package/dist/chunks/{super-editor.es-Bg0-F_x5.es.js → super-editor.es-BVxfhpAJ.es.js} +1562 -1267
  6. package/dist/chunks/{super-editor.es-D6u8E2n4.cjs → super-editor.es-BoUJEZaF.cjs} +1562 -1267
  7. package/dist/core/types/index.d.ts.map +1 -1
  8. package/dist/stores/comments-store.d.ts +4 -1
  9. package/dist/stores/comments-store.d.ts.map +1 -1
  10. package/dist/style.css +45 -44
  11. package/dist/super-editor/ai-writer.es.js +2 -2
  12. package/dist/super-editor/chunks/{converter-BgedUNCW.js → converter-C-yWLpFM.js} +150 -105
  13. package/dist/super-editor/chunks/{docx-zipper-ByLK3trM.js → docx-zipper-CmGlSUQM.js} +1 -1
  14. package/dist/super-editor/chunks/{editor-M2L6bDyC.js → editor-BBnC1DzI.js} +1532 -1280
  15. package/dist/super-editor/chunks/{toolbar-DsPK5x8a.js → toolbar-QJANo61B.js} +2 -2
  16. package/dist/super-editor/converter.es.js +1 -1
  17. package/dist/super-editor/docx-zipper.es.js +2 -2
  18. package/dist/super-editor/editor.es.js +3 -3
  19. package/dist/super-editor/file-zipper.es.js +1 -1
  20. package/dist/super-editor/src/core/helpers/generateDocxRandomId.d.ts +5 -0
  21. package/dist/super-editor/src/extensions/index.d.ts +2 -1
  22. package/dist/super-editor/src/extensions/structured-content/index.d.ts +1 -0
  23. package/dist/super-editor/src/extensions/structured-content/structured-content-commands.d.ts +67 -0
  24. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentBlockTags.d.ts +6 -0
  25. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentInlineTags.d.ts +6 -0
  26. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTags.d.ts +6 -0
  27. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/getStructuredContentTagsById.d.ts +7 -0
  28. package/dist/super-editor/src/extensions/structured-content/structuredContentHelpers/index.d.ts +4 -0
  29. package/dist/super-editor/style.css +1 -0
  30. package/dist/super-editor/super-editor.es.js +7 -7
  31. package/dist/super-editor/toolbar.es.js +2 -2
  32. package/dist/super-editor.cjs +1 -1
  33. package/dist/super-editor.es.js +1 -1
  34. package/dist/superdoc.cjs +2 -2
  35. package/dist/superdoc.es.js +2 -2
  36. package/dist/superdoc.umd.js +1573 -1271
  37. package/dist/superdoc.umd.js.map +1 -1
  38. package/package.json +1 -1
@@ -22730,6 +22730,10 @@
22730
22730
  }
22731
22731
  return id.join("");
22732
22732
  }
22733
+ function generateRandomSigned32BitIntStrId() {
22734
+ const val = Math.floor(Math.random() * 2147483647);
22735
+ return val.toString();
22736
+ }
22733
22737
  function generateRandom32BitHex() {
22734
22738
  const val = Math.floor(Math.random() * 2147483647);
22735
22739
  return val.toString(16).toUpperCase().padStart(8, "0");
@@ -30308,6 +30312,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
30308
30312
  generateDocxListAttributes,
30309
30313
  generateDocxRandomId,
30310
30314
  generateRandom32BitHex,
30315
+ generateRandomSigned32BitIntStrId,
30311
30316
  getActiveFormatting,
30312
30317
  getExtensionConfigField,
30313
30318
  getMarkRange,
@@ -35902,6 +35907,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
35902
35907
  const node = nodes[0];
35903
35908
  const sdtPr = node.elements.find((el) => el.name === "w:sdtPr");
35904
35909
  const sdtContent = node.elements.find((el) => el.name === "w:sdtContent");
35910
+ const id = sdtPr?.elements?.find((el) => el.name === "w:id");
35911
+ const tag = sdtPr?.elements?.find((el) => el.name === "w:tag");
35912
+ const alias = sdtPr?.elements?.find((el) => el.name === "w:alias");
35905
35913
  if (!sdtContent) {
35906
35914
  return null;
35907
35915
  }
@@ -35913,15 +35921,16 @@ Please report this to https://github.com/markedjs/marked.`, e) {
35913
35921
  nodes: sdtContent.elements,
35914
35922
  path: [...params2.path || [], sdtContent]
35915
35923
  });
35916
- let sdtContentType = "structuredContent";
35917
- if (paragraph || table) {
35918
- sdtContentType = "structuredContentBlock";
35919
- }
35924
+ const isBlockNode2 = paragraph || table;
35925
+ const sdtContentType = isBlockNode2 ? "structuredContentBlock" : "structuredContent";
35920
35926
  let result = {
35921
35927
  type: sdtContentType,
35922
35928
  content: translatedContent,
35923
35929
  marks,
35924
35930
  attrs: {
35931
+ id: id?.attributes?.["w:val"] || null,
35932
+ tag: tag?.attributes?.["w:val"] || null,
35933
+ alias: alias?.attributes?.["w:val"] || null,
35925
35934
  sdtPr
35926
35935
  }
35927
35936
  };
@@ -38179,21 +38188,55 @@ Please report this to https://github.com/markedjs/marked.`, e) {
38179
38188
  };
38180
38189
  function translateStructuredContent(params2) {
38181
38190
  const { node } = params2;
38182
- const { attrs = {} } = node;
38183
38191
  const childContent = translateChildNodes({ ...params2, nodes: node.content });
38184
- const nodeElements = [
38185
- {
38186
- name: "w:sdtContent",
38187
- elements: childContent
38188
- }
38189
- ];
38190
- nodeElements.unshift(attrs.sdtPr);
38192
+ const sdtContent = { name: "w:sdtContent", elements: childContent };
38193
+ const sdtPr = generateSdtPrTagForStructuredContent({ node });
38194
+ const nodeElements = [sdtPr, sdtContent];
38191
38195
  const result = {
38192
38196
  name: "w:sdt",
38193
38197
  elements: nodeElements
38194
38198
  };
38195
38199
  return result;
38196
38200
  }
38201
+ function generateSdtPrTagForStructuredContent({ node }) {
38202
+ const { attrs = {} } = node;
38203
+ const id = {
38204
+ name: "w:id",
38205
+ type: "element",
38206
+ attributes: { "w:val": attrs.id }
38207
+ };
38208
+ const alias = {
38209
+ name: "w:alias",
38210
+ type: "element",
38211
+ attributes: { "w:val": attrs.alias }
38212
+ };
38213
+ const tag = {
38214
+ name: "w:tag",
38215
+ type: "element",
38216
+ attributes: { "w:val": attrs.tag }
38217
+ };
38218
+ const resultElements = [];
38219
+ if (attrs.id) resultElements.push(id);
38220
+ if (attrs.alias) resultElements.push(alias);
38221
+ if (attrs.tag) resultElements.push(tag);
38222
+ if (attrs.sdtPr) {
38223
+ const elements = attrs.sdtPr.elements || [];
38224
+ const elementsToExclude = ["w:id", "w:alias", "w:tag"];
38225
+ const restElements = elements.filter((el) => !elementsToExclude.includes(el.name));
38226
+ const result2 = {
38227
+ name: "w:sdtPr",
38228
+ type: "element",
38229
+ elements: [...resultElements, ...restElements]
38230
+ };
38231
+ return result2;
38232
+ }
38233
+ const result = {
38234
+ name: "w:sdtPr",
38235
+ type: "element",
38236
+ elements: resultElements
38237
+ };
38238
+ return result;
38239
+ }
38197
38240
  const XML_NODE_NAME$3 = "w:sdt";
38198
38241
  const SD_NODE_NAME$3 = ["fieldAnnotation", "structuredContent", "structuredContentBlock", "documentSection"];
38199
38242
  const validXmlAttributes$3 = [];
@@ -39254,7 +39297,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
39254
39297
  const pict = {
39255
39298
  name: "w:pict",
39256
39299
  attributes: {
39257
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
39300
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
39258
39301
  },
39259
39302
  elements: [shape]
39260
39303
  };
@@ -39321,7 +39364,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
39321
39364
  const pict = {
39322
39365
  name: "w:pict",
39323
39366
  attributes: {
39324
- "w14:anchorId": Math.floor(Math.random() * 4294967295).toString()
39367
+ "w14:anchorId": generateRandomSigned32BitIntStrId()
39325
39368
  },
39326
39369
  elements: [rect]
39327
39370
  };
@@ -40794,7 +40837,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
40794
40837
  gutter: "0"
40795
40838
  })
40796
40839
  });
40797
- function ensureSectionProperties(bodyNode, converter) {
40840
+ function ensureSectionProperties(bodyNode) {
40798
40841
  if (!bodyNode.elements) bodyNode.elements = [];
40799
40842
  let sectPr = bodyNode.elements.find((el) => el.name === "w:sectPr");
40800
40843
  if (!sectPr) {
@@ -44293,7 +44336,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
44293
44336
  var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
44294
44337
  var __privateSet = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
44295
44338
  var __privateMethod$1 = (obj, member, method) => (__accessCheck$1(obj, member, "access private method"), method);
44296
- 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;
44339
+ 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;
44297
44340
  var GOOD_LEAF_SIZE = 200;
44298
44341
  var RopeSequence = function RopeSequence2() {
44299
44342
  };
@@ -56368,28 +56411,25 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56368
56411
  if (emitParams) editor.emit("commentsUpdate", emitParams);
56369
56412
  return newTrackedChanges;
56370
56413
  };
56371
- const getTrackedChangeText = ({ state: state2, node, mark, marks, trackedChangeType, isDeletionInsertion }) => {
56414
+ const getTrackedChangeText = ({ nodes, mark, trackedChangeType, isDeletionInsertion }) => {
56372
56415
  let trackedChangeText = "";
56373
56416
  let deletionText = "";
56374
56417
  if (trackedChangeType === TrackInsertMarkName) {
56375
- trackedChangeText = node?.text ?? "";
56418
+ trackedChangeText = nodes.reduce((acc, node) => {
56419
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === mark.type.name)) return acc;
56420
+ acc += node?.text || node?.textContent || "";
56421
+ return acc;
56422
+ }, "");
56376
56423
  }
56377
56424
  if (trackedChangeType === TrackFormatMarkName) {
56378
56425
  trackedChangeText = translateFormatChangesToEnglish(mark.attrs);
56379
56426
  }
56380
56427
  if (trackedChangeType === TrackDeleteMarkName || isDeletionInsertion) {
56381
- deletionText = node?.text ?? "";
56382
- if (isDeletionInsertion) {
56383
- let { id } = marks.deletionMark.attrs;
56384
- let deletionNode = findNode$1(state2.doc, (node2) => {
56385
- const { marks: marks2 = [] } = node2;
56386
- const changeMarks = marks2.filter((mark2) => TRACK_CHANGE_MARKS.includes(mark2.type.name));
56387
- if (!changeMarks.length) return false;
56388
- const hasMatchingId = changeMarks.find((mark2) => mark2.attrs.id === id);
56389
- if (hasMatchingId) return true;
56390
- });
56391
- deletionText = deletionNode?.node.text ?? "";
56392
- }
56428
+ deletionText = nodes.reduce((acc, node) => {
56429
+ if (!node.marks.find((nodeMark) => nodeMark.type.name === TrackDeleteMarkName)) return acc;
56430
+ acc += node?.text || node?.textContent || "";
56431
+ return acc;
56432
+ }, "");
56393
56433
  }
56394
56434
  return {
56395
56435
  deletionText,
@@ -56404,20 +56444,17 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56404
56444
  const id = attrs.id;
56405
56445
  const node = nodes[0];
56406
56446
  const isDeletionInsertion = !!(marks.insertedMark && marks.deletionMark);
56407
- let existingNode;
56447
+ let nodesWithMark = [];
56408
56448
  newEditorState.doc.descendants((node2) => {
56409
56449
  const { marks: marks2 = [] } = node2;
56410
56450
  const changeMarks = marks2.filter((mark) => TRACK_CHANGE_MARKS.includes(mark.type.name));
56411
56451
  if (!changeMarks.length) return;
56412
56452
  const hasMatchingId = changeMarks.find((mark) => mark.attrs.id === id);
56413
- if (hasMatchingId) existingNode = node2;
56414
- if (existingNode) return false;
56453
+ if (hasMatchingId) nodesWithMark.push(node2);
56415
56454
  });
56416
56455
  const { deletionText, trackedChangeText } = getTrackedChangeText({
56417
- state: newEditorState,
56418
- node: existingNode || node,
56456
+ nodes: nodesWithMark.length ? nodesWithMark : [node],
56419
56457
  mark: trackedMark,
56420
- marks,
56421
56458
  trackedChangeType,
56422
56459
  isDeletionInsertion
56423
56460
  });
@@ -56445,14 +56482,6 @@ Please report this to https://github.com/markedjs/marked.`, e) {
56445
56482
  else if (event === "update") params2.event = comments_module_events$1.UPDATE;
56446
56483
  return params2;
56447
56484
  };
56448
- function findNode$1(node, predicate) {
56449
- let found2 = null;
56450
- node.descendants((node2, pos) => {
56451
- if (predicate(node2)) found2 = { node: node2, pos };
56452
- if (found2) return false;
56453
- });
56454
- return found2;
56455
- }
56456
56485
  function findRangeById(doc2, id) {
56457
56486
  let from2 = null, to = null;
56458
56487
  doc2.descendants((node, pos) => {
@@ -61080,196 +61109,1191 @@ Please report this to https://github.com/markedjs/marked.`, e) {
61080
61109
  return this.editor.options.isHeadless ? [] : [slashMenuPlugin];
61081
61110
  }
61082
61111
  });
61083
- const Document = Node$1.create({
61084
- name: "doc",
61085
- topNode: true,
61086
- content: "block+",
61087
- parseDOM() {
61088
- return [{ tag: "doc" }];
61089
- },
61090
- renderDOM() {
61091
- return ["doc", 0];
61092
- },
61093
- addAttributes() {
61094
- return {
61095
- attributes: {
61096
- rendered: false,
61097
- "aria-label": "Document node"
61098
- }
61099
- };
61100
- },
61101
- addCommands() {
61102
- return {
61103
- /**
61104
- * Get document statistics
61105
- * @category Command
61106
- * @example
61107
- * // Get word and character count
61108
- * const stats = editor.commands.getDocumentStats()
61109
- * console.log(`${stats.words} words, ${stats.characters} characters`)
61110
- * @note Returns word count, character count, and paragraph count
61111
- */
61112
- getDocumentStats: () => ({ editor }) => {
61113
- const text = editor.getText();
61114
- const words = text.split(/\s+/).filter((word) => word.length > 0).length;
61115
- const characters = text.length;
61116
- const paragraphs = editor.state.doc.content.childCount;
61117
- return {
61118
- words,
61119
- characters,
61120
- paragraphs
61121
- };
61122
- },
61123
- /**
61124
- * Clear entire document
61125
- * @category Command
61126
- * @example
61127
- * editor.commands.clearDocument()
61128
- * @note Replaces all content with an empty paragraph
61129
- */
61130
- clearDocument: () => ({ commands: commands2 }) => {
61131
- return commands2.setContent("<p></p>");
61112
+ class StructuredContentViewBase {
61113
+ constructor(props) {
61114
+ __publicField$1(this, "node");
61115
+ __publicField$1(this, "view");
61116
+ __publicField$1(this, "getPos");
61117
+ __publicField$1(this, "decorations");
61118
+ __publicField$1(this, "innerDecorations");
61119
+ __publicField$1(this, "editor");
61120
+ __publicField$1(this, "extension");
61121
+ __publicField$1(this, "htmlAttributes");
61122
+ __publicField$1(this, "root");
61123
+ __publicField$1(this, "isDragging", false);
61124
+ this.node = props.node;
61125
+ this.view = props.editor.view;
61126
+ this.getPos = props.getPos;
61127
+ this.decorations = props.decorations;
61128
+ this.innerDecorations = props.innerDecorations;
61129
+ this.editor = props.editor;
61130
+ this.extension = props.extension;
61131
+ this.htmlAttributes = props.htmlAttributes;
61132
+ this.mount(props);
61133
+ }
61134
+ mount() {
61135
+ return;
61136
+ }
61137
+ get dom() {
61138
+ return this.root;
61139
+ }
61140
+ get contentDOM() {
61141
+ return null;
61142
+ }
61143
+ update(node, decorations, innerDecorations) {
61144
+ if (node.type !== this.node.type) {
61145
+ return false;
61146
+ }
61147
+ this.node = node;
61148
+ this.decorations = decorations;
61149
+ this.innerDecorations = innerDecorations;
61150
+ this.updateHTMLAttributes();
61151
+ return true;
61152
+ }
61153
+ stopEvent(event) {
61154
+ if (!this.dom) return false;
61155
+ const target = event.target;
61156
+ const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
61157
+ if (!isInElement) return false;
61158
+ const isDragEvent = event.type.startsWith("drag");
61159
+ const isDropEvent = event.type === "drop";
61160
+ const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
61161
+ if (isInput && !isDropEvent && !isDragEvent) return true;
61162
+ const { isEditable } = this.editor;
61163
+ const { isDragging } = this;
61164
+ const isDraggable = !!this.node.type.spec.draggable;
61165
+ const isSelectable = NodeSelection.isSelectable(this.node);
61166
+ const isCopyEvent = event.type === "copy";
61167
+ const isPasteEvent = event.type === "paste";
61168
+ const isCutEvent = event.type === "cut";
61169
+ const isClickEvent = event.type === "mousedown";
61170
+ if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
61171
+ event.preventDefault();
61172
+ }
61173
+ if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
61174
+ event.preventDefault();
61175
+ return false;
61176
+ }
61177
+ if (isDraggable && isEditable && !isDragging && isClickEvent) {
61178
+ const dragHandle = target.closest("[data-drag-handle]");
61179
+ const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
61180
+ if (isValidDragHandle) {
61181
+ this.isDragging = true;
61182
+ document.addEventListener(
61183
+ "dragend",
61184
+ () => {
61185
+ this.isDragging = false;
61186
+ },
61187
+ { once: true }
61188
+ );
61189
+ document.addEventListener(
61190
+ "drop",
61191
+ () => {
61192
+ this.isDragging = false;
61193
+ },
61194
+ { once: true }
61195
+ );
61196
+ document.addEventListener(
61197
+ "mouseup",
61198
+ () => {
61199
+ this.isDragging = false;
61200
+ },
61201
+ { once: true }
61202
+ );
61132
61203
  }
61133
- };
61204
+ }
61205
+ if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
61206
+ return false;
61207
+ }
61208
+ return true;
61134
61209
  }
61135
- });
61136
- const Text = Node$1.create({
61137
- name: "text",
61138
- group: "inline",
61139
- inline: true,
61140
- addOptions() {
61141
- return {};
61210
+ ignoreMutation(mutation) {
61211
+ if (!this.dom || !this.contentDOM) return true;
61212
+ if (this.node.isLeaf || this.node.isAtom) return true;
61213
+ if (mutation.type === "selection") return false;
61214
+ if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
61215
+ if (this.contentDOM.contains(mutation.target)) return false;
61216
+ return true;
61142
61217
  }
61143
- });
61144
- const splitRun = () => (props) => {
61145
- const { state: state2, view, tr } = props;
61146
- const { $from, empty: empty2 } = state2.selection;
61147
- if (!empty2) return false;
61148
- if ($from.parent.type.name !== "run") return false;
61149
- const handled = splitBlock(state2, (transaction) => {
61218
+ destroy() {
61219
+ this.dom.remove();
61220
+ this.contentDOM?.remove();
61221
+ }
61222
+ updateAttributes(attrs) {
61223
+ const pos = this.getPos();
61224
+ if (typeof pos !== "number") {
61225
+ return;
61226
+ }
61227
+ return this.view.dispatch(
61228
+ this.view.state.tr.setNodeMarkup(pos, void 0, {
61229
+ ...this.node.attrs,
61230
+ ...attrs
61231
+ })
61232
+ );
61233
+ }
61234
+ updateHTMLAttributes() {
61235
+ const { extensionService } = this.editor;
61236
+ const { attributes } = extensionService;
61237
+ const extensionAttrs = attributes.filter((i2) => i2.type === this.node.type.name);
61238
+ this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
61239
+ }
61240
+ createDragHandle() {
61241
+ const dragHandle = document.createElement("span");
61242
+ dragHandle.classList.add("sd-structured-content-draggable");
61243
+ dragHandle.draggable = true;
61244
+ dragHandle.contentEditable = "false";
61245
+ dragHandle.dataset.dragHandle = "";
61246
+ const textElement = document.createElement("span");
61247
+ textElement.textContent = this.node.attrs.alias || "Structured content";
61248
+ dragHandle.append(textElement);
61249
+ return dragHandle;
61250
+ }
61251
+ onDragStart(event) {
61252
+ const { view } = this.editor;
61253
+ const target = event.target;
61254
+ const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
61255
+ if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
61256
+ return;
61257
+ }
61258
+ let x = 0;
61259
+ let y2 = 0;
61260
+ if (this.dom !== dragHandle) {
61261
+ const domBox = this.dom.getBoundingClientRect();
61262
+ const handleBox = dragHandle.getBoundingClientRect();
61263
+ const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
61264
+ const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
61265
+ x = handleBox.x - domBox.x + offsetX;
61266
+ y2 = handleBox.y - domBox.y + offsetY;
61267
+ }
61268
+ event.dataTransfer?.setDragImage(this.dom, x, y2);
61269
+ const pos = this.getPos();
61270
+ if (typeof pos !== "number") {
61271
+ return;
61272
+ }
61273
+ const selection = NodeSelection.create(view.state.doc, pos);
61274
+ const transaction = view.state.tr.setSelection(selection);
61150
61275
  view.dispatch(transaction);
61151
- });
61152
- if (handled) {
61153
- tr.setMeta("preventDispatch", true);
61154
61276
  }
61155
- return handled;
61156
- };
61157
- const Run = OxmlNode.create({
61158
- name: "run",
61159
- oXmlName: "w:r",
61160
- group: "inline",
61277
+ }
61278
+ class StructuredContentInlineView extends StructuredContentViewBase {
61279
+ constructor(props) {
61280
+ super(props);
61281
+ }
61282
+ mount() {
61283
+ this.buildView();
61284
+ }
61285
+ get contentDOM() {
61286
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
61287
+ return contentElement || null;
61288
+ }
61289
+ createElement() {
61290
+ const element = document.createElement("span");
61291
+ element.classList.add(structuredContentClass$1);
61292
+ element.setAttribute("data-structured-content", "");
61293
+ const contentElement = document.createElement("span");
61294
+ contentElement.classList.add(structuredContentInnerClass$1);
61295
+ element.append(contentElement);
61296
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61297
+ updateDOMAttributes(element, { ...domAttrs });
61298
+ return { element, contentElement };
61299
+ }
61300
+ buildView() {
61301
+ const { element } = this.createElement();
61302
+ const dragHandle = this.createDragHandle();
61303
+ element.prepend(dragHandle);
61304
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61305
+ this.root = element;
61306
+ }
61307
+ updateView() {
61308
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61309
+ updateDOMAttributes(this.dom, { ...domAttrs });
61310
+ }
61311
+ update(node, decorations, innerDecorations) {
61312
+ const result = super.update(node, decorations, innerDecorations);
61313
+ if (!result) return false;
61314
+ this.updateView();
61315
+ return true;
61316
+ }
61317
+ }
61318
+ const structuredContentClass$1 = "sd-structured-content";
61319
+ const structuredContentInnerClass$1 = "sd-structured-content__content";
61320
+ const StructuredContent = Node$1.create({
61321
+ name: "structuredContent",
61322
+ group: "inline structuredContent",
61161
61323
  inline: true,
61162
61324
  content: "inline*",
61163
- selectable: false,
61164
- childToAttributes: ["runProperties"],
61325
+ isolating: true,
61326
+ atom: false,
61327
+ // false - has editable content.
61328
+ draggable: true,
61165
61329
  addOptions() {
61166
61330
  return {
61167
61331
  htmlAttributes: {
61168
- "data-run": "1"
61332
+ class: structuredContentClass$1,
61333
+ "aria-label": "Structured content node"
61169
61334
  }
61170
61335
  };
61171
61336
  },
61172
61337
  addAttributes() {
61173
61338
  return {
61174
- runProperties: {
61339
+ id: {
61175
61340
  default: null,
61176
- rendered: false,
61177
- keepOnSplit: true
61341
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61342
+ renderDOM: (attrs) => {
61343
+ if (!attrs.id) return {};
61344
+ return { "data-id": attrs.id };
61345
+ }
61178
61346
  },
61179
- rsidR: {
61347
+ tag: {
61180
61348
  default: null,
61181
- rendered: false,
61182
- keepOnSplit: true
61349
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61350
+ renderDOM: (attrs) => {
61351
+ if (!attrs.tag) return {};
61352
+ return { "data-tag": attrs.tag };
61353
+ }
61183
61354
  },
61184
- rsidRPr: {
61355
+ alias: {
61185
61356
  default: null,
61186
- rendered: false,
61187
- keepOnSplit: true
61357
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61358
+ renderDOM: (attrs) => {
61359
+ if (!attrs.alias) return {};
61360
+ return { "data-alias": attrs.alias };
61361
+ }
61188
61362
  },
61189
- rsidDel: {
61190
- default: null,
61191
- rendered: false,
61192
- keepOnSplit: true
61363
+ sdtPr: {
61364
+ rendered: false
61193
61365
  }
61194
61366
  };
61195
61367
  },
61196
- addCommands() {
61197
- return {
61198
- splitRun
61199
- };
61200
- },
61201
61368
  parseDOM() {
61202
- return [{ tag: "span[data-run]" }];
61369
+ return [{ tag: "span[data-structured-content]" }];
61203
61370
  },
61204
61371
  renderDOM({ htmlAttributes }) {
61205
- const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
61206
- return ["span", base2, 0];
61372
+ return [
61373
+ "span",
61374
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61375
+ "data-structured-content": ""
61376
+ }),
61377
+ 0
61378
+ ];
61379
+ },
61380
+ addNodeView() {
61381
+ return (props) => {
61382
+ return new StructuredContentInlineView({ ...props });
61383
+ };
61207
61384
  }
61208
61385
  });
61209
- const inputRegex$1 = /^\s*([-+*])\s$/;
61210
- const BulletList = Node$1.create({
61211
- name: "bulletList",
61212
- group: "block list",
61213
- selectable: false,
61214
- content() {
61215
- return `${this.options.itemTypeName}+`;
61216
- },
61386
+ class StructuredContentBlockView extends StructuredContentViewBase {
61387
+ constructor(props) {
61388
+ super(props);
61389
+ }
61390
+ mount() {
61391
+ this.buildView();
61392
+ }
61393
+ get contentDOM() {
61394
+ const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
61395
+ return contentElement || null;
61396
+ }
61397
+ createElement() {
61398
+ const element = document.createElement("div");
61399
+ element.classList.add(structuredContentClass);
61400
+ element.setAttribute("data-structured-content-block", "");
61401
+ const contentElement = document.createElement("div");
61402
+ contentElement.classList.add(structuredContentInnerClass);
61403
+ element.append(contentElement);
61404
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61405
+ updateDOMAttributes(element, { ...domAttrs });
61406
+ return { element, contentElement };
61407
+ }
61408
+ buildView() {
61409
+ const { element } = this.createElement();
61410
+ const dragHandle = this.createDragHandle();
61411
+ element.prepend(dragHandle);
61412
+ element.addEventListener("dragstart", (e) => this.onDragStart(e));
61413
+ this.root = element;
61414
+ }
61415
+ updateView() {
61416
+ const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
61417
+ updateDOMAttributes(this.dom, { ...domAttrs });
61418
+ }
61419
+ update(node, decorations, innerDecorations) {
61420
+ const result = super.update(node, decorations, innerDecorations);
61421
+ if (!result) return false;
61422
+ this.updateView();
61423
+ return true;
61424
+ }
61425
+ }
61426
+ const structuredContentClass = "sd-structured-content-block";
61427
+ const structuredContentInnerClass = "sd-structured-content-block__content";
61428
+ const StructuredContentBlock = Node$1.create({
61429
+ name: "structuredContentBlock",
61430
+ group: "block structuredContent",
61431
+ content: "block*",
61432
+ isolating: true,
61433
+ atom: false,
61434
+ // false - has editable content.
61435
+ draggable: true,
61217
61436
  addOptions() {
61218
61437
  return {
61219
- itemTypeName: "listItem",
61220
61438
  htmlAttributes: {
61221
- "aria-label": "Bullet list node"
61222
- },
61223
- keepMarks: true,
61224
- keepAttributes: false
61439
+ class: structuredContentClass,
61440
+ "aria-label": "Structured content block node"
61441
+ }
61225
61442
  };
61226
61443
  },
61227
- parseDOM() {
61228
- return [{ tag: "ul" }];
61229
- },
61230
- renderDOM({ htmlAttributes }) {
61231
- const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
61232
- return ["ul", attributes, 0];
61233
- },
61234
61444
  addAttributes() {
61235
61445
  return {
61236
- "list-style-type": {
61237
- default: "bullet",
61238
- rendered: false
61446
+ id: {
61447
+ default: null,
61448
+ parseDOM: (elem) => elem.getAttribute("data-id"),
61449
+ renderDOM: (attrs) => {
61450
+ if (!attrs.id) return {};
61451
+ return { "data-id": attrs.id };
61452
+ }
61239
61453
  },
61240
- listId: {
61241
- rendered: false
61454
+ tag: {
61455
+ default: null,
61456
+ parseDOM: (elem) => elem.getAttribute("data-tag"),
61457
+ renderDOM: (attrs) => {
61458
+ if (!attrs.tag) return {};
61459
+ return { "data-tag": attrs.tag };
61460
+ }
61242
61461
  },
61243
- sdBlockId: {
61462
+ alias: {
61244
61463
  default: null,
61245
- keepOnSplit: false,
61246
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61464
+ parseDOM: (elem) => elem.getAttribute("data-alias"),
61247
61465
  renderDOM: (attrs) => {
61248
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61466
+ if (!attrs.alias) return {};
61467
+ return { "data-alias": attrs.alias };
61249
61468
  }
61250
61469
  },
61251
- attributes: {
61252
- rendered: false,
61253
- keepOnSplit: true
61470
+ sdtPr: {
61471
+ rendered: false
61254
61472
  }
61255
61473
  };
61256
61474
  },
61475
+ parseDOM() {
61476
+ return [{ tag: "div[data-structured-content-block]" }];
61477
+ },
61478
+ renderDOM({ htmlAttributes }) {
61479
+ return [
61480
+ "div",
61481
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
61482
+ "data-structured-content-block": ""
61483
+ }),
61484
+ 0
61485
+ ];
61486
+ },
61487
+ addNodeView() {
61488
+ return (props) => {
61489
+ return new StructuredContentBlockView({ ...props });
61490
+ };
61491
+ }
61492
+ });
61493
+ function getStructuredContentTagsById(idOrIds, state2) {
61494
+ const result = findChildren$5(state2.doc, (node) => {
61495
+ const isStructuredContent = ["structuredContent", "structuredContentBlock"].includes(node.type.name);
61496
+ if (Array.isArray(idOrIds)) {
61497
+ return isStructuredContent && idOrIds.includes(node.attrs.id);
61498
+ } else {
61499
+ return isStructuredContent && node.attrs.id === idOrIds;
61500
+ }
61501
+ });
61502
+ return result;
61503
+ }
61504
+ function getStructuredContentTags(state2) {
61505
+ const result = findChildren$5(state2.doc, (node) => {
61506
+ return node.type.name === "structuredContent" || node.type.name === "structuredContentBlock";
61507
+ });
61508
+ return result;
61509
+ }
61510
+ function getStructuredContentInlineTags(state2) {
61511
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContent");
61512
+ return result;
61513
+ }
61514
+ function getStructuredContentBlockTags(state2) {
61515
+ const result = findChildren$5(state2.doc, (node) => node.type.name === "structuredContentBlock");
61516
+ return result;
61517
+ }
61518
+ const structuredContentHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
61519
+ __proto__: null,
61520
+ getStructuredContentBlockTags,
61521
+ getStructuredContentInlineTags,
61522
+ getStructuredContentTags,
61523
+ getStructuredContentTagsById
61524
+ }, Symbol.toStringTag, { value: "Module" }));
61525
+ const STRUCTURED_CONTENT_NAMES = ["structuredContent", "structuredContentBlock"];
61526
+ const StructuredContentCommands = Extension.create({
61527
+ name: "structuredContentCommands",
61257
61528
  addCommands() {
61258
61529
  return {
61259
61530
  /**
61260
- * Toggle a bullet list at the current selection
61531
+ * Inserts a structured content inline at selection.
61261
61532
  * @category Command
61262
- * @example
61263
- * // Toggle bullet list on selected text
61264
- * editor.commands.toggleBulletList()
61265
- * @note Converts selected paragraphs to list items or removes list formatting
61533
+ * @param {StructuredContentInlineInsert} options
61266
61534
  */
61267
- toggleBulletList: () => (params2) => {
61268
- return toggleList(this.type)(params2);
61269
- }
61270
- };
61271
- },
61272
- addShortcuts() {
61535
+ insertStructuredContentInline: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61536
+ const { schema } = editor;
61537
+ let { from: from2, to } = state2.selection;
61538
+ if (dispatch) {
61539
+ const selectionText = state2.doc.textBetween(from2, to);
61540
+ let content = null;
61541
+ if (selectionText) {
61542
+ content = schema.text(selectionText);
61543
+ }
61544
+ if (options.text) {
61545
+ content = schema.text(options.text);
61546
+ }
61547
+ if (options.json) {
61548
+ content = schema.nodeFromJSON(options.json);
61549
+ }
61550
+ if (!content) {
61551
+ content = schema.text(" ");
61552
+ }
61553
+ const attrs = {
61554
+ ...options.attrs,
61555
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61556
+ tag: "inline_text_sdt",
61557
+ alias: options.attrs?.alias || "Structured content"
61558
+ };
61559
+ const node = schema.nodes.structuredContent.create(attrs, content, null);
61560
+ const parent = findParentNode((node2) => node2.type.name === "structuredContent")(state2.selection);
61561
+ if (parent) {
61562
+ const insertPos = parent.pos + parent.node.nodeSize;
61563
+ from2 = to = insertPos;
61564
+ }
61565
+ tr.replaceWith(from2, to, node);
61566
+ }
61567
+ return true;
61568
+ },
61569
+ /**
61570
+ * Inserts a structured content block at selection.
61571
+ * @category Command
61572
+ * @param {StructuredContentBlockInsert} options
61573
+ */
61574
+ insertStructuredContentBlock: (options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61575
+ const { schema } = editor;
61576
+ let { from: from2, to } = state2.selection;
61577
+ if (dispatch) {
61578
+ const selectionContent = state2.selection.content();
61579
+ let content = null;
61580
+ if (selectionContent.size) {
61581
+ content = selectionContent.content;
61582
+ }
61583
+ if (options.html) {
61584
+ const html = htmlHandler(options.html, editor);
61585
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61586
+ content = doc2.content;
61587
+ }
61588
+ if (options.json) {
61589
+ content = schema.nodeFromJSON(options.json);
61590
+ }
61591
+ if (!content) {
61592
+ content = schema.nodeFromJSON({ type: "paragraph", content: [] });
61593
+ }
61594
+ const attrs = {
61595
+ ...options.attrs,
61596
+ id: options.attrs?.id || generateRandomSigned32BitIntStrId(),
61597
+ tag: "block_table_sdt",
61598
+ alias: options.attrs?.alias || "Structured content"
61599
+ };
61600
+ const node = schema.nodes.structuredContentBlock.create(attrs, content, null);
61601
+ const parent = findParentNode((node2) => node2.type.name === "structuredContentBlock")(state2.selection);
61602
+ if (parent) {
61603
+ const insertPos = parent.pos + parent.node.nodeSize;
61604
+ from2 = to = insertPos;
61605
+ }
61606
+ tr.replaceRangeWith(from2, to, node);
61607
+ }
61608
+ return true;
61609
+ },
61610
+ /**
61611
+ * Updates a structured content attributes or content.
61612
+ * If the updated node does not match the schema, it will not be updated.
61613
+ * @category Command
61614
+ * @param {string} id
61615
+ * @param {StructuredContentUpdate} options
61616
+ */
61617
+ updateStructuredContentById: (id, options = {}) => ({ editor, dispatch, state: state2, tr }) => {
61618
+ const structuredContentTags = getStructuredContentTagsById(id, state2);
61619
+ if (!structuredContentTags.length) {
61620
+ return true;
61621
+ }
61622
+ const { schema } = editor;
61623
+ if (dispatch) {
61624
+ const structuredContent = structuredContentTags[0];
61625
+ const { pos, node } = structuredContent;
61626
+ const posFrom = pos;
61627
+ const posTo = pos + node.nodeSize;
61628
+ let content = null;
61629
+ if (options.text) {
61630
+ content = schema.text(options.text);
61631
+ }
61632
+ if (options.html) {
61633
+ const html = htmlHandler(options.html, editor);
61634
+ const doc2 = DOMParser$1.fromSchema(schema).parse(html);
61635
+ content = doc2.content;
61636
+ }
61637
+ if (options.json) {
61638
+ content = schema.nodeFromJSON(options.json);
61639
+ }
61640
+ if (!content) {
61641
+ content = node.content;
61642
+ }
61643
+ const updatedNode = node.type.create({ ...node.attrs, ...options.attrs }, content, node.marks);
61644
+ try {
61645
+ updatedNode.check();
61646
+ } catch {
61647
+ console.error("Updated node does not conform to the schema");
61648
+ return false;
61649
+ }
61650
+ tr.replaceWith(posFrom, posTo, updatedNode);
61651
+ }
61652
+ return true;
61653
+ },
61654
+ /**
61655
+ * Removes a structured content.
61656
+ * @category Command
61657
+ * @param {Array<{ node: Node, pos: number }>} structuredContentTags
61658
+ */
61659
+ deleteStructuredContent: (structuredContentTags) => ({ dispatch, tr }) => {
61660
+ if (!structuredContentTags.length) {
61661
+ return true;
61662
+ }
61663
+ if (dispatch) {
61664
+ structuredContentTags.forEach((structuredContent) => {
61665
+ const { pos, node } = structuredContent;
61666
+ const posFrom = tr.mapping.map(pos);
61667
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61668
+ const currentNode = tr.doc.nodeAt(posFrom);
61669
+ if (currentNode && node.eq(currentNode)) {
61670
+ tr.delete(posFrom, posTo);
61671
+ }
61672
+ });
61673
+ }
61674
+ return true;
61675
+ },
61676
+ /**
61677
+ * Removes a structured content by ID.
61678
+ * @category Command
61679
+ * @param {string | string[]} idOrIds
61680
+ */
61681
+ deleteStructuredContentById: (idOrIds) => ({ dispatch, state: state2, tr }) => {
61682
+ const structuredContentTags = getStructuredContentTagsById(idOrIds, state2);
61683
+ if (!structuredContentTags.length) {
61684
+ return true;
61685
+ }
61686
+ if (dispatch) {
61687
+ structuredContentTags.forEach((structuredContent) => {
61688
+ const { pos, node } = structuredContent;
61689
+ const posFrom = tr.mapping.map(pos);
61690
+ const posTo = tr.mapping.map(pos + node.nodeSize);
61691
+ const currentNode = tr.doc.nodeAt(posFrom);
61692
+ if (currentNode && node.eq(currentNode)) {
61693
+ tr.delete(posFrom, posTo);
61694
+ }
61695
+ });
61696
+ }
61697
+ return true;
61698
+ },
61699
+ /**
61700
+ * Removes a structured content at cursor, preserving its content.
61701
+ * @category Command
61702
+ */
61703
+ deleteStructuredContentAtSelection: () => ({ dispatch, state: state2, tr }) => {
61704
+ const predicate = (node) => STRUCTURED_CONTENT_NAMES.includes(node.type.name);
61705
+ const structuredContent = findParentNode(predicate)(state2.selection);
61706
+ if (!structuredContent) {
61707
+ return true;
61708
+ }
61709
+ if (dispatch) {
61710
+ const { node, pos } = structuredContent;
61711
+ const posFrom = pos;
61712
+ const posTo = posFrom + node.nodeSize;
61713
+ const content = node.content;
61714
+ tr.replaceWith(posFrom, posTo, content);
61715
+ }
61716
+ return true;
61717
+ }
61718
+ };
61719
+ },
61720
+ addHelpers() {
61721
+ return {
61722
+ ...structuredContentHelpers
61723
+ };
61724
+ }
61725
+ });
61726
+ class DocumentSectionView {
61727
+ constructor(node, getPos, decorations, editor) {
61728
+ __privateAdd$1(this, _DocumentSectionView_instances);
61729
+ this.node = node;
61730
+ this.editor = editor;
61731
+ this.decorations = decorations;
61732
+ this.view = editor.view;
61733
+ this.getPos = getPos;
61734
+ __privateMethod$1(this, _DocumentSectionView_instances, init_fn2).call(this);
61735
+ }
61736
+ }
61737
+ _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
61738
+ init_fn2 = function() {
61739
+ const { attrs } = this.node;
61740
+ const { id, title, description } = attrs;
61741
+ this.dom = document.createElement("div");
61742
+ this.dom.className = "sd-document-section-block";
61743
+ this.dom.setAttribute("data-id", id);
61744
+ this.dom.setAttribute("data-title", title);
61745
+ this.dom.setAttribute("data-description", description);
61746
+ this.dom.setAttribute("aria-label", "Document section");
61747
+ __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
61748
+ this.contentDOM = document.createElement("div");
61749
+ this.contentDOM.className = "sd-document-section-block-content";
61750
+ this.contentDOM.setAttribute("contenteditable", "true");
61751
+ this.dom.appendChild(this.contentDOM);
61752
+ };
61753
+ addToolTip_fn = function() {
61754
+ const { title } = this.node.attrs;
61755
+ this.infoDiv = document.createElement("div");
61756
+ this.infoDiv.className = "sd-document-section-block-info";
61757
+ const textSpan = document.createElement("span");
61758
+ textSpan.textContent = title || "Document section";
61759
+ this.infoDiv.appendChild(textSpan);
61760
+ this.infoDiv.setAttribute("contenteditable", "false");
61761
+ this.dom.appendChild(this.infoDiv);
61762
+ };
61763
+ const getAllSections = (editor) => {
61764
+ if (!editor) return [];
61765
+ const type2 = editor.schema.nodes.documentSection;
61766
+ if (!type2) return [];
61767
+ const sections = [];
61768
+ const { state: state2 } = editor;
61769
+ state2.doc.descendants((node, pos) => {
61770
+ if (node.type.name === type2.name) {
61771
+ sections.push({ node, pos });
61772
+ }
61773
+ });
61774
+ return sections;
61775
+ };
61776
+ const exportSectionsToHTML = (editor) => {
61777
+ const sections = getAllSections(editor);
61778
+ const processedSections = /* @__PURE__ */ new Set();
61779
+ const result = [];
61780
+ sections.forEach(({ node }) => {
61781
+ const { attrs } = node;
61782
+ const { id, title, description } = attrs;
61783
+ if (processedSections.has(id)) return;
61784
+ processedSections.add(id);
61785
+ const html = getHTMLFromNode(node, editor);
61786
+ result.push({
61787
+ id,
61788
+ title,
61789
+ description,
61790
+ html
61791
+ });
61792
+ });
61793
+ return result;
61794
+ };
61795
+ const getHTMLFromNode = (node, editor) => {
61796
+ const tempDocument = document.implementation.createHTMLDocument();
61797
+ const container = tempDocument.createElement("div");
61798
+ const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
61799
+ container.appendChild(fragment);
61800
+ let html = container.innerHTML;
61801
+ return html;
61802
+ };
61803
+ const exportSectionsToJSON = (editor) => {
61804
+ const sections = getAllSections(editor);
61805
+ const processedSections = /* @__PURE__ */ new Set();
61806
+ const result = [];
61807
+ sections.forEach(({ node }) => {
61808
+ const { attrs } = node;
61809
+ const { id, title, description } = attrs;
61810
+ if (processedSections.has(id)) return;
61811
+ processedSections.add(id);
61812
+ result.push({
61813
+ id,
61814
+ title,
61815
+ description,
61816
+ content: node.toJSON()
61817
+ });
61818
+ });
61819
+ return result;
61820
+ };
61821
+ const getLinkedSectionEditor = (id, options, editor) => {
61822
+ const sections = getAllSections(editor);
61823
+ const section = sections.find((s) => s.node.attrs.id === id);
61824
+ if (!section) return null;
61825
+ const child = editor.createChildEditor({
61826
+ ...options,
61827
+ onUpdate: ({ editor: childEditor, transaction }) => {
61828
+ const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
61829
+ if (isFromtLinkedParent) return;
61830
+ const updatedContent = childEditor.state.doc.content;
61831
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61832
+ if (!sectionNode) return;
61833
+ const { pos, node } = sectionNode;
61834
+ const newNode = node.type.create(node.attrs, updatedContent, node.marks);
61835
+ const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
61836
+ tr.setMeta("fromLinkedChild", true);
61837
+ editor.view.dispatch(tr);
61838
+ }
61839
+ });
61840
+ editor.on("update", ({ transaction }) => {
61841
+ const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
61842
+ if (isFromLinkedChild) return;
61843
+ const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
61844
+ if (!sectionNode) return;
61845
+ const sectionContent = sectionNode.node.content;
61846
+ const json = {
61847
+ type: "doc",
61848
+ content: sectionContent.content.map((node) => node.toJSON())
61849
+ };
61850
+ const childTr = child.state.tr;
61851
+ childTr.setMeta("fromLinkedParent", true);
61852
+ childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
61853
+ child.view.dispatch(childTr);
61854
+ });
61855
+ return child;
61856
+ };
61857
+ const SectionHelpers = {
61858
+ getAllSections,
61859
+ exportSectionsToHTML,
61860
+ exportSectionsToJSON,
61861
+ getLinkedSectionEditor
61862
+ };
61863
+ const DocumentSection = Node$1.create({
61864
+ name: "documentSection",
61865
+ group: "block",
61866
+ content: "block*",
61867
+ atom: true,
61868
+ isolating: true,
61869
+ addOptions() {
61870
+ return {
61871
+ htmlAttributes: {
61872
+ class: "sd-document-section-block",
61873
+ "aria-label": "Structured content block"
61874
+ }
61875
+ };
61876
+ },
61877
+ parseDOM() {
61878
+ return [
61879
+ {
61880
+ tag: "div.sd-document-section-block",
61881
+ priority: 60
61882
+ }
61883
+ ];
61884
+ },
61885
+ renderDOM({ htmlAttributes }) {
61886
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
61887
+ },
61888
+ addAttributes() {
61889
+ return {
61890
+ id: {},
61891
+ sdBlockId: {
61892
+ default: null,
61893
+ keepOnSplit: false,
61894
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
61895
+ renderDOM: (attrs) => {
61896
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
61897
+ }
61898
+ },
61899
+ title: {},
61900
+ description: {},
61901
+ sectionType: {},
61902
+ isLocked: { default: false }
61903
+ };
61904
+ },
61905
+ addNodeView() {
61906
+ return ({ node, editor, getPos, decorations }) => {
61907
+ return new DocumentSectionView(node, getPos, decorations, editor);
61908
+ };
61909
+ },
61910
+ addCommands() {
61911
+ return {
61912
+ /**
61913
+ * Create a lockable content section
61914
+ * @category Command
61915
+ * @param {SectionCreate} [options={}] - Section configuration
61916
+ * @example
61917
+ * editor.commands.createDocumentSection({
61918
+ * id: 1,
61919
+ * title: 'Terms & Conditions',
61920
+ * isLocked: true,
61921
+ * html: '<p>Legal content...</p>'
61922
+ * })
61923
+ */
61924
+ createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
61925
+ const { selection } = state2;
61926
+ let { from: from2, to } = selection;
61927
+ let content = selection.content().content;
61928
+ const { html: optionsHTML, json: optionsJSON } = options;
61929
+ if (optionsHTML) {
61930
+ const html = htmlHandler(optionsHTML, this.editor);
61931
+ const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
61932
+ content = doc2.content;
61933
+ }
61934
+ if (optionsJSON) {
61935
+ content = this.editor.schema.nodeFromJSON(optionsJSON);
61936
+ }
61937
+ if (!content?.content?.length) {
61938
+ content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
61939
+ }
61940
+ if (!options.id) {
61941
+ const allSections = SectionHelpers.getAllSections(editor);
61942
+ options.id = allSections.length + 1;
61943
+ }
61944
+ if (!options.title) {
61945
+ options.title = "Document section";
61946
+ }
61947
+ const node = this.type.createAndFill(options, content);
61948
+ if (!node) return false;
61949
+ const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
61950
+ if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
61951
+ const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
61952
+ from2 = insertPos2;
61953
+ to = insertPos2;
61954
+ }
61955
+ tr.replaceRangeWith(from2, to, node);
61956
+ const nodeEnd = from2 + node.nodeSize;
61957
+ let shouldInsertParagraph = true;
61958
+ let insertPos = nodeEnd;
61959
+ if (nodeEnd >= tr.doc.content.size) {
61960
+ insertPos = tr.doc.content.size;
61961
+ if (insertPos > 0) {
61962
+ const $endPos = tr.doc.resolve(insertPos);
61963
+ if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
61964
+ shouldInsertParagraph = false;
61965
+ }
61966
+ }
61967
+ }
61968
+ if (shouldInsertParagraph) {
61969
+ const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
61970
+ tr.insert(insertPos, emptyParagraph);
61971
+ }
61972
+ if (dispatch) {
61973
+ tr.setMeta("documentSection", { action: "create" });
61974
+ dispatch(tr);
61975
+ setTimeout(() => {
61976
+ try {
61977
+ const currentState = editor.state;
61978
+ const docSize = currentState.doc.content.size;
61979
+ let targetPos = from2 + node.nodeSize;
61980
+ if (shouldInsertParagraph) {
61981
+ targetPos += 1;
61982
+ }
61983
+ targetPos = Math.min(targetPos, docSize);
61984
+ if (targetPos < docSize && targetPos > 0) {
61985
+ const newSelection = Selection.near(currentState.doc.resolve(targetPos));
61986
+ const newTr = currentState.tr.setSelection(newSelection);
61987
+ editor.view.dispatch(newTr);
61988
+ }
61989
+ } catch (e) {
61990
+ console.warn("Could not set delayed selection:", e);
61991
+ }
61992
+ }, 0);
61993
+ }
61994
+ return true;
61995
+ },
61996
+ /**
61997
+ * Remove section wrapper at cursor, preserving its content
61998
+ * @category Command
61999
+ * @example
62000
+ * editor.commands.removeSectionAtSelection()
62001
+ * @note Content stays in document, only section wrapper is removed
62002
+ */
62003
+ removeSectionAtSelection: () => ({ tr, dispatch }) => {
62004
+ const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
62005
+ if (!sdtNode) return false;
62006
+ const { node, pos } = sdtNode;
62007
+ const nodeStart = pos;
62008
+ const nodeEnd = nodeStart + node.nodeSize;
62009
+ const contentToPreserve = node.content;
62010
+ tr.delete(nodeStart, nodeEnd);
62011
+ if (contentToPreserve.size > 0) {
62012
+ tr.insert(nodeStart, contentToPreserve);
62013
+ }
62014
+ const newPos = Math.min(nodeStart, tr.doc.content.size);
62015
+ tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
62016
+ if (dispatch) {
62017
+ tr.setMeta("documentSection", { action: "delete" });
62018
+ dispatch(tr);
62019
+ }
62020
+ return true;
62021
+ },
62022
+ /**
62023
+ * Delete section and all its content
62024
+ * @category Command
62025
+ * @param {number} id - Section to delete
62026
+ * @example
62027
+ * editor.commands.removeSectionById(123)
62028
+ */
62029
+ removeSectionById: (id) => ({ tr, dispatch }) => {
62030
+ const sections = SectionHelpers.getAllSections(this.editor);
62031
+ const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
62032
+ if (!sectionToRemove) return false;
62033
+ const { pos, node } = sectionToRemove;
62034
+ const nodeStart = pos;
62035
+ const nodeEnd = nodeStart + node.nodeSize;
62036
+ tr.delete(nodeStart, nodeEnd);
62037
+ if (dispatch) {
62038
+ tr.setMeta("documentSection", { action: "delete", id });
62039
+ dispatch(tr);
62040
+ }
62041
+ return true;
62042
+ },
62043
+ /**
62044
+ * Lock section against edits
62045
+ * @category Command
62046
+ * @param {number} id - Section to lock
62047
+ * @example
62048
+ * editor.commands.lockSectionById(123)
62049
+ */
62050
+ lockSectionById: (id) => ({ tr, dispatch }) => {
62051
+ const sections = SectionHelpers.getAllSections(this.editor);
62052
+ const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
62053
+ if (!sectionToLock) return false;
62054
+ tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
62055
+ if (dispatch) {
62056
+ tr.setMeta("documentSection", { action: "lock", id });
62057
+ dispatch(tr);
62058
+ }
62059
+ return true;
62060
+ },
62061
+ /**
62062
+ * Modify section attributes or content
62063
+ * @category Command
62064
+ * @param {SectionUpdate} options - Changes to apply
62065
+ * @example
62066
+ * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
62067
+ * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
62068
+ * editor.commands.updateSectionById({
62069
+ * id: 123,
62070
+ * html: '<p>Updated</p>',
62071
+ * attrs: { title: 'New Title' }
62072
+ * })
62073
+ */
62074
+ updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
62075
+ const sections = SectionHelpers.getAllSections(editor || this.editor);
62076
+ const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
62077
+ if (!sectionToUpdate) return false;
62078
+ const { pos, node } = sectionToUpdate;
62079
+ let newContent = null;
62080
+ if (html) {
62081
+ const htmlDoc = htmlHandler(html, editor || this.editor);
62082
+ const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
62083
+ newContent = doc2.content;
62084
+ }
62085
+ if (json) {
62086
+ newContent = (editor || this.editor).schema.nodeFromJSON(json);
62087
+ }
62088
+ if (!newContent) {
62089
+ newContent = node.content;
62090
+ }
62091
+ const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
62092
+ tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
62093
+ if (dispatch) {
62094
+ tr.setMeta("documentSection", { action: "update", id, attrs });
62095
+ dispatch(tr);
62096
+ }
62097
+ return true;
62098
+ }
62099
+ };
62100
+ },
62101
+ addHelpers() {
62102
+ return {
62103
+ ...SectionHelpers
62104
+ };
62105
+ }
62106
+ });
62107
+ const Document = Node$1.create({
62108
+ name: "doc",
62109
+ topNode: true,
62110
+ content: "block+",
62111
+ parseDOM() {
62112
+ return [{ tag: "doc" }];
62113
+ },
62114
+ renderDOM() {
62115
+ return ["doc", 0];
62116
+ },
62117
+ addAttributes() {
62118
+ return {
62119
+ attributes: {
62120
+ rendered: false,
62121
+ "aria-label": "Document node"
62122
+ }
62123
+ };
62124
+ },
62125
+ addCommands() {
62126
+ return {
62127
+ /**
62128
+ * Get document statistics
62129
+ * @category Command
62130
+ * @example
62131
+ * // Get word and character count
62132
+ * const stats = editor.commands.getDocumentStats()
62133
+ * console.log(`${stats.words} words, ${stats.characters} characters`)
62134
+ * @note Returns word count, character count, and paragraph count
62135
+ */
62136
+ getDocumentStats: () => ({ editor }) => {
62137
+ const text = editor.getText();
62138
+ const words = text.split(/\s+/).filter((word) => word.length > 0).length;
62139
+ const characters = text.length;
62140
+ const paragraphs = editor.state.doc.content.childCount;
62141
+ return {
62142
+ words,
62143
+ characters,
62144
+ paragraphs
62145
+ };
62146
+ },
62147
+ /**
62148
+ * Clear entire document
62149
+ * @category Command
62150
+ * @example
62151
+ * editor.commands.clearDocument()
62152
+ * @note Replaces all content with an empty paragraph
62153
+ */
62154
+ clearDocument: () => ({ commands: commands2 }) => {
62155
+ return commands2.setContent("<p></p>");
62156
+ }
62157
+ };
62158
+ }
62159
+ });
62160
+ const Text = Node$1.create({
62161
+ name: "text",
62162
+ group: "inline",
62163
+ inline: true,
62164
+ addOptions() {
62165
+ return {};
62166
+ }
62167
+ });
62168
+ const splitRun = () => (props) => {
62169
+ const { state: state2, view, tr } = props;
62170
+ const { $from, empty: empty2 } = state2.selection;
62171
+ if (!empty2) return false;
62172
+ if ($from.parent.type.name !== "run") return false;
62173
+ const handled = splitBlock(state2, (transaction) => {
62174
+ view.dispatch(transaction);
62175
+ });
62176
+ if (handled) {
62177
+ tr.setMeta("preventDispatch", true);
62178
+ }
62179
+ return handled;
62180
+ };
62181
+ const Run = OxmlNode.create({
62182
+ name: "run",
62183
+ oXmlName: "w:r",
62184
+ group: "inline",
62185
+ inline: true,
62186
+ content: "inline*",
62187
+ selectable: false,
62188
+ childToAttributes: ["runProperties"],
62189
+ addOptions() {
62190
+ return {
62191
+ htmlAttributes: {
62192
+ "data-run": "1"
62193
+ }
62194
+ };
62195
+ },
62196
+ addAttributes() {
62197
+ return {
62198
+ runProperties: {
62199
+ default: null,
62200
+ rendered: false,
62201
+ keepOnSplit: true
62202
+ },
62203
+ rsidR: {
62204
+ default: null,
62205
+ rendered: false,
62206
+ keepOnSplit: true
62207
+ },
62208
+ rsidRPr: {
62209
+ default: null,
62210
+ rendered: false,
62211
+ keepOnSplit: true
62212
+ },
62213
+ rsidDel: {
62214
+ default: null,
62215
+ rendered: false,
62216
+ keepOnSplit: true
62217
+ }
62218
+ };
62219
+ },
62220
+ addCommands() {
62221
+ return {
62222
+ splitRun
62223
+ };
62224
+ },
62225
+ parseDOM() {
62226
+ return [{ tag: "span[data-run]" }];
62227
+ },
62228
+ renderDOM({ htmlAttributes }) {
62229
+ const base2 = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62230
+ return ["span", base2, 0];
62231
+ }
62232
+ });
62233
+ const inputRegex$1 = /^\s*([-+*])\s$/;
62234
+ const BulletList = Node$1.create({
62235
+ name: "bulletList",
62236
+ group: "block list",
62237
+ selectable: false,
62238
+ content() {
62239
+ return `${this.options.itemTypeName}+`;
62240
+ },
62241
+ addOptions() {
62242
+ return {
62243
+ itemTypeName: "listItem",
62244
+ htmlAttributes: {
62245
+ "aria-label": "Bullet list node"
62246
+ },
62247
+ keepMarks: true,
62248
+ keepAttributes: false
62249
+ };
62250
+ },
62251
+ parseDOM() {
62252
+ return [{ tag: "ul" }];
62253
+ },
62254
+ renderDOM({ htmlAttributes }) {
62255
+ const attributes = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
62256
+ return ["ul", attributes, 0];
62257
+ },
62258
+ addAttributes() {
62259
+ return {
62260
+ "list-style-type": {
62261
+ default: "bullet",
62262
+ rendered: false
62263
+ },
62264
+ listId: {
62265
+ rendered: false
62266
+ },
62267
+ sdBlockId: {
62268
+ default: null,
62269
+ keepOnSplit: false,
62270
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
62271
+ renderDOM: (attrs) => {
62272
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
62273
+ }
62274
+ },
62275
+ attributes: {
62276
+ rendered: false,
62277
+ keepOnSplit: true
62278
+ }
62279
+ };
62280
+ },
62281
+ addCommands() {
62282
+ return {
62283
+ /**
62284
+ * Toggle a bullet list at the current selection
62285
+ * @category Command
62286
+ * @example
62287
+ * // Toggle bullet list on selected text
62288
+ * editor.commands.toggleBulletList()
62289
+ * @note Converts selected paragraphs to list items or removes list formatting
62290
+ */
62291
+ toggleBulletList: () => (params2) => {
62292
+ return toggleList(this.type)(params2);
62293
+ }
62294
+ };
62295
+ },
62296
+ addShortcuts() {
61273
62297
  return {
61274
62298
  "Mod-Shift-8": () => {
61275
62299
  return this.editor.commands.toggleBulletList();
@@ -62662,7 +63686,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62662
63686
  this.decorations = decorations;
62663
63687
  this.view = editor.view;
62664
63688
  this.getPos = getPos;
62665
- __privateMethod$1(this, _ListItemNodeView_instances, init_fn2).call(this);
63689
+ __privateMethod$1(this, _ListItemNodeView_instances, init_fn3).call(this);
62666
63690
  activeListItemNodeViews.add(this);
62667
63691
  }
62668
63692
  refreshIndentStyling() {
@@ -62723,7 +63747,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
62723
63747
  }
62724
63748
  }
62725
63749
  _ListItemNodeView_instances = /* @__PURE__ */ new WeakSet();
62726
- init_fn2 = function() {
63750
+ init_fn3 = function() {
62727
63751
  const { attrs } = this.node;
62728
63752
  const { listLevel, listNumberingType, lvlText, numId, level, customFormat } = attrs;
62729
63753
  let orderMarker = "";
@@ -69835,981 +70859,332 @@ Please report this to https://github.com/markedjs/marked.`, e) {
69835
70859
  addAttributes() {
69836
70860
  return {
69837
70861
  marksAsAttrs: {
69838
- default: null,
69839
- rendered: false
69840
- }
69841
- };
69842
- },
69843
- addNodeView() {
69844
- return ({ node, editor, getPos, decorations }) => {
69845
- const htmlAttributes = this.options.htmlAttributes;
69846
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
69847
- };
69848
- },
69849
- parseDOM() {
69850
- return [{ tag: 'span[data-id="auto-page-number"' }];
69851
- },
69852
- renderDOM({ htmlAttributes }) {
69853
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
69854
- },
69855
- addCommands() {
69856
- return {
69857
- /**
69858
- * Insert an automatic page number
69859
- * @category Command
69860
- * @returns {Function} Command function
69861
- * @example
69862
- * editor.commands.addAutoPageNumber()
69863
- * @note Only works in header/footer contexts
69864
- */
69865
- addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
69866
- const { options } = editor;
69867
- if (!options.isHeaderOrFooter) return false;
69868
- const { schema } = state2;
69869
- const pageNumberType = schema?.nodes?.["page-number"];
69870
- if (!pageNumberType) return false;
69871
- const pageNumberNodeJSON = { type: "page-number" };
69872
- const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
69873
- if (dispatch) {
69874
- tr.replaceSelectionWith(pageNumberNode, false);
69875
- tr.setMeta("forceUpdatePagination", true);
69876
- }
69877
- return true;
69878
- }
69879
- };
69880
- },
69881
- addShortcuts() {
69882
- return {
69883
- "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
69884
- };
69885
- }
69886
- });
69887
- const TotalPageCount = Node$1.create({
69888
- name: "total-page-number",
69889
- group: "inline",
69890
- inline: true,
69891
- atom: true,
69892
- draggable: false,
69893
- selectable: false,
69894
- content: "text*",
69895
- addOptions() {
69896
- return {
69897
- htmlAttributes: {
69898
- contenteditable: false,
69899
- "data-id": "auto-total-pages",
69900
- "aria-label": "Total page count node",
69901
- class: "sd-editor-auto-total-pages"
69902
- }
69903
- };
69904
- },
69905
- addAttributes() {
69906
- return {
69907
- marksAsAttrs: {
69908
- default: null,
69909
- rendered: false
69910
- }
69911
- };
69912
- },
69913
- addNodeView() {
69914
- return ({ node, editor, getPos, decorations }) => {
69915
- const htmlAttributes = this.options.htmlAttributes;
69916
- return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
69917
- };
69918
- },
69919
- parseDOM() {
69920
- return [{ tag: 'span[data-id="auto-total-pages"' }];
69921
- },
69922
- renderDOM({ htmlAttributes }) {
69923
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
69924
- },
69925
- addCommands() {
69926
- return {
69927
- /**
69928
- * Insert total page count
69929
- * @category Command
69930
- * @returns {Function} Command function
69931
- * @example
69932
- * editor.commands.addTotalPageCount()
69933
- * @note Only works in header/footer contexts
69934
- */
69935
- addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
69936
- const { options } = editor;
69937
- if (!options.isHeaderOrFooter) return false;
69938
- const { schema } = state2;
69939
- const pageNumberType = schema.nodes?.["total-page-number"];
69940
- if (!pageNumberType) return false;
69941
- const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
69942
- const pageNumberNode = {
69943
- type: "total-page-number",
69944
- content: [{ type: "text", text: String(currentPages) }]
69945
- };
69946
- const pageNode = schema.nodeFromJSON(pageNumberNode);
69947
- if (dispatch) {
69948
- tr.replaceSelectionWith(pageNode, false);
69949
- }
69950
- return true;
69951
- }
69952
- };
69953
- },
69954
- addShortcuts() {
69955
- return {
69956
- "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
69957
- };
69958
- }
69959
- });
69960
- const getNodeAttributes = (nodeName, editor) => {
69961
- switch (nodeName) {
69962
- case "page-number":
69963
- return {
69964
- text: editor.options.currentPageNumber || "1",
69965
- className: "sd-editor-auto-page-number",
69966
- dataId: "auto-page-number",
69967
- ariaLabel: "Page number node"
69968
- };
69969
- case "total-page-number":
69970
- return {
69971
- text: editor.options.parentEditor?.currentTotalPages || "1",
69972
- className: "sd-editor-auto-total-pages",
69973
- dataId: "auto-total-pages",
69974
- ariaLabel: "Total page count node"
69975
- };
69976
- default:
69977
- return {};
69978
- }
69979
- };
69980
- class AutoPageNumberNodeView {
69981
- constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
69982
- __privateAdd$1(this, _AutoPageNumberNodeView_instances);
69983
- this.node = node;
69984
- this.editor = editor;
69985
- this.view = editor.view;
69986
- this.getPos = getPos;
69987
- this.editor = editor;
69988
- this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
69989
- }
69990
- update(node) {
69991
- const incomingType = node?.type?.name;
69992
- const currentType = this.node?.type?.name;
69993
- if (!incomingType || incomingType !== currentType) return false;
69994
- this.node = node;
69995
- return true;
69996
- }
69997
- }
69998
- _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
69999
- renderDom_fn = function(node, htmlAttributes) {
70000
- const attrs = getNodeAttributes(this.node.type.name, this.editor);
70001
- const content = document.createTextNode(String(attrs.text));
70002
- const nodeContent = document.createElement("span");
70003
- nodeContent.className = attrs.className;
70004
- nodeContent.setAttribute("data-id", attrs.dataId);
70005
- nodeContent.setAttribute("aria-label", attrs.ariaLabel);
70006
- const currentPos = this.getPos();
70007
- const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
70008
- __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
70009
- Object.assign(nodeContent.style, styles);
70010
- nodeContent.appendChild(content);
70011
- Object.entries(htmlAttributes).forEach(([key2, value]) => {
70012
- if (value) nodeContent.setAttribute(key2, value);
70013
- });
70014
- return nodeContent;
70015
- };
70016
- scheduleUpdateNodeStyle_fn = function(pos, marks) {
70017
- setTimeout(() => {
70018
- const { state: state2 } = this.editor;
70019
- const { dispatch } = this.view;
70020
- const node = state2.doc.nodeAt(pos);
70021
- if (!node || node.isText) return;
70022
- const currentMarks = node.attrs.marksAsAttrs || [];
70023
- const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
70024
- const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
70025
- if (isEqual) return;
70026
- const newAttrs = {
70027
- ...node.attrs,
70028
- marksAsAttrs: newMarks
70029
- };
70030
- const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
70031
- dispatch(tr);
70032
- }, 0);
70033
- };
70034
- const getMarksFromNeighbors = (currentPos, view) => {
70035
- const $pos = view.state.doc.resolve(currentPos);
70036
- const styles = {};
70037
- const marks = [];
70038
- const before = $pos.nodeBefore;
70039
- if (before) {
70040
- Object.assign(styles, processMarks(before.marks));
70041
- marks.push(...before.marks);
70042
- }
70043
- const after = $pos.nodeAfter;
70044
- if (after) {
70045
- Object.assign(styles, { ...styles, ...processMarks(after.marks) });
70046
- marks.push(...after.marks);
70047
- }
70048
- return {
70049
- styles,
70050
- marks
70051
- };
70052
- };
70053
- const processMarks = (marks) => {
70054
- const styles = {};
70055
- marks.forEach((mark) => {
70056
- const { type: type2, attrs } = mark;
70057
- switch (type2.name) {
70058
- case "textStyle":
70059
- if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
70060
- if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
70061
- if (attrs.color) styles["color"] = attrs.color;
70062
- if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
70063
- break;
70064
- case "bold":
70065
- styles["font-weight"] = "bold";
70066
- break;
70067
- case "italic":
70068
- styles["font-style"] = "italic";
70069
- break;
70070
- case "underline":
70071
- styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
70072
- break;
70073
- case "strike":
70074
- styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
70075
- break;
70076
- default:
70077
- if (attrs?.style) {
70078
- Object.entries(attrs.style).forEach(([key2, value]) => {
70079
- styles[key2] = value;
70080
- });
70081
- }
70082
- break;
70083
- }
70084
- });
70085
- return styles;
70086
- };
70087
- const ShapeContainer = Node$1.create({
70088
- name: "shapeContainer",
70089
- group: "block",
70090
- content: "block+",
70091
- isolating: true,
70092
- addOptions() {
70093
- return {
70094
- htmlAttributes: {
70095
- class: "sd-editor-shape-container",
70096
- "aria-label": "Shape container node"
70097
- }
70098
- };
70099
- },
70100
- addAttributes() {
70101
- return {
70102
- fillcolor: {
70103
- renderDOM: (attrs) => {
70104
- if (!attrs.fillcolor) return {};
70105
- return {
70106
- style: `background-color: ${attrs.fillcolor}`
70107
- };
70108
- }
70109
- },
70110
- sdBlockId: {
70111
- default: null,
70112
- keepOnSplit: false,
70113
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70114
- renderDOM: (attrs) => {
70115
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70116
- }
70117
- },
70118
- style: {
70119
- renderDOM: (attrs) => {
70120
- if (!attrs.style) return {};
70121
- return {
70122
- style: attrs.style
70123
- };
70124
- }
70125
- },
70126
- wrapAttributes: {
70127
- rendered: false
70128
- },
70129
- attributes: {
70130
- rendered: false
70131
- }
70132
- };
70133
- },
70134
- parseDOM() {
70135
- return [
70136
- {
70137
- tag: `div[data-type="${this.name}"]`
70138
- }
70139
- ];
70140
- },
70141
- renderDOM({ htmlAttributes }) {
70142
- return [
70143
- "div",
70144
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70145
- 0
70146
- ];
70147
- }
70148
- });
70149
- const ShapeTextbox = Node$1.create({
70150
- name: "shapeTextbox",
70151
- group: "block",
70152
- content: "paragraph* block*",
70153
- isolating: true,
70154
- addOptions() {
70155
- return {
70156
- htmlAttributes: {
70157
- class: "sd-editor-shape-textbox",
70158
- "aria-label": "Shape textbox node"
70159
- }
70160
- };
70161
- },
70162
- addAttributes() {
70163
- return {
70164
- sdBlockId: {
70165
- default: null,
70166
- keepOnSplit: false,
70167
- parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70168
- renderDOM: (attrs) => {
70169
- return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70170
- }
70171
- },
70172
- attributes: {
70173
- rendered: false
70174
- }
70175
- };
70176
- },
70177
- parseDOM() {
70178
- return [
70179
- {
70180
- tag: `div[data-type="${this.name}"]`
70181
- }
70182
- ];
70183
- },
70184
- renderDOM({ htmlAttributes }) {
70185
- return [
70186
- "div",
70187
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70188
- 0
70189
- ];
70190
- }
70191
- });
70192
- const ContentBlock = Node$1.create({
70193
- name: "contentBlock",
70194
- group: "inline",
70195
- content: "",
70196
- isolating: true,
70197
- atom: true,
70198
- inline: true,
70199
- addOptions() {
70200
- return {
70201
- htmlAttributes: {
70202
- contenteditable: false
70203
- }
70204
- };
70205
- },
70206
- addAttributes() {
70207
- return {
70208
- horizontalRule: {
70209
- default: false,
70210
- renderDOM: ({ horizontalRule }) => {
70211
- if (!horizontalRule) return {};
70212
- return { "data-horizontal-rule": "true" };
70213
- }
70214
- },
70215
- size: {
70216
- default: null,
70217
- renderDOM: ({ size: size2 }) => {
70218
- if (!size2) return {};
70219
- let style2 = "";
70220
- if (size2.top) style2 += `top: ${size2.top}px; `;
70221
- if (size2.left) style2 += `left: ${size2.left}px; `;
70222
- if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
70223
- if (size2.height)
70224
- style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
70225
- return { style: style2 };
70226
- }
70227
- },
70228
- background: {
70229
- default: null,
70230
- renderDOM: (attrs) => {
70231
- if (!attrs.background) return {};
70232
- return {
70233
- style: `background-color: ${attrs.background}`
70234
- };
70235
- }
70236
- },
70237
- drawingContent: {
70238
- rendered: false
70239
- },
70240
- attributes: {
70862
+ default: null,
70241
70863
  rendered: false
70242
70864
  }
70243
70865
  };
70244
70866
  },
70867
+ addNodeView() {
70868
+ return ({ node, editor, getPos, decorations }) => {
70869
+ const htmlAttributes = this.options.htmlAttributes;
70870
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70871
+ };
70872
+ },
70245
70873
  parseDOM() {
70246
- return [
70247
- {
70248
- tag: `div[data-type="${this.name}"]`
70249
- }
70250
- ];
70874
+ return [{ tag: 'span[data-id="auto-page-number"' }];
70251
70875
  },
70252
70876
  renderDOM({ htmlAttributes }) {
70253
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
70877
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes)];
70254
70878
  },
70255
70879
  addCommands() {
70256
70880
  return {
70257
70881
  /**
70258
- * Insert a horizontal rule
70259
- * @category Command
70260
- * @example
70261
- * editor.commands.insertHorizontalRule()
70262
- * @note Creates a visual separator between content sections
70263
- */
70264
- insertHorizontalRule: () => ({ commands: commands2 }) => {
70265
- return commands2.insertContent({
70266
- type: this.name,
70267
- attrs: {
70268
- horizontalRule: true,
70269
- size: { width: "100%", height: 2 },
70270
- background: "#e5e7eb"
70271
- }
70272
- });
70273
- },
70274
- /**
70275
- * Insert a content block
70882
+ * Insert an automatic page number
70276
70883
  * @category Command
70277
- * @param {ContentBlockConfig} config - Block configuration
70278
- * @example
70279
- * // Insert a spacer block
70280
- * editor.commands.insertContentBlock({ size: { height: 20 } })
70281
- *
70884
+ * @returns {Function} Command function
70282
70885
  * @example
70283
- * // Insert a colored divider
70284
- * editor.commands.insertContentBlock({
70285
- * size: { width: '50%', height: 3 },
70286
- * background: '#3b82f6'
70287
- * })
70288
- * @note Used for spacing, dividers, and special inline content
70886
+ * editor.commands.addAutoPageNumber()
70887
+ * @note Only works in header/footer contexts
70289
70888
  */
70290
- insertContentBlock: (config2) => ({ commands: commands2 }) => {
70291
- return commands2.insertContent({
70292
- type: this.name,
70293
- attrs: config2
70294
- });
70889
+ addAutoPageNumber: () => ({ tr, dispatch, state: state2, editor }) => {
70890
+ const { options } = editor;
70891
+ if (!options.isHeaderOrFooter) return false;
70892
+ const { schema } = state2;
70893
+ const pageNumberType = schema?.nodes?.["page-number"];
70894
+ if (!pageNumberType) return false;
70895
+ const pageNumberNodeJSON = { type: "page-number" };
70896
+ const pageNumberNode = schema.nodeFromJSON(pageNumberNodeJSON);
70897
+ if (dispatch) {
70898
+ tr.replaceSelectionWith(pageNumberNode, false);
70899
+ tr.setMeta("forceUpdatePagination", true);
70900
+ }
70901
+ return true;
70295
70902
  }
70296
70903
  };
70904
+ },
70905
+ addShortcuts() {
70906
+ return {
70907
+ "Mod-Shift-alt-p": () => this.editor.commands.addAutoPageNumber()
70908
+ };
70297
70909
  }
70298
70910
  });
70299
- class StructuredContentViewBase {
70300
- constructor(props) {
70301
- __publicField$1(this, "node");
70302
- __publicField$1(this, "view");
70303
- __publicField$1(this, "getPos");
70304
- __publicField$1(this, "decorations");
70305
- __publicField$1(this, "innerDecorations");
70306
- __publicField$1(this, "editor");
70307
- __publicField$1(this, "extension");
70308
- __publicField$1(this, "htmlAttributes");
70309
- __publicField$1(this, "root");
70310
- __publicField$1(this, "isDragging", false);
70311
- this.node = props.node;
70312
- this.view = props.editor.view;
70313
- this.getPos = props.getPos;
70314
- this.decorations = props.decorations;
70315
- this.innerDecorations = props.innerDecorations;
70316
- this.editor = props.editor;
70317
- this.extension = props.extension;
70318
- this.htmlAttributes = props.htmlAttributes;
70319
- this.mount(props);
70320
- }
70321
- mount() {
70322
- return;
70323
- }
70324
- get dom() {
70325
- return this.root;
70326
- }
70327
- get contentDOM() {
70328
- return null;
70329
- }
70330
- update(node, decorations, innerDecorations) {
70331
- if (node.type !== this.node.type) {
70332
- return false;
70333
- }
70334
- this.node = node;
70335
- this.decorations = decorations;
70336
- this.innerDecorations = innerDecorations;
70337
- this.updateHTMLAttributes();
70338
- return true;
70339
- }
70340
- stopEvent(event) {
70341
- if (!this.dom) return false;
70342
- const target = event.target;
70343
- const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target);
70344
- if (!isInElement) return false;
70345
- const isDragEvent = event.type.startsWith("drag");
70346
- const isDropEvent = event.type === "drop";
70347
- const isInput = ["INPUT", "BUTTON", "SELECT", "TEXTAREA"].includes(target.tagName) || target.isContentEditable;
70348
- if (isInput && !isDropEvent && !isDragEvent) return true;
70349
- const { isEditable } = this.editor;
70350
- const { isDragging } = this;
70351
- const isDraggable = !!this.node.type.spec.draggable;
70352
- const isSelectable = NodeSelection.isSelectable(this.node);
70353
- const isCopyEvent = event.type === "copy";
70354
- const isPasteEvent = event.type === "paste";
70355
- const isCutEvent = event.type === "cut";
70356
- const isClickEvent = event.type === "mousedown";
70357
- if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {
70358
- event.preventDefault();
70359
- }
70360
- if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {
70361
- event.preventDefault();
70362
- return false;
70363
- }
70364
- if (isDraggable && isEditable && !isDragging && isClickEvent) {
70365
- const dragHandle = target.closest("[data-drag-handle]");
70366
- const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle));
70367
- if (isValidDragHandle) {
70368
- this.isDragging = true;
70369
- document.addEventListener(
70370
- "dragend",
70371
- () => {
70372
- this.isDragging = false;
70373
- },
70374
- { once: true }
70375
- );
70376
- document.addEventListener(
70377
- "drop",
70378
- () => {
70379
- this.isDragging = false;
70380
- },
70381
- { once: true }
70382
- );
70383
- document.addEventListener(
70384
- "mouseup",
70385
- () => {
70386
- this.isDragging = false;
70387
- },
70388
- { once: true }
70389
- );
70390
- }
70391
- }
70392
- if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || isClickEvent && isSelectable) {
70393
- return false;
70394
- }
70395
- return true;
70396
- }
70397
- ignoreMutation(mutation) {
70398
- if (!this.dom || !this.contentDOM) return true;
70399
- if (this.node.isLeaf || this.node.isAtom) return true;
70400
- if (mutation.type === "selection") return false;
70401
- if (this.contentDOM === mutation.target && mutation.type === "attributes") return true;
70402
- if (this.contentDOM.contains(mutation.target)) return false;
70403
- return true;
70404
- }
70405
- destroy() {
70406
- this.dom.remove();
70407
- this.contentDOM?.remove();
70408
- }
70409
- updateAttributes(attrs) {
70410
- const pos = this.getPos();
70411
- if (typeof pos !== "number") {
70412
- return;
70413
- }
70414
- return this.view.dispatch(
70415
- this.view.state.tr.setNodeMarkup(pos, void 0, {
70416
- ...this.node.attrs,
70417
- ...attrs
70418
- })
70419
- );
70420
- }
70421
- updateHTMLAttributes() {
70422
- const { extensionService } = this.editor;
70423
- const { attributes } = extensionService;
70424
- const extensionAttrs = attributes.filter((i2) => i2.type === this.node.type.name);
70425
- this.htmlAttributes = Attribute.getAttributesToRender(this.node, extensionAttrs);
70426
- }
70427
- createDragHandle() {
70428
- const dragHandle = document.createElement("span");
70429
- dragHandle.classList.add("sd-structured-content-draggable");
70430
- dragHandle.draggable = true;
70431
- dragHandle.contentEditable = "false";
70432
- dragHandle.dataset.dragHandle = "";
70433
- const textElement = document.createElement("span");
70434
- textElement.textContent = "Structured content";
70435
- dragHandle.append(textElement);
70436
- return dragHandle;
70437
- }
70438
- onDragStart(event) {
70439
- const { view } = this.editor;
70440
- const target = event.target;
70441
- const dragHandle = target.nodeType === 3 ? target.parentElement?.closest("[data-drag-handle]") : target.closest("[data-drag-handle]");
70442
- if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {
70443
- return;
70444
- }
70445
- let x = 0;
70446
- let y2 = 0;
70447
- if (this.dom !== dragHandle) {
70448
- const domBox = this.dom.getBoundingClientRect();
70449
- const handleBox = dragHandle.getBoundingClientRect();
70450
- const offsetX = event.offsetX ?? event.nativeEvent?.offsetX;
70451
- const offsetY = event.offsetY ?? event.nativeEvent?.offsetY;
70452
- x = handleBox.x - domBox.x + offsetX;
70453
- y2 = handleBox.y - domBox.y + offsetY;
70454
- }
70455
- event.dataTransfer?.setDragImage(this.dom, x, y2);
70456
- const pos = this.getPos();
70457
- if (typeof pos !== "number") {
70458
- return;
70459
- }
70460
- const selection = NodeSelection.create(view.state.doc, pos);
70461
- const transaction = view.state.tr.setSelection(selection);
70462
- view.dispatch(transaction);
70463
- }
70464
- }
70465
- class StructuredContentInlineView extends StructuredContentViewBase {
70466
- constructor(props) {
70467
- super(props);
70468
- }
70469
- mount() {
70470
- this.buildView();
70471
- }
70472
- get contentDOM() {
70473
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass$1}`);
70474
- return contentElement || null;
70475
- }
70476
- createElement() {
70477
- const element = document.createElement("span");
70478
- element.classList.add(structuredContentClass$1);
70479
- element.setAttribute("data-structured-content", "");
70480
- const contentElement = document.createElement("span");
70481
- contentElement.classList.add(structuredContentInnerClass$1);
70482
- element.append(contentElement);
70483
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70484
- updateDOMAttributes(element, { ...domAttrs });
70485
- return { element, contentElement };
70486
- }
70487
- buildView() {
70488
- const { element } = this.createElement();
70489
- const dragHandle = this.createDragHandle();
70490
- element.prepend(dragHandle);
70491
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
70492
- this.root = element;
70493
- }
70494
- updateView() {
70495
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70496
- updateDOMAttributes(this.dom, { ...domAttrs });
70497
- }
70498
- update(node, decorations, innerDecorations) {
70499
- const result = super.update(node, decorations, innerDecorations);
70500
- if (!result) return false;
70501
- this.updateView();
70502
- return true;
70503
- }
70504
- }
70505
- const structuredContentClass$1 = "sd-structured-content";
70506
- const structuredContentInnerClass$1 = "sd-structured-content__content";
70507
- const StructuredContent = Node$1.create({
70508
- name: "structuredContent",
70509
- group: "inline structuredContent",
70911
+ const TotalPageCount = Node$1.create({
70912
+ name: "total-page-number",
70913
+ group: "inline",
70510
70914
  inline: true,
70511
- content: "inline*",
70512
- isolating: true,
70513
- atom: false,
70514
- // false - has editable content.
70515
- draggable: true,
70915
+ atom: true,
70916
+ draggable: false,
70917
+ selectable: false,
70918
+ content: "text*",
70516
70919
  addOptions() {
70517
70920
  return {
70518
70921
  htmlAttributes: {
70519
- class: structuredContentClass$1,
70520
- "aria-label": "Structured content node"
70922
+ contenteditable: false,
70923
+ "data-id": "auto-total-pages",
70924
+ "aria-label": "Total page count node",
70925
+ class: "sd-editor-auto-total-pages"
70521
70926
  }
70522
70927
  };
70523
70928
  },
70524
70929
  addAttributes() {
70525
70930
  return {
70526
- id: {
70931
+ marksAsAttrs: {
70527
70932
  default: null,
70528
- parseDOM: (elem) => elem.getAttribute("data-id"),
70529
- renderDOM: (attrs) => {
70530
- if (!attrs.id) return {};
70531
- return { "data-id": attrs.id };
70532
- }
70533
- },
70534
- sdtPr: {
70535
70933
  rendered: false
70536
70934
  }
70537
70935
  };
70538
70936
  },
70937
+ addNodeView() {
70938
+ return ({ node, editor, getPos, decorations }) => {
70939
+ const htmlAttributes = this.options.htmlAttributes;
70940
+ return new AutoPageNumberNodeView(node, getPos, decorations, editor, htmlAttributes);
70941
+ };
70942
+ },
70539
70943
  parseDOM() {
70540
- return [{ tag: "span[data-structured-content]" }];
70944
+ return [{ tag: 'span[data-id="auto-total-pages"' }];
70541
70945
  },
70542
70946
  renderDOM({ htmlAttributes }) {
70543
- return [
70544
- "span",
70545
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70546
- "data-structured-content": ""
70547
- }),
70548
- 0
70549
- ];
70947
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
70550
70948
  },
70551
- addNodeView() {
70552
- return (props) => {
70553
- return new StructuredContentInlineView({ ...props });
70949
+ addCommands() {
70950
+ return {
70951
+ /**
70952
+ * Insert total page count
70953
+ * @category Command
70954
+ * @returns {Function} Command function
70955
+ * @example
70956
+ * editor.commands.addTotalPageCount()
70957
+ * @note Only works in header/footer contexts
70958
+ */
70959
+ addTotalPageCount: () => ({ tr, dispatch, state: state2, editor }) => {
70960
+ const { options } = editor;
70961
+ if (!options.isHeaderOrFooter) return false;
70962
+ const { schema } = state2;
70963
+ const pageNumberType = schema.nodes?.["total-page-number"];
70964
+ if (!pageNumberType) return false;
70965
+ const currentPages = editor?.options?.parentEditor?.currentTotalPages || 1;
70966
+ const pageNumberNode = {
70967
+ type: "total-page-number",
70968
+ content: [{ type: "text", text: String(currentPages) }]
70969
+ };
70970
+ const pageNode = schema.nodeFromJSON(pageNumberNode);
70971
+ if (dispatch) {
70972
+ tr.replaceSelectionWith(pageNode, false);
70973
+ }
70974
+ return true;
70975
+ }
70976
+ };
70977
+ },
70978
+ addShortcuts() {
70979
+ return {
70980
+ "Mod-Shift-alt-c": () => this.editor.commands.addTotalPageCount()
70554
70981
  };
70555
70982
  }
70556
70983
  });
70557
- class StructuredContentBlockView extends StructuredContentViewBase {
70558
- constructor(props) {
70559
- super(props);
70560
- }
70561
- mount() {
70562
- this.buildView();
70563
- }
70564
- get contentDOM() {
70565
- const contentElement = this.dom?.querySelector(`.${structuredContentInnerClass}`);
70566
- return contentElement || null;
70567
- }
70568
- createElement() {
70569
- const element = document.createElement("div");
70570
- element.classList.add(structuredContentClass);
70571
- element.setAttribute("data-structured-content-block", "");
70572
- const contentElement = document.createElement("div");
70573
- contentElement.classList.add(structuredContentInnerClass);
70574
- element.append(contentElement);
70575
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70576
- updateDOMAttributes(element, { ...domAttrs });
70577
- return { element, contentElement };
70578
- }
70579
- buildView() {
70580
- const { element } = this.createElement();
70581
- const dragHandle = this.createDragHandle();
70582
- element.prepend(dragHandle);
70583
- element.addEventListener("dragstart", (e) => this.onDragStart(e));
70584
- this.root = element;
70984
+ const getNodeAttributes = (nodeName, editor) => {
70985
+ switch (nodeName) {
70986
+ case "page-number":
70987
+ return {
70988
+ text: editor.options.currentPageNumber || "1",
70989
+ className: "sd-editor-auto-page-number",
70990
+ dataId: "auto-page-number",
70991
+ ariaLabel: "Page number node"
70992
+ };
70993
+ case "total-page-number":
70994
+ return {
70995
+ text: editor.options.parentEditor?.currentTotalPages || "1",
70996
+ className: "sd-editor-auto-total-pages",
70997
+ dataId: "auto-total-pages",
70998
+ ariaLabel: "Total page count node"
70999
+ };
71000
+ default:
71001
+ return {};
70585
71002
  }
70586
- updateView() {
70587
- const domAttrs = Attribute.mergeAttributes(this.htmlAttributes);
70588
- updateDOMAttributes(this.dom, { ...domAttrs });
71003
+ };
71004
+ class AutoPageNumberNodeView {
71005
+ constructor(node, getPos, decorations, editor, htmlAttributes = {}) {
71006
+ __privateAdd$1(this, _AutoPageNumberNodeView_instances);
71007
+ this.node = node;
71008
+ this.editor = editor;
71009
+ this.view = editor.view;
71010
+ this.getPos = getPos;
71011
+ this.editor = editor;
71012
+ this.dom = __privateMethod$1(this, _AutoPageNumberNodeView_instances, renderDom_fn).call(this, node, htmlAttributes);
70589
71013
  }
70590
- update(node, decorations, innerDecorations) {
70591
- const result = super.update(node, decorations, innerDecorations);
70592
- if (!result) return false;
70593
- this.updateView();
71014
+ update(node) {
71015
+ const incomingType = node?.type?.name;
71016
+ const currentType = this.node?.type?.name;
71017
+ if (!incomingType || incomingType !== currentType) return false;
71018
+ this.node = node;
70594
71019
  return true;
70595
71020
  }
70596
71021
  }
70597
- const structuredContentClass = "sd-structured-content-block";
70598
- const structuredContentInnerClass = "sd-structured-content-block__content";
70599
- const StructuredContentBlock = Node$1.create({
70600
- name: "structuredContentBlock",
70601
- group: "block structuredContent",
70602
- content: "block*",
71022
+ _AutoPageNumberNodeView_instances = /* @__PURE__ */ new WeakSet();
71023
+ renderDom_fn = function(node, htmlAttributes) {
71024
+ const attrs = getNodeAttributes(this.node.type.name, this.editor);
71025
+ const content = document.createTextNode(String(attrs.text));
71026
+ const nodeContent = document.createElement("span");
71027
+ nodeContent.className = attrs.className;
71028
+ nodeContent.setAttribute("data-id", attrs.dataId);
71029
+ nodeContent.setAttribute("aria-label", attrs.ariaLabel);
71030
+ const currentPos = this.getPos();
71031
+ const { styles, marks } = getMarksFromNeighbors(currentPos, this.view);
71032
+ __privateMethod$1(this, _AutoPageNumberNodeView_instances, scheduleUpdateNodeStyle_fn).call(this, currentPos, marks);
71033
+ Object.assign(nodeContent.style, styles);
71034
+ nodeContent.appendChild(content);
71035
+ Object.entries(htmlAttributes).forEach(([key2, value]) => {
71036
+ if (value) nodeContent.setAttribute(key2, value);
71037
+ });
71038
+ return nodeContent;
71039
+ };
71040
+ scheduleUpdateNodeStyle_fn = function(pos, marks) {
71041
+ setTimeout(() => {
71042
+ const { state: state2 } = this.editor;
71043
+ const { dispatch } = this.view;
71044
+ const node = state2.doc.nodeAt(pos);
71045
+ if (!node || node.isText) return;
71046
+ const currentMarks = node.attrs.marksAsAttrs || [];
71047
+ const newMarks = marks.map((m2) => ({ type: m2.type.name, attrs: m2.attrs }));
71048
+ const isEqual = JSON.stringify(currentMarks) === JSON.stringify(newMarks);
71049
+ if (isEqual) return;
71050
+ const newAttrs = {
71051
+ ...node.attrs,
71052
+ marksAsAttrs: newMarks
71053
+ };
71054
+ const tr = state2.tr.setNodeMarkup(pos, void 0, newAttrs);
71055
+ dispatch(tr);
71056
+ }, 0);
71057
+ };
71058
+ const getMarksFromNeighbors = (currentPos, view) => {
71059
+ const $pos = view.state.doc.resolve(currentPos);
71060
+ const styles = {};
71061
+ const marks = [];
71062
+ const before = $pos.nodeBefore;
71063
+ if (before) {
71064
+ Object.assign(styles, processMarks(before.marks));
71065
+ marks.push(...before.marks);
71066
+ }
71067
+ const after = $pos.nodeAfter;
71068
+ if (after) {
71069
+ Object.assign(styles, { ...styles, ...processMarks(after.marks) });
71070
+ marks.push(...after.marks);
71071
+ }
71072
+ return {
71073
+ styles,
71074
+ marks
71075
+ };
71076
+ };
71077
+ const processMarks = (marks) => {
71078
+ const styles = {};
71079
+ marks.forEach((mark) => {
71080
+ const { type: type2, attrs } = mark;
71081
+ switch (type2.name) {
71082
+ case "textStyle":
71083
+ if (attrs.fontFamily) styles["font-family"] = attrs.fontFamily;
71084
+ if (attrs.fontSize) styles["font-size"] = attrs.fontSize;
71085
+ if (attrs.color) styles["color"] = attrs.color;
71086
+ if (attrs.backgroundColor) styles["background-color"] = attrs.backgroundColor;
71087
+ break;
71088
+ case "bold":
71089
+ styles["font-weight"] = "bold";
71090
+ break;
71091
+ case "italic":
71092
+ styles["font-style"] = "italic";
71093
+ break;
71094
+ case "underline":
71095
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " underline";
71096
+ break;
71097
+ case "strike":
71098
+ styles["text-decoration"] = (styles["text-decoration"] || "") + " line-through";
71099
+ break;
71100
+ default:
71101
+ if (attrs?.style) {
71102
+ Object.entries(attrs.style).forEach(([key2, value]) => {
71103
+ styles[key2] = value;
71104
+ });
71105
+ }
71106
+ break;
71107
+ }
71108
+ });
71109
+ return styles;
71110
+ };
71111
+ const ShapeContainer = Node$1.create({
71112
+ name: "shapeContainer",
71113
+ group: "block",
71114
+ content: "block+",
70603
71115
  isolating: true,
70604
- atom: false,
70605
- // false - has editable content.
70606
- draggable: true,
70607
71116
  addOptions() {
70608
71117
  return {
70609
71118
  htmlAttributes: {
70610
- class: structuredContentClass,
70611
- "aria-label": "Structured content block node"
71119
+ class: "sd-editor-shape-container",
71120
+ "aria-label": "Shape container node"
70612
71121
  }
70613
71122
  };
70614
71123
  },
70615
71124
  addAttributes() {
70616
71125
  return {
70617
- id: {
71126
+ fillcolor: {
71127
+ renderDOM: (attrs) => {
71128
+ if (!attrs.fillcolor) return {};
71129
+ return {
71130
+ style: `background-color: ${attrs.fillcolor}`
71131
+ };
71132
+ }
71133
+ },
71134
+ sdBlockId: {
70618
71135
  default: null,
70619
- parseDOM: (elem) => elem.getAttribute("data-id"),
71136
+ keepOnSplit: false,
71137
+ parseDOM: (elem) => elem.getAttribute("data-sd-block-id"),
70620
71138
  renderDOM: (attrs) => {
70621
- if (!attrs.id) return {};
70622
- return { "data-id": attrs.id };
71139
+ return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70623
71140
  }
70624
71141
  },
70625
- sdtPr: {
71142
+ style: {
71143
+ renderDOM: (attrs) => {
71144
+ if (!attrs.style) return {};
71145
+ return {
71146
+ style: attrs.style
71147
+ };
71148
+ }
71149
+ },
71150
+ wrapAttributes: {
71151
+ rendered: false
71152
+ },
71153
+ attributes: {
70626
71154
  rendered: false
70627
71155
  }
70628
71156
  };
70629
71157
  },
70630
71158
  parseDOM() {
70631
- return [{ tag: "div[data-structured-content-block]" }];
71159
+ return [
71160
+ {
71161
+ tag: `div[data-type="${this.name}"]`
71162
+ }
71163
+ ];
70632
71164
  },
70633
71165
  renderDOM({ htmlAttributes }) {
70634
71166
  return [
70635
71167
  "div",
70636
- Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, {
70637
- "data-structured-content-block": ""
70638
- }),
71168
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
70639
71169
  0
70640
71170
  ];
70641
- },
70642
- addNodeView() {
70643
- return (props) => {
70644
- return new StructuredContentBlockView({ ...props });
70645
- };
70646
71171
  }
70647
71172
  });
70648
- class DocumentSectionView {
70649
- constructor(node, getPos, decorations, editor) {
70650
- __privateAdd$1(this, _DocumentSectionView_instances);
70651
- this.node = node;
70652
- this.editor = editor;
70653
- this.decorations = decorations;
70654
- this.view = editor.view;
70655
- this.getPos = getPos;
70656
- __privateMethod$1(this, _DocumentSectionView_instances, init_fn3).call(this);
70657
- }
70658
- }
70659
- _DocumentSectionView_instances = /* @__PURE__ */ new WeakSet();
70660
- init_fn3 = function() {
70661
- const { attrs } = this.node;
70662
- const { id, title, description } = attrs;
70663
- this.dom = document.createElement("div");
70664
- this.dom.className = "sd-document-section-block";
70665
- this.dom.setAttribute("data-id", id);
70666
- this.dom.setAttribute("data-title", title);
70667
- this.dom.setAttribute("data-description", description);
70668
- this.dom.setAttribute("aria-label", "Document section");
70669
- __privateMethod$1(this, _DocumentSectionView_instances, addToolTip_fn).call(this);
70670
- this.contentDOM = document.createElement("div");
70671
- this.contentDOM.className = "sd-document-section-block-content";
70672
- this.contentDOM.setAttribute("contenteditable", "true");
70673
- this.dom.appendChild(this.contentDOM);
70674
- };
70675
- addToolTip_fn = function() {
70676
- const { title } = this.node.attrs;
70677
- this.infoDiv = document.createElement("div");
70678
- this.infoDiv.className = "sd-document-section-block-info";
70679
- const textSpan = document.createElement("span");
70680
- textSpan.textContent = title || "Document section";
70681
- this.infoDiv.appendChild(textSpan);
70682
- this.infoDiv.setAttribute("contenteditable", "false");
70683
- this.dom.appendChild(this.infoDiv);
70684
- };
70685
- const getAllSections = (editor) => {
70686
- if (!editor) return [];
70687
- const type2 = editor.schema.nodes.documentSection;
70688
- if (!type2) return [];
70689
- const sections = [];
70690
- const { state: state2 } = editor;
70691
- state2.doc.descendants((node, pos) => {
70692
- if (node.type.name === type2.name) {
70693
- sections.push({ node, pos });
70694
- }
70695
- });
70696
- return sections;
70697
- };
70698
- const exportSectionsToHTML = (editor) => {
70699
- const sections = getAllSections(editor);
70700
- const processedSections = /* @__PURE__ */ new Set();
70701
- const result = [];
70702
- sections.forEach(({ node }) => {
70703
- const { attrs } = node;
70704
- const { id, title, description } = attrs;
70705
- if (processedSections.has(id)) return;
70706
- processedSections.add(id);
70707
- const html = getHTMLFromNode(node, editor);
70708
- result.push({
70709
- id,
70710
- title,
70711
- description,
70712
- html
70713
- });
70714
- });
70715
- return result;
70716
- };
70717
- const getHTMLFromNode = (node, editor) => {
70718
- const tempDocument = document.implementation.createHTMLDocument();
70719
- const container = tempDocument.createElement("div");
70720
- const fragment = DOMSerializer.fromSchema(editor.schema).serializeFragment(node.content);
70721
- container.appendChild(fragment);
70722
- let html = container.innerHTML;
70723
- return html;
70724
- };
70725
- const exportSectionsToJSON = (editor) => {
70726
- const sections = getAllSections(editor);
70727
- const processedSections = /* @__PURE__ */ new Set();
70728
- const result = [];
70729
- sections.forEach(({ node }) => {
70730
- const { attrs } = node;
70731
- const { id, title, description } = attrs;
70732
- if (processedSections.has(id)) return;
70733
- processedSections.add(id);
70734
- result.push({
70735
- id,
70736
- title,
70737
- description,
70738
- content: node.toJSON()
70739
- });
70740
- });
70741
- return result;
70742
- };
70743
- const getLinkedSectionEditor = (id, options, editor) => {
70744
- const sections = getAllSections(editor);
70745
- const section = sections.find((s) => s.node.attrs.id === id);
70746
- if (!section) return null;
70747
- const child = editor.createChildEditor({
70748
- ...options,
70749
- onUpdate: ({ editor: childEditor, transaction }) => {
70750
- const isFromtLinkedParent = transaction.getMeta("fromLinkedParent");
70751
- if (isFromtLinkedParent) return;
70752
- const updatedContent = childEditor.state.doc.content;
70753
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
70754
- if (!sectionNode) return;
70755
- const { pos, node } = sectionNode;
70756
- const newNode = node.type.create(node.attrs, updatedContent, node.marks);
70757
- const tr = editor.state.tr.replaceWith(pos, pos + node.nodeSize, newNode);
70758
- tr.setMeta("fromLinkedChild", true);
70759
- editor.view.dispatch(tr);
70760
- }
70761
- });
70762
- editor.on("update", ({ transaction }) => {
70763
- const isFromLinkedChild = transaction.getMeta("fromLinkedChild");
70764
- if (isFromLinkedChild) return;
70765
- const sectionNode = getAllSections(editor)?.find((s) => s.node.attrs.id === id);
70766
- if (!sectionNode) return;
70767
- const sectionContent = sectionNode.node.content;
70768
- const json = {
70769
- type: "doc",
70770
- content: sectionContent.content.map((node) => node.toJSON())
70771
- };
70772
- const childTr = child.state.tr;
70773
- childTr.setMeta("fromLinkedParent", true);
70774
- childTr.replaceWith(0, child.state.doc.content.size, child.schema.nodeFromJSON(json));
70775
- child.view.dispatch(childTr);
70776
- });
70777
- return child;
70778
- };
70779
- const SectionHelpers = {
70780
- getAllSections,
70781
- exportSectionsToHTML,
70782
- exportSectionsToJSON,
70783
- getLinkedSectionEditor
70784
- };
70785
- const DocumentSection = Node$1.create({
70786
- name: "documentSection",
71173
+ const ShapeTextbox = Node$1.create({
71174
+ name: "shapeTextbox",
70787
71175
  group: "block",
70788
- content: "block*",
70789
- atom: true,
71176
+ content: "paragraph* block*",
70790
71177
  isolating: true,
70791
71178
  addOptions() {
70792
71179
  return {
70793
71180
  htmlAttributes: {
70794
- class: "sd-document-section-block",
70795
- "aria-label": "Structured content block"
71181
+ class: "sd-editor-shape-textbox",
71182
+ "aria-label": "Shape textbox node"
70796
71183
  }
70797
71184
  };
70798
71185
  },
70799
- parseDOM() {
70800
- return [
70801
- {
70802
- tag: "div.sd-document-section-block",
70803
- priority: 60
70804
- }
70805
- ];
70806
- },
70807
- renderDOM({ htmlAttributes }) {
70808
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
70809
- },
70810
71186
  addAttributes() {
70811
71187
  return {
70812
- id: {},
70813
71188
  sdBlockId: {
70814
71189
  default: null,
70815
71190
  keepOnSplit: false,
@@ -70818,212 +71193,131 @@ Please report this to https://github.com/markedjs/marked.`, e) {
70818
71193
  return attrs.sdBlockId ? { "data-sd-block-id": attrs.sdBlockId } : {};
70819
71194
  }
70820
71195
  },
70821
- title: {},
70822
- description: {},
70823
- sectionType: {},
70824
- isLocked: { default: false }
71196
+ attributes: {
71197
+ rendered: false
71198
+ }
70825
71199
  };
70826
71200
  },
70827
- addNodeView() {
70828
- return ({ node, editor, getPos, decorations }) => {
70829
- return new DocumentSectionView(node, getPos, decorations, editor);
71201
+ parseDOM() {
71202
+ return [
71203
+ {
71204
+ tag: `div[data-type="${this.name}"]`
71205
+ }
71206
+ ];
71207
+ },
71208
+ renderDOM({ htmlAttributes }) {
71209
+ return [
71210
+ "div",
71211
+ Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name }),
71212
+ 0
71213
+ ];
71214
+ }
71215
+ });
71216
+ const ContentBlock = Node$1.create({
71217
+ name: "contentBlock",
71218
+ group: "inline",
71219
+ content: "",
71220
+ isolating: true,
71221
+ atom: true,
71222
+ inline: true,
71223
+ addOptions() {
71224
+ return {
71225
+ htmlAttributes: {
71226
+ contenteditable: false
71227
+ }
70830
71228
  };
70831
71229
  },
70832
- addCommands() {
71230
+ addAttributes() {
70833
71231
  return {
70834
- /**
70835
- * Create a lockable content section
70836
- * @category Command
70837
- * @param {SectionCreate} [options={}] - Section configuration
70838
- * @example
70839
- * editor.commands.createDocumentSection({
70840
- * id: 1,
70841
- * title: 'Terms & Conditions',
70842
- * isLocked: true,
70843
- * html: '<p>Legal content...</p>'
70844
- * })
70845
- */
70846
- createDocumentSection: (options = {}) => ({ tr, state: state2, dispatch, editor }) => {
70847
- const { selection } = state2;
70848
- let { from: from2, to } = selection;
70849
- let content = selection.content().content;
70850
- const { html: optionsHTML, json: optionsJSON } = options;
70851
- if (optionsHTML) {
70852
- const html = htmlHandler(optionsHTML, this.editor);
70853
- const doc2 = DOMParser$1.fromSchema(this.editor.schema).parse(html);
70854
- content = doc2.content;
70855
- }
70856
- if (optionsJSON) {
70857
- content = this.editor.schema.nodeFromJSON(optionsJSON);
70858
- }
70859
- if (!content?.content?.length) {
70860
- content = this.editor.schema.nodeFromJSON({ type: "paragraph", content: [] });
70861
- }
70862
- if (!options.id) {
70863
- const allSections = SectionHelpers.getAllSections(editor);
70864
- options.id = allSections.length + 1;
70865
- }
70866
- if (!options.title) {
70867
- options.title = "Document section";
70868
- }
70869
- const node = this.type.createAndFill(options, content);
70870
- if (!node) return false;
70871
- const isAlreadyInSdtBlock = findParentNode((node2) => node2.type.name === "documentSection")(selection);
70872
- if (isAlreadyInSdtBlock && isAlreadyInSdtBlock.node) {
70873
- const insertPos2 = isAlreadyInSdtBlock.pos + isAlreadyInSdtBlock.node.nodeSize;
70874
- from2 = insertPos2;
70875
- to = insertPos2;
70876
- }
70877
- tr.replaceRangeWith(from2, to, node);
70878
- const nodeEnd = from2 + node.nodeSize;
70879
- let shouldInsertParagraph = true;
70880
- let insertPos = nodeEnd;
70881
- if (nodeEnd >= tr.doc.content.size) {
70882
- insertPos = tr.doc.content.size;
70883
- if (insertPos > 0) {
70884
- const $endPos = tr.doc.resolve(insertPos);
70885
- if ($endPos.nodeBefore && $endPos.nodeBefore.type.name === "paragraph") {
70886
- shouldInsertParagraph = false;
70887
- }
70888
- }
70889
- }
70890
- if (shouldInsertParagraph) {
70891
- const emptyParagraph = tr.doc.type.schema.nodes.paragraph.create();
70892
- tr.insert(insertPos, emptyParagraph);
70893
- }
70894
- if (dispatch) {
70895
- tr.setMeta("documentSection", { action: "create" });
70896
- dispatch(tr);
70897
- setTimeout(() => {
70898
- try {
70899
- const currentState = editor.state;
70900
- const docSize = currentState.doc.content.size;
70901
- let targetPos = from2 + node.nodeSize;
70902
- if (shouldInsertParagraph) {
70903
- targetPos += 1;
70904
- }
70905
- targetPos = Math.min(targetPos, docSize);
70906
- if (targetPos < docSize && targetPos > 0) {
70907
- const newSelection = Selection.near(currentState.doc.resolve(targetPos));
70908
- const newTr = currentState.tr.setSelection(newSelection);
70909
- editor.view.dispatch(newTr);
70910
- }
70911
- } catch (e) {
70912
- console.warn("Could not set delayed selection:", e);
70913
- }
70914
- }, 0);
71232
+ horizontalRule: {
71233
+ default: false,
71234
+ renderDOM: ({ horizontalRule }) => {
71235
+ if (!horizontalRule) return {};
71236
+ return { "data-horizontal-rule": "true" };
70915
71237
  }
70916
- return true;
70917
71238
  },
70918
- /**
70919
- * Remove section wrapper at cursor, preserving its content
70920
- * @category Command
70921
- * @example
70922
- * editor.commands.removeSectionAtSelection()
70923
- * @note Content stays in document, only section wrapper is removed
70924
- */
70925
- removeSectionAtSelection: () => ({ tr, dispatch }) => {
70926
- const sdtNode = findParentNode((node2) => node2.type.name === "documentSection")(tr.selection);
70927
- if (!sdtNode) return false;
70928
- const { node, pos } = sdtNode;
70929
- const nodeStart = pos;
70930
- const nodeEnd = nodeStart + node.nodeSize;
70931
- const contentToPreserve = node.content;
70932
- tr.delete(nodeStart, nodeEnd);
70933
- if (contentToPreserve.size > 0) {
70934
- tr.insert(nodeStart, contentToPreserve);
70935
- }
70936
- const newPos = Math.min(nodeStart, tr.doc.content.size);
70937
- tr.setSelection(Selection.near(tr.doc.resolve(newPos)));
70938
- if (dispatch) {
70939
- tr.setMeta("documentSection", { action: "delete" });
70940
- dispatch(tr);
71239
+ size: {
71240
+ default: null,
71241
+ renderDOM: ({ size: size2 }) => {
71242
+ if (!size2) return {};
71243
+ let style2 = "";
71244
+ if (size2.top) style2 += `top: ${size2.top}px; `;
71245
+ if (size2.left) style2 += `left: ${size2.left}px; `;
71246
+ if (size2.width) style2 += `width: ${size2.width.toString().endsWith("%") ? size2.width : `${size2.width}px`}; `;
71247
+ if (size2.height)
71248
+ style2 += `height: ${size2.height.toString().endsWith("%") ? size2.height : `${size2.height}px`}; `;
71249
+ return { style: style2 };
70941
71250
  }
70942
- return true;
70943
71251
  },
70944
- /**
70945
- * Delete section and all its content
70946
- * @category Command
70947
- * @param {number} id - Section to delete
70948
- * @example
70949
- * editor.commands.removeSectionById(123)
70950
- */
70951
- removeSectionById: (id) => ({ tr, dispatch }) => {
70952
- const sections = SectionHelpers.getAllSections(this.editor);
70953
- const sectionToRemove = sections.find(({ node: node2 }) => node2.attrs.id === id);
70954
- if (!sectionToRemove) return false;
70955
- const { pos, node } = sectionToRemove;
70956
- const nodeStart = pos;
70957
- const nodeEnd = nodeStart + node.nodeSize;
70958
- tr.delete(nodeStart, nodeEnd);
70959
- if (dispatch) {
70960
- tr.setMeta("documentSection", { action: "delete", id });
70961
- dispatch(tr);
71252
+ background: {
71253
+ default: null,
71254
+ renderDOM: (attrs) => {
71255
+ if (!attrs.background) return {};
71256
+ return {
71257
+ style: `background-color: ${attrs.background}`
71258
+ };
70962
71259
  }
70963
- return true;
70964
71260
  },
71261
+ drawingContent: {
71262
+ rendered: false
71263
+ },
71264
+ attributes: {
71265
+ rendered: false
71266
+ }
71267
+ };
71268
+ },
71269
+ parseDOM() {
71270
+ return [
71271
+ {
71272
+ tag: `div[data-type="${this.name}"]`
71273
+ }
71274
+ ];
71275
+ },
71276
+ renderDOM({ htmlAttributes }) {
71277
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-type": this.name })];
71278
+ },
71279
+ addCommands() {
71280
+ return {
70965
71281
  /**
70966
- * Lock section against edits
71282
+ * Insert a horizontal rule
70967
71283
  * @category Command
70968
- * @param {number} id - Section to lock
70969
71284
  * @example
70970
- * editor.commands.lockSectionById(123)
71285
+ * editor.commands.insertHorizontalRule()
71286
+ * @note Creates a visual separator between content sections
70971
71287
  */
70972
- lockSectionById: (id) => ({ tr, dispatch }) => {
70973
- const sections = SectionHelpers.getAllSections(this.editor);
70974
- const sectionToLock = sections.find(({ node }) => node.attrs.id === id);
70975
- if (!sectionToLock) return false;
70976
- tr.setNodeMarkup(sectionToLock.pos, null, { ...sectionToLock.node.attrs, isLocked: true });
70977
- if (dispatch) {
70978
- tr.setMeta("documentSection", { action: "lock", id });
70979
- dispatch(tr);
70980
- }
70981
- return true;
71288
+ insertHorizontalRule: () => ({ commands: commands2 }) => {
71289
+ return commands2.insertContent({
71290
+ type: this.name,
71291
+ attrs: {
71292
+ horizontalRule: true,
71293
+ size: { width: "100%", height: 2 },
71294
+ background: "#e5e7eb"
71295
+ }
71296
+ });
70982
71297
  },
70983
71298
  /**
70984
- * Modify section attributes or content
71299
+ * Insert a content block
70985
71300
  * @category Command
70986
- * @param {SectionUpdate} options - Changes to apply
71301
+ * @param {ContentBlockConfig} config - Block configuration
70987
71302
  * @example
70988
- * editor.commands.updateSectionById({ id: 123, attrs: { isLocked: false } })
70989
- * editor.commands.updateSectionById({ id: 123, html: '<p>New content</p>' })
70990
- * editor.commands.updateSectionById({
70991
- * id: 123,
70992
- * html: '<p>Updated</p>',
70993
- * attrs: { title: 'New Title' }
71303
+ * // Insert a spacer block
71304
+ * editor.commands.insertContentBlock({ size: { height: 20 } })
71305
+ *
71306
+ * @example
71307
+ * // Insert a colored divider
71308
+ * editor.commands.insertContentBlock({
71309
+ * size: { width: '50%', height: 3 },
71310
+ * background: '#3b82f6'
70994
71311
  * })
71312
+ * @note Used for spacing, dividers, and special inline content
70995
71313
  */
70996
- updateSectionById: ({ id, html, json, attrs }) => ({ tr, dispatch, editor }) => {
70997
- const sections = SectionHelpers.getAllSections(editor || this.editor);
70998
- const sectionToUpdate = sections.find(({ node: node2 }) => node2.attrs.id === id);
70999
- if (!sectionToUpdate) return false;
71000
- const { pos, node } = sectionToUpdate;
71001
- let newContent = null;
71002
- if (html) {
71003
- const htmlDoc = htmlHandler(html, editor || this.editor);
71004
- const doc2 = DOMParser$1.fromSchema((editor || this.editor).schema).parse(htmlDoc);
71005
- newContent = doc2.content;
71006
- }
71007
- if (json) {
71008
- newContent = (editor || this.editor).schema.nodeFromJSON(json);
71009
- }
71010
- if (!newContent) {
71011
- newContent = node.content;
71012
- }
71013
- const updatedNode = node.type.create({ ...node.attrs, ...attrs }, newContent, node.marks);
71014
- tr.replaceWith(pos, pos + node.nodeSize, updatedNode);
71015
- if (dispatch) {
71016
- tr.setMeta("documentSection", { action: "update", id, attrs });
71017
- dispatch(tr);
71018
- }
71019
- return true;
71314
+ insertContentBlock: (config2) => ({ commands: commands2 }) => {
71315
+ return commands2.insertContent({
71316
+ type: this.name,
71317
+ attrs: config2
71318
+ });
71020
71319
  }
71021
71320
  };
71022
- },
71023
- addHelpers() {
71024
- return {
71025
- ...SectionHelpers
71026
- };
71027
71321
  }
71028
71322
  });
71029
71323
  const { findChildren } = helpers;
@@ -77873,6 +78167,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
77873
78167
  Search,
77874
78168
  StructuredContent,
77875
78169
  StructuredContentBlock,
78170
+ StructuredContentCommands,
77876
78171
  DocumentSection,
77877
78172
  NodeResizer,
77878
78173
  CustomSelection,
@@ -96738,7 +97033,7 @@ ${reason}`);
96738
97033
  const c1 = changes[i2];
96739
97034
  const c2 = changes[i2 + 1];
96740
97035
  const c1Key = markMetaKeys[c1.mark.type.name];
96741
- if (c1 && c2 && c1.to === c2.from) {
97036
+ if (c1 && c2 && c1.to === c2.from && c1.mark.attrs.id === c2.mark.attrs.id) {
96742
97037
  const c2Key = markMetaKeys[c2.mark.type.name];
96743
97038
  grouped.push({
96744
97039
  from: c1.from,
@@ -96783,6 +97078,7 @@ ${reason}`);
96783
97078
  const isCommentsListVisible = ref$1(false);
96784
97079
  const editorCommentIds = ref$1([]);
96785
97080
  const editorCommentPositions = ref$1({});
97081
+ const isCommentHighlighted = ref$1(false);
96786
97082
  const floatingCommentsOffset = ref$1(0);
96787
97083
  const sortedConversations = ref$1([]);
96788
97084
  const visibleConversations = ref$1([]);
@@ -97061,8 +97357,8 @@ ${reason}`);
97061
97357
  tr.setMeta(CommentsPluginKey, { type: "forceTrackChanges" });
97062
97358
  tr.setMeta(TrackChangesBasePluginKey, trackChangesPayload);
97063
97359
  }
97360
+ dispatch(tr);
97064
97361
  });
97065
- dispatch(tr);
97066
97362
  };
97067
97363
  const translateCommentsForExport = () => {
97068
97364
  const processedComments = [];
@@ -97132,6 +97428,7 @@ ${reason}`);
97132
97428
  try {
97133
97429
  const normalizedContent = normalizeCommentForEditor(commentTextJson);
97134
97430
  const schemaContent = Array.isArray(normalizedContent) ? normalizedContent[0] : normalizedContent;
97431
+ if (!schemaContent.content.length) return null;
97135
97432
  const editor = new Editor({
97136
97433
  mode: "text",
97137
97434
  isHeadless: true,
@@ -97165,6 +97462,7 @@ ${reason}`);
97165
97462
  commentsParentElement,
97166
97463
  editorCommentPositions,
97167
97464
  hasInitializedLocations,
97465
+ isCommentHighlighted,
97168
97466
  // Floating comments
97169
97467
  floatingCommentsOffset,
97170
97468
  sortedConversations,
@@ -108859,7 +109157,8 @@ ${style2}
108859
109157
  currentCommentText,
108860
109158
  isDebugging: isDebugging2,
108861
109159
  editingCommentId,
108862
- editorCommentPositions
109160
+ editorCommentPositions,
109161
+ isCommentHighlighted
108863
109162
  } = storeToRefs(commentsStore);
108864
109163
  const { activeZoom } = storeToRefs(superdocStore);
108865
109164
  const isInternal = ref$1(true);
@@ -108921,13 +109220,14 @@ ${style2}
108921
109220
  "track-format",
108922
109221
  "track-format-dec"
108923
109222
  ];
108924
- if (excludedClasses.some((className) => e.target.classList.contains(className))) return;
109223
+ if (excludedClasses.some((className) => e.target.classList.contains(className)) || isCommentHighlighted.value) return;
108925
109224
  if (activeComment.value === props.comment.commentId) {
108926
109225
  floatingCommentsOffset.value = 0;
108927
109226
  emit2("dialog-exit");
108928
109227
  }
108929
109228
  activeComment.value = null;
108930
109229
  commentsStore.setActiveComment(proxy.$superdoc, activeComment.value);
109230
+ isCommentHighlighted.value = false;
108931
109231
  };
108932
109232
  const handleAddComment = () => {
108933
109233
  const options = {
@@ -109140,7 +109440,7 @@ ${style2}
109140
109440
  };
109141
109441
  }
109142
109442
  };
109143
- const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-dc5e6675"]]);
109443
+ const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["__scopeId", "data-v-e07f3426"]]);
109144
109444
  const _hoisted_1$b = { class: "comments-list" };
109145
109445
  const _hoisted_2$5 = { key: 0 };
109146
109446
  const _hoisted_3$3 = { class: "comment-item" };
@@ -110163,7 +110463,8 @@ ${style2}
110163
110463
  getFloatingComments,
110164
110464
  hasSyncedCollaborationComments,
110165
110465
  editorCommentPositions,
110166
- hasInitializedLocations
110466
+ hasInitializedLocations,
110467
+ isCommentHighlighted
110167
110468
  } = storeToRefs(commentsStore);
110168
110469
  const { showAddComment, handleEditorLocationsUpdate, handleTrackedChangeUpdate } = commentsStore;
110169
110470
  const { proxy } = getCurrentInstance();
@@ -110358,6 +110659,7 @@ ${style2}
110358
110659
  nextTick(() => {
110359
110660
  if (pendingComment.value) return;
110360
110661
  commentsStore.setActiveComment(proxy.$superdoc, activeCommentId);
110662
+ isCommentHighlighted.value = true;
110361
110663
  });
110362
110664
  if (typeof proxy.$superdoc.config.onCommentsUpdate === "function") {
110363
110665
  proxy.$superdoc.config.onCommentsUpdate(params2);
@@ -110680,7 +110982,7 @@ ${style2}
110680
110982
  };
110681
110983
  }
110682
110984
  };
110683
- const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-dbfba5b9"]]);
110985
+ const App = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-3f71b1bf"]]);
110684
110986
  const createSuperdocVueApp = () => {
110685
110987
  const app = createApp(App);
110686
110988
  const pinia = createPinia();