roosterjs 9.54.0 → 9.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/rooster.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // Type definitions for roosterjs (Version 9.54.0)
1
+ // Type definitions for roosterjs (Version 9.55.0)
2
2
  // Generated by dts tool from roosterjs
3
3
  // Project: https://github.com/Microsoft/roosterjs
4
4
 
@@ -7872,7 +7872,7 @@ function normalizeFontFamily(fontFamily: string): string;
7872
7872
  * @param allowedCustomPasteType Allowed custom content type when paste besides text/plain, text/html and images
7873
7873
  Only text types are supported, and do not add "text/" prefix to the type values
7874
7874
  */
7875
- function extractClipboardItems(items: DataTransferItem[], allowedCustomPasteType?: string[]): Promise<ClipboardData>;
7875
+ function extractClipboardItems(items: DataTransferItem[], allowedCustomPasteType?: string[], isPasteNative?: boolean): Promise<ClipboardData>;
7876
7876
 
7877
7877
  /**
7878
7878
  * Gets the cached event data by cache key from event object if there is already one.
@@ -8109,6 +8109,13 @@ function setSelection(group: ReadonlyContentModelBlockGroup, start?: ReadonlySel
8109
8109
  */
8110
8110
  function cloneModel(model: ReadonlyContentModelDocument, options?: CloneModelOptions): ContentModelDocument;
8111
8111
 
8112
+ /**
8113
+ * Clone a content model for paste operations, ensuring that cached elements are handled appropriately.
8114
+ * @param model The content model to clone
8115
+ * @returns A cloned content model suitable for paste operations
8116
+ */
8117
+ function cloneModelForPaste(model: ReadonlyContentModelDocument): ContentModelDocument;
8118
+
8112
8119
  /**
8113
8120
  * Merge source model into target mode
8114
8121
  * @param target Target Content Model that will merge content into
@@ -11043,6 +11050,11 @@ interface MarkdownPasteOptions {
11043
11050
  * @default false
11044
11051
  */
11045
11052
  autoConversion: boolean;
11053
+ /**
11054
+ * When true, the plugin will undo the markdown conversion when the user undoes the action.
11055
+ * @default false
11056
+ */
11057
+ undoConversion: boolean;
11046
11058
  }
11047
11059
 
11048
11060
  }
package/dist/rooster.js CHANGED
@@ -6752,7 +6752,7 @@ var formatSegmentWithContentModel_1 = __webpack_require__(/*! ../utils/formatSeg
6752
6752
  */
6753
6753
  function changeCapitalization(editor, capitalization, language) {
6754
6754
  editor.focus();
6755
- (0, formatSegmentWithContentModel_1.formatSegmentWithContentModel)(editor, 'changeCapitalization', function (_, __, segment) {
6755
+ (0, formatSegmentWithContentModel_1.formatSegmentWithContentModel)(editor, 'changeCapitalization', function (_, __, segment, paragraph) {
6756
6756
  if ((segment === null || segment === void 0 ? void 0 : segment.segmentType) == 'Text') {
6757
6757
  switch (capitalization) {
6758
6758
  case 'lowerCase':
@@ -6763,7 +6763,13 @@ function changeCapitalization(editor, capitalization, language) {
6763
6763
  break;
6764
6764
  case 'capitalize':
6765
6765
  var wordArray = segment.text.toLocaleLowerCase(language).split(' ');
6766
- for (var i = 0; i < wordArray.length; i++) {
6766
+ // When a collapsed selection is in the middle of a word, the word is split
6767
+ // into multiple text segments around the selection marker. In that case the
6768
+ // first segment of the word continues from a previous segment, so its first
6769
+ // word must not be capitalized to avoid results like "HeLlo" for "he|llo".
6770
+ var precedingChar = getPrecedingCharacter(paragraph, segment);
6771
+ var startIndex = precedingChar && precedingChar != ' ' ? 1 : 0;
6772
+ for (var i = startIndex; i < wordArray.length; i++) {
6767
6773
  wordArray[i] =
6768
6774
  wordArray[i].charAt(0).toLocaleUpperCase(language) +
6769
6775
  wordArray[i].slice(1);
@@ -6787,6 +6793,30 @@ function changeCapitalization(editor, capitalization, language) {
6787
6793
  });
6788
6794
  }
6789
6795
  exports.changeCapitalization = changeCapitalization;
6796
+ /**
6797
+ * Get the character immediately preceding the given segment within its paragraph, skipping
6798
+ * selection markers (which carry no text). Returns an empty string if there is no preceding
6799
+ * text character (e.g. the segment starts the paragraph or follows a non-text segment).
6800
+ */
6801
+ function getPrecedingCharacter(paragraph, segment) {
6802
+ if (!paragraph) {
6803
+ return '';
6804
+ }
6805
+ var index = paragraph.segments.indexOf(segment);
6806
+ for (var i = index - 1; i >= 0; i--) {
6807
+ var previous = paragraph.segments[i];
6808
+ if (previous.segmentType == 'SelectionMarker') {
6809
+ continue;
6810
+ }
6811
+ else if (previous.segmentType == 'Text' && previous.text.length > 0) {
6812
+ return previous.text[previous.text.length - 1];
6813
+ }
6814
+ else {
6815
+ break;
6816
+ }
6817
+ }
6818
+ return '';
6819
+ }
6790
6820
 
6791
6821
 
6792
6822
  /***/ },
@@ -9349,21 +9379,11 @@ exports.generatePasteOptionFromPlugins = generatePasteOptionFromPlugins;
9349
9379
  "use strict";
9350
9380
 
9351
9381
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9352
- exports.mergePasteContent = exports.cloneModelForPaste = void 0;
9382
+ exports.mergePasteContent = void 0;
9353
9383
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
9354
9384
  var createDomToModelContextForSanitizing_1 = __webpack_require__(/*! ../createModelFromHtml/createDomToModelContextForSanitizing */ "./packages/roosterjs-content-model-core/lib/command/createModelFromHtml/createDomToModelContextForSanitizing.ts");
9355
9385
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
9356
9386
  var BlackColor = 'rgb(0,0,0)';
9357
- var CloneOption = {
9358
- includeCachedElement: function (node, type) { return (type == 'cache' ? undefined : node); },
9359
- };
9360
- /**
9361
- * @internal
9362
- */
9363
- function cloneModelForPaste(model) {
9364
- return (0, roosterjs_content_model_dom_1.cloneModel)(model, CloneOption);
9365
- }
9366
- exports.cloneModelForPaste = cloneModelForPaste;
9367
9387
  /**
9368
9388
  * @internal
9369
9389
  */
@@ -9371,7 +9391,7 @@ function mergePasteContent(editor, eventResult, isFirstPaste) {
9371
9391
  var fragment = eventResult.fragment, domToModelOption = eventResult.domToModelOption, customizedMerge = eventResult.customizedMerge, pasteType = eventResult.pasteType, clipboardData = eventResult.clipboardData, containsBlockElements = eventResult.containsBlockElements;
9372
9392
  editor.formatContentModel(function (model, context) {
9373
9393
  if (!isFirstPaste && clipboardData.modelBeforePaste) {
9374
- var clonedModel = cloneModelForPaste(clipboardData.modelBeforePaste);
9394
+ var clonedModel = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(clipboardData.modelBeforePaste);
9375
9395
  model.blocks = clonedModel.blocks;
9376
9396
  }
9377
9397
  var domToModelContext = (0, createDomToModelContextForSanitizing_1.createDomToModelContextForSanitizing)(editor.getDocument(), undefined /*defaultFormat*/, editor.getEnvironment().domToModelSettings.customized, domToModelOption, editor.getDOMHelper());
@@ -9450,10 +9470,11 @@ function getLastSegmentFormat(pasteModel) {
9450
9470
  Object.defineProperty(exports, "__esModule", ({ value: true }));
9451
9471
  exports.paste = void 0;
9452
9472
  var cleanHtmlComments_1 = __webpack_require__(/*! ./cleanHtmlComments */ "./packages/roosterjs-content-model-core/lib/command/paste/cleanHtmlComments.ts");
9453
- var mergePasteContent_1 = __webpack_require__(/*! ./mergePasteContent */ "./packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts");
9473
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
9454
9474
  var convertInlineCss_1 = __webpack_require__(/*! ../createModelFromHtml/convertInlineCss */ "./packages/roosterjs-content-model-core/lib/command/createModelFromHtml/convertInlineCss.ts");
9455
9475
  var createPasteFragment_1 = __webpack_require__(/*! ./createPasteFragment */ "./packages/roosterjs-content-model-core/lib/command/paste/createPasteFragment.ts");
9456
9476
  var generatePasteOptionFromPlugins_1 = __webpack_require__(/*! ./generatePasteOptionFromPlugins */ "./packages/roosterjs-content-model-core/lib/command/paste/generatePasteOptionFromPlugins.ts");
9477
+ var mergePasteContent_1 = __webpack_require__(/*! ./mergePasteContent */ "./packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts");
9457
9478
  var retrieveHtmlInfo_1 = __webpack_require__(/*! ./retrieveHtmlInfo */ "./packages/roosterjs-content-model-core/lib/command/paste/retrieveHtmlInfo.ts");
9458
9479
  /**
9459
9480
  * Paste into editor using a clipboardData object
@@ -9469,7 +9490,7 @@ function paste(editor, clipboardData, pasteTypeOrGetter) {
9469
9490
  if (!clipboardData.modelBeforePaste) {
9470
9491
  isFirstPaste = true;
9471
9492
  editor.formatContentModel(function (model) {
9472
- clipboardData.modelBeforePaste = (0, mergePasteContent_1.cloneModelForPaste)(model);
9493
+ clipboardData.modelBeforePaste = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(model);
9473
9494
  return false;
9474
9495
  });
9475
9496
  }
@@ -12754,7 +12775,7 @@ var CopyPastePlugin = /** @class */ (function () {
12754
12775
  var dataTransfer = event.clipboardData;
12755
12776
  if (shouldPreventDefaultPaste(dataTransfer, editor_1)) {
12756
12777
  event.preventDefault();
12757
- (0, roosterjs_content_model_dom_1.extractClipboardItems)((0, roosterjs_content_model_dom_1.toArray)(dataTransfer.items), _this.state.allowedCustomPasteType).then(function (clipboardData) {
12778
+ (0, roosterjs_content_model_dom_1.extractClipboardItems)((0, roosterjs_content_model_dom_1.toArray)(dataTransfer.items), _this.state.allowedCustomPasteType, true /* isPasteNative */).then(function (clipboardData) {
12758
12779
  if (!editor_1.isDisposed()) {
12759
12780
  (0, paste_1.paste)(editor_1, clipboardData, _this.state.defaultPasteType);
12760
12781
  }
@@ -19774,7 +19795,7 @@ var ContentHandlers = (_a = {},
19774
19795
  * @param allowedCustomPasteType Allowed custom content type when paste besides text/plain, text/html and images
19775
19796
  Only text types are supported, and do not add "text/" prefix to the type values
19776
19797
  */
19777
- function extractClipboardItems(items, allowedCustomPasteType) {
19798
+ function extractClipboardItems(items, allowedCustomPasteType, isPasteNative) {
19778
19799
  var data = {
19779
19800
  types: [],
19780
19801
  text: '',
@@ -19782,7 +19803,7 @@ function extractClipboardItems(items, allowedCustomPasteType) {
19782
19803
  files: [],
19783
19804
  rawHtml: null,
19784
19805
  customValues: {},
19785
- pasteNativeEvent: true,
19806
+ pasteNativeEvent: !!isPasteNative,
19786
19807
  };
19787
19808
  return Promise.all((items || []).map(function (item) {
19788
19809
  var type = item.type;
@@ -20723,6 +20744,7 @@ function pruneUnselectedModel(model) {
20723
20744
  exports.pruneUnselectedModel = pruneUnselectedModel;
20724
20745
  function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20725
20746
  var e_1, _a, _b;
20747
+ var _c;
20726
20748
  for (var index = model.blocks.length - 1; index >= 0; index--) {
20727
20749
  var block = model.blocks[index];
20728
20750
  switch (block.blockType) {
@@ -20746,15 +20768,17 @@ function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20746
20768
  case 'Paragraph':
20747
20769
  var newSegments = [];
20748
20770
  try {
20749
- for (var _c = (e_1 = void 0, (0, tslib_1.__values)(block.segments)), _d = _c.next(); !_d.done; _d = _c.next()) {
20750
- var segment = _d.value;
20771
+ for (var _d = (e_1 = void 0, (0, tslib_1.__values)(block.segments)), _e = _d.next(); !_e.done; _e = _d.next()) {
20772
+ var segment = _e.value;
20773
+ var isSelectedOrUndeletable = segment.isSelected || ((_c = segment.link) === null || _c === void 0 ? void 0 : _c.format.undeletable);
20751
20774
  if (segment.segmentType == 'General') {
20752
20775
  pruneUnselectedModel(segment);
20753
- if (segment.blocks.length > 0 || segment.isSelected) {
20776
+ if (segment.blocks.length > 0 || isSelectedOrUndeletable) {
20754
20777
  newSegments.push(segment);
20755
20778
  }
20756
20779
  }
20757
- else if (segment.isSelected && segment.segmentType != 'SelectionMarker') {
20780
+ else if (isSelectedOrUndeletable &&
20781
+ segment.segmentType != 'SelectionMarker') {
20758
20782
  newSegments.push(segment);
20759
20783
  }
20760
20784
  }
@@ -20762,7 +20786,7 @@ function pruneUnselectedModelInternal(model, isSelectionAfterElement) {
20762
20786
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
20763
20787
  finally {
20764
20788
  try {
20765
- if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
20789
+ if (_e && !_e.done && (_a = _d.return)) _a.call(_d);
20766
20790
  }
20767
20791
  finally { if (e_1) throw e_1.error; }
20768
20792
  }
@@ -23735,8 +23759,8 @@ exports.shouldSetValue = shouldSetValue;
23735
23759
  Object.defineProperty(exports, "__esModule", ({ value: true }));
23736
23760
  exports.createImage = exports.createText = exports.createTableCell = exports.createTable = exports.createSelectionMarker = exports.createParagraph = exports.createFormatContainer = exports.createListItem = exports.createBr = exports.isLinkUndeletable = exports.setLinkUndeletable = exports.scrollRectIntoView = exports.normalizeRect = exports.isWhiteSpacePreserved = exports.reuseCachedElement = exports.findClosestBlockEntityContainer = exports.isBlockEntityContainer = exports.isEntityDelimiter = exports.addDelimiters = exports.generateEntityClassNames = exports.parseEntityFormat = exports.getAllEntityWrappers = exports.findClosestEntityWrapper = exports.isEntityElement = exports.unwrap = exports.wrap = exports.wrapAllChildNodes = exports.moveChildNodes = exports.toArray = exports.getSafeIdSelector = exports.getObjectKeys = exports.isElementOfType = exports.isNodeOfType = exports.hasMetadata = exports.getMetadata = exports.updateMetadata = exports.buildSelectionMarker = exports.isBlockElement = exports.areSameFormats = exports.parseFormat = exports.getRegularSelectionOffsets = exports.formatContainerProcessor = exports.tableProcessor = exports.entityProcessor = exports.processChildNode = exports.handleRegularSelection = exports.childProcessor = exports.contentModelToText = exports.contentModelToDom = exports.domToContentModel = void 0;
23737
23761
  exports.trimModelForSelection = exports.getDOMInsertPointRect = exports.getSelectionRootNode = exports.isBold = exports.createModelToDomConfig = exports.createModelToDomContextWithConfig = exports.createModelToDomContext = exports.createDomToModelConfig = exports.createDomToModelContextWithConfig = exports.createDomToModelContext = exports.defaultGenerateColorKey = exports.parseColor = exports.setColor = exports.getColor = exports.DeprecatedColors = exports.BorderKeys = exports.parseValueWithUnit = exports.getAutoListStyleType = exports.getOrderedListNumberStr = exports.ParagraphFormats = exports.ListFormatsToMove = exports.ListFormatsToKeep = exports.ListFormats = exports.copyFormat = exports.setParagraphNotImplicit = exports.normalizeSegmentFormat = exports.mergeTextSegments = exports.normalizeSingleSegment = exports.isEmpty = exports.addSegment = exports.unwrapBlock = exports.isGeneralSegment = exports.normalizeContentModel = exports.normalizeParagraph = exports.addTextSegment = exports.addLink = exports.addCode = exports.addBlock = exports.mutateSegment = exports.mutateSegments = exports.mutateBlock = exports.createTableRow = exports.createEmptyModel = exports.createListLevel = exports.createDivider = exports.createEntity = exports.createGeneralBlock = exports.createGeneralSegment = exports.createParagraphDecorator = exports.createContentModelDocument = void 0;
23738
- exports.runEditSteps = exports.getClosestAncestorBlockGroupIndex = exports.getSegmentTextFormat = exports.getListStyleTypeFromString = exports.retrieveModelFormatState = exports.setTableCellBackgroundColor = exports.MIN_ALLOWED_TABLE_CELL_HEIGHT = exports.MIN_ALLOWED_TABLE_CELL_WIDTH = exports.normalizeTable = exports.setFirstColumnFormatBorders = exports.applyTableFormat = exports.deleteBlock = exports.deleteSegment = exports.deleteSelection = exports.mergeModel = exports.cloneModel = exports.setSelection = exports.hasSelectionInBlockGroup = exports.hasSelectionInSegment = exports.hasSelectionInBlock = exports.getSelectedCells = exports.getSelectedSegmentsAndParagraphs = exports.getSelectedSegments = exports.getSelectedParagraphs = exports.getOperationalBlocks = exports.getFirstSelectedTable = exports.getFirstSelectedListItem = exports.iterateSelections = exports.isBlockGroupOfType = exports.getRangesByText = exports.getImageState = exports.setImageState = exports.getParagraphMarker = exports.setParagraphMarker = exports.cacheGetEventData = exports.extractClipboardItems = exports.normalizeFontFamily = exports.transformColor = exports.retrieveDocumentMetadata = exports.readFile = exports.parseTableCells = exports.normalizeText = exports.isSpace = exports.isPunctuation = exports.extractBorderValues = exports.combineBorderValue = exports.getNodePositionFromEvent = exports.isCursorMovingKey = exports.isModifierKey = exports.isCharacterValue = void 0;
23739
- exports.EmptySegmentFormat = exports.UnorderedListStyleMap = exports.OrderedListStyleMap = exports.TableBorderFormat = exports.NumberingListType = exports.BulletListType = exports.ChangeSource = exports.ListMetadataDefinition = exports.getListMetadata = exports.updateListMetadata = exports.getTableMetadata = exports.updateTableMetadata = exports.getTableCellMetadata = exports.updateTableCellMetadata = exports.getImageMetadata = exports.updateImageMetadata = void 0;
23762
+ exports.getClosestAncestorBlockGroupIndex = exports.getSegmentTextFormat = exports.getListStyleTypeFromString = exports.retrieveModelFormatState = exports.setTableCellBackgroundColor = exports.MIN_ALLOWED_TABLE_CELL_HEIGHT = exports.MIN_ALLOWED_TABLE_CELL_WIDTH = exports.normalizeTable = exports.setFirstColumnFormatBorders = exports.applyTableFormat = exports.deleteBlock = exports.deleteSegment = exports.deleteSelection = exports.mergeModel = exports.cloneModelForPaste = exports.cloneModel = exports.setSelection = exports.hasSelectionInBlockGroup = exports.hasSelectionInSegment = exports.hasSelectionInBlock = exports.getSelectedCells = exports.getSelectedSegmentsAndParagraphs = exports.getSelectedSegments = exports.getSelectedParagraphs = exports.getOperationalBlocks = exports.getFirstSelectedTable = exports.getFirstSelectedListItem = exports.iterateSelections = exports.isBlockGroupOfType = exports.getRangesByText = exports.getImageState = exports.setImageState = exports.getParagraphMarker = exports.setParagraphMarker = exports.cacheGetEventData = exports.extractClipboardItems = exports.normalizeFontFamily = exports.transformColor = exports.retrieveDocumentMetadata = exports.readFile = exports.parseTableCells = exports.normalizeText = exports.isSpace = exports.isPunctuation = exports.extractBorderValues = exports.combineBorderValue = exports.getNodePositionFromEvent = exports.isCursorMovingKey = exports.isModifierKey = exports.isCharacterValue = void 0;
23763
+ exports.EmptySegmentFormat = exports.UnorderedListStyleMap = exports.OrderedListStyleMap = exports.TableBorderFormat = exports.NumberingListType = exports.BulletListType = exports.ChangeSource = exports.ListMetadataDefinition = exports.getListMetadata = exports.updateListMetadata = exports.getTableMetadata = exports.updateTableMetadata = exports.getTableCellMetadata = exports.updateTableCellMetadata = exports.getImageMetadata = exports.updateImageMetadata = exports.runEditSteps = void 0;
23740
23764
  var domToContentModel_1 = __webpack_require__(/*! ./domToModel/domToContentModel */ "./packages/roosterjs-content-model-dom/lib/domToModel/domToContentModel.ts");
23741
23765
  Object.defineProperty(exports, "domToContentModel", ({ enumerable: true, get: function () { return domToContentModel_1.domToContentModel; } }));
23742
23766
  var contentModelToDom_1 = __webpack_require__(/*! ./modelToDom/contentModelToDom */ "./packages/roosterjs-content-model-dom/lib/modelToDom/contentModelToDom.ts");
@@ -23967,6 +23991,7 @@ var setSelection_1 = __webpack_require__(/*! ./modelApi/selection/setSelection *
23967
23991
  Object.defineProperty(exports, "setSelection", ({ enumerable: true, get: function () { return setSelection_1.setSelection; } }));
23968
23992
  var cloneModel_1 = __webpack_require__(/*! ./modelApi/editing/cloneModel */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/cloneModel.ts");
23969
23993
  Object.defineProperty(exports, "cloneModel", ({ enumerable: true, get: function () { return cloneModel_1.cloneModel; } }));
23994
+ Object.defineProperty(exports, "cloneModelForPaste", ({ enumerable: true, get: function () { return cloneModel_1.cloneModelForPaste; } }));
23970
23995
  var mergeModel_1 = __webpack_require__(/*! ./modelApi/editing/mergeModel */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/mergeModel.ts");
23971
23996
  Object.defineProperty(exports, "mergeModel", ({ enumerable: true, get: function () { return mergeModel_1.mergeModel; } }));
23972
23997
  var deleteSelection_1 = __webpack_require__(/*! ./modelApi/editing/deleteSelection */ "./packages/roosterjs-content-model-dom/lib/modelApi/editing/deleteSelection.ts");
@@ -24796,7 +24821,34 @@ exports.normalizeAllSegments = normalizeAllSegments;
24796
24821
  function normalizeSingleSegment(paragraph, segment, ignoreTrailingSpaces) {
24797
24822
  if (ignoreTrailingSpaces === void 0) { ignoreTrailingSpaces = false; }
24798
24823
  var context = resetNormalizeSegmentContext();
24824
+ var index = paragraph.segments.indexOf(segment);
24799
24825
  context.ignoreTrailingSpaces = ignoreTrailingSpaces;
24826
+ // Search backward for the nearest non-empty text segment (skipping SelectionMarkers and empty text),
24827
+ // to determine whether leading spaces of the current segment should be preserved.
24828
+ // If the previous text doesn't end with a space, keep the leading space to maintain word separation.
24829
+ for (var i = index - 1; i >= 0; i--) {
24830
+ var s = paragraph.segments[i];
24831
+ if (s.segmentType == 'Text') {
24832
+ if (s.text.length > 0) {
24833
+ if (s.text.substr(-1) != SPACE) {
24834
+ context.ignoreLeadingSpaces = false;
24835
+ }
24836
+ break;
24837
+ }
24838
+ }
24839
+ else if (s.segmentType == 'Image') {
24840
+ // If the previous segment is an image, we should keep the leading space of the current segment to maintain word separation.
24841
+ context.ignoreLeadingSpaces = false;
24842
+ break;
24843
+ }
24844
+ else if (s.segmentType == 'Br') {
24845
+ // If the previous segment is a line break, we should ignore the leading space of the current segment.
24846
+ context.ignoreLeadingSpaces = true;
24847
+ }
24848
+ else if (s.segmentType != 'SelectionMarker') {
24849
+ break;
24850
+ }
24851
+ }
24800
24852
  normalizeSegment(paragraph, segment, context);
24801
24853
  }
24802
24854
  exports.normalizeSingleSegment = normalizeSingleSegment;
@@ -24837,6 +24889,10 @@ function normalizeSegment(paragraph, segment, context) {
24837
24889
  context.ignoreLeadingSpaces = false;
24838
24890
  break;
24839
24891
  case 'Text':
24892
+ // Skip empty text segments to avoid affecting normalization context
24893
+ if (segment.text.length == 0) {
24894
+ break;
24895
+ }
24840
24896
  context.textSegments.push(segment);
24841
24897
  context.lastInlineSegment = segment;
24842
24898
  context.lastTextSegment = segment;
@@ -24844,9 +24900,9 @@ function normalizeSegment(paragraph, segment, context) {
24844
24900
  var last = segment.text.substr(-1);
24845
24901
  if (!(0, hasSpacesOnly_1.hasSpacesOnly)(segment.text)) {
24846
24902
  if (first == SPACE) {
24847
- // 1. Multiple leading space => single &nbsp; or empty (depends on if previous segment ends with space)
24903
+ // 1. Multiple leading space => single space or empty (depends on if previous segment ends with space)
24848
24904
  (0, mutate_1.mutateSegment)(paragraph, segment, function (textSegment) {
24849
- textSegment.text = textSegment.text.replace(LEADING_SPACE_REGEX, context.ignoreLeadingSpaces ? '' : NONE_BREAK_SPACE);
24905
+ textSegment.text = textSegment.text.replace(LEADING_SPACE_REGEX, context.ignoreLeadingSpaces ? '' : SPACE);
24850
24906
  });
24851
24907
  }
24852
24908
  if (last == SPACE) {
@@ -25945,8 +26001,20 @@ function getBorderStyleFromColor(color) {
25945
26001
  "use strict";
25946
26002
 
25947
26003
  Object.defineProperty(exports, "__esModule", ({ value: true }));
25948
- exports.cloneModel = void 0;
26004
+ exports.cloneModel = exports.cloneModelForPaste = void 0;
25949
26005
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
26006
+ var CloneOption = {
26007
+ includeCachedElement: function (node, type) { return (type == 'cache' ? undefined : node); },
26008
+ };
26009
+ /**
26010
+ * Clone a content model for paste operations, ensuring that cached elements are handled appropriately.
26011
+ * @param model The content model to clone
26012
+ * @returns A cloned content model suitable for paste operations
26013
+ */
26014
+ function cloneModelForPaste(model) {
26015
+ return cloneModel(model, CloneOption);
26016
+ }
26017
+ exports.cloneModelForPaste = cloneModelForPaste;
25950
26018
  /**
25951
26019
  * Clone a content model
25952
26020
  * @param model The content model to clone
@@ -32020,11 +32088,12 @@ exports.modelProcessor = modelProcessor;
32020
32088
 
32021
32089
  Object.defineProperty(exports, "__esModule", ({ value: true }));
32022
32090
  exports.MarkdownPastePlugin = void 0;
32023
- var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
32024
32091
  var convertMarkdownToContentModel_1 = __webpack_require__(/*! ../markdownToModel/convertMarkdownToContentModel */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts");
32025
32092
  var isPastedContentMarkdown_1 = __webpack_require__(/*! ../publicApi/isPastedContentMarkdown */ "./packages/roosterjs-content-model-markdown/lib/publicApi/isPastedContentMarkdown.ts");
32093
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
32026
32094
  var DefaultOptions = {
32027
32095
  autoConversion: false,
32096
+ undoConversion: false,
32028
32097
  };
32029
32098
  /**
32030
32099
  * Markdown paste plugin. Handles the BeforePaste event and, when the pasted content
@@ -32070,12 +32139,37 @@ var MarkdownPastePlugin = /** @class */ (function () {
32070
32139
  * @param event The event to handle:
32071
32140
  */
32072
32141
  MarkdownPastePlugin.prototype.onPluginEvent = function (event) {
32073
- if (!this.editor || event.eventType != 'beforePaste') {
32142
+ if (!this.editor) {
32074
32143
  return;
32075
32144
  }
32076
- var shouldConvert = event.pasteType === 'asMarkdown' || this.options.autoConversion;
32077
- if (shouldConvert && (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, event.clipboardData)) {
32078
- convertPastedTextToMarkdown(this.editor, event.fragment, event.clipboardData.text);
32145
+ if (event.eventType === 'contentChanged' &&
32146
+ event.source === roosterjs_content_model_dom_1.ChangeSource.Paste &&
32147
+ this.options.autoConversion &&
32148
+ event.data) {
32149
+ var clipboardData = event.data;
32150
+ var shouldConvert = clipboardData && clipboardData.pasteNativeEvent && this.options.autoConversion;
32151
+ if (shouldConvert &&
32152
+ (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, clipboardData) &&
32153
+ clipboardData.modelBeforePaste) {
32154
+ var modelBeforePaste_1 = (0, roosterjs_content_model_dom_1.cloneModelForPaste)(clipboardData.modelBeforePaste);
32155
+ (0, roosterjs_content_model_dom_1.mergeModel)(modelBeforePaste_1, (0, convertMarkdownToContentModel_1.convertMarkdownToContentModel)(clipboardData.text, { emptyLine: 'merge' }));
32156
+ if (this.options.undoConversion) {
32157
+ this.editor.takeSnapshot();
32158
+ }
32159
+ this.editor.formatContentModel(function (model) {
32160
+ model.blocks = modelBeforePaste_1.blocks;
32161
+ return true;
32162
+ }, {
32163
+ apiName: 'MarkdownConversion',
32164
+ scrollCaretIntoView: true,
32165
+ });
32166
+ }
32167
+ }
32168
+ else if (event.eventType === 'beforePaste' && !event.clipboardData.pasteNativeEvent) {
32169
+ var shouldConvert = event.pasteType === 'asMarkdown';
32170
+ if (shouldConvert && (0, isPastedContentMarkdown_1.isPastedContentMarkdown)(this.editor, event.clipboardData)) {
32171
+ convertPastedTextToMarkdown(this.editor, event.fragment, event.clipboardData.text);
32172
+ }
32079
32173
  }
32080
32174
  };
32081
32175
  return MarkdownPastePlugin;
@@ -38018,16 +38112,31 @@ function createImageWrapper(editor, image, options, editInfo, htmlOptions, opera
38018
38112
  croppers = (0, createImageCropper_1.createImageCropper)(doc);
38019
38113
  }
38020
38114
  var wrapper = createWrapper(editor, imageClone, options, editInfo, resizers, rotators, croppers);
38115
+ // Capture the image footprint before attaching the shadow root, since the light-DOM image
38116
+ // stops being rendered once the shadow root takes over.
38117
+ var imageWidth = image.offsetWidth;
38118
+ var imageHeight = image.offsetHeight;
38021
38119
  var imageSpan = (0, roosterjs_content_model_dom_1.wrap)(doc, image, 'span');
38022
- var shadowSpan = createShadowSpan(wrapper, imageSpan);
38120
+ var shadowSpan = createShadowSpan(wrapper, imageSpan, imageWidth, imageHeight);
38023
38121
  return { wrapper: wrapper, shadowSpan: shadowSpan, imageClone: imageClone, resizers: resizers, rotators: rotators, croppers: croppers };
38024
38122
  }
38025
38123
  exports.createImageWrapper = createImageWrapper;
38026
- var createShadowSpan = function (wrapper, imageSpan) {
38124
+ var createShadowSpan = function (wrapper, imageSpan, imageWidth, imageHeight) {
38027
38125
  var shadowRoot = imageSpan.attachShadow({
38028
38126
  mode: 'open',
38029
38127
  });
38030
38128
  imageSpan.id = IMAGE_EDIT_SHADOW_ROOT;
38129
+ // Pin the shadow host to the original image's box so that wrapping the image does not grow the
38130
+ // surrounding line box. Without this, the taller edit wrapper enlarges the line and the browser
38131
+ // scrolls the selection back into view, making the editor jump. The wrapper and handles still
38132
+ // overflow the host visually because overflow is left visible.
38133
+ if (imageWidth > 0 && imageHeight > 0) {
38134
+ imageSpan.style.display = 'inline-block';
38135
+ imageSpan.style.width = imageWidth + "px";
38136
+ imageSpan.style.height = imageHeight + "px";
38137
+ imageSpan.style.verticalAlign = 'bottom';
38138
+ imageSpan.style.overflow = 'visible';
38139
+ }
38031
38140
  shadowRoot.appendChild(wrapper);
38032
38141
  return imageSpan;
38033
38142
  };