superdoc 0.31.0-next.10 → 0.31.0-next.11

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 (74) hide show
  1. package/dist/chunks/{PdfViewer-CxLHqmsp.cjs → PdfViewer-D9P6oSyP.cjs} +1 -1
  2. package/dist/chunks/{PdfViewer-Ck2UtdkO.es.js → PdfViewer-DrGdcxMi.es.js} +1 -1
  3. package/dist/chunks/{index-S2kR904m.es.js → index-8TkBu9O_.es.js} +3 -3
  4. package/dist/chunks/{index-qZtf9OYt.cjs → index-ZC9yiay1.cjs} +3 -3
  5. package/dist/chunks/{index-TPvf306Z-DLAdtd9g.es.js → index-hUtaGvC8-Vf8Tdemp.es.js} +1 -1
  6. package/dist/chunks/{index-TPvf306Z-Dymhw354.cjs → index-hUtaGvC8-aunRWvRG.cjs} +1 -1
  7. package/dist/chunks/{super-editor.es-d0fSEchl.cjs → super-editor.es-4uI_8knu.cjs} +2315 -2525
  8. package/dist/chunks/{super-editor.es-CNPHD-mt.es.js → super-editor.es-DS6vzo6Y.es.js} +2315 -2525
  9. package/dist/super-editor/ai-writer.es.js +2 -2
  10. package/dist/super-editor/chunks/{converter-CuN7BQ8r.js → converter-DNZlAyEp.js} +236 -129
  11. package/dist/super-editor/chunks/{docx-zipper-DvU3g1QE.js → docx-zipper-CGXr6ESB.js} +1 -1
  12. package/dist/super-editor/chunks/{editor-CJUnMzXV.js → editor-BG6XbVA7.js} +1808 -2103
  13. package/dist/super-editor/chunks/{index-TPvf306Z.js → index-hUtaGvC8.js} +1 -1
  14. package/dist/super-editor/chunks/{toolbar-_AUH-ukY.js → toolbar-8Nf_jruN.js} +2 -2
  15. package/dist/super-editor/converter.es.js +2 -2
  16. package/dist/super-editor/docx-zipper.es.js +2 -2
  17. package/dist/super-editor/editor.es.js +3 -3
  18. package/dist/super-editor/file-zipper.es.js +1 -1
  19. package/dist/super-editor/super-editor/src/components/toolbar/defaultItems.d.ts.map +1 -1
  20. package/dist/super-editor/super-editor/src/components/toolbar/super-toolbar.d.ts.map +1 -1
  21. package/dist/super-editor/super-editor/src/core/commands/changeListLevel.d.ts.map +1 -1
  22. package/dist/super-editor/super-editor/src/core/commands/index.d.ts +2 -0
  23. package/dist/super-editor/super-editor/src/core/commands/lineHeight.d.ts +7 -0
  24. package/dist/super-editor/super-editor/src/core/commands/lineHeight.d.ts.map +1 -0
  25. package/dist/super-editor/super-editor/src/core/commands/list-helpers/is-list.d.ts.map +1 -1
  26. package/dist/super-editor/super-editor/src/core/commands/removeNumberingProperties.d.ts.map +1 -1
  27. package/dist/super-editor/super-editor/src/core/commands/resetAttributes.d.ts.map +1 -1
  28. package/dist/super-editor/super-editor/src/core/commands/restartNumbering.d.ts.map +1 -1
  29. package/dist/super-editor/super-editor/src/core/commands/textIndent.d.ts +5 -0
  30. package/dist/super-editor/super-editor/src/core/commands/textIndent.d.ts.map +1 -0
  31. package/dist/super-editor/super-editor/src/core/commands/toggleList.d.ts.map +1 -1
  32. package/dist/super-editor/super-editor/src/core/commands/updateAttributes.d.ts +4 -4
  33. package/dist/super-editor/super-editor/src/core/commands/updateAttributes.d.ts.map +1 -1
  34. package/dist/super-editor/super-editor/src/core/super-converter/styles.d.ts +57 -12
  35. package/dist/super-editor/super-editor/src/core/super-converter/styles.d.ts.map +1 -1
  36. package/dist/super-editor/super-editor/src/core/super-converter/v2/importer/types/index.d.ts +4 -0
  37. package/dist/super-editor/super-editor/src/core/super-converter/v2/importer/types/index.d.ts.map +1 -1
  38. package/dist/super-editor/super-editor/src/core/super-converter/v3/handlers/w/p/helpers/generate-paragraph-properties.d.ts.map +1 -1
  39. package/dist/super-editor/super-editor/src/core/super-converter/v3/handlers/w/p/helpers/legacy-handle-paragraph-node.d.ts.map +1 -1
  40. package/dist/super-editor/super-editor/src/core/utilities/deleteProps.d.ts.map +1 -1
  41. package/dist/super-editor/super-editor/src/extensions/index.d.ts +1 -3
  42. package/dist/super-editor/super-editor/src/extensions/index.d.ts.map +1 -1
  43. package/dist/super-editor/super-editor/src/extensions/linked-styles/helpers.d.ts.map +1 -1
  44. package/dist/super-editor/super-editor/src/extensions/linked-styles/linked-styles.d.ts.map +1 -1
  45. package/dist/super-editor/super-editor/src/extensions/linked-styles/plugin.d.ts.map +1 -1
  46. package/dist/super-editor/super-editor/src/extensions/paragraph/ParagraphNodeView.d.ts.map +1 -1
  47. package/dist/super-editor/super-editor/src/extensions/paragraph/dropcapPlugin.d.ts +4 -0
  48. package/dist/super-editor/super-editor/src/extensions/paragraph/dropcapPlugin.d.ts.map +1 -0
  49. package/dist/super-editor/super-editor/src/extensions/paragraph/numberingPlugin.d.ts.map +1 -1
  50. package/dist/super-editor/super-editor/src/extensions/paragraph/paragraph.d.ts +0 -9
  51. package/dist/super-editor/super-editor/src/extensions/paragraph/paragraph.d.ts.map +1 -1
  52. package/dist/super-editor/super-editor/src/extensions/paragraph/resolvedPropertiesCache.d.ts +3 -0
  53. package/dist/super-editor/super-editor/src/extensions/paragraph/resolvedPropertiesCache.d.ts.map +1 -0
  54. package/dist/super-editor/super-editor/src/extensions/tab/helpers/tabDecorations.d.ts.map +1 -1
  55. package/dist/super-editor/super-editor/src/extensions/text-align/text-align.d.ts +0 -12
  56. package/dist/super-editor/super-editor/src/extensions/text-align/text-align.d.ts.map +1 -1
  57. package/dist/super-editor/super-editor/src/tests/data/annotations_doc_content.d.ts +113 -61
  58. package/dist/super-editor/super-editor.es.js +41 -63
  59. package/dist/super-editor/toolbar.es.js +2 -2
  60. package/dist/super-editor.cjs +1 -1
  61. package/dist/super-editor.es.js +1 -1
  62. package/dist/superdoc.cjs +2 -2
  63. package/dist/superdoc.es.js +2 -2
  64. package/dist/superdoc.umd.js +2320 -2530
  65. package/dist/superdoc.umd.js.map +1 -1
  66. package/package.json +1 -1
  67. package/dist/super-editor/super-editor/src/extensions/line-height/index.d.ts +0 -2
  68. package/dist/super-editor/super-editor/src/extensions/line-height/index.d.ts.map +0 -1
  69. package/dist/super-editor/super-editor/src/extensions/line-height/line-height.d.ts +0 -44
  70. package/dist/super-editor/super-editor/src/extensions/line-height/line-height.d.ts.map +0 -1
  71. package/dist/super-editor/super-editor/src/extensions/text-indent/index.d.ts +0 -2
  72. package/dist/super-editor/super-editor/src/extensions/text-indent/index.d.ts.map +0 -1
  73. package/dist/super-editor/super-editor/src/extensions/text-indent/text-indent.d.ts +0 -37
  74. package/dist/super-editor/super-editor/src/extensions/text-indent/text-indent.d.ts.map +0 -1
@@ -9,11 +9,11 @@ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read fr
9
9
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
10
10
  var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
11
11
  var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
12
- var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, isElementNode_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, getPluginKeyName_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, checkFonts_fn, determineUnsupportedFonts_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, _ParagraphNodeView_instances, updateHTMLAttributes_fn, updateListStyles_fn, initList_fn, checkIsList_fn, createMarker_fn, createSeparator_fn, calculateTabSeparatorStyle_fn, calculateMarkerStyle_fn, removeList_fn, getParagraphContext_fn, scheduleAnimation_fn, cancelScheduledAnimation_fn, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn;
12
+ var _Attribute_static, getGlobalAttributes_fn, getNodeAndMarksAttributes_fn, isElementNode_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, getPluginKeyName_fn, createExtensionService_fn, createCommandService_fn, createConverter_fn, initMedia_fn, initFonts_fn, checkFonts_fn, determineUnsupportedFonts_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, _ParagraphNodeView_instances, updateHTMLAttributes_fn, updateDOMStyles_fn, updateListStyles_fn, initList_fn, checkIsList_fn, createMarker_fn, createSeparator_fn, calculateTabSeparatorStyle_fn, calculateMarkerStyle_fn, removeList_fn, getParagraphContext_fn, scheduleAnimation_fn, cancelScheduledAnimation_fn, _FieldAnnotationView_instances, createAnnotation_fn, _AutoPageNumberNodeView_instances, renderDom_fn, scheduleUpdateNodeStyle_fn;
13
13
  import * as Y from "yjs";
14
14
  import { UndoManager, Item as Item$1, ContentType, Text as Text$1, XmlElement, encodeStateAsUpdate } from "yjs";
15
- import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as htmlHandler, E as ReplaceStep, L as ListHelpers, G as updateNumberingProperties, H as changeListLevel, I as findParentNode, J as isList, K as isMacOS, O as isIOS, Q as getSchemaTypeByName, U as inputRulesPlugin, V as TrackDeleteMarkName, W as TrackInsertMarkName, X as v4, Y as TrackFormatMarkName, Z as comments_module_events, _ as findMark, $ as objectIncludes, a0 as AddMarkStep, a1 as RemoveMarkStep, a2 as twipsToLines, a3 as pixelsToTwips, a4 as helpers, a5 as posToDOMRect, a6 as CommandService, a7 as SuperConverter, a8 as createDocument, a9 as createDocFromMarkdown, aa as createDocFromHTML, ab as EditorState, ac as hasSomeParentWithClass, ad as isActive, ae as unflattenListsInHtml, af as parseSizeUnit, ag as minMax, ah as getLineHeightValueString, ai as updateDOMAttributes, aj as findChildren$5, ak as generateRandomSigned32BitIntStrId, al as kebabCase, am as twipsToPixels, an as halfPointToPixels, ao as getUnderlineCssString, ap as findParentNodeClosestToPos, aq as resolveRunProperties, ar as encodeCSSFromRPr, as as docxNumberingHelpers, at as InputRule, au as resolveParagraphProperties, av as eighthPointsToPixels, aw as linesToTwips, ax as convertSizeToCSS, ay as SelectionRange, az as Transform, aA as isInTable$1, aB as generateDocxRandomId, aC as insertNewRelationship, aD as inchesToPixels } from "./converter-CuN7BQ8r.js";
16
- import { D as DocxZipper } from "./docx-zipper-DvU3g1QE.js";
15
+ import { P as PluginKey, a as Plugin, M as Mapping, N as NodeSelection, S as Selection, T as TextSelection, b as Slice, D as DOMSerializer, F as Fragment, c as DOMParser$1, d as Mark$1, e as dropPoint, A as AllSelection, p as process$1, B as Buffer2, f as callOrGet, g as getExtensionConfigField, h as getMarkType, i as getMarksFromSelection, j as getNodeType, k as getSchemaTypeNameByName, l as Schema$1, m as cleanSchemaItem, n as canSplit, o as defaultBlockAt$1, q as liftTarget, r as canJoin, s as joinPoint, t as replaceStep$1, R as ReplaceAroundStep$1, u as isTextSelection, v as getMarkRange, w as isMarkActive, x as isNodeActive, y as deleteProps, z as processContent, C as htmlHandler, E as ReplaceStep, G as ptToTwips, H as getResolvedParagraphProperties, I as linesToTwips, L as ListHelpers, J as updateNumberingProperties, K as changeListLevel, O as findParentNode, Q as isList, U as isMacOS, V as isIOS, W as getSchemaTypeByName, X as inputRulesPlugin, Y as TrackDeleteMarkName, Z as TrackInsertMarkName, _ as v4, $ as TrackFormatMarkName, a0 as comments_module_events, a1 as findMark, a2 as objectIncludes, a3 as AddMarkStep, a4 as RemoveMarkStep, a5 as twipsToLines, a6 as pixelsToTwips, a7 as helpers, a8 as posToDOMRect, a9 as CommandService, aa as SuperConverter, ab as createDocument, ac as createDocFromMarkdown, ad as createDocFromHTML, ae as EditorState, af as hasSomeParentWithClass, ag as isActive, ah as unflattenListsInHtml, ai as parseSizeUnit, aj as minMax, ak as updateDOMAttributes, al as findChildren$5, am as generateRandomSigned32BitIntStrId, an as twipsToPixels, ao as calculateResolvedParagraphProperties, ap as encodeCSSFromPPr, aq as resolveRunProperties, ar as encodeCSSFromRPr, as as docxNumberingHelpers, at as InputRule, au as convertSizeToCSS, av as SelectionRange, aw as Transform, ax as findParentNodeClosestToPos, ay as isInTable$1, az as generateDocxRandomId, aA as insertNewRelationship, aB as inchesToPixels, aC as kebabCase, aD as getUnderlineCssString } from "./converter-DNZlAyEp.js";
16
+ import { D as DocxZipper } from "./docx-zipper-CGXr6ESB.js";
17
17
  import { ref, computed, createElementBlock, openBlock, withModifiers, Fragment as Fragment$1, renderList, normalizeClass, createCommentVNode, toDisplayString, createElementVNode, createApp } from "vue";
18
18
  var GOOD_LEAF_SIZE = 200;
19
19
  var RopeSequence = function RopeSequence2() {
@@ -9028,22 +9028,53 @@ const updateAttributes = (typeOrName, attrs = {}) => ({ tr, state, dispatch }) =
9028
9028
  const to = range.$to.pos;
9029
9029
  state.doc.nodesBetween(from2, to, (node, pos) => {
9030
9030
  if (nodeType && nodeType === node.type) {
9031
- tr.setNodeMarkup(pos, void 0, { ...node.attrs, ...attrs });
9031
+ const resolvedAttrs = mergeAttributes(node.attrs, attrs);
9032
+ tr.setNodeMarkup(pos, void 0, resolvedAttrs);
9032
9033
  }
9033
9034
  if (markType && node.marks.length) {
9034
9035
  node.marks.forEach((mark) => {
9035
9036
  if (markType === mark.type) {
9036
9037
  const trimmedFrom = Math.max(pos, from2);
9037
9038
  const trimmedTo = Math.min(pos + node.nodeSize, to);
9038
- tr.addMark(trimmedFrom, trimmedTo, markType.create({ ...mark.attrs, ...attrs }));
9039
+ const resolvedAttrs = mergeAttributes(mark.attrs, attrs);
9040
+ tr.addMark(trimmedFrom, trimmedTo, markType.create(resolvedAttrs));
9039
9041
  }
9040
9042
  });
9041
9043
  }
9042
9044
  });
9043
9045
  });
9046
+ dispatch(tr);
9044
9047
  }
9045
9048
  return true;
9046
9049
  };
9050
+ const isPlainObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
9051
+ const assignNestedValue = (target, path, value) => {
9052
+ if (!path.includes(".")) {
9053
+ target[path] = value;
9054
+ return;
9055
+ }
9056
+ const parts = path.split(".");
9057
+ let current = target;
9058
+ for (let i = 0; i < parts.length; i += 1) {
9059
+ const part = parts[i];
9060
+ const isLast = i === parts.length - 1;
9061
+ if (isLast) {
9062
+ current[part] = value;
9063
+ } else {
9064
+ if (!isPlainObject(current[part])) {
9065
+ current[part] = {};
9066
+ }
9067
+ current = current[part];
9068
+ }
9069
+ }
9070
+ };
9071
+ const mergeAttributes = (existingAttrs = {}, newAttrs = {}) => {
9072
+ const expandedAttrs = JSON.parse(JSON.stringify(existingAttrs));
9073
+ Object.entries(newAttrs).forEach(([key2, value]) => {
9074
+ assignNestedValue(expandedAttrs, key2, value);
9075
+ });
9076
+ return expandedAttrs;
9077
+ };
9047
9078
  const resetAttributes = (typeOrName, attrs) => ({ tr, state, dispatch }) => {
9048
9079
  let nodeType = null;
9049
9080
  let markType = null;
@@ -9073,6 +9104,7 @@ const resetAttributes = (typeOrName, attrs) => ({ tr, state, dispatch }) => {
9073
9104
  }
9074
9105
  });
9075
9106
  });
9107
+ dispatch(tr);
9076
9108
  }
9077
9109
  return true;
9078
9110
  };
@@ -9351,12 +9383,92 @@ const undoInputRule = () => ({ state, dispatch }) => {
9351
9383
  }
9352
9384
  return false;
9353
9385
  };
9386
+ const defaultIncrementPoints = 36;
9387
+ const increaseTextIndent = () => modifyIndentation((node) => calculateNewIndentation(node, 1));
9388
+ const decreaseTextIndent = () => modifyIndentation((node) => calculateNewIndentation(node, -1));
9389
+ const setTextIndentation = (points) => modifyIndentation(() => ptToTwips(points));
9390
+ const unsetTextIndentation = () => modifyIndentation(() => null);
9391
+ function calculateNewIndentation(node, delta) {
9392
+ let { indent } = getResolvedParagraphProperties(node);
9393
+ let { left: left2 } = indent || {};
9394
+ const increment = ptToTwips(delta * defaultIncrementPoints);
9395
+ if (!left2) {
9396
+ left2 = increment;
9397
+ } else {
9398
+ left2 += increment;
9399
+ }
9400
+ if (left2 <= 0) {
9401
+ left2 = null;
9402
+ }
9403
+ return left2;
9404
+ }
9405
+ function modifyIndentation(calcFunc) {
9406
+ return ({ state, dispatch }) => {
9407
+ const tr = state.tr;
9408
+ const { from: from2, to } = state.selection;
9409
+ const results = [];
9410
+ state.doc.nodesBetween(from2, to, (node, pos) => {
9411
+ if (node.type.name === "paragraph") {
9412
+ let left2 = calcFunc(node);
9413
+ if (Number.isNaN(left2)) {
9414
+ results.push(false);
9415
+ return false;
9416
+ }
9417
+ const newAttrs = {
9418
+ ...node.attrs,
9419
+ paragraphProperties: {
9420
+ ...node.attrs.paragraphProperties || {},
9421
+ indent: {
9422
+ ...node.attrs.paragraphProperties?.indent || {},
9423
+ left: left2
9424
+ }
9425
+ }
9426
+ };
9427
+ if (left2 == null) {
9428
+ delete newAttrs.paragraphProperties.indent.left;
9429
+ if (Object.keys(newAttrs.paragraphProperties.indent).length === 0) {
9430
+ delete newAttrs.paragraphProperties.indent;
9431
+ }
9432
+ }
9433
+ tr.setNodeMarkup(pos, void 0, newAttrs);
9434
+ results.push(true);
9435
+ return false;
9436
+ }
9437
+ return true;
9438
+ });
9439
+ const success = results.every((result) => result);
9440
+ if (dispatch && success) {
9441
+ dispatch(tr);
9442
+ }
9443
+ return success;
9444
+ };
9445
+ }
9446
+ const setLineHeight = (lineHeight) => ({ commands: commands2 }) => {
9447
+ if (!lineHeight) return false;
9448
+ return commands2.updateAttributes("paragraph", {
9449
+ "paragraphProperties.spacing.line": linesToTwips(lineHeight),
9450
+ "paragraphProperties.spacing.lineRule": "auto"
9451
+ });
9452
+ };
9453
+ const unsetLineHeight = () => ({ commands: commands2 }) => {
9454
+ return commands2.resetAttributes(
9455
+ "paragraph",
9456
+ "paragraphProperties.spacing.line",
9457
+ "paragraphProperties.spacing.lineRule"
9458
+ );
9459
+ };
9354
9460
  const toggleList = (listType) => ({ editor, state, tr, dispatch }) => {
9355
9461
  let predicate;
9356
9462
  if (listType === "orderedList") {
9357
- predicate = (n) => n.attrs.numberingProperties && n.attrs.listRendering && n.attrs.listRendering.numberingType !== "bullet";
9463
+ predicate = (n) => {
9464
+ const paraProps = getResolvedParagraphProperties(n);
9465
+ return paraProps.numberingProperties && n.attrs.listRendering && n.attrs.listRendering.numberingType !== "bullet";
9466
+ };
9358
9467
  } else if (listType === "bulletList") {
9359
- predicate = (n) => n.attrs.numberingProperties && n.attrs.listRendering && n.attrs.listRendering.numberingType === "bullet";
9468
+ predicate = (n) => {
9469
+ const paraProps = getResolvedParagraphProperties(n);
9470
+ return paraProps.numberingProperties && n.attrs.listRendering && n.attrs.listRendering.numberingType === "bullet";
9471
+ };
9360
9472
  } else {
9361
9473
  return false;
9362
9474
  }
@@ -9394,7 +9506,8 @@ const toggleList = (listType) => ({ editor, state, tr, dispatch }) => {
9394
9506
  mode = "remove";
9395
9507
  } else {
9396
9508
  mode = "reuse";
9397
- const baseNumbering = firstListNode.attrs.numberingProperties || {};
9509
+ const paraProps = getResolvedParagraphProperties(firstListNode);
9510
+ const baseNumbering = paraProps.numberingProperties || {};
9398
9511
  sharedNumberingProperties = {
9399
9512
  ...baseNumbering,
9400
9513
  ilvl: baseNumbering.ilvl ?? 0
@@ -9445,7 +9558,7 @@ const removeNumberingProperties = ({ checkType = "startParagraph" } = {}) => (pr
9445
9558
  const { $from, empty: empty2 } = state.selection;
9446
9559
  if ((!empty2 || $from.parentOffset !== 0) && !isVisuallyEmptyParagraph(paragraph)) return false;
9447
9560
  }
9448
- const ilvl = paragraph.attrs.numberingProperties.ilvl;
9561
+ const ilvl = getResolvedParagraphProperties(paragraph).numberingProperties.ilvl;
9449
9562
  if (ilvl > 0) {
9450
9563
  const outdented = decreaseListIndent()(props);
9451
9564
  if (outdented) {
@@ -9540,6 +9653,7 @@ const commands$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
9540
9653
  command,
9541
9654
  createParagraphNear,
9542
9655
  decreaseListIndent,
9656
+ decreaseTextIndent,
9543
9657
  defaultStyleDetector,
9544
9658
  deleteSelection,
9545
9659
  exitCode,
@@ -9548,6 +9662,7 @@ const commands$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
9548
9662
  getSelectionMarks,
9549
9663
  getStyleIdFromMarks,
9550
9664
  increaseListIndent,
9665
+ increaseTextIndent,
9551
9666
  insertContent,
9552
9667
  insertContentAt,
9553
9668
  insertTabChar,
@@ -9569,9 +9684,11 @@ const commands$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
9569
9684
  selectNodeForward,
9570
9685
  selectTextblockEnd,
9571
9686
  selectTextblockStart,
9687
+ setLineHeight,
9572
9688
  setMark,
9573
9689
  setMeta,
9574
9690
  setNode,
9691
+ setTextIndentation,
9575
9692
  setTextSelection,
9576
9693
  splitBlock: splitBlock$1,
9577
9694
  toggleList,
@@ -9580,7 +9697,9 @@ const commands$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePr
9580
9697
  toggleNode,
9581
9698
  undoInputRule,
9582
9699
  unsetAllMarks,
9700
+ unsetLineHeight,
9583
9701
  unsetMark,
9702
+ unsetTextIndentation,
9584
9703
  updateAttributes,
9585
9704
  updateNumberingProperties
9586
9705
  }, Symbol.toStringTag, { value: "Module" }));
@@ -13502,7 +13621,7 @@ const getLevel = (node) => {
13502
13621
  const lvl = node.getAttribute("data-level");
13503
13622
  return lvl ? parseInt(lvl, 10) : 0;
13504
13623
  };
13505
- const summaryVersion = "0.31.0-next.10";
13624
+ const summaryVersion = "0.31.0-next.11";
13506
13625
  const nodeKeys = [
13507
13626
  "group",
13508
13627
  "content",
@@ -14222,7 +14341,7 @@ const _Editor = class _Editor extends EventEmitter {
14222
14341
  { default: remarkStringify },
14223
14342
  { default: remarkGfm }
14224
14343
  ] = await Promise.all([
14225
- import("./index-TPvf306Z.js"),
14344
+ import("./index-hUtaGvC8.js"),
14226
14345
  import("./index-DRCvimau.js"),
14227
14346
  import("./index-C_x_N6Uh.js"),
14228
14347
  import("./index-D_sWOSiG.js"),
@@ -14423,7 +14542,7 @@ const _Editor = class _Editor extends EventEmitter {
14423
14542
  * Process collaboration migrations
14424
14543
  */
14425
14544
  processCollaborationMigrations() {
14426
- console.debug("[checkVersionMigrations] Current editor version", "0.31.0-next.10");
14545
+ console.debug("[checkVersionMigrations] Current editor version", "0.31.0-next.11");
14427
14546
  if (!this.options.ydoc) return;
14428
14547
  const metaMap = this.options.ydoc.getMap("meta");
14429
14548
  let docVersion = metaMap.get("version");
@@ -15339,37 +15458,9 @@ const TextAlign = Extension.create({
15339
15458
  name: "textAlign",
15340
15459
  addOptions() {
15341
15460
  return {
15342
- types: ["heading", "paragraph"],
15343
15461
  alignments: ["left", "center", "right", "justify"]
15344
15462
  };
15345
15463
  },
15346
- addGlobalAttributes() {
15347
- return [
15348
- {
15349
- types: this.options.types,
15350
- attributes: {
15351
- /**
15352
- * @category Attribute
15353
- * @param {string} [textAlign='left'] - Text alignment value (left, center, right, justify)
15354
- */
15355
- textAlign: {
15356
- default: this.options.defaultAlignment,
15357
- parseDOM: (el) => {
15358
- const alignment = el.style.textAlign || this.options.defaultAlignment;
15359
- const containsAlignment = this.options.alignments.includes(alignment);
15360
- return containsAlignment ? alignment : this.options.defaultAlignment;
15361
- },
15362
- renderDOM: (attrs) => {
15363
- if (attrs.textAlign === this.options.defaultAlignment) return {};
15364
- const textAlign = attrs.textAlign === "both" ? "justify" : attrs.textAlign;
15365
- if (!textAlign) return {};
15366
- return { style: `text-align: ${textAlign}` };
15367
- }
15368
- }
15369
- }
15370
- }
15371
- ];
15372
- },
15373
15464
  addCommands() {
15374
15465
  return {
15375
15466
  /**
@@ -15379,12 +15470,11 @@ const TextAlign = Extension.create({
15379
15470
  * @example
15380
15471
  * editor.commands.setTextAlign('center')
15381
15472
  * editor.commands.setTextAlign('justify')
15382
- * @note Applies to all configured node types (heading, paragraph by default)
15383
15473
  */
15384
15474
  setTextAlign: (alignment) => ({ commands: commands2 }) => {
15385
15475
  const containsAlignment = this.options.alignments.includes(alignment);
15386
15476
  if (!containsAlignment) return false;
15387
- return this.options.types.map((type) => commands2.updateAttributes(type, { textAlign: alignment })).every((result) => result);
15477
+ return commands2.updateAttributes("paragraph", { "paragraphProperties.justification": alignment });
15388
15478
  },
15389
15479
  /**
15390
15480
  * Remove text alignment (reset to default)
@@ -15393,9 +15483,7 @@ const TextAlign = Extension.create({
15393
15483
  * editor.commands.unsetTextAlign()
15394
15484
  * @note Resets alignment to the default value
15395
15485
  */
15396
- unsetTextAlign: () => ({ commands: commands2 }) => {
15397
- return this.options.types.map((type) => commands2.resetAttributes(type, "textAlign")).every((result) => result);
15398
- }
15486
+ unsetTextAlign: () => ({ commands: commands2 }) => commands2.resetAttributes("paragraph", "paragraphProperties.justification")
15399
15487
  };
15400
15488
  },
15401
15489
  addShortcuts() {
@@ -15407,189 +15495,6 @@ const TextAlign = Extension.create({
15407
15495
  };
15408
15496
  }
15409
15497
  });
15410
- const TextIndent = Extension.create({
15411
- name: "textIndent",
15412
- addOptions() {
15413
- return {
15414
- types: ["heading", "paragraph"],
15415
- defaults: {
15416
- unit: "in",
15417
- increment: 0.125
15418
- }
15419
- };
15420
- },
15421
- addGlobalAttributes() {
15422
- return [
15423
- {
15424
- types: this.options.types,
15425
- attributes: {
15426
- /**
15427
- * @category Attribute
15428
- * @param {string} [textIndent] - Text indentation value with unit (e.g., '0.5in')
15429
- */
15430
- textIndent: {
15431
- default: null,
15432
- parseDOM: (el) => el.style.textIndent,
15433
- renderDOM: (attrs) => {
15434
- if (!attrs.textIndent) return {};
15435
- let [value, unit] = parseSizeUnit(attrs.textIndent);
15436
- if (Number.isNaN(value) || !value) return {};
15437
- unit = unit ? unit : this.options.defaults.unit;
15438
- return { style: `margin-left: ${value}${unit}` };
15439
- }
15440
- }
15441
- }
15442
- }
15443
- ];
15444
- },
15445
- addCommands() {
15446
- return {
15447
- /**
15448
- * Set text indentation
15449
- * @category Command
15450
- * @param {string} indent - Indentation value with unit (e.g., '0.5in', '2cm')
15451
- * @returns {Function} Command function
15452
- * @example
15453
- * // Set to 0.5 inches
15454
- * setTextIndent('0.5in')
15455
- *
15456
- * // Set to 2 centimeters
15457
- * setTextIndent('2cm')
15458
- * @note Accepts any valid CSS unit (in, cm, px, em, etc.)
15459
- */
15460
- setTextIndent: (indent) => ({ commands: commands2 }) => {
15461
- if (!indent) return false;
15462
- return this.options.types.map((type) => commands2.updateAttributes(type, { textIndent: indent })).every((result) => result);
15463
- },
15464
- /**
15465
- * Remove text indentation
15466
- * @category Command
15467
- * @returns {Function} Command function
15468
- * @example
15469
- * unsetTextIndent()
15470
- * @note Removes all indentation from the selected nodes
15471
- */
15472
- unsetTextIndent: () => ({ commands: commands2 }) => {
15473
- return this.options.types.map((type) => commands2.resetAttributes(type, "textIndent")).every((result) => result);
15474
- },
15475
- /**
15476
- * Increase text indentation
15477
- * @category Command
15478
- * @returns {Function} Command function
15479
- * @example
15480
- * increaseTextIndent()
15481
- * @note Increments by the default value (0.125in by default)
15482
- * @note Creates initial indent if none exists
15483
- */
15484
- increaseTextIndent: () => ({ commands: commands2 }) => {
15485
- return this.options.types.map((type) => {
15486
- let { textIndent } = this.editor.getAttributes(type);
15487
- if (!textIndent) {
15488
- let { increment, unit: unit2 } = this.options.defaults;
15489
- return commands2.updateAttributes(type, {
15490
- textIndent: `${increment}${unit2}`
15491
- });
15492
- }
15493
- let [value, unit] = parseSizeUnit(textIndent);
15494
- value = Number(value) + this.options.defaults.increment;
15495
- unit = unit ? unit : this.options.defaults.unit;
15496
- if (Number.isNaN(value)) return false;
15497
- return commands2.updateAttributes(type, {
15498
- textIndent: `${value}${unit}`
15499
- });
15500
- }).every((result) => result);
15501
- },
15502
- /**
15503
- * Decrease text indentation
15504
- * @category Command
15505
- * @returns {Function} Command function
15506
- * @example
15507
- * decreaseTextIndent()
15508
- * @note Decrements by the default value (0.125in by default)
15509
- * @note Removes indentation completely if it reaches 0 or below
15510
- */
15511
- decreaseTextIndent: () => ({ commands: commands2 }) => {
15512
- return this.options.types.map((type) => {
15513
- let { textIndent } = this.editor.getAttributes(type);
15514
- if (!textIndent) return false;
15515
- let [value, unit] = parseSizeUnit(textIndent);
15516
- value = Number(value) - this.options.defaults.increment;
15517
- unit = unit ? unit : this.options.defaults.unit;
15518
- if (Number.isNaN(value)) return false;
15519
- if (value <= 0) {
15520
- return commands2.unsetTextIndent();
15521
- }
15522
- return commands2.updateAttributes(type, {
15523
- textIndent: `${value}${unit}`
15524
- });
15525
- }).every((result) => result);
15526
- }
15527
- };
15528
- }
15529
- });
15530
- const LineHeight = Extension.create({
15531
- name: "lineHeight",
15532
- addOptions() {
15533
- return {
15534
- types: ["heading", "paragraph"],
15535
- defaults: {
15536
- unit: ""
15537
- }
15538
- };
15539
- },
15540
- addGlobalAttributes() {
15541
- return [
15542
- {
15543
- types: this.options.types,
15544
- attributes: {
15545
- lineHeight: {
15546
- default: null,
15547
- parseDOM: (el) => el.style.lineHeight,
15548
- renderDOM: (attrs) => {
15549
- if (!attrs.lineHeight) return {};
15550
- const lineHeightStyle = getLineHeightValueString(
15551
- attrs.lineHeight,
15552
- this.options.defaults.unit,
15553
- attrs.spacing?.lineRule
15554
- );
15555
- return {
15556
- style: `${lineHeightStyle}`
15557
- };
15558
- }
15559
- }
15560
- }
15561
- }
15562
- ];
15563
- },
15564
- addCommands() {
15565
- return {
15566
- /**
15567
- * Set line height for blocks
15568
- * @category Command
15569
- * @param {LineHeightValue} lineHeight - Line height to apply
15570
- * @example
15571
- * editor.commands.setLineHeight(1.5)
15572
- * editor.commands.setLineHeight('24px')
15573
- * editor.commands.setLineHeight(2)
15574
- * @note Applies to paragraphs and headings
15575
- */
15576
- setLineHeight: (lineHeight) => ({ commands: commands2 }) => {
15577
- if (!lineHeight) return false;
15578
- return this.options.types.map((type) => commands2.updateAttributes(type, { lineHeight })).every((result) => result);
15579
- },
15580
- /**
15581
- * Remove line height
15582
- * @category Command
15583
- * @example
15584
- * editor.commands.unsetLineHeight()
15585
- * @note Reverts to default line spacing
15586
- */
15587
- unsetLineHeight: () => ({ commands: commands2 }) => {
15588
- return this.options.types.map((type) => commands2.resetAttributes(type, "lineHeight")).every((result) => result);
15589
- }
15590
- };
15591
- }
15592
- });
15593
15498
  const FormatCommands = Extension.create({
15594
15499
  name: "formatCommands",
15595
15500
  addOptions() {
@@ -17979,1301 +17884,462 @@ const Run = OxmlNode.create({
17979
17884
  return ["span", base2, 0];
17980
17885
  }
17981
17886
  });
17982
- const isKeyboardInvocation = (event) => {
17983
- return event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
17984
- };
17985
- const prefersNativeMenu = (event) => {
17986
- if (!event) return false;
17987
- if (event.ctrlKey || event.metaKey) {
17988
- return true;
17989
- }
17990
- return isKeyboardInvocation(event);
17991
- };
17992
- const shouldAllowNativeContextMenu = (event) => {
17993
- return prefersNativeMenu(event);
17994
- };
17995
- const shouldBypassContextMenu = shouldAllowNativeContextMenu;
17996
- const DEFAULT_SELECTION_STATE = Object.freeze({
17997
- focused: false,
17998
- preservedSelection: null,
17999
- showVisualSelection: false,
18000
- skipFocusReset: false
18001
- });
18002
- const normalizeSelectionState = (state = {}) => ({
18003
- ...DEFAULT_SELECTION_STATE,
18004
- ...state
18005
- });
18006
- const CustomSelectionPluginKey = new PluginKey("CustomSelection");
18007
- const handleClickOutside = (event, editor) => {
18008
- const editorElem = editor?.options?.element;
18009
- if (!editorElem) return;
18010
- const isInsideEditor = editorElem?.contains(event.target);
18011
- if (!isInsideEditor) {
18012
- editor.setOptions({
18013
- focusTarget: event.target
18014
- });
18015
- } else {
18016
- editor.setOptions({
18017
- focusTarget: null
18018
- });
18019
- }
18020
- };
18021
- function getFocusMeta(tr) {
18022
- return tr.getMeta(CustomSelectionPluginKey);
18023
- }
18024
- function setFocusMeta(tr, value) {
18025
- return tr.setMeta(CustomSelectionPluginKey, value);
18026
- }
18027
- function getFocusState(state) {
18028
- return CustomSelectionPluginKey.getState(state);
18029
- }
18030
- const isToolbarInput = (target) => {
18031
- return !!target?.closest(".button-text-input") || target?.classList?.contains("button-text-input");
18032
- };
18033
- const isToolbarButton = (target) => {
18034
- return !!target?.closest(".toolbar-button") || target?.classList?.contains("toolbar-button");
18035
- };
18036
- const CustomSelection = Extension.create({
18037
- name: "customSelection",
18038
- addPmPlugins() {
18039
- const editor = this.editor;
18040
- const customSelectionPlugin = new Plugin({
18041
- key: CustomSelectionPluginKey,
18042
- state: {
18043
- init: () => ({ ...DEFAULT_SELECTION_STATE }),
18044
- apply: (tr, value) => {
18045
- const meta = getFocusMeta(tr);
18046
- if (meta !== void 0) {
18047
- return { ...value, ...meta };
18048
- }
18049
- return value;
18050
- }
18051
- },
18052
- view: () => {
18053
- const clickHandler = (event) => handleClickOutside(event, editor);
18054
- document?.addEventListener("mousedown", clickHandler);
18055
- return {
18056
- destroy: () => {
18057
- document?.removeEventListener("mousedown", clickHandler);
18058
- }
18059
- };
18060
- },
18061
- props: {
18062
- handleDOMEvents: {
18063
- contextmenu: (view, event) => {
18064
- if (shouldAllowNativeContextMenu(event)) {
18065
- return false;
18066
- }
18067
- event.preventDefault();
18068
- const { selection } = view.state;
18069
- if (!selection.empty) {
18070
- view.dispatch(
18071
- setFocusMeta(view.state.tr, {
18072
- focused: true,
18073
- preservedSelection: selection,
18074
- showVisualSelection: true,
18075
- skipFocusReset: true
18076
- })
18077
- );
18078
- }
18079
- setTimeout(() => {
18080
- view.focus();
18081
- }, 0);
18082
- return false;
18083
- },
18084
- mousedown: (view, event) => {
18085
- if (event.button === 2) {
18086
- if (shouldAllowNativeContextMenu(event)) {
18087
- return false;
18088
- }
18089
- event.preventDefault();
18090
- const { selection: selection2 } = view.state;
18091
- if (!selection2.empty) {
18092
- view.dispatch(
18093
- setFocusMeta(view.state.tr, {
18094
- focused: true,
18095
- preservedSelection: selection2,
18096
- showVisualSelection: true,
18097
- skipFocusReset: true
18098
- })
18099
- );
18100
- this.editor.setOptions({
18101
- lastSelection: selection2,
18102
- preservedSelection: selection2
18103
- });
18104
- }
18105
- return false;
18106
- }
18107
- const { selection } = view.state;
18108
- const target = event.target;
18109
- const isElement2 = target instanceof Element;
18110
- const isToolbarBtn = isElement2 && isToolbarButton(target);
18111
- const isToolbarInp = isElement2 && isToolbarInput(target);
18112
- this.editor.setOptions({
18113
- focusTarget: target
18114
- });
18115
- if (isToolbarInp && !selection.empty) {
18116
- view.dispatch(
18117
- setFocusMeta(view.state.tr, {
18118
- focused: true,
18119
- preservedSelection: selection,
18120
- showVisualSelection: true,
18121
- skipFocusReset: false
18122
- })
18123
- );
18124
- this.editor.setOptions({
18125
- lastSelection: selection,
18126
- preservedSelection: selection
18127
- });
18128
- return false;
18129
- }
18130
- if (isToolbarBtn && !isToolbarInp) {
18131
- if (!selection.empty) {
18132
- this.editor.setOptions({
18133
- lastSelection: selection
18134
- });
18135
- view.dispatch(
18136
- setFocusMeta(view.state.tr, {
18137
- focused: true,
18138
- preservedSelection: selection,
18139
- showVisualSelection: true,
18140
- skipFocusReset: false
18141
- })
18142
- );
18143
- }
18144
- return false;
18145
- }
18146
- if (!isToolbarBtn && !isToolbarInp) {
18147
- view.dispatch(
18148
- setFocusMeta(view.state.tr, {
18149
- focused: false,
18150
- preservedSelection: null,
18151
- showVisualSelection: false,
18152
- skipFocusReset: false
18153
- })
18154
- );
18155
- if (!selection.empty && !this.editor.options.element?.contains(target)) {
18156
- this.editor.setOptions({
18157
- lastSelection: selection
18158
- });
18159
- const clearSelectionTr = view.state.tr.setSelection(TextSelection.create(view.state.doc, 0));
18160
- view.dispatch(clearSelectionTr);
18161
- }
18162
- }
18163
- },
18164
- focus: (view) => {
18165
- const target = this.editor.options.focusTarget;
18166
- const isElement2 = target instanceof Element;
18167
- const isToolbarBtn = isElement2 && isToolbarButton(target);
18168
- const isToolbarInp = isElement2 && isToolbarInput(target);
18169
- const focusState = getFocusState(view.state);
18170
- if (focusState?.skipFocusReset) {
18171
- view.dispatch(
18172
- setFocusMeta(view.state.tr, normalizeSelectionState({ ...focusState, skipFocusReset: false }))
18173
- );
18174
- return false;
18175
- }
18176
- if (!isToolbarBtn && !isToolbarInp) {
18177
- view.dispatch(
18178
- setFocusMeta(view.state.tr, {
18179
- focused: false,
18180
- preservedSelection: null,
18181
- showVisualSelection: false,
18182
- skipFocusReset: false
18183
- })
18184
- );
18185
- }
18186
- },
18187
- blur: (view) => {
18188
- const target = this.editor.options.focusTarget;
18189
- const isElement2 = target instanceof Element;
18190
- const isToolbarBtn = isElement2 && isToolbarButton(target);
18191
- const isToolbarInp = isElement2 && isToolbarInput(target);
18192
- const state = getFocusState(view.state);
18193
- if (state?.skipFocusReset) {
18194
- return false;
18195
- }
18196
- if (isToolbarBtn || isToolbarInp) {
18197
- view.dispatch(
18198
- setFocusMeta(view.state.tr, {
18199
- focused: true,
18200
- preservedSelection: state.preservedSelection || view.state.selection,
18201
- showVisualSelection: true,
18202
- skipFocusReset: false
18203
- })
18204
- );
18205
- } else {
18206
- view.dispatch(
18207
- setFocusMeta(view.state.tr, {
18208
- focused: false,
18209
- preservedSelection: null,
18210
- showVisualSelection: false,
18211
- skipFocusReset: false
18212
- })
18213
- );
18214
- }
18215
- }
18216
- },
18217
- decorations: (state) => {
18218
- const { selection, doc: doc2 } = state;
18219
- const focusState = getFocusState(state);
18220
- const shouldShowSelection = focusState.showVisualSelection && (focusState.preservedSelection || !selection.empty && focusState.focused);
18221
- if (!shouldShowSelection) {
18222
- return null;
18223
- }
18224
- const targetSelection = focusState.preservedSelection || selection;
18225
- if (targetSelection.empty) {
18226
- return null;
18227
- }
18228
- return DecorationSet.create(doc2, [
18229
- Decoration.inline(targetSelection.from, targetSelection.to, {
18230
- class: "sd-custom-selection"
18231
- })
18232
- ]);
18233
- }
18234
- }
18235
- });
18236
- return [customSelectionPlugin];
18237
- },
18238
- addCommands() {
18239
- return {
18240
- /**
18241
- * Restore the preserved selection
18242
- * @category Command
18243
- * @returns {Function} Command function
18244
- * @example
18245
- * // Restore selection after toolbar interaction
18246
- * editor.commands.restorePreservedSelection()
18247
- * @note Used internally to maintain selection when interacting with toolbar
18248
- */
18249
- restorePreservedSelection: () => ({ tr, state }) => {
18250
- const focusState = getFocusState(state);
18251
- if (focusState.preservedSelection) {
18252
- return tr.setSelection(focusState.preservedSelection);
18253
- }
18254
- const lastSelection = this.editor.options.lastSelection;
18255
- if (lastSelection) {
18256
- return tr.setSelection(lastSelection);
18257
- }
18258
- return tr;
17887
+ const restartNumbering = ({ editor, tr, state, dispatch }) => {
17888
+ const { node: paragraph, pos } = findParentNode(isList)(state.selection) || {};
17889
+ if (!paragraph) return false;
17890
+ const allParagraphs = [{ node: paragraph, pos }];
17891
+ const startPos = pos + paragraph.nodeSize;
17892
+ const myNumId = getResolvedParagraphProperties(paragraph).numberingProperties.numId;
17893
+ let stop = false;
17894
+ state.doc.nodesBetween(startPos, state.doc.content.size, (node, nodePos) => {
17895
+ if (node.type.name === "paragraph") {
17896
+ const paraProps = getResolvedParagraphProperties(node);
17897
+ if (isList(node) && paraProps.numberingProperties?.numId === myNumId) {
17898
+ allParagraphs.push({ node, pos: nodePos });
17899
+ } else {
17900
+ stop = true;
18259
17901
  }
18260
- };
18261
- }
18262
- });
18263
- const getLinkedStyle = (styleId, styles = []) => {
18264
- const linkedStyle = styles.find((style) => style.id === styleId);
18265
- const basedOn = linkedStyle?.definition?.attrs?.basedOn;
18266
- const basedOnStyle = styles.find((style) => style.id === basedOn);
18267
- return { linkedStyle, basedOnStyle };
17902
+ return false;
17903
+ }
17904
+ return !stop;
17905
+ });
17906
+ const { numberingType } = paragraph.attrs.listRendering || {};
17907
+ const listType = numberingType === "bullet" ? "bulletList" : "orderedList";
17908
+ const numId = ListHelpers.getNewListId(editor);
17909
+ ListHelpers.generateNewListDefinition({ numId: Number(numId), listType, editor });
17910
+ allParagraphs.forEach(({ node, pos: pos2 }) => {
17911
+ const paragraphProps = getResolvedParagraphProperties(node);
17912
+ updateNumberingProperties(
17913
+ {
17914
+ ...paragraphProps.numberingProperties || {},
17915
+ numId: Number(numId)
17916
+ },
17917
+ node,
17918
+ pos2,
17919
+ editor,
17920
+ tr
17921
+ );
17922
+ });
17923
+ if (dispatch) dispatch(tr);
17924
+ return true;
18268
17925
  };
18269
- const getSpacingStyle = (spacing) => {
18270
- const { lineSpaceBefore, lineSpaceAfter, line, lineRule, beforeAutoSpacing, afterAutoSpacing } = spacing;
18271
- const lineHeightResult = getLineHeightValueString(line, "", lineRule, true);
18272
- const lineHeightStyles = typeof lineHeightResult === "object" && lineHeightResult !== null ? lineHeightResult : {};
18273
- const result = {};
18274
- if (!beforeAutoSpacing) {
18275
- result["margin-top"] = lineSpaceBefore + "px";
18276
- }
18277
- if (!afterAutoSpacing) {
18278
- result["margin-bottom"] = lineSpaceAfter + "px";
18279
- }
18280
- return {
18281
- ...result,
18282
- ...lineHeightStyles
18283
- };
17926
+ const defaultTabDistance = 48;
17927
+ const defaultLineLength = 816;
17928
+ const getTabDecorations = (doc2, view, helpers2, from2 = 0, to = null) => {
17929
+ const decorations = [];
17930
+ const paragraphCache = /* @__PURE__ */ new Map();
17931
+ const coordCache = /* @__PURE__ */ new Map();
17932
+ const domPosCache = /* @__PURE__ */ new Map();
17933
+ const end2 = to ?? doc2.content.size;
17934
+ doc2.nodesBetween(from2, end2, (node, pos) => {
17935
+ if (node.type.name !== "tab") return;
17936
+ const $pos = doc2.resolve(pos);
17937
+ const paragraphContext = findParagraphContext($pos, paragraphCache, helpers2);
17938
+ if (!paragraphContext) return;
17939
+ const blockParent2 = $pos.node(paragraphContext.paragraphDepth);
17940
+ const style = calculateTabStyle(node.nodeSize, view, pos, blockParent2, paragraphContext, coordCache, domPosCache);
17941
+ if (style) {
17942
+ decorations.push(
17943
+ Decoration.node(pos, pos + node.nodeSize, {
17944
+ style
17945
+ })
17946
+ );
17947
+ }
17948
+ });
17949
+ return decorations;
18284
17950
  };
18285
- const getSpacingStyleString = (spacing, marks, isListItem) => {
18286
- let { before, after, line, lineRule, beforeAutospacing, afterAutospacing } = spacing;
18287
- line = twipsToLines(line);
18288
- if (line != null && line < 1) {
18289
- line = 1;
18290
- }
18291
- if (lineRule === "exact" && line) {
18292
- line = String(line);
18293
- }
18294
- const textStyleMark = marks?.find((mark) => mark.type === "textStyle");
18295
- const fontSize = textStyleMark?.attrs?.fontSize;
18296
- before = twipsToPixels(before);
18297
- if (beforeAutospacing) {
18298
- if (fontSize) {
18299
- before += halfPointToPixels(parseInt(fontSize) * 0.5);
17951
+ function calculateTabStyle(nodeSize2, view, pos, blockParent2, paragraphContext, coordCache = null, domPosCache = null) {
17952
+ let extraStyles = "";
17953
+ try {
17954
+ const { tabStops, flattened, positionMap, startPos } = paragraphContext;
17955
+ if (paragraphContext.indentWidth === void 0) {
17956
+ paragraphContext.indentWidth = getIndentWidth(view, startPos, paragraphContext.indent, coordCache, domPosCache);
18300
17957
  }
18301
- if (isListItem) {
18302
- before = 0;
17958
+ if (paragraphContext.tabHeight === void 0) {
17959
+ paragraphContext.tabHeight = calcTabHeight(blockParent2);
18303
17960
  }
18304
- }
18305
- after = twipsToPixels(after);
18306
- if (afterAutospacing) {
18307
- if (fontSize) {
18308
- after += halfPointToPixels(parseInt(fontSize) * 0.5);
17961
+ if (paragraphContext.paragraphWidth === void 0) {
17962
+ paragraphContext.paragraphWidth = getBlockNodeWidth(view, startPos);
18309
17963
  }
18310
- if (isListItem) {
18311
- after = 0;
17964
+ const indentWidth = paragraphContext.indentWidth;
17965
+ const hanging = twipsToPixels(Number(paragraphContext.indent.hanging) || 0);
17966
+ if (hanging > 0) {
17967
+ tabStops.unshift({ val: "start", pos: indentWidth + hanging });
17968
+ }
17969
+ const accumulatedTabWidth = paragraphContext.accumulatedTabWidth || 0;
17970
+ const currentWidth = indentWidth + measureRangeWidth(view, startPos + 1, pos, coordCache, domPosCache) + accumulatedTabWidth;
17971
+ let tabWidth;
17972
+ if (tabStops.length) {
17973
+ const tabStop = tabStops.find((stop) => stop.pos > currentWidth && stop.val !== "clear");
17974
+ if (tabStop) {
17975
+ tabWidth = Math.min(tabStop.pos, paragraphContext.paragraphWidth) - currentWidth;
17976
+ let val = tabStop.val;
17977
+ const aliases = { left: "start", right: "end" };
17978
+ if (aliases[val]) val = aliases[val];
17979
+ if (val === "center" || val === "end" || val === "right") {
17980
+ const entryIndex = positionMap.get(pos);
17981
+ if (entryIndex === void 0) return;
17982
+ const nextTabIndex = findNextTabIndex(flattened, entryIndex + 1);
17983
+ const segmentStartPos = pos + nodeSize2;
17984
+ const segmentEndPos = nextTabIndex === -1 ? startPos + paragraphContext.paragraph.nodeSize - 1 : flattened[nextTabIndex].pos;
17985
+ const segmentWidth = measureRangeWidth(view, segmentStartPos, segmentEndPos, coordCache, domPosCache);
17986
+ tabWidth -= val === "center" ? segmentWidth / 2 : segmentWidth;
17987
+ } else if (val === "decimal" || val === "num") {
17988
+ const entryIndex = positionMap.get(pos);
17989
+ if (entryIndex === void 0) return;
17990
+ const breakChar = tabStop.decimalChar || ".";
17991
+ const decimalPos = findDecimalBreakPos(flattened, entryIndex + 1, breakChar);
17992
+ const integralWidth = decimalPos ? measureRangeWidth(view, pos + nodeSize2, decimalPos, coordCache, domPosCache) : measureRangeWidth(
17993
+ view,
17994
+ pos + nodeSize2,
17995
+ startPos + paragraphContext.paragraph.nodeSize - 1,
17996
+ coordCache,
17997
+ domPosCache
17998
+ );
17999
+ tabWidth -= integralWidth;
18000
+ }
18001
+ if (tabStop.leader) {
18002
+ const leaderStyles = {
18003
+ dot: "border-bottom: 1px dotted black;",
18004
+ heavy: "border-bottom: 2px solid black;",
18005
+ hyphen: "border-bottom: 1px solid black;",
18006
+ middleDot: "border-bottom: 1px dotted black; margin-bottom: 2px;",
18007
+ underscore: "border-bottom: 1px solid black;"
18008
+ };
18009
+ extraStyles += leaderStyles[tabStop.leader] || "";
18010
+ }
18011
+ }
18312
18012
  }
18013
+ if (!tabWidth || tabWidth < 1) {
18014
+ tabWidth = defaultTabDistance - currentWidth % defaultLineLength % defaultTabDistance;
18015
+ if (tabWidth === 0) tabWidth = defaultTabDistance;
18016
+ }
18017
+ const tabHeight = paragraphContext.tabHeight;
18018
+ paragraphContext.accumulatedTabWidth = accumulatedTabWidth + tabWidth;
18019
+ return `width: ${tabWidth}px; height: ${tabHeight}; ${extraStyles}`;
18020
+ } catch (error) {
18021
+ console.error("tab decoration error", error);
18313
18022
  }
18314
- return `
18315
- ${before ? `margin-top: ${before}px;` : ""}
18316
- ${after ? `margin-bottom: ${after}px;` : ""}
18317
- ${line ? getLineHeightValueString(line, "") : ""}
18318
- `.trim();
18319
- };
18320
- const getMarksStyle = (attrs) => {
18321
- let styles = "";
18322
- for (const attr of attrs) {
18323
- switch (attr.type) {
18324
- case "bold":
18325
- styles += `font-weight: bold; `;
18326
- break;
18327
- case "italic":
18328
- styles += `font-style: italic; `;
18329
- break;
18330
- case "underline":
18331
- styles += `text-decoration: underline; `;
18332
- break;
18333
- case "highlight":
18334
- styles += `background-color: ${attr.attrs.color}; `;
18335
- break;
18336
- case "textStyle":
18337
- const { fontFamily, fontSize } = attr.attrs;
18338
- styles += `${fontFamily ? `font-family: ${fontFamily};` : ""} ${fontSize ? `font-size: ${fontSize};` : ""}`;
18339
- break;
18023
+ }
18024
+ function findParagraphContext($pos, cache, helpers2) {
18025
+ for (let depth = $pos.depth; depth >= 0; depth--) {
18026
+ const node = $pos.node(depth);
18027
+ if (node?.type?.name === "paragraph") {
18028
+ const startPos = $pos.start(depth);
18029
+ if (!cache.has(startPos)) {
18030
+ const paragraphContext = extractParagraphContext(node, startPos, helpers2, depth);
18031
+ cache.set(startPos, paragraphContext);
18032
+ }
18033
+ return cache.get(startPos);
18340
18034
  }
18341
18035
  }
18342
- return styles.trim();
18343
- };
18344
- const getQuickFormatList = (editor) => {
18345
- if (!editor?.converter?.linkedStyles) return [];
18346
- return editor.converter.linkedStyles.filter((style) => style.type === "paragraph" && style.definition?.attrs).sort((a, b) => {
18347
- const nameA = a.definition.attrs?.name ?? "";
18348
- const nameB = b.definition.attrs?.name ?? "";
18349
- return nameA.localeCompare(nameB);
18350
- });
18351
- };
18352
- const generateLinkedStyleString = (linkedStyle, basedOnStyle, node, parent, includeSpacing = true) => {
18353
- if (!linkedStyle?.definition?.styles) return "";
18354
- const markValue = {};
18355
- const linkedDefinitionStyles = { ...linkedStyle.definition.styles };
18356
- const basedOnDefinitionStyles = { ...basedOnStyle?.definition?.styles };
18357
- const resultStyles = { ...linkedDefinitionStyles };
18358
- const inheritKeys = [
18359
- "font-size",
18360
- "font-family",
18361
- "text-transform",
18362
- "bold",
18363
- "italic",
18364
- "underline",
18365
- "strike",
18366
- "color",
18367
- "highlight"
18368
- ];
18369
- inheritKeys.forEach((k2) => {
18370
- if (!linkedDefinitionStyles[k2] && basedOnDefinitionStyles[k2]) {
18371
- resultStyles[k2] = basedOnDefinitionStyles[k2];
18036
+ return null;
18037
+ }
18038
+ function extractParagraphContext(node, startPos, helpers2, depth = 0) {
18039
+ const paragraphProperties = getResolvedParagraphProperties(node);
18040
+ let tabStops = [];
18041
+ if (Array.isArray(paragraphProperties.tabStops)) {
18042
+ tabStops = paragraphProperties.tabStops.map((stop) => {
18043
+ const ref2 = stop?.tab;
18044
+ if (!ref2) return stop || null;
18045
+ return {
18046
+ val: ref2.tabType || "start",
18047
+ pos: twipsToPixels(Number(ref2.pos) || 0),
18048
+ leader: ref2.leader
18049
+ };
18050
+ }).filter(Boolean);
18051
+ }
18052
+ const { entries, positionMap } = flattenParagraph(node, startPos);
18053
+ return {
18054
+ paragraph: node,
18055
+ paragraphDepth: depth,
18056
+ startPos,
18057
+ indent: paragraphProperties.indent || {},
18058
+ tabStops,
18059
+ flattened: entries,
18060
+ positionMap,
18061
+ // Store position map for O(1) lookups
18062
+ accumulatedTabWidth: 0
18063
+ };
18064
+ }
18065
+ function flattenParagraph(paragraph, paragraphStartPos) {
18066
+ const entries = [];
18067
+ const positionMap = /* @__PURE__ */ new Map();
18068
+ const walk = (node, basePos) => {
18069
+ if (!node) return;
18070
+ if (node.type?.name === "run") {
18071
+ node.forEach((child, offset2) => {
18072
+ const childPos = basePos + offset2 + 1;
18073
+ walk(child, childPos);
18074
+ });
18075
+ return;
18372
18076
  }
18077
+ const pos = basePos - 1;
18078
+ const index2 = entries.length;
18079
+ entries.push({ node, pos });
18080
+ positionMap.set(pos, index2);
18081
+ };
18082
+ paragraph.forEach((child, offset2) => {
18083
+ const childPos = paragraphStartPos + offset2 + 1;
18084
+ walk(child, childPos);
18373
18085
  });
18374
- Object.entries(resultStyles).forEach(([k2, value]) => {
18375
- const key2 = kebabCase(k2);
18376
- const flattenedMarks = [];
18377
- node?.marks?.forEach((n) => {
18378
- if (n.type.name === "textStyle") {
18379
- Object.entries(n.attrs).forEach(([styleKey, value2]) => {
18380
- const parsedKey = kebabCase(styleKey);
18381
- if (!value2) return;
18382
- flattenedMarks.push({ key: parsedKey, value: value2 });
18383
- });
18384
- return;
18086
+ return { entries, positionMap };
18087
+ }
18088
+ function findNextTabIndex(flattened, fromIndex) {
18089
+ for (let i = fromIndex; i < flattened.length; i++) {
18090
+ if (flattened[i]?.node?.type?.name === "tab") {
18091
+ return i;
18092
+ }
18093
+ }
18094
+ return -1;
18095
+ }
18096
+ function findDecimalBreakPos(flattened, startIndex, breakChar) {
18097
+ for (let i = startIndex; i < flattened.length; i++) {
18098
+ const entry = flattened[i];
18099
+ if (!entry) break;
18100
+ if (entry.node.type?.name === "tab") break;
18101
+ if (entry.node.type?.name === "text") {
18102
+ const index2 = entry.node.text?.indexOf(breakChar);
18103
+ if (index2 !== void 0 && index2 !== -1) {
18104
+ return entry.pos + index2 + 1;
18385
18105
  }
18386
- flattenedMarks.push({ key: n.type.name, value: n.attrs[key2] });
18387
- });
18388
- const underlineNone = node?.marks?.some((m) => m.type?.name === "underline" && m.attrs?.underlineType === "none");
18389
- if (underlineNone) {
18390
- markValue["text-decoration"] = "none";
18391
18106
  }
18392
- const mark = flattenedMarks.find((n) => n.key === key2);
18393
- const hasParentIndent = Object.keys(parent?.attrs?.indent || {});
18394
- const hasParentSpacing = Object.keys(parent?.attrs?.spacing || {});
18395
- const listTypes = ["orderedList", "listItem"];
18396
- if (!mark) {
18397
- if (key2 === "spacing" && includeSpacing && !hasParentSpacing) {
18398
- const space = getSpacingStyle(value);
18399
- Object.entries(space).forEach(([k3, v]) => {
18400
- markValue[k3] = v;
18401
- });
18402
- } else if (key2 === "indent" && includeSpacing && !hasParentIndent) {
18403
- const { leftIndent, rightIndent, firstLine } = value;
18404
- if (leftIndent) markValue["margin-left"] = leftIndent + "px";
18405
- if (rightIndent) markValue["margin-right"] = rightIndent + "px";
18406
- if (firstLine) markValue["text-indent"] = firstLine + "px";
18407
- } else if (key2 === "bold" && node) {
18408
- const boldValue = typeof value === "object" && value !== null ? value.value : value;
18409
- const hasInlineBoldOff = node.marks?.some((m) => m.type?.name === "bold" && m.attrs?.value === "0");
18410
- const hasInlineBoldOn = node.marks?.some((m) => m.type?.name === "bold" && m.attrs?.value !== "0");
18411
- if (!listTypes.includes(node.type.name) && !hasInlineBoldOff && !hasInlineBoldOn && boldValue !== "0" && boldValue !== false) {
18412
- markValue["font-weight"] = "bold";
18413
- }
18414
- } else if (key2 === "italic" && node) {
18415
- const italicValue = typeof value === "object" && value !== null ? value.value : value;
18416
- const hasInlineItalicOff = node.marks?.some((m) => m.type?.name === "italic" && m.attrs?.value === "0");
18417
- const hasInlineItalicOn = node.marks?.some((m) => m.type?.name === "italic" && m.attrs?.value !== "0");
18418
- if (!listTypes.includes(node.type.name) && !hasInlineItalicOff && !hasInlineItalicOn && italicValue !== "0" && italicValue !== false) {
18419
- markValue["font-style"] = "italic";
18420
- }
18421
- } else if (key2 === "strike" && node) {
18422
- const strikeValue = typeof value === "object" && value !== null ? value.value : value;
18423
- const hasInlineStrikeOff = node.marks?.some((m) => m.type?.name === "strike" && m.attrs?.value === "0");
18424
- const hasInlineStrikeOn = node.marks?.some(
18425
- (m) => m.type?.name === "strike" && (m.attrs?.value === void 0 || m.attrs?.value !== "0")
18426
- );
18427
- if (!listTypes.includes(node.type.name) && !hasInlineStrikeOff && !hasInlineStrikeOn && strikeValue !== "0" && strikeValue !== false) {
18428
- markValue["text-decoration"] = "line-through";
18429
- }
18430
- } else if (key2 === "text-transform" && node) {
18431
- if (!listTypes.includes(node.type.name)) {
18432
- markValue[key2] = value;
18433
- }
18434
- } else if (key2 === "font-size" && node) {
18435
- if (!listTypes.includes(node.type.name)) {
18436
- markValue[key2] = value;
18437
- }
18438
- } else if (key2 === "font-family" && node) {
18439
- if (!listTypes.includes(node.type.name)) {
18440
- markValue[key2] = value;
18441
- }
18442
- } else if (key2 === "color" && node) {
18443
- if (!listTypes.includes(node.type.name)) {
18444
- markValue[key2] = value;
18445
- }
18446
- } else if (key2 === "highlight" && node) {
18447
- const hasInlineHighlight = node.marks?.some((m) => m.type?.name === "highlight");
18448
- if (!listTypes.includes(node.type.name) && !hasInlineHighlight) {
18449
- const color = typeof value === "string" ? value : value?.color;
18450
- if (color) markValue["background-color"] = color;
18451
- }
18452
- } else if (key2 === "underline" && node) {
18453
- const styleValRaw = value?.value ?? value ?? "";
18454
- const styleVal = styleValRaw.toString().toLowerCase();
18455
- const hasInlineUnderlineOff = node.marks?.some(
18456
- (m) => m.type?.name === "underline" && m.attrs?.underlineType === "none"
18457
- );
18458
- const hasInlineUnderlineOn = node.marks?.some(
18459
- (m) => m.type?.name === "underline" && m.attrs?.underlineType && m.attrs.underlineType !== "none"
18460
- );
18461
- if (!listTypes.includes(node.type.name) && !hasInlineUnderlineOff && !hasInlineUnderlineOn) {
18462
- if (styleVal && styleVal !== "none" && styleVal !== "0") {
18463
- const colorVal = value && typeof value === "object" ? value.color || value.underlineColor || null : null;
18464
- const css = getUnderlineCssString({ type: styleVal, color: colorVal });
18465
- css.split(";").forEach((decl) => {
18466
- const d2 = decl.trim();
18467
- if (!d2) return;
18468
- const idx = d2.indexOf(":");
18469
- if (idx === -1) return;
18470
- const k3 = d2.slice(0, idx).trim();
18471
- const v = d2.slice(idx + 1).trim();
18472
- markValue[k3] = v;
18473
- });
18474
- }
18107
+ }
18108
+ return null;
18109
+ }
18110
+ function measureRangeWidth(view, from2, to, coordCache = null, domPosCache = null) {
18111
+ if (!Number.isFinite(from2) || !Number.isFinite(to) || to <= from2) return 0;
18112
+ try {
18113
+ const range = document.createRange();
18114
+ const fromRef = getCachedDomAtPos(view, from2, domPosCache);
18115
+ const toRef = getCachedDomAtPos(view, to, domPosCache);
18116
+ range.setStart(fromRef.node, fromRef.offset);
18117
+ range.setEnd(toRef.node, toRef.offset);
18118
+ const rect = range.getBoundingClientRect();
18119
+ range.detach?.();
18120
+ return rect.width || 0;
18121
+ } catch {
18122
+ const startLeft = getLeftCoord(view, from2, coordCache, domPosCache);
18123
+ const endLeft = getLeftCoord(view, to, coordCache, domPosCache);
18124
+ if (startLeft == null || endLeft == null) return 0;
18125
+ return Math.max(0, endLeft - startLeft);
18126
+ }
18127
+ }
18128
+ function getIndentWidth(view, paragraphStartPos, indentAttrs = {}, coordCache = null, domPosCache = null) {
18129
+ const marginLeft = getLeftCoord(view, paragraphStartPos, coordCache, domPosCache);
18130
+ const lineLeft = getLeftCoord(view, paragraphStartPos + 1, coordCache, domPosCache);
18131
+ if (marginLeft != null && lineLeft != null) {
18132
+ const diff = lineLeft - marginLeft;
18133
+ if (!Number.isNaN(diff) && Math.abs(diff) > 0.5) {
18134
+ return diff;
18135
+ }
18136
+ }
18137
+ return calculateIndentFallback(indentAttrs);
18138
+ }
18139
+ function getBlockNodeWidth(view, blockStartPos) {
18140
+ const blockDom = view.nodeDOM(blockStartPos - 1);
18141
+ if (blockDom instanceof HTMLElement) {
18142
+ const styles = window.getComputedStyle(blockDom);
18143
+ const width = blockDom.clientWidth + parseFloat(styles.marginLeft || "0") + parseFloat(styles.marginRight || "0") + parseFloat(styles.borderLeftWidth || "0") + parseFloat(styles.borderRightWidth || "0") + parseFloat(styles.paddingLeft || "0") + parseFloat(styles.paddingRight || "0");
18144
+ return width;
18145
+ }
18146
+ return defaultLineLength;
18147
+ }
18148
+ function calculateIndentFallback(indentAttrs = {}) {
18149
+ if (!indentAttrs) return 0;
18150
+ const left2 = twipsToPixels(Number(indentAttrs.left) || 0);
18151
+ const firstLine = twipsToPixels(Number(indentAttrs.firstLine) || 0);
18152
+ const hanging = twipsToPixels(Number(indentAttrs.hanging) || 0);
18153
+ let textIndent = 0;
18154
+ if (firstLine && hanging) {
18155
+ textIndent = firstLine - hanging;
18156
+ } else if (firstLine) {
18157
+ textIndent = firstLine;
18158
+ } else if (hanging) {
18159
+ textIndent = -hanging;
18160
+ }
18161
+ if (textIndent) return left2 + textIndent;
18162
+ if (left2) return left2;
18163
+ return 0;
18164
+ }
18165
+ function getLeftCoord(view, pos, coordCache = null, domPosCache = null) {
18166
+ if (!Number.isFinite(pos)) return null;
18167
+ if (coordCache && coordCache.has(pos)) {
18168
+ return coordCache.get(pos);
18169
+ }
18170
+ let result = null;
18171
+ try {
18172
+ result = view.coordsAtPos(pos).left;
18173
+ } catch {
18174
+ try {
18175
+ const ref2 = getCachedDomAtPos(view, pos, domPosCache);
18176
+ const range = document.createRange();
18177
+ range.setStart(ref2.node, ref2.offset);
18178
+ range.setEnd(ref2.node, ref2.offset);
18179
+ const rect = range.getBoundingClientRect();
18180
+ range.detach?.();
18181
+ result = rect.left;
18182
+ } catch {
18183
+ result = null;
18184
+ }
18185
+ }
18186
+ if (coordCache) {
18187
+ coordCache.set(pos, result);
18188
+ }
18189
+ return result;
18190
+ }
18191
+ function getCachedDomAtPos(view, pos, domPosCache = null) {
18192
+ if (domPosCache && domPosCache.has(pos)) {
18193
+ return domPosCache.get(pos);
18194
+ }
18195
+ const result = view.domAtPos(pos);
18196
+ if (domPosCache) {
18197
+ domPosCache.set(pos, result);
18198
+ }
18199
+ return result;
18200
+ }
18201
+ function calcTabHeight(blockParent2) {
18202
+ const ptToPxRatio = 1.333;
18203
+ const defaultFontSize = 16;
18204
+ const defaultLineHeight = 1.1;
18205
+ const parentTextStyleMark = blockParent2.firstChild?.marks?.find((mark) => mark.type.name === "textStyle");
18206
+ const fontSize = parseInt(parentTextStyleMark?.attrs.fontSize) * ptToPxRatio || defaultFontSize;
18207
+ return `${fontSize * defaultLineHeight}px`;
18208
+ }
18209
+ class ParagraphNodeView {
18210
+ /**
18211
+ * @param {import('prosemirror-model').Node} node Current paragraph node.
18212
+ * @param {import('../../core/Editor').Editor} editor Editor instance providing schema/helpers.
18213
+ * @param {() => number} getPos Position getter provided by ProseMirror.
18214
+ * @param {import('prosemirror-view').Decoration[]} decorations Decorations applied to this node.
18215
+ * @param {Record<string, unknown>} extensionAttrs Extra attributes declared by the paragraph extension.
18216
+ */
18217
+ constructor(node, editor, getPos, decorations, extensionAttrs) {
18218
+ __privateAdd(this, _ParagraphNodeView_instances);
18219
+ this.node = node;
18220
+ this.editor = editor;
18221
+ this.getPos = getPos;
18222
+ this.decorations = decorations;
18223
+ this.extensionAttrs = extensionAttrs;
18224
+ this._animationFrameRequest = null;
18225
+ calculateResolvedParagraphProperties(this.editor, this.node, this.editor.state.doc.resolve(this.getPos()));
18226
+ this.dom = document.createElement("p");
18227
+ this.contentDOM = document.createElement("span");
18228
+ this.dom.appendChild(this.contentDOM);
18229
+ if (__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
18230
+ __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
18231
+ __privateMethod(this, _ParagraphNodeView_instances, scheduleAnimation_fn).call(this, () => {
18232
+ if (!__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
18233
+ return;
18475
18234
  }
18476
- } else if (typeof value === "string") {
18477
- markValue[key2] = value;
18478
- }
18235
+ __privateMethod(this, _ParagraphNodeView_instances, updateListStyles_fn).call(this);
18236
+ });
18479
18237
  }
18480
- });
18481
- const final = Object.entries(markValue).map(([key2, value]) => `${key2}: ${value}`).join(";");
18482
- return final;
18483
- };
18484
- const applyLinkedStyleToTransaction = (tr, editor, style) => {
18485
- if (!style) return false;
18486
- let selection = tr.selection;
18487
- const state = editor.state;
18488
- const focusState = CustomSelectionPluginKey.getState(state);
18489
- if (selection.empty && focusState?.preservedSelection && !focusState?.preservedSelection.empty) {
18490
- selection = focusState.preservedSelection;
18491
- tr.setSelection(selection);
18492
- } else if (selection.empty && editor.options.lastSelection) {
18493
- selection = editor.options.lastSelection;
18494
- tr.setSelection(selection);
18238
+ __privateMethod(this, _ParagraphNodeView_instances, updateHTMLAttributes_fn).call(this);
18239
+ __privateMethod(this, _ParagraphNodeView_instances, updateDOMStyles_fn).call(this);
18495
18240
  }
18496
- const { from: from2, to } = selection;
18497
- const getCleanParagraphAttrs = (node) => {
18498
- const cleanAttrs = {};
18499
- const preservedAttrs = ["id", "class"];
18500
- preservedAttrs.forEach((attr) => {
18501
- if (node.attrs[attr] !== void 0) {
18502
- cleanAttrs[attr] = node.attrs[attr];
18503
- }
18504
- });
18505
- cleanAttrs.styleId = style.id;
18506
- return cleanAttrs;
18507
- };
18508
- const clearFormattingMarks = (startPos, endPos) => {
18509
- tr.doc.nodesBetween(startPos, endPos, (node, pos) => {
18510
- if (node.isText && node.marks.length > 0) {
18511
- const marksToRemove = [
18512
- "textStyle",
18513
- "bold",
18514
- "italic",
18515
- "underline",
18516
- "strike",
18517
- "subscript",
18518
- "superscript",
18519
- "highlight"
18520
- ];
18521
- node.marks.forEach((mark) => {
18522
- if (marksToRemove.includes(mark.type.name)) {
18523
- tr.removeMark(pos, pos + node.nodeSize, mark);
18524
- }
18525
- });
18526
- }
18241
+ /**
18242
+ * @param {import('prosemirror-model').Node} node
18243
+ * @param {import('prosemirror-view').Decoration[]} decorations
18244
+ */
18245
+ update(node, decorations) {
18246
+ const oldAttrs = this.node.attrs;
18247
+ const newAttrs = node.attrs;
18248
+ this.node = node;
18249
+ this.decorations = decorations;
18250
+ if (JSON.stringify(oldAttrs) === JSON.stringify(newAttrs)) {
18527
18251
  return true;
18528
- });
18529
- };
18530
- if (from2 === to) {
18531
- let pos = from2;
18532
- let paragraphNode = tr.doc.nodeAt(from2);
18533
- if (paragraphNode?.type.name !== "paragraph") {
18534
- const parentNode2 = findParentNode((node) => node.type.name === "paragraph")(selection);
18535
- if (!parentNode2) return false;
18536
- pos = parentNode2.pos;
18537
- paragraphNode = parentNode2.node;
18538
18252
  }
18539
- clearFormattingMarks(pos + 1, pos + paragraphNode.nodeSize - 1);
18540
- tr.setNodeMarkup(pos, void 0, getCleanParagraphAttrs(paragraphNode));
18253
+ calculateResolvedParagraphProperties(this.editor, this.node, this.editor.state.doc.resolve(this.getPos()));
18254
+ __privateMethod(this, _ParagraphNodeView_instances, updateHTMLAttributes_fn).call(this);
18255
+ __privateMethod(this, _ParagraphNodeView_instances, updateDOMStyles_fn).call(this);
18256
+ if (!__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
18257
+ __privateMethod(this, _ParagraphNodeView_instances, removeList_fn).call(this);
18258
+ return true;
18259
+ }
18260
+ __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
18261
+ __privateMethod(this, _ParagraphNodeView_instances, scheduleAnimation_fn).call(this, () => {
18262
+ __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
18263
+ __privateMethod(this, _ParagraphNodeView_instances, updateListStyles_fn).call(this);
18264
+ });
18541
18265
  return true;
18542
18266
  }
18543
- const paragraphPositions = [];
18544
- tr.doc.nodesBetween(from2, to, (node, pos) => {
18545
- if (node.type.name === "paragraph") {
18546
- paragraphPositions.push({ node, pos });
18267
+ /**
18268
+ * @param {MutationRecord} mutation
18269
+ */
18270
+ ignoreMutation(mutation) {
18271
+ if (this.marker && (mutation.target === this.marker || this.marker.contains(mutation.target))) {
18272
+ return true;
18273
+ }
18274
+ if (this.separator && (mutation.target === this.separator || this.separator.contains(mutation.target))) {
18275
+ return true;
18276
+ }
18277
+ if (mutation.type === "attributes" && mutation.target === this.dom && mutation.attributeName === "style") {
18278
+ return true;
18279
+ }
18280
+ if (mutation.type === "childList") {
18281
+ if (this.marker && Array.from(mutation.removedNodes).includes(this.marker)) {
18282
+ return true;
18283
+ }
18284
+ if (this.marker && Array.from(mutation.addedNodes).includes(this.marker)) {
18285
+ return true;
18286
+ }
18287
+ if (this.separator && Array.from(mutation.removedNodes).includes(this.separator)) {
18288
+ return true;
18289
+ }
18290
+ if (this.separator && Array.from(mutation.addedNodes).includes(this.separator)) {
18291
+ return true;
18292
+ }
18547
18293
  }
18548
- return true;
18549
- });
18550
- paragraphPositions.forEach(({ node, pos }) => {
18551
- clearFormattingMarks(pos + 1, pos + node.nodeSize - 1);
18552
- tr.setNodeMarkup(pos, void 0, getCleanParagraphAttrs(node));
18553
- });
18554
- return true;
18555
- };
18556
- const stepInsertsTextIntoStyledParagraph = (tr, oldEditorState, step, stepIndex) => {
18557
- if (!step.slice || step.slice.size === 0 || typeof step.from !== "number") {
18558
18294
  return false;
18559
18295
  }
18560
- let insertsText = false;
18561
- step.slice.content.descendants((node) => {
18562
- if (node.type?.name === "text" && node.text?.length) {
18563
- insertsText = true;
18564
- return false;
18565
- }
18566
- return true;
18567
- });
18568
- if (!insertsText) return false;
18569
- const docBeforeStep = tr.docs?.[stepIndex] || oldEditorState.doc;
18570
- if (!docBeforeStep) return false;
18571
- const resolvedPos = Math.min(step.from, docBeforeStep.content.size);
18572
- const $pos = docBeforeStep.resolve(resolvedPos);
18573
- for (let depth = $pos.depth; depth >= 0; depth--) {
18574
- const node = $pos.node(depth);
18575
- if (node?.type?.name === "paragraph") {
18576
- return Boolean(node.attrs?.styleId);
18296
+ destroy() {
18297
+ __privateMethod(this, _ParagraphNodeView_instances, cancelScheduledAnimation_fn).call(this);
18298
+ }
18299
+ }
18300
+ _ParagraphNodeView_instances = new WeakSet();
18301
+ updateHTMLAttributes_fn = function() {
18302
+ const htmlAttributes = Attribute.getAttributesToRender(this.node, this.extensionAttrs);
18303
+ htmlAttributes.style = htmlAttributes.style || "";
18304
+ for (const [key2, value] of Object.entries(htmlAttributes || {})) {
18305
+ if (value == null) {
18306
+ this.dom.removeAttribute(key2);
18307
+ continue;
18577
18308
  }
18309
+ this.dom.setAttribute(key2, value);
18578
18310
  }
18579
- return false;
18580
- };
18581
- const LinkedStylesPluginKey = new PluginKey("linkedStyles");
18582
- const createLinkedStylesPlugin = (editor) => {
18583
- return new Plugin({
18584
- key: LinkedStylesPluginKey,
18585
- state: {
18586
- /**
18587
- * Initialize plugin state with styles and decorations
18588
- * @returns {Object} Initial state with styles and decorations
18589
- * @private
18590
- */
18591
- init() {
18592
- if (!editor.converter || editor.options.mode !== "docx") return {};
18593
- const styles = editor.converter?.linkedStyles || [];
18594
- return {
18595
- styles,
18596
- decorations: generateDecorations(editor.state, styles)
18597
- };
18598
- },
18599
- /**
18600
- * Update decorations when document changes
18601
- * @param {Object} tr - The transaction
18602
- * @param {Object} prev - Previous plugin state
18603
- * @param {Object} oldEditorState - Old editor state
18604
- * @param {Object} newEditorState - New editor state
18605
- * @returns {Object} Updated state with styles and decorations
18606
- * @private
18607
- */
18608
- apply(tr, prev, oldEditorState, newEditorState) {
18609
- if (!editor.converter || editor.options.mode !== "docx") return { ...prev };
18610
- let decorations = prev.decorations || DecorationSet.empty;
18611
- if (tr.docChanged) {
18612
- let mightAffectStyles = false;
18613
- const styleRelatedMarks = /* @__PURE__ */ new Set(["textStyle", "bold", "italic", "underline", "strike"]);
18614
- tr.steps.forEach((step, index2) => {
18615
- if (step.slice) {
18616
- step.slice.content.descendants((node) => {
18617
- if (node.attrs?.styleId) {
18618
- mightAffectStyles = true;
18619
- return false;
18620
- }
18621
- if (node.marks.length > 0) {
18622
- const hasStyleMarks = node.marks.some((mark) => styleRelatedMarks.has(mark.type.name));
18623
- if (hasStyleMarks) {
18624
- mightAffectStyles = true;
18625
- return false;
18626
- }
18627
- }
18628
- });
18629
- }
18630
- if (step.jsonID === "addMark" || step.jsonID === "removeMark") {
18631
- if (step.mark && styleRelatedMarks.has(step.mark.type.name)) {
18632
- mightAffectStyles = true;
18633
- }
18634
- }
18635
- if (!mightAffectStyles && stepInsertsTextIntoStyledParagraph(tr, oldEditorState, step, index2)) {
18636
- mightAffectStyles = true;
18637
- }
18638
- });
18639
- if (mightAffectStyles) {
18640
- const styles = LinkedStylesPluginKey.getState(editor.state).styles;
18641
- decorations = generateDecorations(newEditorState, styles);
18642
- } else {
18643
- decorations = decorations.map(tr.mapping, tr.doc);
18644
- }
18645
- }
18646
- return { ...prev, decorations };
18647
- }
18648
- },
18649
- props: {
18650
- /**
18651
- * Provide decorations to the editor view
18652
- * @param {Object} state - Current editor state
18653
- * @returns {Object} The decoration set
18654
- * @private
18655
- */
18656
- decorations(state) {
18657
- return LinkedStylesPluginKey.getState(state)?.decorations;
18658
- }
18659
- }
18660
- });
18661
- };
18662
- const generateDecorations = (state, styles) => {
18663
- const decorations = [];
18664
- const doc2 = state?.doc;
18665
- const getParagraphStyleId = (pos) => {
18666
- const $pos = state.doc.resolve(pos);
18667
- for (let d2 = $pos.depth; d2 >= 0; d2--) {
18668
- const n = $pos.node(d2);
18669
- if (n?.type?.name === "paragraph") return n.attrs?.styleId || null;
18670
- }
18671
- return null;
18672
- };
18673
- doc2.descendants((node, pos) => {
18674
- const { name } = node.type;
18675
- if (name !== "text") return;
18676
- const paragraphStyleId = getParagraphStyleId(pos);
18677
- let runStyleId = null;
18678
- let inlineTextStyleId = null;
18679
- for (const mark of node.marks) {
18680
- if (mark.type.name === "run") {
18681
- const rp = mark.attrs?.runProperties;
18682
- if (rp && typeof rp === "object" && !Array.isArray(rp) && rp.styleId) runStyleId = rp.styleId;
18683
- else if (Array.isArray(rp)) {
18684
- const ent = rp.find((e) => e?.xmlName === "w:rStyle");
18685
- const sid = ent?.attributes?.["w:val"];
18686
- if (sid) runStyleId = sid;
18687
- }
18688
- } else if (mark.type.name === "textStyle" && mark.attrs?.styleId) {
18689
- inlineTextStyleId = mark.attrs.styleId;
18690
- }
18691
- }
18692
- const buildStyleMap = (sid) => {
18693
- if (!sid) return {};
18694
- const { linkedStyle, basedOnStyle: basedOnStyle2 } = getLinkedStyle(sid, styles);
18695
- if (!linkedStyle) return {};
18696
- const base2 = { ...basedOnStyle2?.definition?.styles || {} };
18697
- return { ...base2, ...linkedStyle.definition?.styles || {} };
18698
- };
18699
- const pMap = buildStyleMap(paragraphStyleId);
18700
- let tMap;
18701
- if (paragraphStyleId?.startsWith("TOC")) {
18702
- tMap = {};
18703
- } else {
18704
- tMap = buildStyleMap(inlineTextStyleId);
18705
- }
18706
- const rMap = buildStyleMap(runStyleId);
18707
- const finalStyles = { ...pMap, ...tMap, ...rMap };
18708
- if (Object.keys(finalStyles).length === 0) return;
18709
- const mergedLinkedStyle = { definition: { styles: finalStyles, attrs: {} } };
18710
- const basedOnStyle = null;
18711
- const $pos = state.doc.resolve(pos);
18712
- const parent = $pos.parent;
18713
- const styleString = generateLinkedStyleString(mergedLinkedStyle, basedOnStyle, node, parent);
18714
- if (!styleString) return;
18715
- const decoration = Decoration.inline(pos, pos + node.nodeSize, { style: styleString });
18716
- decorations.push(decoration);
18717
- });
18718
- return DecorationSet.create(doc2, decorations);
18719
- };
18720
- const LinkedStyles = Extension.create({
18721
- name: "linkedStyles",
18722
- priority: 1,
18723
- // We need this plugin to run before the list plugins
18724
- addOptions() {
18725
- return {};
18726
- },
18727
- addPmPlugins() {
18728
- return [createLinkedStylesPlugin(this.editor)];
18729
- },
18730
- addCommands() {
18731
- return {
18732
- /**
18733
- * Apply a linked style to the selected paragraphs
18734
- * @category Command
18735
- * @param {LinkedStyle} style - The style object to apply
18736
- * @example
18737
- * const style = editor.helpers.linkedStyles.getStyleById('Heading1');
18738
- * editor.commands.setLinkedStyle(style);
18739
- * @note Clears existing formatting when applying a style
18740
- * @note Works with custom selection preservation
18741
- */
18742
- setLinkedStyle: (style) => (params2) => {
18743
- const { tr } = params2;
18744
- return applyLinkedStyleToTransaction(tr, this.editor, style);
18745
- },
18746
- /**
18747
- * Toggle a linked style on the current selection
18748
- * @category Command
18749
- * @param {LinkedStyle} style - The linked style to apply (with id property)
18750
- * @param {string|null} [nodeType=null] - Node type to restrict toggle to (e.g., 'paragraph')
18751
- * @example
18752
- * const style = editor.helpers.linkedStyles.getStyleById('Heading1');
18753
- * editor.commands.toggleLinkedStyle(style)
18754
- * editor.commands.toggleLinkedStyle(style, 'paragraph')
18755
- * @note If selection is empty, returns false
18756
- * @note Removes style if already applied, applies it if not
18757
- */
18758
- toggleLinkedStyle: (style, nodeType = null) => (params2) => {
18759
- const { tr } = params2;
18760
- if (tr.selection.empty) {
18761
- return false;
18762
- }
18763
- let node = tr.doc.nodeAt(tr.selection.$from.pos);
18764
- if (node && nodeType && node.type.name !== nodeType) {
18765
- node = findParentNodeClosestToPos(tr.selection.$from, (n) => {
18766
- return nodeType ? n.type.name === nodeType : true;
18767
- })?.node;
18768
- }
18769
- if (!node) {
18770
- return false;
18771
- }
18772
- const currentStyleId = node.attrs.styleId;
18773
- if (currentStyleId === style.id) {
18774
- return applyLinkedStyleToTransaction(tr, this.editor, { id: null });
18775
- }
18776
- return applyLinkedStyleToTransaction(tr, this.editor, style);
18777
- },
18778
- /**
18779
- * Apply a linked style by its ID
18780
- * @category Command
18781
- * @param {string} styleId - The style ID to apply (e.g., 'Heading1')
18782
- * @example
18783
- * editor.commands.setStyleById('Heading1')
18784
- * editor.commands.setStyleById('Normal')
18785
- * @note Looks up the style from loaded Word styles
18786
- */
18787
- setStyleById: (styleId) => (params2) => {
18788
- const { state, tr } = params2;
18789
- const pluginState = LinkedStylesPluginKey.getState(state);
18790
- if (!pluginState) return false;
18791
- const style = pluginState.styles?.find((s2) => s2.id === styleId);
18792
- if (!style) return false;
18793
- return applyLinkedStyleToTransaction(tr, this.editor, style);
18794
- }
18795
- };
18796
- },
18797
- addHelpers() {
18798
- return {
18799
- /**
18800
- * Get all available linked styles
18801
- * @category Helper
18802
- * @returns {Array} Array of linked style objects
18803
- * @example
18804
- * const styles = editor.helpers.linkedStyles.getStyles();
18805
- * // Returns all styles from the Word document
18806
- */
18807
- getStyles: () => {
18808
- const styles = LinkedStylesPluginKey.getState(this.editor.state)?.styles || [];
18809
- return styles;
18810
- },
18811
- /**
18812
- * Get a specific style by ID
18813
- * @category Helper
18814
- * @param {string} styleId - The style ID to find
18815
- * @returns {Object} The style object or undefined
18816
- * @example
18817
- * const headingStyle = editor.helpers.linkedStyles.getStyleById('Heading1');
18818
- */
18819
- getStyleById: (styleId) => {
18820
- const styles = this.editor.helpers[this.name].getStyles();
18821
- return styles.find((s2) => s2.id === styleId);
18822
- },
18823
- /**
18824
- * Get the CSS string for a style
18825
- * @category Helper
18826
- * @param {string} styleId - The style ID
18827
- * @returns {string} CSS style string
18828
- * @example
18829
- * const css = editor.helpers.linkedStyles.getLinkedStyleString('Heading1');
18830
- * // Returns: "font-size: 16pt; font-weight: bold; color: #2E74B5"
18831
- * @private
18832
- */
18833
- getLinkedStyleString: (styleId) => {
18834
- const styles = this.editor.helpers.linkedStyles.getStyles();
18835
- const style = styles.find((s2) => s2.id === styleId);
18836
- if (!style) return "";
18837
- return generateLinkedStyleString(style);
18838
- }
18839
- };
18840
- }
18841
- });
18842
- const getDefaultSpacing = () => ({
18843
- after: null,
18844
- before: null,
18845
- line: null,
18846
- lineRule: "auto"
18847
- });
18848
- const restartNumbering = ({ editor, tr, state, dispatch }) => {
18849
- const { node: paragraph, pos } = findParentNode(isList)(state.selection) || {};
18850
- if (!paragraph) return false;
18851
- const allParagraphs = [{ node: paragraph, pos }];
18852
- const startPos = pos + paragraph.nodeSize;
18853
- const myNumId = paragraph.attrs.numberingProperties.numId;
18854
- let stop = false;
18855
- state.doc.nodesBetween(startPos, state.doc.content.size, (node, nodePos) => {
18856
- if (node.type.name === "paragraph") {
18857
- if (isList(node) && node.attrs.paragraphProperties?.numberingProperties?.numId === myNumId) {
18858
- allParagraphs.push({ node, pos: nodePos });
18859
- } else {
18860
- stop = true;
18861
- }
18862
- return false;
18863
- }
18864
- return !stop;
18865
- });
18866
- const { numberingType } = paragraph.attrs.listRendering || {};
18867
- const listType = numberingType === "bullet" ? "bulletList" : "orderedList";
18868
- const numId = ListHelpers.getNewListId(editor);
18869
- ListHelpers.generateNewListDefinition({ numId: Number(numId), listType, editor });
18870
- allParagraphs.forEach(({ node, pos: pos2 }) => {
18871
- updateNumberingProperties(
18872
- {
18873
- ...node.attrs.numberingProperties,
18874
- numId: Number(numId)
18875
- },
18876
- node,
18877
- pos2,
18878
- editor,
18879
- tr
18880
- );
18881
- });
18882
- if (dispatch) dispatch(tr);
18883
- return true;
18884
- };
18885
- const defaultTabDistance = 48;
18886
- const defaultLineLength = 816;
18887
- const getTabDecorations = (doc2, view, helpers2, from2 = 0, to = null) => {
18888
- const decorations = [];
18889
- const paragraphCache = /* @__PURE__ */ new Map();
18890
- const coordCache = /* @__PURE__ */ new Map();
18891
- const domPosCache = /* @__PURE__ */ new Map();
18892
- const end2 = to ?? doc2.content.size;
18893
- doc2.nodesBetween(from2, end2, (node, pos) => {
18894
- if (node.type.name !== "tab") return;
18895
- const $pos = doc2.resolve(pos);
18896
- const paragraphContext = findParagraphContext($pos, paragraphCache, helpers2);
18897
- if (!paragraphContext) return;
18898
- const blockParent2 = $pos.node(paragraphContext.paragraphDepth);
18899
- const style = calculateTabStyle(node.nodeSize, view, pos, blockParent2, paragraphContext, coordCache, domPosCache);
18900
- if (style) {
18901
- decorations.push(
18902
- Decoration.node(pos, pos + node.nodeSize, {
18903
- style
18904
- })
18905
- );
18906
- }
18907
- });
18908
- return decorations;
18909
- };
18910
- function calculateTabStyle(nodeSize2, view, pos, blockParent2, paragraphContext, coordCache = null, domPosCache = null) {
18911
- let extraStyles = "";
18912
- try {
18913
- const { tabStops, flattened, positionMap, startPos } = paragraphContext;
18914
- if (paragraphContext.indentWidth === void 0) {
18915
- paragraphContext.indentWidth = getIndentWidth(view, startPos, paragraphContext.indent, coordCache, domPosCache);
18916
- }
18917
- if (paragraphContext.tabHeight === void 0) {
18918
- paragraphContext.tabHeight = calcTabHeight(blockParent2);
18919
- }
18920
- if (paragraphContext.paragraphWidth === void 0) {
18921
- paragraphContext.paragraphWidth = getBlockNodeWidth(view, startPos);
18922
- }
18923
- const indentWidth = paragraphContext.indentWidth;
18924
- const hanging = twipsToPixels(Number(paragraphContext.indent.hanging) || 0);
18925
- if (hanging > 0) {
18926
- tabStops.unshift({ val: "start", pos: indentWidth + hanging });
18927
- }
18928
- const accumulatedTabWidth = paragraphContext.accumulatedTabWidth || 0;
18929
- const currentWidth = indentWidth + measureRangeWidth(view, startPos + 1, pos, coordCache, domPosCache) + accumulatedTabWidth;
18930
- let tabWidth;
18931
- if (tabStops.length) {
18932
- const tabStop = tabStops.find((stop) => stop.pos > currentWidth && stop.val !== "clear");
18933
- if (tabStop) {
18934
- tabWidth = Math.min(tabStop.pos, paragraphContext.paragraphWidth) - currentWidth;
18935
- let val = tabStop.val;
18936
- const aliases = { left: "start", right: "end" };
18937
- if (aliases[val]) val = aliases[val];
18938
- if (val === "center" || val === "end" || val === "right") {
18939
- const entryIndex = positionMap.get(pos);
18940
- if (entryIndex === void 0) return;
18941
- const nextTabIndex = findNextTabIndex(flattened, entryIndex + 1);
18942
- const segmentStartPos = pos + nodeSize2;
18943
- const segmentEndPos = nextTabIndex === -1 ? startPos + paragraphContext.paragraph.nodeSize - 1 : flattened[nextTabIndex].pos;
18944
- const segmentWidth = measureRangeWidth(view, segmentStartPos, segmentEndPos, coordCache, domPosCache);
18945
- tabWidth -= val === "center" ? segmentWidth / 2 : segmentWidth;
18946
- } else if (val === "decimal" || val === "num") {
18947
- const entryIndex = positionMap.get(pos);
18948
- if (entryIndex === void 0) return;
18949
- const breakChar = tabStop.decimalChar || ".";
18950
- const decimalPos = findDecimalBreakPos(flattened, entryIndex + 1, breakChar);
18951
- const integralWidth = decimalPos ? measureRangeWidth(view, pos + nodeSize2, decimalPos, coordCache, domPosCache) : measureRangeWidth(
18952
- view,
18953
- pos + nodeSize2,
18954
- startPos + paragraphContext.paragraph.nodeSize - 1,
18955
- coordCache,
18956
- domPosCache
18957
- );
18958
- tabWidth -= integralWidth;
18959
- }
18960
- if (tabStop.leader) {
18961
- const leaderStyles = {
18962
- dot: "border-bottom: 1px dotted black;",
18963
- heavy: "border-bottom: 2px solid black;",
18964
- hyphen: "border-bottom: 1px solid black;",
18965
- middleDot: "border-bottom: 1px dotted black; margin-bottom: 2px;",
18966
- underscore: "border-bottom: 1px solid black;"
18967
- };
18968
- extraStyles += leaderStyles[tabStop.leader] || "";
18969
- }
18970
- }
18971
- }
18972
- if (!tabWidth || tabWidth < 1) {
18973
- tabWidth = defaultTabDistance - currentWidth % defaultLineLength % defaultTabDistance;
18974
- if (tabWidth === 0) tabWidth = defaultTabDistance;
18975
- }
18976
- const tabHeight = paragraphContext.tabHeight;
18977
- paragraphContext.accumulatedTabWidth = accumulatedTabWidth + tabWidth;
18978
- return `width: ${tabWidth}px; height: ${tabHeight}; ${extraStyles}`;
18979
- } catch (error) {
18980
- console.error("tab decoration error", error);
18981
- }
18982
- }
18983
- function findParagraphContext($pos, cache, helpers2) {
18984
- for (let depth = $pos.depth; depth >= 0; depth--) {
18985
- const node = $pos.node(depth);
18986
- if (node?.type?.name === "paragraph") {
18987
- const startPos = $pos.start(depth);
18988
- if (!cache.has(startPos)) {
18989
- const paragraphContext = extractParagraphContext(node, startPos, helpers2, depth);
18990
- cache.set(startPos, paragraphContext);
18991
- }
18992
- return cache.get(startPos);
18993
- }
18994
- }
18995
- return null;
18996
- }
18997
- function extractParagraphContext(node, startPos, helpers2, depth = 0) {
18998
- let tabStops = [];
18999
- if (Array.isArray(node.attrs?.tabStops)) {
19000
- tabStops = node.attrs.tabStops.map((stop) => {
19001
- const ref2 = stop?.tab;
19002
- if (!ref2) return stop || null;
19003
- return {
19004
- val: ref2.tabType || "start",
19005
- pos: twipsToPixels(Number(ref2.pos) || 0),
19006
- leader: ref2.leader
19007
- };
19008
- }).filter(Boolean);
18311
+ const paragraphProperties = getResolvedParagraphProperties(this.node);
18312
+ if (__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
18313
+ this.dom.setAttribute("data-num-id", paragraphProperties.numberingProperties.numId);
18314
+ this.dom.setAttribute("data-level", paragraphProperties.numberingProperties.ilvl);
19009
18315
  } else {
19010
- const style = helpers2.linkedStyles.getStyleById(node.attrs?.styleId);
19011
- if (Array.isArray(style?.definition?.styles?.tabStops)) {
19012
- tabStops = style.definition.styles.tabStops;
19013
- }
19014
- }
19015
- const { entries, positionMap } = flattenParagraph(node, startPos);
19016
- return {
19017
- paragraph: node,
19018
- paragraphDepth: depth,
19019
- startPos,
19020
- indent: node.attrs?.indent || {},
19021
- tabStops,
19022
- flattened: entries,
19023
- positionMap,
19024
- // Store position map for O(1) lookups
19025
- accumulatedTabWidth: 0
19026
- };
19027
- }
19028
- function flattenParagraph(paragraph, paragraphStartPos) {
19029
- const entries = [];
19030
- const positionMap = /* @__PURE__ */ new Map();
19031
- const walk = (node, basePos) => {
19032
- if (!node) return;
19033
- if (node.type?.name === "run") {
19034
- node.forEach((child, offset2) => {
19035
- const childPos = basePos + offset2 + 1;
19036
- walk(child, childPos);
19037
- });
19038
- return;
19039
- }
19040
- const pos = basePos - 1;
19041
- const index2 = entries.length;
19042
- entries.push({ node, pos });
19043
- positionMap.set(pos, index2);
19044
- };
19045
- paragraph.forEach((child, offset2) => {
19046
- const childPos = paragraphStartPos + offset2 + 1;
19047
- walk(child, childPos);
19048
- });
19049
- return { entries, positionMap };
19050
- }
19051
- function findNextTabIndex(flattened, fromIndex) {
19052
- for (let i = fromIndex; i < flattened.length; i++) {
19053
- if (flattened[i]?.node?.type?.name === "tab") {
19054
- return i;
19055
- }
19056
- }
19057
- return -1;
19058
- }
19059
- function findDecimalBreakPos(flattened, startIndex, breakChar) {
19060
- for (let i = startIndex; i < flattened.length; i++) {
19061
- const entry = flattened[i];
19062
- if (!entry) break;
19063
- if (entry.node.type?.name === "tab") break;
19064
- if (entry.node.type?.name === "text") {
19065
- const index2 = entry.node.text?.indexOf(breakChar);
19066
- if (index2 !== void 0 && index2 !== -1) {
19067
- return entry.pos + index2 + 1;
19068
- }
19069
- }
19070
- }
19071
- return null;
19072
- }
19073
- function measureRangeWidth(view, from2, to, coordCache = null, domPosCache = null) {
19074
- if (!Number.isFinite(from2) || !Number.isFinite(to) || to <= from2) return 0;
19075
- try {
19076
- const range = document.createRange();
19077
- const fromRef = getCachedDomAtPos(view, from2, domPosCache);
19078
- const toRef = getCachedDomAtPos(view, to, domPosCache);
19079
- range.setStart(fromRef.node, fromRef.offset);
19080
- range.setEnd(toRef.node, toRef.offset);
19081
- const rect = range.getBoundingClientRect();
19082
- range.detach?.();
19083
- return rect.width || 0;
19084
- } catch {
19085
- const startLeft = getLeftCoord(view, from2, coordCache, domPosCache);
19086
- const endLeft = getLeftCoord(view, to, coordCache, domPosCache);
19087
- if (startLeft == null || endLeft == null) return 0;
19088
- return Math.max(0, endLeft - startLeft);
19089
- }
19090
- }
19091
- function getIndentWidth(view, paragraphStartPos, indentAttrs = {}, coordCache = null, domPosCache = null) {
19092
- const marginLeft = getLeftCoord(view, paragraphStartPos, coordCache, domPosCache);
19093
- const lineLeft = getLeftCoord(view, paragraphStartPos + 1, coordCache, domPosCache);
19094
- if (marginLeft != null && lineLeft != null) {
19095
- const diff = lineLeft - marginLeft;
19096
- if (!Number.isNaN(diff) && Math.abs(diff) > 0.5) {
19097
- return diff;
19098
- }
19099
- }
19100
- return calculateIndentFallback(indentAttrs);
19101
- }
19102
- function getBlockNodeWidth(view, blockStartPos) {
19103
- const blockDom = view.nodeDOM(blockStartPos - 1);
19104
- if (blockDom instanceof HTMLElement) {
19105
- const styles = window.getComputedStyle(blockDom);
19106
- const width = blockDom.clientWidth + parseFloat(styles.marginLeft || "0") + parseFloat(styles.marginRight || "0") + parseFloat(styles.borderLeftWidth || "0") + parseFloat(styles.borderRightWidth || "0") + parseFloat(styles.paddingLeft || "0") + parseFloat(styles.paddingRight || "0");
19107
- return width;
19108
- }
19109
- return defaultLineLength;
19110
- }
19111
- function calculateIndentFallback(indentAttrs = {}) {
19112
- if (!indentAttrs) return 0;
19113
- const left2 = twipsToPixels(Number(indentAttrs.left) || 0);
19114
- const firstLine = twipsToPixels(Number(indentAttrs.firstLine) || 0);
19115
- const hanging = twipsToPixels(Number(indentAttrs.hanging) || 0);
19116
- let textIndent = 0;
19117
- if (firstLine && hanging) {
19118
- textIndent = firstLine - hanging;
19119
- } else if (firstLine) {
19120
- textIndent = firstLine;
19121
- } else if (hanging) {
19122
- textIndent = -hanging;
18316
+ this.dom.removeAttribute("data-num-id");
18317
+ this.dom.removeAttribute("data-level");
19123
18318
  }
19124
- if (textIndent) return left2 + textIndent;
19125
- if (left2) return left2;
19126
- return 0;
19127
- }
19128
- function getLeftCoord(view, pos, coordCache = null, domPosCache = null) {
19129
- if (!Number.isFinite(pos)) return null;
19130
- if (coordCache && coordCache.has(pos)) {
19131
- return coordCache.get(pos);
19132
- }
19133
- let result = null;
19134
- try {
19135
- result = view.coordsAtPos(pos).left;
19136
- } catch {
19137
- try {
19138
- const ref2 = getCachedDomAtPos(view, pos, domPosCache);
19139
- const range = document.createRange();
19140
- range.setStart(ref2.node, ref2.offset);
19141
- range.setEnd(ref2.node, ref2.offset);
19142
- const rect = range.getBoundingClientRect();
19143
- range.detach?.();
19144
- result = rect.left;
19145
- } catch {
19146
- result = null;
19147
- }
19148
- }
19149
- if (coordCache) {
19150
- coordCache.set(pos, result);
19151
- }
19152
- return result;
19153
- }
19154
- function getCachedDomAtPos(view, pos, domPosCache = null) {
19155
- if (domPosCache && domPosCache.has(pos)) {
19156
- return domPosCache.get(pos);
19157
- }
19158
- const result = view.domAtPos(pos);
19159
- if (domPosCache) {
19160
- domPosCache.set(pos, result);
19161
- }
19162
- return result;
19163
- }
19164
- function calcTabHeight(blockParent2) {
19165
- const ptToPxRatio = 1.333;
19166
- const defaultFontSize = 16;
19167
- const defaultLineHeight = 1.1;
19168
- const parentTextStyleMark = blockParent2.firstChild?.marks?.find((mark) => mark.type.name === "textStyle");
19169
- const fontSize = parseInt(parentTextStyleMark?.attrs.fontSize) * ptToPxRatio || defaultFontSize;
19170
- return `${fontSize * defaultLineHeight}px`;
19171
- }
19172
- class ParagraphNodeView {
19173
- /**
19174
- * @param {import('prosemirror-model').Node} node Current paragraph node.
19175
- * @param {import('../../core/Editor').Editor} editor Editor instance providing schema/helpers.
19176
- * @param {() => number} getPos Position getter provided by ProseMirror.
19177
- * @param {import('prosemirror-view').Decoration[]} decorations Decorations applied to this node.
19178
- * @param {Record<string, unknown>} extensionAttrs Extra attributes declared by the paragraph extension.
19179
- */
19180
- constructor(node, editor, getPos, decorations, extensionAttrs) {
19181
- __privateAdd(this, _ParagraphNodeView_instances);
19182
- this.node = node;
19183
- this.editor = editor;
19184
- this.getPos = getPos;
19185
- this.decorations = decorations;
19186
- this.extensionAttrs = extensionAttrs;
19187
- this._animationFrameRequest = null;
19188
- this.dom = document.createElement("p");
19189
- this.contentDOM = document.createElement("span");
19190
- this.dom.appendChild(this.contentDOM);
19191
- if (__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
19192
- __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
19193
- __privateMethod(this, _ParagraphNodeView_instances, scheduleAnimation_fn).call(this, () => {
19194
- if (!__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
19195
- return;
19196
- }
19197
- __privateMethod(this, _ParagraphNodeView_instances, updateListStyles_fn).call(this);
19198
- });
19199
- }
19200
- __privateMethod(this, _ParagraphNodeView_instances, updateHTMLAttributes_fn).call(this);
19201
- }
19202
- /**
19203
- * @param {import('prosemirror-model').Node} node
19204
- * @param {import('prosemirror-view').Decoration[]} decorations
19205
- */
19206
- update(node, decorations) {
19207
- const oldAttrs = this.node.attrs;
19208
- const newAttrs = node.attrs;
19209
- this.node = node;
19210
- this.decorations = decorations;
19211
- if (JSON.stringify(oldAttrs) === JSON.stringify(newAttrs)) {
19212
- return true;
19213
- }
19214
- __privateMethod(this, _ParagraphNodeView_instances, updateHTMLAttributes_fn).call(this);
19215
- if (!__privateMethod(this, _ParagraphNodeView_instances, checkIsList_fn).call(this)) {
19216
- __privateMethod(this, _ParagraphNodeView_instances, removeList_fn).call(this);
19217
- return true;
19218
- }
19219
- __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
19220
- __privateMethod(this, _ParagraphNodeView_instances, scheduleAnimation_fn).call(this, () => {
19221
- __privateMethod(this, _ParagraphNodeView_instances, initList_fn).call(this, node.attrs.listRendering);
19222
- __privateMethod(this, _ParagraphNodeView_instances, updateListStyles_fn).call(this);
19223
- });
19224
- return true;
19225
- }
19226
- /**
19227
- * @param {MutationRecord} mutation
19228
- */
19229
- ignoreMutation(mutation) {
19230
- if (this.marker && (mutation.target === this.marker || this.marker.contains(mutation.target))) {
19231
- return true;
19232
- }
19233
- if (this.separator && (mutation.target === this.separator || this.separator.contains(mutation.target))) {
19234
- return true;
19235
- }
19236
- if (mutation.type === "attributes" && mutation.target === this.dom && mutation.attributeName === "style") {
19237
- return true;
19238
- }
19239
- if (mutation.type === "childList") {
19240
- if (this.marker && Array.from(mutation.removedNodes).includes(this.marker)) {
19241
- return true;
19242
- }
19243
- if (this.marker && Array.from(mutation.addedNodes).includes(this.marker)) {
19244
- return true;
19245
- }
19246
- if (this.separator && Array.from(mutation.removedNodes).includes(this.separator)) {
19247
- return true;
19248
- }
19249
- if (this.separator && Array.from(mutation.addedNodes).includes(this.separator)) {
19250
- return true;
19251
- }
19252
- }
19253
- return false;
19254
- }
19255
- destroy() {
19256
- __privateMethod(this, _ParagraphNodeView_instances, cancelScheduledAnimation_fn).call(this);
18319
+ if (paragraphProperties.framePr?.dropCap) {
18320
+ this.dom.classList.add("sd-editor-dropcap");
18321
+ } else {
18322
+ this.dom.classList.remove("sd-editor-dropcap");
19257
18323
  }
19258
- }
19259
- _ParagraphNodeView_instances = new WeakSet();
19260
- updateHTMLAttributes_fn = function() {
19261
- const htmlAttributes = Attribute.getAttributesToRender(this.node, this.extensionAttrs);
19262
- htmlAttributes.style = htmlAttributes.style || "";
19263
- for (const [key2, value] of Object.entries(htmlAttributes || {})) {
19264
- if (value == null) {
19265
- this.dom.removeAttribute(key2);
19266
- continue;
19267
- }
19268
- this.dom.setAttribute(key2, value);
18324
+ if (paragraphProperties.styleId) {
18325
+ this.dom.setAttribute("styleid", paragraphProperties.styleId);
19269
18326
  }
19270
18327
  };
18328
+ updateDOMStyles_fn = function() {
18329
+ this.dom.style.cssText = "";
18330
+ const paragraphProperties = getResolvedParagraphProperties(this.node);
18331
+ const style = encodeCSSFromPPr(paragraphProperties);
18332
+ Object.entries(style).forEach(([k2, v]) => {
18333
+ this.dom.style[k2] = v;
18334
+ });
18335
+ };
19271
18336
  updateListStyles_fn = function() {
19272
18337
  let { suffix, justification } = this.node.attrs.listRendering;
19273
18338
  suffix = suffix ?? "tab";
19274
18339
  __privateMethod(this, _ParagraphNodeView_instances, calculateMarkerStyle_fn).call(this, justification);
19275
18340
  if (suffix === "tab") {
19276
- __privateMethod(this, _ParagraphNodeView_instances, calculateTabSeparatorStyle_fn).call(this, justification, this.node.attrs.indent);
18341
+ const paragraphProperties = getResolvedParagraphProperties(this.node);
18342
+ __privateMethod(this, _ParagraphNodeView_instances, calculateTabSeparatorStyle_fn).call(this, justification, paragraphProperties.indent);
19277
18343
  } else {
19278
18344
  this.separator.textContent = suffix === "space" ? " " : "";
19279
18345
  }
@@ -19390,10 +18456,11 @@ calculateTabSeparatorStyle_fn = function(justification, indent) {
19390
18456
  * @param {'left' | 'right' | 'center'} justification
19391
18457
  */
19392
18458
  calculateMarkerStyle_fn = function(justification) {
18459
+ const paragraphProperties = getResolvedParagraphProperties(this.node);
19393
18460
  const runProperties = resolveRunProperties(
19394
18461
  { docx: this.editor.converter.convertedXml, numbering: this.editor.converter.numbering },
19395
- this.node.attrs.paragraphProperties.runProperties || {},
19396
- { ...this.node.attrs.paragraphProperties, numberingProperties: this.node.attrs.numberingProperties },
18462
+ paragraphProperties.runProperties || {},
18463
+ paragraphProperties,
19397
18464
  true,
19398
18465
  Boolean(this.node.attrs.paragraphProperties.numberingProperties)
19399
18466
  );
@@ -19857,10 +18924,11 @@ function createNumberingPlugin(editor) {
19857
18924
  tr.setMeta("orderedListSync", true);
19858
18925
  numberingManager.enableCache();
19859
18926
  newState.doc.descendants((node, pos) => {
19860
- if (node.type.name !== "paragraph" || !node.attrs.numberingProperties) {
18927
+ let resolvedProps = calculateResolvedParagraphProperties(editor, node, newState.doc.resolve(pos));
18928
+ if (node.type.name !== "paragraph" || !resolvedProps.numberingProperties) {
19861
18929
  return;
19862
18930
  }
19863
- const { numId, ilvl: level = 0 } = node.attrs.numberingProperties;
18931
+ const { numId, ilvl: level = 0 } = resolvedProps.numberingProperties;
19864
18932
  const definitionDetails = ListHelpers.getListDefinitionDetails({ numId, level, editor });
19865
18933
  if (!definitionDetails || Object.keys(definitionDetails).length === 0) {
19866
18934
  tr.setNodeAttribute(pos, "listRendering", null);
@@ -19904,6 +18972,110 @@ function createNumberingPlugin(editor) {
19904
18972
  }
19905
18973
  });
19906
18974
  }
18975
+ function createDropcapPlugin(editor) {
18976
+ const { view } = editor;
18977
+ const dropcapWidthCache = /* @__PURE__ */ new Map();
18978
+ const invalidateCacheForRange = (from2, to) => {
18979
+ for (const [pos] of dropcapWidthCache) {
18980
+ if (pos >= from2 && pos <= to) {
18981
+ dropcapWidthCache.delete(pos);
18982
+ }
18983
+ }
18984
+ };
18985
+ const getDropcapDecorations = (state, view2, widthCache) => {
18986
+ const decorations = [];
18987
+ state.doc.descendants((node, pos) => {
18988
+ if (hasDropcapParagraph(node, pos, state)) {
18989
+ const width = getDropcapWidth(view2, pos, widthCache);
18990
+ decorations.push(Decoration.inline(pos, pos + node.nodeSize, { style: `margin-left: -${width}px;` }));
18991
+ return false;
18992
+ }
18993
+ return node.type.name !== "paragraph";
18994
+ });
18995
+ return decorations;
18996
+ };
18997
+ function getDropcapWidth(view2, pos, widthCache) {
18998
+ if (widthCache.has(pos)) {
18999
+ return widthCache.get(pos);
19000
+ }
19001
+ const domNode = view2.nodeDOM(pos);
19002
+ if (domNode) {
19003
+ const range = document.createRange();
19004
+ range.selectNodeContents(domNode);
19005
+ const width = range.getBoundingClientRect().width;
19006
+ widthCache.set(pos, width);
19007
+ return width;
19008
+ }
19009
+ return 0;
19010
+ }
19011
+ const hasDropcapParagraph = (node, pos, state) => {
19012
+ if (node.type.name !== "paragraph") return false;
19013
+ const paragraphProps = calculateResolvedParagraphProperties(editor, node, state.doc.resolve(pos));
19014
+ return paragraphProps.framePr?.dropCap === "margin";
19015
+ };
19016
+ return new Plugin({
19017
+ name: "dropcapPlugin",
19018
+ key: new PluginKey("dropcapPlugin"),
19019
+ state: {
19020
+ init(_, state) {
19021
+ const decorations = getDropcapDecorations(state, view, dropcapWidthCache);
19022
+ return DecorationSet.create(state.doc, decorations);
19023
+ },
19024
+ apply(tr, oldDecorationSet, oldState, newState) {
19025
+ if (!tr.docChanged) return oldDecorationSet;
19026
+ let hasDropcaps = false;
19027
+ newState.doc.descendants((node, pos) => {
19028
+ if (hasDropcapParagraph(node, pos, newState)) {
19029
+ hasDropcaps = true;
19030
+ return false;
19031
+ }
19032
+ });
19033
+ if (!hasDropcaps) {
19034
+ dropcapWidthCache.clear();
19035
+ return DecorationSet.empty;
19036
+ }
19037
+ let affectsDropcaps = false;
19038
+ tr.steps.forEach((step) => {
19039
+ if (step.slice?.content) {
19040
+ step.slice.content.descendants((node, pos) => {
19041
+ if (hasDropcapParagraph(node, pos, newState)) {
19042
+ affectsDropcaps = true;
19043
+ return false;
19044
+ }
19045
+ });
19046
+ }
19047
+ if (step.jsonID === "replace" && step.from !== void 0 && step.to !== void 0) {
19048
+ try {
19049
+ oldState.doc.nodesBetween(step.from, step.to, (node, pos) => {
19050
+ if (hasDropcapParagraph(node, pos, newState)) {
19051
+ affectsDropcaps = true;
19052
+ return false;
19053
+ }
19054
+ });
19055
+ } catch {
19056
+ affectsDropcaps = true;
19057
+ }
19058
+ }
19059
+ });
19060
+ if (!affectsDropcaps) {
19061
+ return oldDecorationSet.map(tr.mapping, tr.doc);
19062
+ }
19063
+ tr.steps.forEach((step) => {
19064
+ if (step.from !== void 0 && step.to !== void 0) {
19065
+ invalidateCacheForRange(step.from, step.to);
19066
+ }
19067
+ });
19068
+ const decorations = getDropcapDecorations(newState, view, dropcapWidthCache);
19069
+ return DecorationSet.create(newState.doc, decorations);
19070
+ }
19071
+ },
19072
+ props: {
19073
+ decorations(state) {
19074
+ return this.getState(state);
19075
+ }
19076
+ }
19077
+ });
19078
+ }
19907
19079
  const bulletInputRegex = /^\s*([-+*])\s$/;
19908
19080
  const orderedInputRegex = /^(\d+)\.\s$/;
19909
19081
  const Paragraph = OxmlNode.create({
@@ -19929,33 +19101,6 @@ const Paragraph = OxmlNode.create({
19929
19101
  rsidP: { rendered: false },
19930
19102
  rsidRPr: { rendered: false },
19931
19103
  rsidDel: { rendered: false },
19932
- spacing: {
19933
- default: getDefaultSpacing(),
19934
- parseDOM: (element) => {
19935
- if (element && element.closest("[data-superdoc-import]")) {
19936
- return {
19937
- after: pixelsToTwips(11),
19938
- before: 0,
19939
- line: linesToTwips(1.15),
19940
- lineRule: "auto"
19941
- };
19942
- }
19943
- return void 0;
19944
- },
19945
- renderDOM: (attrs) => {
19946
- const { spacing, marksAttrs } = attrs;
19947
- if (!spacing) return { style: null };
19948
- const spacingCopy = { ...spacing };
19949
- if (attrs.lineHeight) delete spacingCopy.line;
19950
- const style = getSpacingStyleString(
19951
- spacingCopy,
19952
- marksAttrs ?? [],
19953
- Boolean(attrs.paragraphProperties?.numberingProperties)
19954
- );
19955
- if (style) return { style };
19956
- return { style: null };
19957
- }
19958
- },
19959
19104
  extraAttrs: {
19960
19105
  default: {},
19961
19106
  parseDOM: (element) => {
@@ -19969,78 +19114,6 @@ const Paragraph = OxmlNode.create({
19969
19114
  return attributes.extraAttrs || {};
19970
19115
  }
19971
19116
  },
19972
- marksAttrs: {
19973
- renderDOM: (attrs) => {
19974
- const { marksAttrs } = attrs;
19975
- if (!marksAttrs?.length) return {};
19976
- const style = getMarksStyle(marksAttrs);
19977
- if (style) return { style };
19978
- return {};
19979
- }
19980
- },
19981
- indent: {
19982
- default: null,
19983
- renderDOM: ({ indent }) => {
19984
- if (!indent) return { style: null };
19985
- const { left: left2, right: right2, firstLine, hanging } = indent;
19986
- if (indent && Object.values(indent).every((v) => v === 0)) {
19987
- return { style: null };
19988
- }
19989
- let style = "";
19990
- if (left2) style += `margin-left: ${twipsToPixels(left2)}px;`;
19991
- if (right2) style += `margin-right: ${twipsToPixels(right2)}px;`;
19992
- if (firstLine && !hanging) style += `text-indent: ${twipsToPixels(firstLine)}px;`;
19993
- if (firstLine && hanging) style += `text-indent: ${twipsToPixels(firstLine - hanging)}px;`;
19994
- if (!firstLine && hanging) style += `text-indent: ${twipsToPixels(-hanging)}px;`;
19995
- return { style };
19996
- }
19997
- },
19998
- borders: {
19999
- default: null,
20000
- renderDOM: ({ borders }) => {
20001
- if (!borders) return {};
20002
- const sideOrder = ["top", "right", "bottom", "left"];
20003
- const valToCss = {
20004
- single: "solid",
20005
- dashed: "dashed",
20006
- dotted: "dotted",
20007
- double: "double"
20008
- };
20009
- let style = "";
20010
- sideOrder.forEach((side) => {
20011
- const b = borders[side];
20012
- if (!b) return;
20013
- if (["nil", "none", void 0, null].includes(b.val)) {
20014
- style += `border-${side}: none;`;
20015
- return;
20016
- }
20017
- const width = b.size != null ? `${eighthPointsToPixels(b.size)}px` : "1px";
20018
- const cssStyle = valToCss[b.val] || "solid";
20019
- const color = !b.color || b.color === "auto" ? "#000000" : `#${b.color}`;
20020
- style += `border-${side}: ${width} ${cssStyle} ${color};`;
20021
- if (b.space != null && side === "bottom") {
20022
- style += `padding-bottom: ${eighthPointsToPixels(b.space)}px;`;
20023
- }
20024
- });
20025
- return style ? { style } : {};
20026
- }
20027
- },
20028
- class: {
20029
- renderDOM: (attributes) => {
20030
- if (attributes.dropcap) {
20031
- return { class: `sd-editor-dropcap` };
20032
- }
20033
- return null;
20034
- }
20035
- },
20036
- styleId: {
20037
- default: null,
20038
- keepOnSplit: false,
20039
- renderDOM: (attrs) => {
20040
- if (!attrs.styleId) return {};
20041
- return { styleid: attrs.styleId };
20042
- }
20043
- },
20044
19117
  sdBlockId: {
20045
19118
  default: null,
20046
19119
  keepOnSplit: false,
@@ -20053,23 +19126,8 @@ const Paragraph = OxmlNode.create({
20053
19126
  rendered: false
20054
19127
  },
20055
19128
  filename: { rendered: false },
20056
- keepLines: { rendered: false },
20057
- keepNext: { rendered: false },
20058
19129
  paragraphProperties: { rendered: false },
20059
- dropcap: { rendered: false },
20060
19130
  pageBreakSource: { rendered: false },
20061
- textAlign: {
20062
- renderDOM: ({ textAlign }) => {
20063
- if (!textAlign) return {};
20064
- let style = "";
20065
- if (textAlign === "left") style += "text-align: left;";
20066
- else if (textAlign === "right") style += "text-align: right;";
20067
- else if (textAlign === "center") style += "text-align: center;";
20068
- else if (textAlign === "both") style += "text-align: justify;";
20069
- return { style };
20070
- }
20071
- },
20072
- tabStops: { rendered: false },
20073
19131
  listRendering: {
20074
19132
  keepOnSplit: false,
20075
19133
  renderDOM: ({ listRendering }) => {
@@ -20079,15 +19137,6 @@ const Paragraph = OxmlNode.create({
20079
19137
  "data-list-numbering-type": listRendering?.numberingType
20080
19138
  };
20081
19139
  }
20082
- },
20083
- numberingProperties: {
20084
- keepOnSplit: true,
20085
- renderDOM: ({ numberingProperties }) => {
20086
- return {
20087
- "data-num-id": numberingProperties?.numId,
20088
- "data-level": numberingProperties?.ilvl
20089
- };
20090
- }
20091
19140
  }
20092
19141
  };
20093
19142
  },
@@ -20125,12 +19174,6 @@ const Paragraph = OxmlNode.create({
20125
19174
  return acc;
20126
19175
  }, {});
20127
19176
  if (Object.keys(numberingProperties).length > 0) {
20128
- const resolvedParagraphProperties = resolveParagraphProperties(
20129
- { docx: this.editor.converter.convertedXml, numbering: this.editor.converter.numbering },
20130
- { styleId, numberingProperties, indent, spacing },
20131
- false,
20132
- true
20133
- );
20134
19177
  return {
20135
19178
  paragraphProperties: {
20136
19179
  numberingProperties,
@@ -20138,17 +19181,10 @@ const Paragraph = OxmlNode.create({
20138
19181
  spacing,
20139
19182
  styleId: styleId || null
20140
19183
  },
20141
- indent: resolvedParagraphProperties.indent,
20142
- spacing: resolvedParagraphProperties.spacing,
20143
- numberingProperties,
20144
- styleId: styleId || null,
20145
19184
  extraAttrs
20146
19185
  };
20147
19186
  }
20148
19187
  return {
20149
- styleId: styleId || null,
20150
- indent,
20151
- spacing,
20152
19188
  extraAttrs
20153
19189
  };
20154
19190
  }
@@ -20165,11 +19201,11 @@ const Paragraph = OxmlNode.create({
20165
19201
  },
20166
19202
  {
20167
19203
  tag: "blockquote",
20168
- attrs: { styleId: "BlockQuote" }
19204
+ attrs: { paragraphProperties: { styleId: "BlockQuote" } }
20169
19205
  },
20170
19206
  ...this.options.headingLevels.map((level) => ({
20171
19207
  tag: `h${level}`,
20172
- attrs: { level, styleId: `Heading${level}` }
19208
+ attrs: { level, paragraphProperties: { styleId: `Heading${level}` } }
20173
19209
  }))
20174
19210
  ];
20175
19211
  },
@@ -20272,109 +19308,11 @@ const Paragraph = OxmlNode.create({
20272
19308
  };
20273
19309
  },
20274
19310
  addPmPlugins() {
20275
- const { view } = this.editor;
20276
- const dropcapWidthCache = /* @__PURE__ */ new Map();
20277
- const hasDropcapParagraph = (node) => node.type.name === "paragraph" && node.attrs.dropcap?.type === "margin";
20278
- const invalidateCacheForRange = (from2, to) => {
20279
- for (const [pos] of dropcapWidthCache) {
20280
- if (pos >= from2 && pos <= to) {
20281
- dropcapWidthCache.delete(pos);
20282
- }
20283
- }
20284
- };
20285
- const dropcapPlugin = new Plugin({
20286
- name: "dropcapPlugin",
20287
- key: new PluginKey("dropcapPlugin"),
20288
- state: {
20289
- init(_, state) {
20290
- const decorations = getDropcapDecorations(state, view, dropcapWidthCache);
20291
- return DecorationSet.create(state.doc, decorations);
20292
- },
20293
- apply(tr, oldDecorationSet, oldState, newState) {
20294
- if (!tr.docChanged) return oldDecorationSet;
20295
- let hasDropcaps = false;
20296
- newState.doc.descendants((node) => {
20297
- if (hasDropcapParagraph(node)) {
20298
- hasDropcaps = true;
20299
- return false;
20300
- }
20301
- });
20302
- if (!hasDropcaps) {
20303
- dropcapWidthCache.clear();
20304
- return DecorationSet.empty;
20305
- }
20306
- let affectsDropcaps = false;
20307
- tr.steps.forEach((step) => {
20308
- if (step.slice?.content) {
20309
- step.slice.content.descendants((node) => {
20310
- if (hasDropcapParagraph(node)) {
20311
- affectsDropcaps = true;
20312
- return false;
20313
- }
20314
- });
20315
- }
20316
- if (step.jsonID === "replace" && step.from !== void 0 && step.to !== void 0) {
20317
- try {
20318
- oldState.doc.nodesBetween(step.from, step.to, (node) => {
20319
- if (hasDropcapParagraph(node)) {
20320
- affectsDropcaps = true;
20321
- return false;
20322
- }
20323
- });
20324
- } catch {
20325
- affectsDropcaps = true;
20326
- }
20327
- }
20328
- });
20329
- if (!affectsDropcaps) {
20330
- return oldDecorationSet.map(tr.mapping, tr.doc);
20331
- }
20332
- tr.steps.forEach((step) => {
20333
- if (step.from !== void 0 && step.to !== void 0) {
20334
- invalidateCacheForRange(step.from, step.to);
20335
- }
20336
- });
20337
- const decorations = getDropcapDecorations(newState, view, dropcapWidthCache);
20338
- return DecorationSet.create(newState.doc, decorations);
20339
- }
20340
- },
20341
- props: {
20342
- decorations(state) {
20343
- return this.getState(state);
20344
- }
20345
- }
20346
- });
19311
+ const dropcapPlugin = createDropcapPlugin(this.editor);
20347
19312
  const numberingPlugin = createNumberingPlugin(this.editor);
20348
19313
  return [dropcapPlugin, numberingPlugin];
20349
19314
  }
20350
19315
  });
20351
- const getDropcapDecorations = (state, view, widthCache) => {
20352
- const decorations = [];
20353
- state.doc.descendants((node, pos) => {
20354
- if (node.type.name === "paragraph") {
20355
- if (node.attrs.dropcap?.type === "margin") {
20356
- const width = getDropcapWidth(view, pos, widthCache);
20357
- decorations.push(Decoration.inline(pos, pos + node.nodeSize, { style: `margin-left: -${width}px;` }));
20358
- }
20359
- return false;
20360
- }
20361
- });
20362
- return decorations;
20363
- };
20364
- function getDropcapWidth(view, pos, widthCache) {
20365
- if (widthCache.has(pos)) {
20366
- return widthCache.get(pos);
20367
- }
20368
- const domNode = view.nodeDOM(pos);
20369
- if (domNode) {
20370
- const range = document.createRange();
20371
- range.selectNodeContents(domNode);
20372
- const width = range.getBoundingClientRect().width;
20373
- widthCache.set(pos, width);
20374
- return width;
20375
- }
20376
- return 0;
20377
- }
20378
19316
  const Heading = Extension.create({
20379
19317
  name: "heading",
20380
19318
  addOptions() {
@@ -30652,434 +29590,1205 @@ class ShapeGroupView {
30652
29590
  svg.appendChild(shapeElement);
30653
29591
  }
30654
29592
  }
30655
- });
30656
- }
30657
- container.appendChild(svg);
30658
- return { element: container };
30659
- }
30660
- createShapeElement(shape) {
30661
- const attrs = shape.attrs;
30662
- if (!attrs) return null;
30663
- const x = attrs.x || 0;
30664
- const y = attrs.y || 0;
30665
- const width = attrs.width || 100;
30666
- const height = attrs.height || 100;
30667
- const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
30668
- const transforms = [];
30669
- transforms.push(`translate(${x}, ${y})`);
30670
- if (attrs.rotation !== 0) {
30671
- transforms.push(`rotate(${attrs.rotation} ${width / 2} ${height / 2})`);
30672
- }
30673
- if (attrs.flipH) {
30674
- transforms.push(`scale(-1, 1) translate(${-width}, 0)`);
30675
- }
30676
- if (attrs.flipV) {
30677
- transforms.push(`scale(1, -1) translate(0, ${-height})`);
30678
- }
30679
- if (transforms.length > 0) {
30680
- g.setAttribute("transform", transforms.join(" "));
30681
- }
30682
- const shapeKind = attrs.kind || "rect";
30683
- const fillColor = attrs.fillColor || "#5b9bd5";
30684
- const strokeColor = attrs.strokeColor || "#000000";
30685
- const strokeWidth = attrs.strokeWidth || 1;
30686
- try {
30687
- const svgContent = k({
30688
- preset: shapeKind,
30689
- styleOverrides: {
30690
- fill: fillColor || "none",
30691
- stroke: strokeColor || "none",
30692
- strokeWidth: strokeWidth || 0
30693
- },
30694
- width,
30695
- height
30696
- });
30697
- if (svgContent) {
30698
- const tempDiv = document.createElement("div");
30699
- tempDiv.innerHTML = svgContent;
30700
- const svgElement = tempDiv.querySelector("svg");
30701
- if (svgElement) {
30702
- Array.from(svgElement.children).forEach((child) => {
30703
- const clonedChild = child.cloneNode(true);
30704
- if (clonedChild.tagName === "ellipse") {
30705
- clonedChild.setAttribute("cx", (width / 2).toString());
30706
- clonedChild.setAttribute("cy", (height / 2).toString());
30707
- clonedChild.setAttribute("rx", (width / 2).toString());
30708
- clonedChild.setAttribute("ry", (height / 2).toString());
30709
- } else if (clonedChild.tagName === "circle") {
30710
- if (width !== height) {
30711
- const ellipse = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
30712
- ellipse.setAttribute("cx", (width / 2).toString());
30713
- ellipse.setAttribute("cy", (height / 2).toString());
30714
- ellipse.setAttribute("rx", (width / 2).toString());
30715
- ellipse.setAttribute("ry", (height / 2).toString());
30716
- Array.from(clonedChild.attributes).forEach((attr) => {
30717
- if (!["cx", "cy", "r"].includes(attr.name)) {
30718
- ellipse.setAttribute(attr.name, attr.value);
30719
- }
29593
+ });
29594
+ }
29595
+ container.appendChild(svg);
29596
+ return { element: container };
29597
+ }
29598
+ createShapeElement(shape) {
29599
+ const attrs = shape.attrs;
29600
+ if (!attrs) return null;
29601
+ const x = attrs.x || 0;
29602
+ const y = attrs.y || 0;
29603
+ const width = attrs.width || 100;
29604
+ const height = attrs.height || 100;
29605
+ const g = document.createElementNS("http://www.w3.org/2000/svg", "g");
29606
+ const transforms = [];
29607
+ transforms.push(`translate(${x}, ${y})`);
29608
+ if (attrs.rotation !== 0) {
29609
+ transforms.push(`rotate(${attrs.rotation} ${width / 2} ${height / 2})`);
29610
+ }
29611
+ if (attrs.flipH) {
29612
+ transforms.push(`scale(-1, 1) translate(${-width}, 0)`);
29613
+ }
29614
+ if (attrs.flipV) {
29615
+ transforms.push(`scale(1, -1) translate(0, ${-height})`);
29616
+ }
29617
+ if (transforms.length > 0) {
29618
+ g.setAttribute("transform", transforms.join(" "));
29619
+ }
29620
+ const shapeKind = attrs.kind || "rect";
29621
+ const fillColor = attrs.fillColor || "#5b9bd5";
29622
+ const strokeColor = attrs.strokeColor || "#000000";
29623
+ const strokeWidth = attrs.strokeWidth || 1;
29624
+ try {
29625
+ const svgContent = k({
29626
+ preset: shapeKind,
29627
+ styleOverrides: {
29628
+ fill: fillColor || "none",
29629
+ stroke: strokeColor || "none",
29630
+ strokeWidth: strokeWidth || 0
29631
+ },
29632
+ width,
29633
+ height
29634
+ });
29635
+ if (svgContent) {
29636
+ const tempDiv = document.createElement("div");
29637
+ tempDiv.innerHTML = svgContent;
29638
+ const svgElement = tempDiv.querySelector("svg");
29639
+ if (svgElement) {
29640
+ Array.from(svgElement.children).forEach((child) => {
29641
+ const clonedChild = child.cloneNode(true);
29642
+ if (clonedChild.tagName === "ellipse") {
29643
+ clonedChild.setAttribute("cx", (width / 2).toString());
29644
+ clonedChild.setAttribute("cy", (height / 2).toString());
29645
+ clonedChild.setAttribute("rx", (width / 2).toString());
29646
+ clonedChild.setAttribute("ry", (height / 2).toString());
29647
+ } else if (clonedChild.tagName === "circle") {
29648
+ if (width !== height) {
29649
+ const ellipse = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
29650
+ ellipse.setAttribute("cx", (width / 2).toString());
29651
+ ellipse.setAttribute("cy", (height / 2).toString());
29652
+ ellipse.setAttribute("rx", (width / 2).toString());
29653
+ ellipse.setAttribute("ry", (height / 2).toString());
29654
+ Array.from(clonedChild.attributes).forEach((attr) => {
29655
+ if (!["cx", "cy", "r"].includes(attr.name)) {
29656
+ ellipse.setAttribute(attr.name, attr.value);
29657
+ }
29658
+ });
29659
+ g.appendChild(ellipse);
29660
+ return;
29661
+ } else {
29662
+ clonedChild.setAttribute("cx", (width / 2).toString());
29663
+ clonedChild.setAttribute("cy", (height / 2).toString());
29664
+ clonedChild.setAttribute("r", (width / 2).toString());
29665
+ }
29666
+ } else if (clonedChild.tagName === "rect") {
29667
+ clonedChild.setAttribute("width", width.toString());
29668
+ clonedChild.setAttribute("height", height.toString());
29669
+ } else if (clonedChild.tagName === "path" && svgElement.hasAttribute("viewBox")) {
29670
+ const viewBox = svgElement.getAttribute("viewBox").split(" ").map(Number);
29671
+ if (viewBox.length === 4) {
29672
+ const [, , vbWidth, vbHeight] = viewBox;
29673
+ const scaleX = width / vbWidth;
29674
+ const scaleY = height / vbHeight;
29675
+ if (scaleX !== 1 || scaleY !== 1) {
29676
+ const pathTransform = `scale(${scaleX}, ${scaleY})`;
29677
+ const existingTransform = clonedChild.getAttribute("transform");
29678
+ clonedChild.setAttribute(
29679
+ "transform",
29680
+ existingTransform ? `${existingTransform} ${pathTransform}` : pathTransform
29681
+ );
29682
+ }
29683
+ }
29684
+ } else if (clonedChild.hasAttribute("width")) {
29685
+ clonedChild.setAttribute("width", width.toString());
29686
+ }
29687
+ if (clonedChild.hasAttribute("height") && clonedChild.tagName !== "ellipse") {
29688
+ clonedChild.setAttribute("height", height.toString());
29689
+ }
29690
+ g.appendChild(clonedChild);
29691
+ });
29692
+ }
29693
+ }
29694
+ } catch (error) {
29695
+ console.warn("Failed to generate shape SVG:", error);
29696
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
29697
+ rect.setAttribute("width", width.toString());
29698
+ rect.setAttribute("height", height.toString());
29699
+ rect.setAttribute("fill", fillColor);
29700
+ rect.setAttribute("stroke", strokeColor);
29701
+ rect.setAttribute("stroke-width", strokeWidth.toString());
29702
+ g.appendChild(rect);
29703
+ }
29704
+ return g;
29705
+ }
29706
+ buildView() {
29707
+ const { element } = this.createElement();
29708
+ this.root = element;
29709
+ }
29710
+ update() {
29711
+ return false;
29712
+ }
29713
+ }
29714
+ const ShapeGroup = Node$1.create({
29715
+ name: "shapeGroup",
29716
+ group: "inline",
29717
+ inline: true,
29718
+ atom: true,
29719
+ addOptions() {
29720
+ return {
29721
+ htmlAttributes: {
29722
+ contenteditable: false
29723
+ }
29724
+ };
29725
+ },
29726
+ addAttributes() {
29727
+ return {
29728
+ groupTransform: {
29729
+ default: {},
29730
+ renderDOM: () => ({})
29731
+ },
29732
+ shapes: {
29733
+ default: [],
29734
+ renderDOM: () => ({})
29735
+ },
29736
+ size: {
29737
+ default: null,
29738
+ renderDOM: (attrs) => {
29739
+ if (!attrs.size) return {};
29740
+ const sizeData = {};
29741
+ if (attrs.size.width) sizeData["data-width"] = attrs.size.width;
29742
+ if (attrs.size.height) sizeData["data-height"] = attrs.size.height;
29743
+ return sizeData;
29744
+ }
29745
+ },
29746
+ padding: {
29747
+ default: null,
29748
+ renderDOM: (attrs) => {
29749
+ if (!attrs.padding) return {};
29750
+ const paddingData = {};
29751
+ if (attrs.padding.top != null) paddingData["data-padding-top"] = attrs.padding.top;
29752
+ if (attrs.padding.right != null) paddingData["data-padding-right"] = attrs.padding.right;
29753
+ if (attrs.padding.bottom != null) paddingData["data-padding-bottom"] = attrs.padding.bottom;
29754
+ if (attrs.padding.left != null) paddingData["data-padding-left"] = attrs.padding.left;
29755
+ return paddingData;
29756
+ }
29757
+ },
29758
+ marginOffset: {
29759
+ default: null,
29760
+ renderDOM: (attrs) => {
29761
+ if (!attrs.marginOffset) return {};
29762
+ const offsetData = {};
29763
+ if (attrs.marginOffset.horizontal != null) offsetData["data-offset-x"] = attrs.marginOffset.horizontal;
29764
+ if (attrs.marginOffset.top != null) offsetData["data-offset-y"] = attrs.marginOffset.top;
29765
+ return offsetData;
29766
+ }
29767
+ },
29768
+ drawingContent: {
29769
+ rendered: false
29770
+ }
29771
+ };
29772
+ },
29773
+ parseDOM() {
29774
+ return false;
29775
+ },
29776
+ renderDOM({ htmlAttributes }) {
29777
+ return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-shape-group": "" })];
29778
+ },
29779
+ addNodeView() {
29780
+ return (props) => {
29781
+ return new ShapeGroupView({ ...props });
29782
+ };
29783
+ }
29784
+ });
29785
+ const TextStyle = Mark.create({
29786
+ name: "textStyle",
29787
+ addOptions() {
29788
+ return {
29789
+ htmlAttributes: {}
29790
+ };
29791
+ },
29792
+ parseDOM() {
29793
+ return [
29794
+ {
29795
+ tag: "span",
29796
+ getAttrs: (el) => {
29797
+ const hasStyles = el.hasAttribute("style");
29798
+ const isAnnotation = el.classList.contains(annotationClass) || el.classList.contains(annotationContentClass);
29799
+ if (!hasStyles || isAnnotation) return false;
29800
+ return {};
29801
+ }
29802
+ },
29803
+ {
29804
+ getAttrs: (node) => {
29805
+ const fontFamily = node.style.fontFamily?.replace(/['"]+/g, "");
29806
+ const fontSize = node.style.fontSize;
29807
+ const textTransform = node.style.textTransform;
29808
+ if (fontFamily || fontSize || textTransform) {
29809
+ return {
29810
+ fontFamily: fontFamily || null,
29811
+ fontSize: fontSize || null,
29812
+ textTransform: textTransform || null
29813
+ };
29814
+ }
29815
+ return false;
29816
+ }
29817
+ }
29818
+ ];
29819
+ },
29820
+ renderDOM({ htmlAttributes }) {
29821
+ return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
29822
+ },
29823
+ addAttributes() {
29824
+ return {
29825
+ /**
29826
+ * @category Attribute
29827
+ * @param {string} [styleId] - Style identifier for referencing predefined styles
29828
+ */
29829
+ styleId: {}
29830
+ };
29831
+ },
29832
+ addCommands() {
29833
+ return {
29834
+ /**
29835
+ * Remove empty text style marks
29836
+ * @category Command
29837
+ * @example
29838
+ * editor.commands.removeEmptyTextStyle()
29839
+ * @note Cleanup utility to prevent empty span elements
29840
+ * @note Automatically checks if any style attributes exist before removal
29841
+ */
29842
+ removeEmptyTextStyle: () => ({ state, commands: commands2 }) => {
29843
+ const attributes = Attribute.getMarkAttributes(state, this.type);
29844
+ const hasStyles = Object.entries(attributes).some(([, value]) => !!value);
29845
+ if (hasStyles) return true;
29846
+ return commands2.unsetMark(this.name);
29847
+ }
29848
+ };
29849
+ }
29850
+ });
29851
+ function createCascadeToggleCommands({
29852
+ markName,
29853
+ setCommand,
29854
+ unsetCommand,
29855
+ toggleCommand,
29856
+ negationAttrs,
29857
+ isNegation,
29858
+ extendEmptyMarkRange
29859
+ } = {}) {
29860
+ if (!markName) throw new Error("createCascadeToggleCommands requires a markName");
29861
+ const capitalized = markName.charAt(0).toUpperCase() + markName.slice(1);
29862
+ const setName = setCommand ?? `set${capitalized}`;
29863
+ const unsetName = unsetCommand ?? `unset${capitalized}`;
29864
+ const toggleName = toggleCommand ?? `toggle${capitalized}`;
29865
+ const cascadeOptions = {};
29866
+ if (negationAttrs) cascadeOptions.negationAttrs = negationAttrs;
29867
+ if (typeof isNegation === "function") cascadeOptions.isNegation = isNegation;
29868
+ if (extendEmptyMarkRange !== void 0) cascadeOptions.extendEmptyMarkRange = extendEmptyMarkRange;
29869
+ return {
29870
+ [setName]: () => ({ commands: commands2 }) => commands2.setMark(markName),
29871
+ [unsetName]: () => ({ commands: commands2 }) => commands2.unsetMark(markName),
29872
+ [toggleName]: () => ({ commands: commands2 }) => commands2.toggleMarkCascade(markName, cascadeOptions)
29873
+ };
29874
+ }
29875
+ const Bold = Mark.create({
29876
+ name: "bold",
29877
+ addOptions() {
29878
+ return {
29879
+ htmlAttributes: {}
29880
+ };
29881
+ },
29882
+ addAttributes() {
29883
+ return {
29884
+ value: {
29885
+ default: null,
29886
+ renderDOM: (attrs) => {
29887
+ if (attrs.value == null) return {};
29888
+ if (attrs.value === "0" || !attrs.value) {
29889
+ return { style: "font-weight: normal" };
29890
+ }
29891
+ return {};
29892
+ }
29893
+ }
29894
+ };
29895
+ },
29896
+ parseDOM() {
29897
+ return [
29898
+ { tag: "strong" },
29899
+ { tag: "b", getAttrs: (node) => node.style.fontWeight != "normal" && null },
29900
+ { style: "font-weight=400", clearMark: (m) => m.type.name == "strong" },
29901
+ { style: "font-weight", getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null }
29902
+ ];
29903
+ },
29904
+ renderDOM({ htmlAttributes }) {
29905
+ const merged = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
29906
+ const { value, ...rest } = merged || {};
29907
+ if (value === "0") {
29908
+ return ["span", rest, 0];
29909
+ }
29910
+ return ["strong", rest, 0];
29911
+ },
29912
+ addCommands() {
29913
+ const { setBold, unsetBold, toggleBold } = createCascadeToggleCommands({
29914
+ markName: this.name,
29915
+ negationAttrs: { value: "0" }
29916
+ });
29917
+ return {
29918
+ /**
29919
+ * Apply bold formatting
29920
+ * @category Command
29921
+ * @example
29922
+ * editor.commands.setBold()
29923
+ * @note '0' renders as normal weight
29924
+ */
29925
+ setBold,
29926
+ /**
29927
+ * Remove bold formatting
29928
+ * @category Command
29929
+ * @example
29930
+ * editor.commands.unsetBold()
29931
+ */
29932
+ unsetBold,
29933
+ /**
29934
+ * Toggle bold formatting
29935
+ * @category Command
29936
+ * @example
29937
+ * editor.commands.toggleBold()
29938
+ */
29939
+ toggleBold
29940
+ };
29941
+ },
29942
+ addShortcuts() {
29943
+ return {
29944
+ "Mod-b": () => this.editor.commands.toggleBold(),
29945
+ "Mod-B": () => this.editor.commands.toggleBold()
29946
+ };
29947
+ }
29948
+ });
29949
+ const Italic = Mark.create({
29950
+ name: "italic",
29951
+ addOptions() {
29952
+ return {
29953
+ htmlAttributes: {}
29954
+ };
29955
+ },
29956
+ addAttributes() {
29957
+ return {
29958
+ /**
29959
+ * @category Attribute
29960
+ * @param {string} [value] - Italic toggle value ('0' renders as normal)
29961
+ */
29962
+ value: {
29963
+ default: null,
29964
+ renderDOM: (attrs) => {
29965
+ if (attrs.value == null) return {};
29966
+ if (attrs.value === "0" || !attrs.value) return { style: "font-style: normal" };
29967
+ return {};
29968
+ }
29969
+ }
29970
+ };
29971
+ },
29972
+ parseDOM() {
29973
+ return [
29974
+ { tag: "i" },
29975
+ { tag: "em" },
29976
+ { style: "font-style=italic" },
29977
+ { style: "font-style=normal", clearMark: (m) => m.type.name == "em" }
29978
+ ];
29979
+ },
29980
+ renderDOM({ htmlAttributes }) {
29981
+ const merged = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
29982
+ const { value, ...rest } = merged || {};
29983
+ if (value === "0") {
29984
+ return ["span", rest, 0];
29985
+ }
29986
+ return ["em", rest, 0];
29987
+ },
29988
+ addCommands() {
29989
+ const { setItalic, unsetItalic, toggleItalic } = createCascadeToggleCommands({
29990
+ markName: this.name,
29991
+ negationAttrs: { value: "0" }
29992
+ });
29993
+ return {
29994
+ /**
29995
+ * Apply italic formatting
29996
+ * @category Command
29997
+ * @example
29998
+ * editor.commands.setItalic()
29999
+ */
30000
+ setItalic,
30001
+ /**
30002
+ * Remove italic formatting
30003
+ * @category Command
30004
+ * @example
30005
+ * editor.commands.unsetItalic()
30006
+ */
30007
+ unsetItalic,
30008
+ /**
30009
+ * Toggle italic formatting
30010
+ * @category Command
30011
+ * @example
30012
+ * editor.commands.toggleItalic()
30013
+ */
30014
+ toggleItalic
30015
+ };
30016
+ },
30017
+ addShortcuts() {
30018
+ return {
30019
+ "Mod-i": () => this.editor.commands.toggleItalic(),
30020
+ "Mod-I": () => this.editor.commands.toggleItalic()
30021
+ };
30022
+ }
30023
+ });
30024
+ const isKeyboardInvocation = (event) => {
30025
+ return event.type === "contextmenu" && typeof event.detail === "number" && event.detail === 0 && (event.button === 0 || event.button === void 0) && event.clientX === 0 && event.clientY === 0;
30026
+ };
30027
+ const prefersNativeMenu = (event) => {
30028
+ if (!event) return false;
30029
+ if (event.ctrlKey || event.metaKey) {
30030
+ return true;
30031
+ }
30032
+ return isKeyboardInvocation(event);
30033
+ };
30034
+ const shouldAllowNativeContextMenu = (event) => {
30035
+ return prefersNativeMenu(event);
30036
+ };
30037
+ const shouldBypassContextMenu = shouldAllowNativeContextMenu;
30038
+ const DEFAULT_SELECTION_STATE = Object.freeze({
30039
+ focused: false,
30040
+ preservedSelection: null,
30041
+ showVisualSelection: false,
30042
+ skipFocusReset: false
30043
+ });
30044
+ const normalizeSelectionState = (state = {}) => ({
30045
+ ...DEFAULT_SELECTION_STATE,
30046
+ ...state
30047
+ });
30048
+ const CustomSelectionPluginKey = new PluginKey("CustomSelection");
30049
+ const handleClickOutside = (event, editor) => {
30050
+ const editorElem = editor?.options?.element;
30051
+ if (!editorElem) return;
30052
+ const isInsideEditor = editorElem?.contains(event.target);
30053
+ if (!isInsideEditor) {
30054
+ editor.setOptions({
30055
+ focusTarget: event.target
30056
+ });
30057
+ } else {
30058
+ editor.setOptions({
30059
+ focusTarget: null
30060
+ });
30061
+ }
30062
+ };
30063
+ function getFocusMeta(tr) {
30064
+ return tr.getMeta(CustomSelectionPluginKey);
30065
+ }
30066
+ function setFocusMeta(tr, value) {
30067
+ return tr.setMeta(CustomSelectionPluginKey, value);
30068
+ }
30069
+ function getFocusState(state) {
30070
+ return CustomSelectionPluginKey.getState(state);
30071
+ }
30072
+ const isToolbarInput = (target) => {
30073
+ return !!target?.closest(".button-text-input") || target?.classList?.contains("button-text-input");
30074
+ };
30075
+ const isToolbarButton = (target) => {
30076
+ return !!target?.closest(".toolbar-button") || target?.classList?.contains("toolbar-button");
30077
+ };
30078
+ const CustomSelection = Extension.create({
30079
+ name: "customSelection",
30080
+ addPmPlugins() {
30081
+ const editor = this.editor;
30082
+ const customSelectionPlugin = new Plugin({
30083
+ key: CustomSelectionPluginKey,
30084
+ state: {
30085
+ init: () => ({ ...DEFAULT_SELECTION_STATE }),
30086
+ apply: (tr, value) => {
30087
+ const meta = getFocusMeta(tr);
30088
+ if (meta !== void 0) {
30089
+ return { ...value, ...meta };
30090
+ }
30091
+ return value;
30092
+ }
30093
+ },
30094
+ view: () => {
30095
+ const clickHandler = (event) => handleClickOutside(event, editor);
30096
+ document?.addEventListener("mousedown", clickHandler);
30097
+ return {
30098
+ destroy: () => {
30099
+ document?.removeEventListener("mousedown", clickHandler);
30100
+ }
30101
+ };
30102
+ },
30103
+ props: {
30104
+ handleDOMEvents: {
30105
+ contextmenu: (view, event) => {
30106
+ if (shouldAllowNativeContextMenu(event)) {
30107
+ return false;
30108
+ }
30109
+ event.preventDefault();
30110
+ const { selection } = view.state;
30111
+ if (!selection.empty) {
30112
+ view.dispatch(
30113
+ setFocusMeta(view.state.tr, {
30114
+ focused: true,
30115
+ preservedSelection: selection,
30116
+ showVisualSelection: true,
30117
+ skipFocusReset: true
30118
+ })
30119
+ );
30120
+ }
30121
+ setTimeout(() => {
30122
+ view.focus();
30123
+ }, 0);
30124
+ return false;
30125
+ },
30126
+ mousedown: (view, event) => {
30127
+ if (event.button === 2) {
30128
+ if (shouldAllowNativeContextMenu(event)) {
30129
+ return false;
30130
+ }
30131
+ event.preventDefault();
30132
+ const { selection: selection2 } = view.state;
30133
+ if (!selection2.empty) {
30134
+ view.dispatch(
30135
+ setFocusMeta(view.state.tr, {
30136
+ focused: true,
30137
+ preservedSelection: selection2,
30138
+ showVisualSelection: true,
30139
+ skipFocusReset: true
30140
+ })
30141
+ );
30142
+ this.editor.setOptions({
30143
+ lastSelection: selection2,
30144
+ preservedSelection: selection2
30720
30145
  });
30721
- g.appendChild(ellipse);
30722
- return;
30723
- } else {
30724
- clonedChild.setAttribute("cx", (width / 2).toString());
30725
- clonedChild.setAttribute("cy", (height / 2).toString());
30726
- clonedChild.setAttribute("r", (width / 2).toString());
30727
30146
  }
30728
- } else if (clonedChild.tagName === "rect") {
30729
- clonedChild.setAttribute("width", width.toString());
30730
- clonedChild.setAttribute("height", height.toString());
30731
- } else if (clonedChild.tagName === "path" && svgElement.hasAttribute("viewBox")) {
30732
- const viewBox = svgElement.getAttribute("viewBox").split(" ").map(Number);
30733
- if (viewBox.length === 4) {
30734
- const [, , vbWidth, vbHeight] = viewBox;
30735
- const scaleX = width / vbWidth;
30736
- const scaleY = height / vbHeight;
30737
- if (scaleX !== 1 || scaleY !== 1) {
30738
- const pathTransform = `scale(${scaleX}, ${scaleY})`;
30739
- const existingTransform = clonedChild.getAttribute("transform");
30740
- clonedChild.setAttribute(
30741
- "transform",
30742
- existingTransform ? `${existingTransform} ${pathTransform}` : pathTransform
30743
- );
30744
- }
30147
+ return false;
30148
+ }
30149
+ const { selection } = view.state;
30150
+ const target = event.target;
30151
+ const isElement2 = target instanceof Element;
30152
+ const isToolbarBtn = isElement2 && isToolbarButton(target);
30153
+ const isToolbarInp = isElement2 && isToolbarInput(target);
30154
+ this.editor.setOptions({
30155
+ focusTarget: target
30156
+ });
30157
+ if (isToolbarInp && !selection.empty) {
30158
+ view.dispatch(
30159
+ setFocusMeta(view.state.tr, {
30160
+ focused: true,
30161
+ preservedSelection: selection,
30162
+ showVisualSelection: true,
30163
+ skipFocusReset: false
30164
+ })
30165
+ );
30166
+ this.editor.setOptions({
30167
+ lastSelection: selection,
30168
+ preservedSelection: selection
30169
+ });
30170
+ return false;
30171
+ }
30172
+ if (isToolbarBtn && !isToolbarInp) {
30173
+ if (!selection.empty) {
30174
+ this.editor.setOptions({
30175
+ lastSelection: selection
30176
+ });
30177
+ view.dispatch(
30178
+ setFocusMeta(view.state.tr, {
30179
+ focused: true,
30180
+ preservedSelection: selection,
30181
+ showVisualSelection: true,
30182
+ skipFocusReset: false
30183
+ })
30184
+ );
30745
30185
  }
30746
- } else if (clonedChild.hasAttribute("width")) {
30747
- clonedChild.setAttribute("width", width.toString());
30186
+ return false;
30748
30187
  }
30749
- if (clonedChild.hasAttribute("height") && clonedChild.tagName !== "ellipse") {
30750
- clonedChild.setAttribute("height", height.toString());
30188
+ if (!isToolbarBtn && !isToolbarInp) {
30189
+ view.dispatch(
30190
+ setFocusMeta(view.state.tr, {
30191
+ focused: false,
30192
+ preservedSelection: null,
30193
+ showVisualSelection: false,
30194
+ skipFocusReset: false
30195
+ })
30196
+ );
30197
+ if (!selection.empty && !this.editor.options.element?.contains(target)) {
30198
+ this.editor.setOptions({
30199
+ lastSelection: selection
30200
+ });
30201
+ const clearSelectionTr = view.state.tr.setSelection(TextSelection.create(view.state.doc, 0));
30202
+ view.dispatch(clearSelectionTr);
30203
+ }
30751
30204
  }
30752
- g.appendChild(clonedChild);
30753
- });
30205
+ },
30206
+ focus: (view) => {
30207
+ const target = this.editor.options.focusTarget;
30208
+ const isElement2 = target instanceof Element;
30209
+ const isToolbarBtn = isElement2 && isToolbarButton(target);
30210
+ const isToolbarInp = isElement2 && isToolbarInput(target);
30211
+ const focusState = getFocusState(view.state);
30212
+ if (focusState?.skipFocusReset) {
30213
+ view.dispatch(
30214
+ setFocusMeta(view.state.tr, normalizeSelectionState({ ...focusState, skipFocusReset: false }))
30215
+ );
30216
+ return false;
30217
+ }
30218
+ if (!isToolbarBtn && !isToolbarInp) {
30219
+ view.dispatch(
30220
+ setFocusMeta(view.state.tr, {
30221
+ focused: false,
30222
+ preservedSelection: null,
30223
+ showVisualSelection: false,
30224
+ skipFocusReset: false
30225
+ })
30226
+ );
30227
+ }
30228
+ },
30229
+ blur: (view) => {
30230
+ const target = this.editor.options.focusTarget;
30231
+ const isElement2 = target instanceof Element;
30232
+ const isToolbarBtn = isElement2 && isToolbarButton(target);
30233
+ const isToolbarInp = isElement2 && isToolbarInput(target);
30234
+ const state = getFocusState(view.state);
30235
+ if (state?.skipFocusReset) {
30236
+ return false;
30237
+ }
30238
+ if (isToolbarBtn || isToolbarInp) {
30239
+ view.dispatch(
30240
+ setFocusMeta(view.state.tr, {
30241
+ focused: true,
30242
+ preservedSelection: state.preservedSelection || view.state.selection,
30243
+ showVisualSelection: true,
30244
+ skipFocusReset: false
30245
+ })
30246
+ );
30247
+ } else {
30248
+ view.dispatch(
30249
+ setFocusMeta(view.state.tr, {
30250
+ focused: false,
30251
+ preservedSelection: null,
30252
+ showVisualSelection: false,
30253
+ skipFocusReset: false
30254
+ })
30255
+ );
30256
+ }
30257
+ }
30258
+ },
30259
+ decorations: (state) => {
30260
+ const { selection, doc: doc2 } = state;
30261
+ const focusState = getFocusState(state);
30262
+ const shouldShowSelection = focusState.showVisualSelection && (focusState.preservedSelection || !selection.empty && focusState.focused);
30263
+ if (!shouldShowSelection) {
30264
+ return null;
30265
+ }
30266
+ const targetSelection = focusState.preservedSelection || selection;
30267
+ if (targetSelection.empty) {
30268
+ return null;
30269
+ }
30270
+ return DecorationSet.create(doc2, [
30271
+ Decoration.inline(targetSelection.from, targetSelection.to, {
30272
+ class: "sd-custom-selection"
30273
+ })
30274
+ ]);
30754
30275
  }
30755
30276
  }
30756
- } catch (error) {
30757
- console.warn("Failed to generate shape SVG:", error);
30758
- const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
30759
- rect.setAttribute("width", width.toString());
30760
- rect.setAttribute("height", height.toString());
30761
- rect.setAttribute("fill", fillColor);
30762
- rect.setAttribute("stroke", strokeColor);
30763
- rect.setAttribute("stroke-width", strokeWidth.toString());
30764
- g.appendChild(rect);
30765
- }
30766
- return g;
30767
- }
30768
- buildView() {
30769
- const { element } = this.createElement();
30770
- this.root = element;
30771
- }
30772
- update() {
30773
- return false;
30774
- }
30775
- }
30776
- const ShapeGroup = Node$1.create({
30777
- name: "shapeGroup",
30778
- group: "inline",
30779
- inline: true,
30780
- atom: true,
30781
- addOptions() {
30277
+ });
30278
+ return [customSelectionPlugin];
30279
+ },
30280
+ addCommands() {
30782
30281
  return {
30783
- htmlAttributes: {
30784
- contenteditable: false
30282
+ /**
30283
+ * Restore the preserved selection
30284
+ * @category Command
30285
+ * @returns {Function} Command function
30286
+ * @example
30287
+ * // Restore selection after toolbar interaction
30288
+ * editor.commands.restorePreservedSelection()
30289
+ * @note Used internally to maintain selection when interacting with toolbar
30290
+ */
30291
+ restorePreservedSelection: () => ({ tr, state }) => {
30292
+ const focusState = getFocusState(state);
30293
+ if (focusState.preservedSelection) {
30294
+ return tr.setSelection(focusState.preservedSelection);
30295
+ }
30296
+ const lastSelection = this.editor.options.lastSelection;
30297
+ if (lastSelection) {
30298
+ return tr.setSelection(lastSelection);
30299
+ }
30300
+ return tr;
30785
30301
  }
30786
30302
  };
30787
- },
30788
- addAttributes() {
30789
- return {
30790
- groupTransform: {
30791
- default: {},
30792
- renderDOM: () => ({})
30793
- },
30794
- shapes: {
30795
- default: [],
30796
- renderDOM: () => ({})
30797
- },
30798
- size: {
30799
- default: null,
30800
- renderDOM: (attrs) => {
30801
- if (!attrs.size) return {};
30802
- const sizeData = {};
30803
- if (attrs.size.width) sizeData["data-width"] = attrs.size.width;
30804
- if (attrs.size.height) sizeData["data-height"] = attrs.size.height;
30805
- return sizeData;
30303
+ }
30304
+ });
30305
+ const getLinkedStyle = (styleId, styles = []) => {
30306
+ const linkedStyle = styles.find((style) => style.id === styleId);
30307
+ const basedOn = linkedStyle?.definition?.attrs?.basedOn;
30308
+ const basedOnStyle = styles.find((style) => style.id === basedOn);
30309
+ return { linkedStyle, basedOnStyle };
30310
+ };
30311
+ const getQuickFormatList = (editor) => {
30312
+ if (!editor?.converter?.linkedStyles) return [];
30313
+ return editor.converter.linkedStyles.filter((style) => style.type === "paragraph" && style.definition?.attrs).sort((a, b) => {
30314
+ const nameA = a.definition.attrs?.name ?? "";
30315
+ const nameB = b.definition.attrs?.name ?? "";
30316
+ return nameA.localeCompare(nameB);
30317
+ });
30318
+ };
30319
+ const generateLinkedStyleString = (linkedStyle, basedOnStyle, node, parent, includeSpacing = true) => {
30320
+ if (!linkedStyle?.definition?.styles) return "";
30321
+ const markValue = {};
30322
+ const linkedDefinitionStyles = { ...linkedStyle.definition.styles };
30323
+ const basedOnDefinitionStyles = { ...basedOnStyle?.definition?.styles };
30324
+ const resultStyles = { ...linkedDefinitionStyles };
30325
+ const inheritKeys = [
30326
+ "font-size",
30327
+ "font-family",
30328
+ "text-transform",
30329
+ "bold",
30330
+ "italic",
30331
+ "underline",
30332
+ "strike",
30333
+ "color",
30334
+ "highlight"
30335
+ ];
30336
+ inheritKeys.forEach((k2) => {
30337
+ if (!linkedDefinitionStyles[k2] && basedOnDefinitionStyles[k2]) {
30338
+ resultStyles[k2] = basedOnDefinitionStyles[k2];
30339
+ }
30340
+ });
30341
+ Object.entries(resultStyles).forEach(([k2, value]) => {
30342
+ const key2 = kebabCase(k2);
30343
+ const flattenedMarks = [];
30344
+ node?.marks?.forEach((n) => {
30345
+ if (n.type.name === "textStyle") {
30346
+ Object.entries(n.attrs).forEach(([styleKey, value2]) => {
30347
+ const parsedKey = kebabCase(styleKey);
30348
+ if (!value2) return;
30349
+ flattenedMarks.push({ key: parsedKey, value: value2 });
30350
+ });
30351
+ return;
30352
+ }
30353
+ flattenedMarks.push({ key: n.type.name, value: n.attrs[key2] });
30354
+ });
30355
+ const underlineNone = node?.marks?.some((m) => m.type?.name === "underline" && m.attrs?.underlineType === "none");
30356
+ if (underlineNone) {
30357
+ markValue["text-decoration"] = "none";
30358
+ }
30359
+ const mark = flattenedMarks.find((n) => n.key === key2);
30360
+ if (!mark) {
30361
+ if (key2 === "bold" && node) {
30362
+ const boldValue = typeof value === "object" && value !== null ? value.value : value;
30363
+ const hasInlineBoldOff = node.marks?.some((m) => m.type?.name === "bold" && m.attrs?.value === "0");
30364
+ const hasInlineBoldOn = node.marks?.some((m) => m.type?.name === "bold" && m.attrs?.value !== "0");
30365
+ if (!hasInlineBoldOff && !hasInlineBoldOn && boldValue !== "0" && boldValue !== false) {
30366
+ markValue["font-weight"] = "bold";
30806
30367
  }
30807
- },
30808
- padding: {
30809
- default: null,
30810
- renderDOM: (attrs) => {
30811
- if (!attrs.padding) return {};
30812
- const paddingData = {};
30813
- if (attrs.padding.top != null) paddingData["data-padding-top"] = attrs.padding.top;
30814
- if (attrs.padding.right != null) paddingData["data-padding-right"] = attrs.padding.right;
30815
- if (attrs.padding.bottom != null) paddingData["data-padding-bottom"] = attrs.padding.bottom;
30816
- if (attrs.padding.left != null) paddingData["data-padding-left"] = attrs.padding.left;
30817
- return paddingData;
30368
+ } else if (key2 === "italic" && node) {
30369
+ const italicValue = typeof value === "object" && value !== null ? value.value : value;
30370
+ const hasInlineItalicOff = node.marks?.some((m) => m.type?.name === "italic" && m.attrs?.value === "0");
30371
+ const hasInlineItalicOn = node.marks?.some((m) => m.type?.name === "italic" && m.attrs?.value !== "0");
30372
+ if (!hasInlineItalicOff && !hasInlineItalicOn && italicValue !== "0" && italicValue !== false) {
30373
+ markValue["font-style"] = "italic";
30818
30374
  }
30819
- },
30820
- marginOffset: {
30821
- default: null,
30822
- renderDOM: (attrs) => {
30823
- if (!attrs.marginOffset) return {};
30824
- const offsetData = {};
30825
- if (attrs.marginOffset.horizontal != null) offsetData["data-offset-x"] = attrs.marginOffset.horizontal;
30826
- if (attrs.marginOffset.top != null) offsetData["data-offset-y"] = attrs.marginOffset.top;
30827
- return offsetData;
30375
+ } else if (key2 === "strike" && node) {
30376
+ const strikeValue = typeof value === "object" && value !== null ? value.value : value;
30377
+ const hasInlineStrikeOff = node.marks?.some((m) => m.type?.name === "strike" && m.attrs?.value === "0");
30378
+ const hasInlineStrikeOn = node.marks?.some(
30379
+ (m) => m.type?.name === "strike" && (m.attrs?.value === void 0 || m.attrs?.value !== "0")
30380
+ );
30381
+ if (!hasInlineStrikeOff && !hasInlineStrikeOn && strikeValue !== "0" && strikeValue !== false) {
30382
+ markValue["text-decoration"] = "line-through";
30383
+ }
30384
+ } else if (key2 === "text-transform" && node) {
30385
+ markValue[key2] = value;
30386
+ } else if (key2 === "font-size" && node) {
30387
+ markValue[key2] = value;
30388
+ } else if (key2 === "font-family" && node) {
30389
+ markValue[key2] = value;
30390
+ } else if (key2 === "color" && node) {
30391
+ markValue[key2] = value;
30392
+ } else if (key2 === "highlight" && node) {
30393
+ const hasInlineHighlight = node.marks?.some((m) => m.type?.name === "highlight");
30394
+ if (!hasInlineHighlight) {
30395
+ const color = typeof value === "string" ? value : value?.color;
30396
+ if (color) markValue["background-color"] = color;
30397
+ }
30398
+ } else if (key2 === "underline" && node) {
30399
+ const styleValRaw = value?.value ?? value ?? "";
30400
+ const styleVal = styleValRaw.toString().toLowerCase();
30401
+ const hasInlineUnderlineOff = node.marks?.some(
30402
+ (m) => m.type?.name === "underline" && m.attrs?.underlineType === "none"
30403
+ );
30404
+ const hasInlineUnderlineOn = node.marks?.some(
30405
+ (m) => m.type?.name === "underline" && m.attrs?.underlineType && m.attrs.underlineType !== "none"
30406
+ );
30407
+ if (!hasInlineUnderlineOff && !hasInlineUnderlineOn) {
30408
+ if (styleVal && styleVal !== "none" && styleVal !== "0") {
30409
+ const colorVal = value && typeof value === "object" ? value.color || value.underlineColor || null : null;
30410
+ const css = getUnderlineCssString({ type: styleVal, color: colorVal });
30411
+ css.split(";").forEach((decl) => {
30412
+ const d2 = decl.trim();
30413
+ if (!d2) return;
30414
+ const idx = d2.indexOf(":");
30415
+ if (idx === -1) return;
30416
+ const k3 = d2.slice(0, idx).trim();
30417
+ const v = d2.slice(idx + 1).trim();
30418
+ markValue[k3] = v;
30419
+ });
30420
+ }
30828
30421
  }
30829
- },
30830
- drawingContent: {
30831
- rendered: false
30422
+ } else if (typeof value === "string") {
30423
+ markValue[key2] = value;
30832
30424
  }
30833
- };
30834
- },
30835
- parseDOM() {
30836
- return false;
30837
- },
30838
- renderDOM({ htmlAttributes }) {
30839
- return ["div", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes, { "data-shape-group": "" })];
30840
- },
30841
- addNodeView() {
30842
- return (props) => {
30843
- return new ShapeGroupView({ ...props });
30844
- };
30425
+ }
30426
+ });
30427
+ const final = Object.entries(markValue).map(([key2, value]) => `${key2}: ${value}`).join(";");
30428
+ return final;
30429
+ };
30430
+ const applyLinkedStyleToTransaction = (tr, editor, style) => {
30431
+ if (!style) return false;
30432
+ let selection = tr.selection;
30433
+ const state = editor.state;
30434
+ const focusState = CustomSelectionPluginKey.getState(state);
30435
+ if (selection.empty && focusState?.preservedSelection && !focusState?.preservedSelection.empty) {
30436
+ selection = focusState.preservedSelection;
30437
+ tr.setSelection(selection);
30438
+ } else if (selection.empty && editor.options.lastSelection) {
30439
+ selection = editor.options.lastSelection;
30440
+ tr.setSelection(selection);
30845
30441
  }
30846
- });
30847
- const TextStyle = Mark.create({
30848
- name: "textStyle",
30849
- addOptions() {
30442
+ const { from: from2, to } = selection;
30443
+ const getUpdatedParagraphAttrs = (node) => {
30850
30444
  return {
30851
- htmlAttributes: {}
30445
+ ...node.attrs,
30446
+ paragraphProperties: {
30447
+ ...node.attrs.paragraphProperties || {},
30448
+ styleId: style.id
30449
+ }
30852
30450
  };
30853
- },
30854
- parseDOM() {
30855
- return [
30856
- {
30857
- tag: "span",
30858
- getAttrs: (el) => {
30859
- const hasStyles = el.hasAttribute("style");
30860
- const isAnnotation = el.classList.contains(annotationClass) || el.classList.contains(annotationContentClass);
30861
- if (!hasStyles || isAnnotation) return false;
30862
- return {};
30863
- }
30864
- },
30865
- {
30866
- getAttrs: (node) => {
30867
- const fontFamily = node.style.fontFamily?.replace(/['"]+/g, "");
30868
- const fontSize = node.style.fontSize;
30869
- const textTransform = node.style.textTransform;
30870
- if (fontFamily || fontSize || textTransform) {
30871
- return {
30872
- fontFamily: fontFamily || null,
30873
- fontSize: fontSize || null,
30874
- textTransform: textTransform || null
30875
- };
30451
+ };
30452
+ const clearFormattingMarks = (startPos, endPos) => {
30453
+ tr.doc.nodesBetween(startPos, endPos, (node, pos) => {
30454
+ if (node.isText && node.marks.length > 0) {
30455
+ const marksToRemove = [
30456
+ "textStyle",
30457
+ "bold",
30458
+ "italic",
30459
+ "underline",
30460
+ "strike",
30461
+ "subscript",
30462
+ "superscript",
30463
+ "highlight"
30464
+ ];
30465
+ node.marks.forEach((mark) => {
30466
+ if (marksToRemove.includes(mark.type.name)) {
30467
+ tr.removeMark(pos, pos + node.nodeSize, mark);
30876
30468
  }
30877
- return false;
30878
- }
30469
+ });
30879
30470
  }
30880
- ];
30881
- },
30882
- renderDOM({ htmlAttributes }) {
30883
- return ["span", Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes), 0];
30884
- },
30885
- addAttributes() {
30886
- return {
30471
+ return true;
30472
+ });
30473
+ };
30474
+ if (from2 === to) {
30475
+ let pos = from2;
30476
+ let paragraphNode = tr.doc.nodeAt(from2);
30477
+ if (paragraphNode?.type.name !== "paragraph") {
30478
+ const parentNode2 = findParentNode((node) => node.type.name === "paragraph")(selection);
30479
+ if (!parentNode2) return false;
30480
+ pos = parentNode2.pos;
30481
+ paragraphNode = parentNode2.node;
30482
+ }
30483
+ clearFormattingMarks(pos + 1, pos + paragraphNode.nodeSize - 1);
30484
+ tr.setNodeMarkup(pos, void 0, getUpdatedParagraphAttrs(paragraphNode));
30485
+ return true;
30486
+ }
30487
+ const paragraphPositions = [];
30488
+ tr.doc.nodesBetween(from2, to, (node, pos) => {
30489
+ if (node.type.name === "paragraph") {
30490
+ paragraphPositions.push({ node, pos });
30491
+ }
30492
+ return true;
30493
+ });
30494
+ paragraphPositions.forEach(({ node, pos }) => {
30495
+ clearFormattingMarks(pos + 1, pos + node.nodeSize - 1);
30496
+ tr.setNodeMarkup(pos, void 0, getUpdatedParagraphAttrs(node));
30497
+ });
30498
+ return true;
30499
+ };
30500
+ const stepInsertsTextIntoStyledParagraph = (tr, oldEditorState, step, stepIndex) => {
30501
+ if (!step.slice || step.slice.size === 0 || typeof step.from !== "number") {
30502
+ return false;
30503
+ }
30504
+ let insertsText = false;
30505
+ step.slice.content.descendants((node) => {
30506
+ if (node.type?.name === "text" && node.text?.length) {
30507
+ insertsText = true;
30508
+ return false;
30509
+ }
30510
+ return true;
30511
+ });
30512
+ if (!insertsText) return false;
30513
+ const docBeforeStep = tr.docs?.[stepIndex] || oldEditorState.doc;
30514
+ if (!docBeforeStep) return false;
30515
+ const resolvedPos = Math.min(step.from, docBeforeStep.content.size);
30516
+ const $pos = docBeforeStep.resolve(resolvedPos);
30517
+ for (let depth = $pos.depth; depth >= 0; depth--) {
30518
+ const node = $pos.node(depth);
30519
+ if (node?.type?.name === "paragraph") {
30520
+ return Boolean(node.attrs?.paragraphProperties?.styleId);
30521
+ }
30522
+ }
30523
+ return false;
30524
+ };
30525
+ const LinkedStylesPluginKey = new PluginKey("linkedStyles");
30526
+ const createLinkedStylesPlugin = (editor) => {
30527
+ return new Plugin({
30528
+ key: LinkedStylesPluginKey,
30529
+ state: {
30887
30530
  /**
30888
- * @category Attribute
30889
- * @param {string} [styleId] - Style identifier for referencing predefined styles
30531
+ * Initialize plugin state with styles and decorations
30532
+ * @returns {Object} Initial state with styles and decorations
30533
+ * @private
30890
30534
  */
30891
- styleId: {}
30892
- };
30893
- },
30894
- addCommands() {
30895
- return {
30535
+ init() {
30536
+ if (!editor.converter || editor.options.mode !== "docx") return {};
30537
+ const styles = editor.converter?.linkedStyles || [];
30538
+ return {
30539
+ styles,
30540
+ decorations: generateDecorations(editor.state, styles)
30541
+ };
30542
+ },
30896
30543
  /**
30897
- * Remove empty text style marks
30898
- * @category Command
30899
- * @example
30900
- * editor.commands.removeEmptyTextStyle()
30901
- * @note Cleanup utility to prevent empty span elements
30902
- * @note Automatically checks if any style attributes exist before removal
30544
+ * Update decorations when document changes
30545
+ * @param {Object} tr - The transaction
30546
+ * @param {Object} prev - Previous plugin state
30547
+ * @param {Object} oldEditorState - Old editor state
30548
+ * @param {Object} newEditorState - New editor state
30549
+ * @returns {Object} Updated state with styles and decorations
30550
+ * @private
30903
30551
  */
30904
- removeEmptyTextStyle: () => ({ state, commands: commands2 }) => {
30905
- const attributes = Attribute.getMarkAttributes(state, this.type);
30906
- const hasStyles = Object.entries(attributes).some(([, value]) => !!value);
30907
- if (hasStyles) return true;
30908
- return commands2.unsetMark(this.name);
30552
+ apply(tr, prev, oldEditorState, newEditorState) {
30553
+ if (!editor.converter || editor.options.mode !== "docx") return { ...prev };
30554
+ let decorations = prev.decorations || DecorationSet.empty;
30555
+ if (tr.docChanged) {
30556
+ let mightAffectStyles = false;
30557
+ const styleRelatedMarks = /* @__PURE__ */ new Set(["textStyle", "bold", "italic", "underline", "strike"]);
30558
+ tr.steps.forEach((step, index2) => {
30559
+ if (step.slice) {
30560
+ step.slice.content.descendants((node, pos) => {
30561
+ if (node.type.name === "paragraph") {
30562
+ const paragraphProps = calculateResolvedParagraphProperties(
30563
+ editor,
30564
+ node,
30565
+ newEditorState.doc.resolve(pos)
30566
+ );
30567
+ if (paragraphProps.styleId) {
30568
+ mightAffectStyles = true;
30569
+ }
30570
+ return false;
30571
+ }
30572
+ if (node.marks.length > 0) {
30573
+ const hasStyleMarks = node.marks.some((mark) => styleRelatedMarks.has(mark.type.name));
30574
+ if (hasStyleMarks) {
30575
+ mightAffectStyles = true;
30576
+ return false;
30577
+ }
30578
+ }
30579
+ });
30580
+ }
30581
+ if (step.jsonID === "addMark" || step.jsonID === "removeMark") {
30582
+ if (step.mark && styleRelatedMarks.has(step.mark.type.name)) {
30583
+ mightAffectStyles = true;
30584
+ }
30585
+ }
30586
+ if (!mightAffectStyles && stepInsertsTextIntoStyledParagraph(tr, oldEditorState, step, index2)) {
30587
+ mightAffectStyles = true;
30588
+ }
30589
+ });
30590
+ if (mightAffectStyles) {
30591
+ const styles = LinkedStylesPluginKey.getState(editor.state).styles;
30592
+ decorations = generateDecorations(newEditorState, styles);
30593
+ } else {
30594
+ decorations = decorations.map(tr.mapping, tr.doc);
30595
+ }
30596
+ }
30597
+ return { ...prev, decorations };
30909
30598
  }
30910
- };
30911
- }
30912
- });
30913
- function createCascadeToggleCommands({
30914
- markName,
30915
- setCommand,
30916
- unsetCommand,
30917
- toggleCommand,
30918
- negationAttrs,
30919
- isNegation,
30920
- extendEmptyMarkRange
30921
- } = {}) {
30922
- if (!markName) throw new Error("createCascadeToggleCommands requires a markName");
30923
- const capitalized = markName.charAt(0).toUpperCase() + markName.slice(1);
30924
- const setName = setCommand ?? `set${capitalized}`;
30925
- const unsetName = unsetCommand ?? `unset${capitalized}`;
30926
- const toggleName = toggleCommand ?? `toggle${capitalized}`;
30927
- const cascadeOptions = {};
30928
- if (negationAttrs) cascadeOptions.negationAttrs = negationAttrs;
30929
- if (typeof isNegation === "function") cascadeOptions.isNegation = isNegation;
30930
- if (extendEmptyMarkRange !== void 0) cascadeOptions.extendEmptyMarkRange = extendEmptyMarkRange;
30931
- return {
30932
- [setName]: () => ({ commands: commands2 }) => commands2.setMark(markName),
30933
- [unsetName]: () => ({ commands: commands2 }) => commands2.unsetMark(markName),
30934
- [toggleName]: () => ({ commands: commands2 }) => commands2.toggleMarkCascade(markName, cascadeOptions)
30599
+ },
30600
+ props: {
30601
+ /**
30602
+ * Provide decorations to the editor view
30603
+ * @param {Object} state - Current editor state
30604
+ * @returns {Object} The decoration set
30605
+ * @private
30606
+ */
30607
+ decorations(state) {
30608
+ return LinkedStylesPluginKey.getState(state)?.decorations;
30609
+ }
30610
+ }
30611
+ });
30612
+ };
30613
+ const generateDecorations = (state, styles) => {
30614
+ const decorations = [];
30615
+ const doc2 = state?.doc;
30616
+ const getParagraphStyleId = (pos) => {
30617
+ const $pos = state.doc.resolve(pos);
30618
+ for (let d2 = $pos.depth; d2 >= 0; d2--) {
30619
+ const n = $pos.node(d2);
30620
+ if (n?.type?.name === "paragraph") {
30621
+ const paragraphProps = getResolvedParagraphProperties(n);
30622
+ return paragraphProps.styleId || null;
30623
+ }
30624
+ }
30625
+ return null;
30935
30626
  };
30936
- }
30937
- const Bold = Mark.create({
30938
- name: "bold",
30939
- addOptions() {
30940
- return {
30941
- htmlAttributes: {}
30942
- };
30943
- },
30944
- addAttributes() {
30945
- return {
30946
- value: {
30947
- default: null,
30948
- renderDOM: (attrs) => {
30949
- if (attrs.value == null) return {};
30950
- if (attrs.value === "0" || !attrs.value) {
30951
- return { style: "font-weight: normal" };
30952
- }
30953
- return {};
30627
+ doc2.descendants((node, pos) => {
30628
+ const { name } = node.type;
30629
+ if (name !== "text") return;
30630
+ const paragraphStyleId = getParagraphStyleId(pos);
30631
+ let runStyleId = null;
30632
+ let inlineTextStyleId = null;
30633
+ for (const mark of node.marks) {
30634
+ if (mark.type.name === "run") {
30635
+ const rp = mark.attrs?.runProperties;
30636
+ if (rp && typeof rp === "object" && !Array.isArray(rp) && rp.styleId) runStyleId = rp.styleId;
30637
+ else if (Array.isArray(rp)) {
30638
+ const ent = rp.find((e) => e?.xmlName === "w:rStyle");
30639
+ const sid = ent?.attributes?.["w:val"];
30640
+ if (sid) runStyleId = sid;
30954
30641
  }
30642
+ } else if (mark.type.name === "textStyle" && mark.attrs?.styleId) {
30643
+ inlineTextStyleId = mark.attrs.styleId;
30955
30644
  }
30645
+ }
30646
+ const buildStyleMap = (sid) => {
30647
+ if (!sid) return {};
30648
+ const { linkedStyle, basedOnStyle: basedOnStyle2 } = getLinkedStyle(sid, styles);
30649
+ if (!linkedStyle) return {};
30650
+ const base2 = { ...basedOnStyle2?.definition?.styles || {} };
30651
+ return { ...base2, ...linkedStyle.definition?.styles || {} };
30956
30652
  };
30957
- },
30958
- parseDOM() {
30959
- return [
30960
- { tag: "strong" },
30961
- { tag: "b", getAttrs: (node) => node.style.fontWeight != "normal" && null },
30962
- { style: "font-weight=400", clearMark: (m) => m.type.name == "strong" },
30963
- { style: "font-weight", getAttrs: (value) => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null }
30964
- ];
30965
- },
30966
- renderDOM({ htmlAttributes }) {
30967
- const merged = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
30968
- const { value, ...rest } = merged || {};
30969
- if (value === "0") {
30970
- return ["span", rest, 0];
30653
+ const pMap = buildStyleMap(paragraphStyleId);
30654
+ let tMap;
30655
+ if (paragraphStyleId?.startsWith("TOC")) {
30656
+ tMap = {};
30657
+ } else {
30658
+ tMap = buildStyleMap(inlineTextStyleId);
30971
30659
  }
30972
- return ["strong", rest, 0];
30660
+ const rMap = buildStyleMap(runStyleId);
30661
+ const finalStyles = { ...pMap, ...tMap, ...rMap };
30662
+ if (Object.keys(finalStyles).length === 0) return;
30663
+ const mergedLinkedStyle = { definition: { styles: finalStyles, attrs: {} } };
30664
+ const basedOnStyle = null;
30665
+ const $pos = state.doc.resolve(pos);
30666
+ $pos.parent;
30667
+ const styleString = generateLinkedStyleString(mergedLinkedStyle, basedOnStyle, node);
30668
+ if (!styleString) return;
30669
+ const decoration = Decoration.inline(pos, pos + node.nodeSize, { style: styleString });
30670
+ decorations.push(decoration);
30671
+ });
30672
+ return DecorationSet.create(doc2, decorations);
30673
+ };
30674
+ const LinkedStyles = Extension.create({
30675
+ name: "linkedStyles",
30676
+ priority: 1,
30677
+ // We need this plugin to run before the list plugins
30678
+ addOptions() {
30679
+ return {};
30680
+ },
30681
+ addPmPlugins() {
30682
+ return [createLinkedStylesPlugin(this.editor)];
30973
30683
  },
30974
30684
  addCommands() {
30975
- const { setBold, unsetBold, toggleBold } = createCascadeToggleCommands({
30976
- markName: this.name,
30977
- negationAttrs: { value: "0" }
30978
- });
30979
30685
  return {
30980
30686
  /**
30981
- * Apply bold formatting
30687
+ * Apply a linked style to the selected paragraphs
30982
30688
  * @category Command
30689
+ * @param {LinkedStyle} style - The style object to apply
30983
30690
  * @example
30984
- * editor.commands.setBold()
30985
- * @note '0' renders as normal weight
30691
+ * const style = editor.helpers.linkedStyles.getStyleById('Heading1');
30692
+ * editor.commands.setLinkedStyle(style);
30693
+ * @note Clears existing formatting when applying a style
30694
+ * @note Works with custom selection preservation
30986
30695
  */
30987
- setBold,
30696
+ setLinkedStyle: (style) => (params2) => {
30697
+ const { tr } = params2;
30698
+ return applyLinkedStyleToTransaction(tr, this.editor, style);
30699
+ },
30988
30700
  /**
30989
- * Remove bold formatting
30701
+ * Toggle a linked style on the current selection
30990
30702
  * @category Command
30703
+ * @param {LinkedStyle} style - The linked style to apply (with id property)
30991
30704
  * @example
30992
- * editor.commands.unsetBold()
30705
+ * const style = editor.helpers.linkedStyles.getStyleById('Heading1');
30706
+ * editor.commands.toggleLinkedStyle(style)
30707
+ * @note If selection is empty, returns false
30708
+ * @note Removes style if already applied, applies it if not
30993
30709
  */
30994
- unsetBold,
30710
+ toggleLinkedStyle: (style) => (params2) => {
30711
+ const { tr } = params2;
30712
+ if (tr.selection.empty) {
30713
+ return false;
30714
+ }
30715
+ let node = tr.doc.nodeAt(tr.selection.$from.pos);
30716
+ if (node && node.type.name !== "paragraph") {
30717
+ node = findParentNodeClosestToPos(tr.selection.$from, (n) => {
30718
+ return n.type.name === "paragraph";
30719
+ })?.node;
30720
+ }
30721
+ if (!node) {
30722
+ return false;
30723
+ }
30724
+ const paragraphProps = getResolvedParagraphProperties(node);
30725
+ const currentStyleId = paragraphProps.styleId;
30726
+ if (currentStyleId === style.id) {
30727
+ return applyLinkedStyleToTransaction(tr, this.editor, { id: null });
30728
+ }
30729
+ return applyLinkedStyleToTransaction(tr, this.editor, style);
30730
+ },
30995
30731
  /**
30996
- * Toggle bold formatting
30732
+ * Apply a linked style by its ID
30997
30733
  * @category Command
30734
+ * @param {string} styleId - The style ID to apply (e.g., 'Heading1')
30998
30735
  * @example
30999
- * editor.commands.toggleBold()
31000
- */
31001
- toggleBold
31002
- };
31003
- },
31004
- addShortcuts() {
31005
- return {
31006
- "Mod-b": () => this.editor.commands.toggleBold(),
31007
- "Mod-B": () => this.editor.commands.toggleBold()
31008
- };
31009
- }
31010
- });
31011
- const Italic = Mark.create({
31012
- name: "italic",
31013
- addOptions() {
31014
- return {
31015
- htmlAttributes: {}
31016
- };
31017
- },
31018
- addAttributes() {
31019
- return {
31020
- /**
31021
- * @category Attribute
31022
- * @param {string} [value] - Italic toggle value ('0' renders as normal)
30736
+ * editor.commands.setStyleById('Heading1')
30737
+ * editor.commands.setStyleById('Normal')
30738
+ * @note Looks up the style from loaded Word styles
31023
30739
  */
31024
- value: {
31025
- default: null,
31026
- renderDOM: (attrs) => {
31027
- if (attrs.value == null) return {};
31028
- if (attrs.value === "0" || !attrs.value) return { style: "font-style: normal" };
31029
- return {};
31030
- }
30740
+ setStyleById: (styleId) => (params2) => {
30741
+ const { state, tr } = params2;
30742
+ const pluginState = LinkedStylesPluginKey.getState(state);
30743
+ if (!pluginState) return false;
30744
+ const style = pluginState.styles?.find((s2) => s2.id === styleId);
30745
+ if (!style) return false;
30746
+ return applyLinkedStyleToTransaction(tr, this.editor, style);
31031
30747
  }
31032
30748
  };
31033
30749
  },
31034
- parseDOM() {
31035
- return [
31036
- { tag: "i" },
31037
- { tag: "em" },
31038
- { style: "font-style=italic" },
31039
- { style: "font-style=normal", clearMark: (m) => m.type.name == "em" }
31040
- ];
31041
- },
31042
- renderDOM({ htmlAttributes }) {
31043
- const merged = Attribute.mergeAttributes(this.options.htmlAttributes, htmlAttributes);
31044
- const { value, ...rest } = merged || {};
31045
- if (value === "0") {
31046
- return ["span", rest, 0];
31047
- }
31048
- return ["em", rest, 0];
31049
- },
31050
- addCommands() {
31051
- const { setItalic, unsetItalic, toggleItalic } = createCascadeToggleCommands({
31052
- markName: this.name,
31053
- negationAttrs: { value: "0" }
31054
- });
30750
+ addHelpers() {
31055
30751
  return {
31056
30752
  /**
31057
- * Apply italic formatting
31058
- * @category Command
30753
+ * Get all available linked styles
30754
+ * @category Helper
30755
+ * @returns {Array} Array of linked style objects
31059
30756
  * @example
31060
- * editor.commands.setItalic()
30757
+ * const styles = editor.helpers.linkedStyles.getStyles();
30758
+ * // Returns all styles from the Word document
31061
30759
  */
31062
- setItalic,
30760
+ getStyles: () => {
30761
+ const styles = LinkedStylesPluginKey.getState(this.editor.state)?.styles || [];
30762
+ return styles;
30763
+ },
31063
30764
  /**
31064
- * Remove italic formatting
31065
- * @category Command
30765
+ * Get a specific style by ID
30766
+ * @category Helper
30767
+ * @param {string} styleId - The style ID to find
30768
+ * @returns {Object} The style object or undefined
31066
30769
  * @example
31067
- * editor.commands.unsetItalic()
30770
+ * const headingStyle = editor.helpers.linkedStyles.getStyleById('Heading1');
31068
30771
  */
31069
- unsetItalic,
30772
+ getStyleById: (styleId) => {
30773
+ const styles = this.editor.helpers[this.name].getStyles();
30774
+ return styles.find((s2) => s2.id === styleId);
30775
+ },
31070
30776
  /**
31071
- * Toggle italic formatting
31072
- * @category Command
30777
+ * Get the CSS string for a style
30778
+ * @category Helper
30779
+ * @param {string} styleId - The style ID
30780
+ * @returns {string} CSS style string
31073
30781
  * @example
31074
- * editor.commands.toggleItalic()
30782
+ * const css = editor.helpers.linkedStyles.getLinkedStyleString('Heading1');
30783
+ * // Returns: "font-size: 16pt; font-weight: bold; color: #2E74B5"
30784
+ * @private
31075
30785
  */
31076
- toggleItalic
31077
- };
31078
- },
31079
- addShortcuts() {
31080
- return {
31081
- "Mod-i": () => this.editor.commands.toggleItalic(),
31082
- "Mod-I": () => this.editor.commands.toggleItalic()
30786
+ getLinkedStyleString: (styleId) => {
30787
+ const styles = this.editor.helpers.linkedStyles.getStyles();
30788
+ const style = styles.find((s2) => s2.id === styleId);
30789
+ if (!style) return "";
30790
+ return generateLinkedStyleString(style);
30791
+ }
31083
30792
  };
31084
30793
  }
31085
30794
  });
@@ -37667,13 +37376,11 @@ const getRichTextExtensions = () => {
37667
37376
  History,
37668
37377
  Heading,
37669
37378
  Italic,
37670
- LineHeight,
37671
37379
  Link,
37672
37380
  Paragraph,
37673
37381
  Strike,
37674
37382
  Text,
37675
37383
  TextAlign,
37676
- TextIndent,
37677
37384
  TextStyle,
37678
37385
  Underline,
37679
37386
  Placeholder,
@@ -37710,7 +37417,6 @@ const getStarterExtensions = () => {
37710
37417
  History,
37711
37418
  Heading,
37712
37419
  Italic,
37713
- LineHeight,
37714
37420
  Link,
37715
37421
  Paragraph,
37716
37422
  LineBreak,
@@ -37722,7 +37428,6 @@ const getStarterExtensions = () => {
37722
37428
  TableOfContents,
37723
37429
  Text,
37724
37430
  TextAlign,
37725
- TextIndent,
37726
37431
  TextStyle,
37727
37432
  Underline,
37728
37433
  FormatCommands,