roosterjs 9.52.0 → 9.53.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.js CHANGED
@@ -9057,9 +9057,12 @@ function getContentForCopy(editor, isCut, event) {
9057
9057
  rawEvent: event,
9058
9058
  isCut: isCut,
9059
9059
  }).clonedRoot;
9060
+ // Build the text content from the (possibly modified) cloned root DOM tree so that any
9061
+ // changes made by beforeCutCopy event handlers are reflected in the plain text result as well
9062
+ var textModel = (0, roosterjs_content_model_dom_1.domToContentModel)(clonedRoot, (0, roosterjs_content_model_dom_1.createDomToModelContext)());
9060
9063
  return {
9061
9064
  htmlContent: clonedRoot,
9062
- textContent: (0, roosterjs_content_model_dom_1.contentModelToText)(pasteModel),
9065
+ textContent: (0, roosterjs_content_model_dom_1.contentModelToText)(textModel),
9063
9066
  };
9064
9067
  }
9065
9068
  }
@@ -9927,6 +9930,7 @@ var announce = function (core, announceData) {
9927
9930
  var textToAnnounce = formatString(template || text, formatStrings);
9928
9931
  if (!core.lifecycle.announceContainer) {
9929
9932
  core.lifecycle.announceContainer = (0, createAriaLiveElement_1.createAriaLiveElement)(core.physicalRoot.ownerDocument);
9933
+ core.domHelper.appendToRoot(core.lifecycle.announceContainer);
9930
9934
  }
9931
9935
  if (textToAnnounce && core.lifecycle.announceContainer) {
9932
9936
  var announceContainer = core.lifecycle.announceContainer;
@@ -10418,16 +10422,19 @@ var getDOMSelection = function (core) {
10418
10422
  exports.getDOMSelection = getDOMSelection;
10419
10423
  function getNewSelection(core) {
10420
10424
  var _a;
10425
+ var range = core.domHelper.getSelectionRange();
10426
+ if (!range || !core.logicalRoot.contains(range.commonAncestorContainer)) {
10427
+ return null;
10428
+ }
10421
10429
  var selection = (_a = core.logicalRoot.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
10422
- var range = selection && selection.rangeCount > 0 ? selection.getRangeAt(0) : null;
10423
- return selection && range && core.logicalRoot.contains(range.commonAncestorContainer)
10424
- ? {
10425
- type: 'range',
10426
- range: range,
10427
- isReverted: selection.focusNode != range.endContainer ||
10428
- selection.focusOffset != range.endOffset,
10429
- }
10430
- : null;
10430
+ var isReverted = selection
10431
+ ? selection.focusNode != range.endContainer || selection.focusOffset != range.endOffset
10432
+ : false;
10433
+ return {
10434
+ type: 'range',
10435
+ range: range,
10436
+ isReverted: isReverted,
10437
+ };
10431
10438
  }
10432
10439
 
10433
10440
 
@@ -10871,43 +10878,6 @@ var setContentModel = function (core, model, option, onNodeCreated, isInitializi
10871
10878
  exports.setContentModel = setContentModel;
10872
10879
 
10873
10880
 
10874
- /***/ },
10875
-
10876
- /***/ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts"
10877
- /*!**************************************************************************************************!*\
10878
- !*** ./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts ***!
10879
- \**************************************************************************************************/
10880
- (__unused_webpack_module, exports, __webpack_require__) {
10881
-
10882
- "use strict";
10883
-
10884
- Object.defineProperty(exports, "__esModule", ({ value: true }));
10885
- exports.addRangeToSelection = void 0;
10886
- var areSameSelections_1 = __webpack_require__(/*! ../../corePlugin/cache/areSameSelections */ "./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts");
10887
- /**
10888
- * @internal
10889
- */
10890
- function addRangeToSelection(doc, range, isReverted) {
10891
- var _a;
10892
- if (isReverted === void 0) { isReverted = false; }
10893
- var selection = (_a = doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
10894
- if (selection) {
10895
- var currentRange = selection.rangeCount > 0 && selection.getRangeAt(0);
10896
- if (currentRange && (0, areSameSelections_1.areSameRanges)(currentRange, range)) {
10897
- return;
10898
- }
10899
- selection.removeAllRanges();
10900
- if (!isReverted) {
10901
- selection.addRange(range);
10902
- }
10903
- else {
10904
- selection.setBaseAndExtent(range.endContainer, range.endOffset, range.startContainer, range.startOffset);
10905
- }
10906
- }
10907
- }
10908
- exports.addRangeToSelection = addRangeToSelection;
10909
-
10910
-
10911
10881
  /***/ },
10912
10882
 
10913
10883
  /***/ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/findLastedCoInMergedCell.ts"
@@ -11009,7 +10979,6 @@ exports.findTableCellElement = findTableCellElement;
11009
10979
 
11010
10980
  Object.defineProperty(exports, "__esModule", ({ value: true }));
11011
10981
  exports.setDOMSelection = void 0;
11012
- var addRangeToSelection_1 = __webpack_require__(/*! ./addRangeToSelection */ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/addRangeToSelection.ts");
11013
10982
  var areSameSelections_1 = __webpack_require__(/*! ../../corePlugin/cache/areSameSelections */ "./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts");
11014
10983
  var ensureUniqueId_1 = __webpack_require__(/*! ../setEditorStyle/ensureUniqueId */ "./packages/roosterjs-content-model-core/lib/coreApi/setEditorStyle/ensureUniqueId.ts");
11015
10984
  var findLastedCoInMergedCell_1 = __webpack_require__(/*! ./findLastedCoInMergedCell */ "./packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/findLastedCoInMergedCell.ts");
@@ -11035,7 +11004,6 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11035
11004
  // We are applying a new selection, so we don't need to apply cached selection in DOMEventPlugin.
11036
11005
  // Set skipReselectOnFocus to skip this behavior
11037
11006
  var skipReselectOnFocus = core.selection.skipReselectOnFocus;
11038
- var doc = core.physicalRoot.ownerDocument;
11039
11007
  var isDarkMode = core.lifecycle.isDarkMode;
11040
11008
  core.selection.skipReselectOnFocus = true;
11041
11009
  core.api.setEditorStyle(core, DOM_SELECTION_CSS_KEY, null /*cssRule*/);
@@ -11051,7 +11019,7 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11051
11019
  : core.selection.imageSelectionBorderColor;
11052
11020
  core.api.setEditorStyle(core, DOM_SELECTION_CSS_KEY, "outline-style:solid!important; outline-color:" + (imageSelectionColor || DEFAULT_SELECTION_BORDER_COLOR) + "!important;", [(0, roosterjs_content_model_dom_1.getSafeIdSelector)((0, ensureUniqueId_1.ensureUniqueId)(image, IMAGE_ID))]);
11053
11021
  core.api.setEditorStyle(core, HIDE_SELECTION_CSS_KEY, TRANSPARENT_SELECTION_CSS_RULE, [SELECTION_SELECTOR]);
11054
- setRangeSelection(doc, image, false /* collapse */);
11022
+ setRangeSelection(core, image, false /* collapse */);
11055
11023
  break;
11056
11024
  case 'table':
11057
11025
  var table = selection.table, firstColumn = selection.firstColumn, firstRow = selection.firstRow, lastColumn = selection.lastColumn, lastRow = selection.lastRow;
@@ -11088,11 +11056,11 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11088
11056
  (0, toggleCaret_1.toggleCaret)(core, true /* hide */);
11089
11057
  var nodeToSelect = ((_a = firstCell.cell) === null || _a === void 0 ? void 0 : _a.firstElementChild) || firstCell.cell;
11090
11058
  if (nodeToSelect) {
11091
- setRangeSelection(doc, nodeToSelect || undefined, true /* collapse */);
11059
+ setRangeSelection(core, nodeToSelect || undefined, true /* collapse */);
11092
11060
  }
11093
11061
  break;
11094
11062
  case 'range':
11095
- (0, addRangeToSelection_1.addRangeToSelection)(doc, selection.range, selection.isReverted);
11063
+ core.domHelper.setSelectionRange(selection.range, selection.isReverted);
11096
11064
  core.selection.selection = core.domHelper.hasFocus() ? null : selection;
11097
11065
  break;
11098
11066
  default:
@@ -11112,9 +11080,10 @@ var setDOMSelection = function (core, selection, skipSelectionChangedEvent) {
11112
11080
  }
11113
11081
  };
11114
11082
  exports.setDOMSelection = setDOMSelection;
11115
- function setRangeSelection(doc, element, collapse) {
11083
+ function setRangeSelection(core, element, collapse) {
11116
11084
  var _a;
11117
- if (element && doc.contains(element)) {
11085
+ if (element && core.domHelper.isNodeInEditor(element)) {
11086
+ var doc = core.physicalRoot.ownerDocument;
11118
11087
  var range = doc.createRange();
11119
11088
  var isReverted = undefined;
11120
11089
  range.selectNode(element);
@@ -11130,7 +11099,7 @@ function setRangeSelection(doc, element, collapse) {
11130
11099
  selection.focusOffset != range_1.endOffset;
11131
11100
  }
11132
11101
  }
11133
- (0, addRangeToSelection_1.addRangeToSelection)(doc, range, isReverted);
11102
+ core.domHelper.setSelectionRange(range, isReverted);
11134
11103
  }
11135
11104
  }
11136
11105
 
@@ -11317,9 +11286,9 @@ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-mo
11317
11286
  */
11318
11287
  function ensureUniqueId(element, idPrefix) {
11319
11288
  idPrefix = element.id || idPrefix;
11320
- var doc = element.ownerDocument;
11289
+ var root = element.getRootNode();
11321
11290
  var i = 0;
11322
- while (!element.id || doc.querySelectorAll((0, roosterjs_content_model_dom_1.getSafeIdSelector)(element.id)).length > 1) {
11291
+ while (!element.id || root.querySelectorAll((0, roosterjs_content_model_dom_1.getSafeIdSelector)(element.id)).length > 1) {
11323
11292
  element.id = idPrefix + '_' + i++;
11324
11293
  }
11325
11294
  return element.id;
@@ -11352,7 +11321,7 @@ var setEditorStyle = function (core, key, cssRule, subSelectors, maxRuleLength)
11352
11321
  if (!styleElement && cssRule) {
11353
11322
  var doc = core.physicalRoot.ownerDocument;
11354
11323
  styleElement = doc.createElement('style');
11355
- doc.head.appendChild(styleElement);
11324
+ core.domHelper.appendToRoot(styleElement);
11356
11325
  styleElement.dataset.roosterjsStyleKey = key;
11357
11326
  core.lifecycle.styleElements[key] = styleElement;
11358
11327
  }
@@ -11830,12 +11799,13 @@ exports.createParagraphMap = createParagraphMap;
11830
11799
  /*!*****************************************************************************************!*\
11831
11800
  !*** ./packages/roosterjs-content-model-core/lib/corePlugin/cache/areSameSelections.ts ***!
11832
11801
  \*****************************************************************************************/
11833
- (__unused_webpack_module, exports) {
11802
+ (__unused_webpack_module, exports, __webpack_require__) {
11834
11803
 
11835
11804
  "use strict";
11836
11805
 
11837
11806
  Object.defineProperty(exports, "__esModule", ({ value: true }));
11838
- exports.areSameRanges = exports.areSameTableSelections = exports.areSameSelections = void 0;
11807
+ exports.areSameTableSelections = exports.areSameSelections = void 0;
11808
+ var areSameRanges_1 = __webpack_require__(/*! ../../utils/areSameRanges */ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts");
11839
11809
  /**
11840
11810
  * @internal
11841
11811
  * Check if the given selections are the same
@@ -11861,7 +11831,7 @@ function areSameSelections(sel1, sel2) {
11861
11831
  range1.endOffset == end.offset);
11862
11832
  }
11863
11833
  else {
11864
- return areSameRanges(range1, sel2.range);
11834
+ return (0, areSameRanges_1.areSameRanges)(range1, sel2.range);
11865
11835
  }
11866
11836
  }
11867
11837
  else {
@@ -11880,7 +11850,6 @@ var TableSelectionKeys = [
11880
11850
  'firstRow',
11881
11851
  'lastRow',
11882
11852
  ];
11883
- var RangeKeys = ['startContainer', 'endContainer', 'startOffset', 'endOffset'];
11884
11853
  /**
11885
11854
  * @internal
11886
11855
  */
@@ -11888,13 +11857,6 @@ function areSameTableSelections(t1, t2) {
11888
11857
  return areSame(t1, t2, TableSelectionKeys);
11889
11858
  }
11890
11859
  exports.areSameTableSelections = areSameTableSelections;
11891
- /**
11892
- * @internal
11893
- */
11894
- function areSameRanges(r1, r2) {
11895
- return areSame(r1, r2, RangeKeys);
11896
- }
11897
- exports.areSameRanges = areSameRanges;
11898
11860
  function isCacheSelection(sel) {
11899
11861
  return !!sel.start;
11900
11862
  }
@@ -12072,7 +12034,13 @@ var DomIndexerImpl = /** @class */ (function () {
12072
12034
  }
12073
12035
  else {
12074
12036
  var marker1 = this.reconcileNodeSelection(startContainer, startOffset);
12075
- var marker2 = this.reconcileNodeSelection(endContainer, endOffset);
12037
+ // Pass marker1 to the second call so its adjacent-marker cleanup
12038
+ // does not consume the SelectionMarker we just inserted. Without
12039
+ // this guard, when marker1 lands directly next to endContainer's
12040
+ // segment in paragraph.segments (e.g. startOffset == startContainer
12041
+ // text length), the second splice would absorb marker1 and leave
12042
+ // setSelection with a dangling reference. See issue #3341.
12043
+ var marker2 = this.reconcileNodeSelection(endContainer, endOffset, undefined, undefined, marker1);
12076
12044
  if (marker1 && marker2) {
12077
12045
  if (newSelection.isReverted) {
12078
12046
  model.hasRevertedRangeSelection = true;
@@ -12148,10 +12116,10 @@ var DomIndexerImpl = /** @class */ (function () {
12148
12116
  var start = selection.start, end = selection.end;
12149
12117
  return start.node == end.node && start.offset == end.offset;
12150
12118
  };
12151
- DomIndexerImpl.prototype.reconcileNodeSelection = function (node, offset, defaultFormat, selectionMarker) {
12119
+ DomIndexerImpl.prototype.reconcileNodeSelection = function (node, offset, defaultFormat, selectionMarker, preserveMarker) {
12152
12120
  if ((0, roosterjs_content_model_dom_1.isNodeOfType)(node, 'TEXT_NODE')) {
12153
12121
  if (isIndexedSegment(node)) {
12154
- return this.reconcileTextSelection(node, offset, undefined, selectionMarker);
12122
+ return this.reconcileTextSelection(node, offset, undefined, selectionMarker, preserveMarker);
12155
12123
  }
12156
12124
  else if (isIndexedDelimiter(node)) {
12157
12125
  return this.reconcileDelimiterSelection(node, defaultFormat);
@@ -12181,7 +12149,7 @@ var DomIndexerImpl = /** @class */ (function () {
12181
12149
  }
12182
12150
  return marker;
12183
12151
  };
12184
- DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset, selectionMarker) {
12152
+ DomIndexerImpl.prototype.reconcileTextSelection = function (textNode, startOffset, endOffset, selectionMarker, preserveMarker) {
12185
12153
  var _a;
12186
12154
  var _b, _c, _d, _e, _f, _g;
12187
12155
  var _h = textNode.__roosterjsContentModel, paragraph = _h.paragraph, segments = _h.segments;
@@ -12234,11 +12202,13 @@ var DomIndexerImpl = /** @class */ (function () {
12234
12202
  var lastIndex = paragraph.segments.indexOf(last);
12235
12203
  if (firstIndex >= 0 && lastIndex >= 0) {
12236
12204
  while (firstIndex > 0 &&
12237
- paragraph.segments[firstIndex - 1].segmentType == 'SelectionMarker') {
12205
+ paragraph.segments[firstIndex - 1].segmentType == 'SelectionMarker' &&
12206
+ paragraph.segments[firstIndex - 1] !== preserveMarker) {
12238
12207
  firstIndex--;
12239
12208
  }
12240
12209
  while (lastIndex < paragraph.segments.length - 1 &&
12241
- paragraph.segments[lastIndex + 1].segmentType == 'SelectionMarker') {
12210
+ paragraph.segments[lastIndex + 1].segmentType == 'SelectionMarker' &&
12211
+ paragraph.segments[lastIndex + 1] !== preserveMarker) {
12242
12212
  lastIndex++;
12243
12213
  }
12244
12214
  (_a = paragraph.segments).splice.apply(_a, (0, tslib_1.__spreadArray)([firstIndex, lastIndex - firstIndex + 1], (0, tslib_1.__read)(newSegments), false));
@@ -14356,6 +14326,7 @@ var LifecyclePlugin = /** @class */ (function () {
14356
14326
  delete this.state.rewriteFromModel;
14357
14327
  // Initialize the Announce container.
14358
14328
  this.state.announceContainer = (0, createAriaLiveElement_1.createAriaLiveElement)(editor.getDocument());
14329
+ editor.getDOMHelper().appendToRoot(this.state.announceContainer);
14359
14330
  };
14360
14331
  /**
14361
14332
  * Dispose this plugin
@@ -14549,20 +14520,23 @@ var SelectionPlugin = /** @class */ (function () {
14549
14520
  }
14550
14521
  };
14551
14522
  this.onSelectionChange = function () {
14552
- var _a;
14523
+ var _a, _b;
14553
14524
  if (((_a = _this.editor) === null || _a === void 0 ? void 0 : _a.hasFocus()) && !_this.editor.isInShadowEdit()) {
14554
14525
  var newSelection = _this.editor.getDOMSelection();
14526
+ var domHelper = _this.editor.getDOMHelper();
14555
14527
  //If am image selection changed to a wider range due a keyboard event, we should update the selection
14556
- var selection = _this.editor.getDocument().getSelection();
14557
- if (selection && selection.focusNode) {
14558
- var image = (0, isSingleImageInSelection_1.isSingleImageInSelection)(selection);
14528
+ var range = domHelper.getSelectionRange();
14529
+ if (range) {
14530
+ var image = (0, isSingleImageInSelection_1.isSingleImageInSelection)(range);
14559
14531
  if ((newSelection === null || newSelection === void 0 ? void 0 : newSelection.type) == 'image' && !image) {
14560
- var range = selection.getRangeAt(0);
14532
+ var sel = (_b = _this.editor.getDocument().defaultView) === null || _b === void 0 ? void 0 : _b.getSelection();
14533
+ var isReverted = sel
14534
+ ? sel.focusNode != range.endContainer || sel.focusOffset != range.endOffset
14535
+ : false;
14561
14536
  _this.editor.setDOMSelection({
14562
14537
  type: 'range',
14563
14538
  range: range,
14564
- isReverted: selection.focusNode != range.endContainer ||
14565
- selection.focusOffset != range.endOffset,
14539
+ isReverted: isReverted,
14566
14540
  });
14567
14541
  }
14568
14542
  else if ((newSelection === null || newSelection === void 0 ? void 0 : newSelection.type) !== 'image' && image) {
@@ -15951,10 +15925,23 @@ exports.Editor = Editor;
15951
15925
 
15952
15926
  Object.defineProperty(exports, "__esModule", ({ value: true }));
15953
15927
  exports.createDOMHelper = void 0;
15928
+ var areSameRanges_1 = __webpack_require__(/*! ../../utils/areSameRanges */ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts");
15954
15929
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
15930
+ function isSelectionWithComposedRanges(sel) {
15931
+ return 'getComposedRanges' in sel;
15932
+ }
15933
+ function isShadowRoot(node) {
15934
+ return 'host' in node;
15935
+ }
15955
15936
  var DOMHelperImpl = /** @class */ (function () {
15956
15937
  function DOMHelperImpl(contentDiv, options) {
15938
+ var _a;
15957
15939
  this.contentDiv = contentDiv;
15940
+ var rootNode = contentDiv.getRootNode();
15941
+ this.shadowRoot = (options === null || options === void 0 ? void 0 : options.useShadowDom) && isShadowRoot(rootNode) ? rootNode : null;
15942
+ this.doc = contentDiv.ownerDocument;
15943
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
15944
+ this.useComposedRanges = !!(this.shadowRoot && sel && 'getComposedRanges' in sel);
15958
15945
  }
15959
15946
  DOMHelperImpl.prototype.queryElements = function (selector) {
15960
15947
  return (0, roosterjs_content_model_dom_1.toArray)(this.contentDiv.querySelectorAll(selector));
@@ -16016,7 +16003,9 @@ var DOMHelperImpl = /** @class */ (function () {
16016
16003
  return this.contentDiv;
16017
16004
  };
16018
16005
  DOMHelperImpl.prototype.hasFocus = function () {
16019
- var activeElement = this.contentDiv.ownerDocument.activeElement;
16006
+ var activeElement = this.shadowRoot
16007
+ ? this.shadowRoot.activeElement
16008
+ : this.doc.activeElement;
16020
16009
  return !!(activeElement && this.contentDiv.contains(activeElement));
16021
16010
  };
16022
16011
  /**
@@ -16083,6 +16072,51 @@ var DOMHelperImpl = /** @class */ (function () {
16083
16072
  DOMHelperImpl.prototype.getRangesByText = function (text, matchCase, wholeWord) {
16084
16073
  return (0, roosterjs_content_model_dom_1.getRangesByText)(this.contentDiv, text, matchCase, wholeWord, true /*editableOnly*/);
16085
16074
  };
16075
+ DOMHelperImpl.prototype.getSelectionRange = function () {
16076
+ var _a;
16077
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
16078
+ if (!sel) {
16079
+ return null;
16080
+ }
16081
+ if (this.useComposedRanges && this.shadowRoot && isSelectionWithComposedRanges(sel)) {
16082
+ var staticRanges = sel.getComposedRanges({
16083
+ shadowRoots: [this.shadowRoot],
16084
+ });
16085
+ if ((staticRanges === null || staticRanges === void 0 ? void 0 : staticRanges.length) > 0) {
16086
+ var sr = staticRanges[0];
16087
+ var range = this.doc.createRange();
16088
+ range.setStart(sr.startContainer, sr.startOffset);
16089
+ range.setEnd(sr.endContainer, sr.endOffset);
16090
+ return range;
16091
+ }
16092
+ return null;
16093
+ }
16094
+ return sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
16095
+ };
16096
+ DOMHelperImpl.prototype.setSelectionRange = function (range, isReverted) {
16097
+ var _a;
16098
+ if (isReverted === void 0) { isReverted = false; }
16099
+ var sel = (_a = this.doc.defaultView) === null || _a === void 0 ? void 0 : _a.getSelection();
16100
+ var currentRange = this.getSelectionRange();
16101
+ if (!sel || (currentRange && (0, areSameRanges_1.areSameRanges)(range, currentRange))) {
16102
+ return;
16103
+ }
16104
+ var startContainer = range.startContainer, startOffset = range.startOffset, endContainer = range.endContainer, endOffset = range.endOffset;
16105
+ if (!isReverted) {
16106
+ sel.setBaseAndExtent(startContainer, startOffset, endContainer, endOffset);
16107
+ }
16108
+ else {
16109
+ sel.setBaseAndExtent(endContainer, endOffset, startContainer, startOffset);
16110
+ }
16111
+ };
16112
+ DOMHelperImpl.prototype.appendToRoot = function (element) {
16113
+ if (this.shadowRoot) {
16114
+ this.shadowRoot.appendChild(element);
16115
+ }
16116
+ else {
16117
+ this.doc.body.appendChild(element);
16118
+ }
16119
+ };
16086
16120
  return DOMHelperImpl;
16087
16121
  }());
16088
16122
  /**
@@ -16199,7 +16233,10 @@ function createEditorCore(contentDiv, options) {
16199
16233
  corePlugins.lifecycle,
16200
16234
  ], false), environment: createEditorEnvironment(contentDiv, options), darkColorHandler: (0, DarkColorHandlerImpl_1.createDarkColorHandler)(contentDiv, (_b = options.getDarkColor) !== null && _b !== void 0 ? _b : getDarkColorFallback, options.knownColors, options.generateColorKey), trustedHTMLHandler: options.trustedHTMLHandler && !(0, domCreator_1.isDOMCreator)(options.trustedHTMLHandler)
16201
16235
  ? options.trustedHTMLHandler
16202
- : (0, domCreator_1.createTrustedHTMLHandler)(domCreator), domCreator: domCreator, domHelper: (0, DOMHelperImpl_1.createDOMHelper)(contentDiv) }, getPluginState(corePlugins)), { disposeErrorHandler: options.disposeErrorHandler, onFixUpModel: options.onFixUpModel, experimentalFeatures: options.experimentalFeatures ? (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(options.experimentalFeatures), false) : [] });
16236
+ : (0, domCreator_1.createTrustedHTMLHandler)(domCreator), domCreator: domCreator, domHelper: (0, DOMHelperImpl_1.createDOMHelper)(contentDiv, {
16237
+ useShadowDom: !!options.experimentalFeatures &&
16238
+ options.experimentalFeatures.indexOf('ShadowDom') >= 0,
16239
+ }) }, getPluginState(corePlugins)), { disposeErrorHandler: options.disposeErrorHandler, onFixUpModel: options.onFixUpModel, experimentalFeatures: options.experimentalFeatures ? (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(options.experimentalFeatures), false) : [] });
16203
16240
  }
16204
16241
  exports.createEditorCore = createEditorCore;
16205
16242
  function createEditorEnvironment(contentDiv, options) {
@@ -16357,6 +16394,8 @@ var containerSizeFormatParser = function (format, element) {
16357
16394
  if (element.tagName == 'DIV' || element.tagName == 'P') {
16358
16395
  delete format.width;
16359
16396
  delete format.height;
16397
+ delete format.maxHeight;
16398
+ delete format.maxWidth;
16360
16399
  }
16361
16400
  };
16362
16401
  exports.containerSizeFormatParser = containerSizeFormatParser;
@@ -16653,6 +16692,29 @@ var pasteWhiteSpaceFormatParser = function (format, element, context, defaultSty
16653
16692
  exports.pasteWhiteSpaceFormatParser = pasteWhiteSpaceFormatParser;
16654
16693
 
16655
16694
 
16695
+ /***/ },
16696
+
16697
+ /***/ "./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts"
16698
+ /*!**************************************************************************!*\
16699
+ !*** ./packages/roosterjs-content-model-core/lib/utils/areSameRanges.ts ***!
16700
+ \**************************************************************************/
16701
+ (__unused_webpack_module, exports) {
16702
+
16703
+ "use strict";
16704
+
16705
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
16706
+ exports.areSameRanges = void 0;
16707
+ var RangeKeys = ['startContainer', 'endContainer', 'startOffset', 'endOffset'];
16708
+ /**
16709
+ * @internal
16710
+ * Check if two ranges have the same start and end positions.
16711
+ */
16712
+ function areSameRanges(r1, r2) {
16713
+ return RangeKeys.every(function (k) { return r1[k] == r2[k]; });
16714
+ }
16715
+ exports.areSameRanges = areSameRanges;
16716
+
16717
+
16656
16718
  /***/ },
16657
16719
 
16658
16720
  /***/ "./packages/roosterjs-content-model-core/lib/utils/createAriaLiveElement.ts"
@@ -16678,7 +16740,6 @@ function createAriaLiveElement(document) {
16678
16740
  div.style.whiteSpace = 'nowrap';
16679
16741
  div.style.width = '1px';
16680
16742
  div.ariaLive = 'assertive';
16681
- document.body.appendChild(div);
16682
16743
  return div;
16683
16744
  }
16684
16745
  exports.createAriaLiveElement = createAriaLiveElement;
@@ -21169,11 +21230,13 @@ exports.directionFormatHandler = {
21169
21230
  format.direction = dir == 'rtl' ? 'rtl' : 'ltr';
21170
21231
  }
21171
21232
  },
21172
- apply: function (format, element) {
21233
+ apply: function (format, element, context) {
21173
21234
  if (format.direction) {
21174
21235
  element.style.direction = format.direction;
21175
21236
  }
21176
- if (format.direction == 'rtl' && (0, isElementOfType_1.isElementOfType)(element, 'table')) {
21237
+ if (format.direction == 'rtl' &&
21238
+ (0, isElementOfType_1.isElementOfType)(element, 'table') &&
21239
+ context.implicitFormat.direction != 'rtl') {
21177
21240
  element.style.justifySelf = 'flex-end';
21178
21241
  }
21179
21242
  },
@@ -21710,6 +21773,7 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
21710
21773
  exports.borderFormatHandler = void 0;
21711
21774
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
21712
21775
  var borderKeys_1 = __webpack_require__(/*! ../utils/borderKeys */ "./packages/roosterjs-content-model-dom/lib/formatHandlers/utils/borderKeys.ts");
21776
+ var borderValues_1 = __webpack_require__(/*! ../../domUtils/style/borderValues */ "./packages/roosterjs-content-model-dom/lib/domUtils/style/borderValues.ts");
21713
21777
  // This array needs to match BorderKeys array
21714
21778
  var BorderWidthKeys = [
21715
21779
  'borderTopWidth',
@@ -21738,7 +21802,17 @@ exports.borderFormatHandler = {
21738
21802
  width = '0px';
21739
21803
  }
21740
21804
  if (value && width != defaultWidth) {
21741
- format[key] = value == 'none' ? '' : value;
21805
+ var result = value;
21806
+ if (result.includes('initial')) {
21807
+ // Remove 'initial' from the last part (color) of the border value
21808
+ // since browsers ignore it when setting the inline style property
21809
+ var border = (0, borderValues_1.extractBorderValues)(value);
21810
+ if (border.color === 'initial') {
21811
+ border.color = '';
21812
+ }
21813
+ result = (0, borderValues_1.combineBorderValue)(border);
21814
+ }
21815
+ format[key] = result == 'none' ? '' : result;
21742
21816
  }
21743
21817
  });
21744
21818
  var borderRadius = element.style.borderRadius;
@@ -24140,6 +24214,7 @@ var createText_1 = __webpack_require__(/*! ../creators/createText */ "./packages
24140
24214
  var ensureParagraph_1 = __webpack_require__(/*! ./ensureParagraph */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/ensureParagraph.ts");
24141
24215
  var hasSpacesOnly_1 = __webpack_require__(/*! ./hasSpacesOnly */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/hasSpacesOnly.ts");
24142
24216
  var isWhiteSpacePreserved_1 = __webpack_require__(/*! ../../domUtils/isWhiteSpacePreserved */ "./packages/roosterjs-content-model-dom/lib/domUtils/isWhiteSpacePreserved.ts");
24217
+ var stripInvisibleUnicode_1 = __webpack_require__(/*! ./stripInvisibleUnicode */ "./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts");
24143
24218
  /**
24144
24219
  * Add a new text segment to current paragraph
24145
24220
  * @param group Current BlockGroup that the paragraph belong to
@@ -24155,7 +24230,11 @@ function addTextSegment(group, text, context) {
24155
24230
  if (!(0, hasSpacesOnly_1.hasSpacesOnly)(text) ||
24156
24231
  ((_a = paragraph === null || paragraph === void 0 ? void 0 : paragraph.segments.length) !== null && _a !== void 0 ? _a : 0) > 0 ||
24157
24232
  (0, isWhiteSpacePreserved_1.isWhiteSpacePreserved)(paragraph === null || paragraph === void 0 ? void 0 : paragraph.format.whiteSpace)) {
24158
- textModel = (0, createText_1.createText)(text, context.segmentFormat);
24233
+ var filteredText = context.experimentalFeatures &&
24234
+ context.experimentalFeatures.indexOf('FilterInvisibleUnicode') > -1
24235
+ ? (0, stripInvisibleUnicode_1.stripInvisibleUnicode)(text)
24236
+ : text;
24237
+ textModel = (0, createText_1.createText)(filteredText, context.segmentFormat);
24159
24238
  if (context.isInSelection) {
24160
24239
  textModel.isSelected = true;
24161
24240
  }
@@ -24790,6 +24869,34 @@ function normalizeSegmentFormat(format, environment) {
24790
24869
  exports.normalizeSegmentFormat = normalizeSegmentFormat;
24791
24870
 
24792
24871
 
24872
+ /***/ },
24873
+
24874
+ /***/ "./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts"
24875
+ /*!*******************************************************************************************!*\
24876
+ !*** ./packages/roosterjs-content-model-dom/lib/modelApi/common/stripInvisibleUnicode.ts ***!
24877
+ \*******************************************************************************************/
24878
+ (__unused_webpack_module, exports) {
24879
+
24880
+ "use strict";
24881
+
24882
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
24883
+ exports.stripInvisibleUnicode = void 0;
24884
+ // According to https://embracethered.com/blog/posts/2024/hiding-and-finding-text-with-unicode-tags/
24885
+ // there are some invisible unicode characters in the range of U+E0000 to U+EFFFF, which are used for hiding text in HTML.
24886
+ // We need to strip them out before processing the pasted content, otherwise they will be treated as normal text and cause unexpected behavior.
24887
+ var INVISIBLE_UNICODE_REGEX = /[\u{E0000}-\u{EFFFF}]/gu;
24888
+ /**
24889
+ * @internal
24890
+ * Strip invisible unicode characters from the given string
24891
+ * @param value The string to be processed
24892
+ * @returns The string with invisible unicode characters removed
24893
+ */
24894
+ function stripInvisibleUnicode(value) {
24895
+ return value.replace(INVISIBLE_UNICODE_REGEX, '');
24896
+ }
24897
+ exports.stripInvisibleUnicode = stripInvisibleUnicode;
24898
+
24899
+
24793
24900
  /***/ },
24794
24901
 
24795
24902
  /***/ "./packages/roosterjs-content-model-dom/lib/modelApi/common/unwrapBlock.ts"
@@ -25248,16 +25355,25 @@ exports.createParagraphDecorator = createParagraphDecorator;
25248
25355
 
25249
25356
  Object.defineProperty(exports, "__esModule", ({ value: true }));
25250
25357
  exports.createSelectionMarker = void 0;
25251
- var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
25358
+ var EmptySegmentFormat_1 = __webpack_require__(/*! ../../constants/EmptySegmentFormat */ "./packages/roosterjs-content-model-dom/lib/constants/EmptySegmentFormat.ts");
25359
+ var getObjectKeys_1 = __webpack_require__(/*! ../../domUtils/getObjectKeys */ "./packages/roosterjs-content-model-dom/lib/domUtils/getObjectKeys.ts");
25252
25360
  /**
25253
25361
  * Create a ContentModelSelectionMarker model
25254
25362
  * @param format @optional The format of this model
25255
25363
  */
25256
25364
  function createSelectionMarker(format) {
25365
+ var filteredFormat = {};
25366
+ if (format) {
25367
+ (0, getObjectKeys_1.getObjectKeys)(EmptySegmentFormat_1.EmptySegmentFormat).forEach(function (key) {
25368
+ if (key in format) {
25369
+ filteredFormat[key] = format[key];
25370
+ }
25371
+ });
25372
+ }
25257
25373
  return {
25258
25374
  segmentType: 'SelectionMarker',
25259
25375
  isSelected: true,
25260
- format: (0, tslib_1.__assign)({}, format),
25376
+ format: filteredFormat,
25261
25377
  };
25262
25378
  }
25263
25379
  exports.createSelectionMarker = createSelectionMarker;
@@ -29332,14 +29448,16 @@ var handleFormatContainer = function (doc, parent, container, context, refNode)
29332
29448
  (0, applyFormat_1.applyFormat)(containerNode_1, context.formatAppliers.segmentOnBlock, container.format, context);
29333
29449
  (0, applyFormat_1.applyFormat)(containerNode_1, context.formatAppliers.container, container.format, context);
29334
29450
  });
29335
- if (container.tagName == 'pre') {
29336
- (0, stackFormat_1.stackFormat)(context, PreChildFormat, function () {
29451
+ (0, stackFormat_1.stackFormat)(context, container.format.direction ? { direction: container.format.direction } : null, function () {
29452
+ if (container.tagName == 'pre') {
29453
+ (0, stackFormat_1.stackFormat)(context, PreChildFormat, function () {
29454
+ context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29455
+ });
29456
+ }
29457
+ else {
29337
29458
  context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29338
- });
29339
- }
29340
- else {
29341
- context.modelHandlers.blockGroupChildren(doc, containerNode_1, container, context);
29342
- }
29459
+ }
29460
+ });
29343
29461
  element = containerNode_1;
29344
29462
  }
29345
29463
  if (element) {
@@ -29629,7 +29747,9 @@ var handleListItem = function (doc, parent, listItem, context, refNode) {
29629
29747
  // Need to apply listItemElement formats after applying metadata since the list numbers value relies on the result of metadata handling
29630
29748
  (0, applyFormat_1.applyFormat)(li, context.formatAppliers.listItemElement, listItem.format, context);
29631
29749
  (0, stackFormat_1.stackFormat)(context, listItem.formatHolder.format, function () {
29632
- context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
29750
+ (0, stackFormat_1.stackFormat)(context, listItem.format.direction ? { direction: listItem.format.direction } : null, function () {
29751
+ context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
29752
+ });
29633
29753
  });
29634
29754
  }
29635
29755
  else {
@@ -30001,7 +30121,9 @@ var handleTable = function (doc, parent, table, context, refNode) {
30001
30121
  (0, applyFormat_1.applyFormat)(td_1, context.formatAppliers.tableCellBorder, cell.format, context);
30002
30122
  (0, applyFormat_1.applyFormat)(td_1, context.formatAppliers.dataset, cell.dataset, context);
30003
30123
  }
30004
- context.modelHandlers.blockGroupChildren(doc, td_1, cell, context);
30124
+ (0, stackFormat_1.stackFormat)(context, cell.format.direction ? { direction: cell.format.direction } : null, function () {
30125
+ context.modelHandlers.blockGroupChildren(doc, td_1, cell, context);
30126
+ });
30005
30127
  });
30006
30128
  (_f = context.onNodeCreated) === null || _f === void 0 ? void 0 : _f.call(context, cell, td_1);
30007
30129
  }
@@ -30527,35 +30649,6 @@ var convertContentModelToMarkdown_1 = __webpack_require__(/*! ./modelToMarkdown/
30527
30649
  Object.defineProperty(exports, "convertContentModelToMarkdown", ({ enumerable: true, get: function () { return convertContentModelToMarkdown_1.convertContentModelToMarkdown; } }));
30528
30650
 
30529
30651
 
30530
- /***/ },
30531
-
30532
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts"
30533
- /*!*********************************************************************************************!*\
30534
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts ***!
30535
- \*********************************************************************************************/
30536
- (__unused_webpack_module, exports) {
30537
-
30538
- "use strict";
30539
-
30540
- Object.defineProperty(exports, "__esModule", ({ value: true }));
30541
- exports.applyLink = void 0;
30542
- /**
30543
- * @internal
30544
- */
30545
- function applyLink(textSegment, text, url) {
30546
- textSegment.text = text;
30547
- textSegment.link = {
30548
- dataset: {},
30549
- format: {
30550
- href: url,
30551
- underline: true,
30552
- },
30553
- };
30554
- return textSegment;
30555
- }
30556
- exports.applyLink = applyLink;
30557
-
30558
-
30559
30652
  /***/ },
30560
30653
 
30561
30654
  /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applySegmentFormatting.ts"
@@ -30570,46 +30663,39 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
30570
30663
  exports.applySegmentFormatting = void 0;
30571
30664
  var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
30572
30665
  var adjustHeading_1 = __webpack_require__(/*! ../utils/adjustHeading */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/adjustHeading.ts");
30573
- var applyLink_1 = __webpack_require__(/*! ./applyLink */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyLink.ts");
30574
- var applyTextFormatting_1 = __webpack_require__(/*! ./applyTextFormatting */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts");
30575
30666
  var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
30576
- var createImageSegment_1 = __webpack_require__(/*! ../creators/createImageSegment */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createImageSegment.ts");
30577
- var splitParagraphSegments_1 = __webpack_require__(/*! ../utils/splitParagraphSegments */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts");
30667
+ var parseInlineSegments_1 = __webpack_require__(/*! ../utils/parseInlineSegments */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts");
30578
30668
  /**
30579
30669
  * @internal
30580
30670
  */
30581
30671
  function applySegmentFormatting(text, paragraph, decorator) {
30582
- var e_1, _a, _b;
30672
+ var e_1, _a;
30583
30673
  if (text.length === 0) {
30584
30674
  var br = (0, roosterjs_content_model_dom_1.createBr)();
30585
30675
  paragraph.segments.push(br);
30586
30676
  }
30587
30677
  else {
30588
- var textSegments = (0, splitParagraphSegments_1.splitParagraphSegments)(text);
30678
+ var segments = [];
30679
+ (0, parseInlineSegments_1.parseInlineSegments)(text, segments);
30680
+ // Apply heading adjustment to the first text-bearing segment, if any.
30681
+ var headingAdjusted = false;
30589
30682
  try {
30590
- for (var textSegments_1 = (0, tslib_1.__values)(textSegments), textSegments_1_1 = textSegments_1.next(); !textSegments_1_1.done; textSegments_1_1 = textSegments_1.next()) {
30591
- var segment = textSegments_1_1.value;
30592
- var formattedSegment = (0, roosterjs_content_model_dom_1.createText)(segment.text);
30593
- if (segment.type === 'image') {
30594
- var image = (0, createImageSegment_1.createImageSegment)(segment.text, segment.url);
30595
- paragraph.segments.push(image);
30596
- }
30597
- else {
30598
- if (segment.type === 'link') {
30599
- (0, applyLink_1.applyLink)(formattedSegment, segment.text, segment.url);
30600
- }
30601
- var segmentWithAdjustedHeading = (0, adjustHeading_1.adjustHeading)(formattedSegment, decorator);
30602
- if (segmentWithAdjustedHeading) {
30603
- var formattedSegments = (0, applyTextFormatting_1.applyTextFormatting)(formattedSegment);
30604
- (_b = paragraph.segments).push.apply(_b, (0, tslib_1.__spreadArray)([], (0, tslib_1.__read)(formattedSegments), false));
30683
+ for (var segments_1 = (0, tslib_1.__values)(segments), segments_1_1 = segments_1.next(); !segments_1_1.done; segments_1_1 = segments_1.next()) {
30684
+ var segment = segments_1_1.value;
30685
+ if (!headingAdjusted && segment.segmentType === 'Text') {
30686
+ var adjusted = (0, adjustHeading_1.adjustHeading)(segment, decorator);
30687
+ headingAdjusted = true;
30688
+ if (!adjusted) {
30689
+ continue;
30605
30690
  }
30606
30691
  }
30692
+ paragraph.segments.push(segment);
30607
30693
  }
30608
30694
  }
30609
30695
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
30610
30696
  finally {
30611
30697
  try {
30612
- if (textSegments_1_1 && !textSegments_1_1.done && (_a = textSegments_1.return)) _a.call(textSegments_1);
30698
+ if (segments_1_1 && !segments_1_1.done && (_a = segments_1.return)) _a.call(segments_1);
30613
30699
  }
30614
30700
  finally { if (e_1) throw e_1.error; }
30615
30701
  }
@@ -30619,148 +30705,6 @@ function applySegmentFormatting(text, paragraph, decorator) {
30619
30705
  exports.applySegmentFormatting = applySegmentFormatting;
30620
30706
 
30621
30707
 
30622
- /***/ },
30623
-
30624
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts"
30625
- /*!*******************************************************************************************************!*\
30626
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/appliers/applyTextFormatting.ts ***!
30627
- \*******************************************************************************************************/
30628
- (__unused_webpack_module, exports, __webpack_require__) {
30629
-
30630
- "use strict";
30631
-
30632
- Object.defineProperty(exports, "__esModule", ({ value: true }));
30633
- exports.applyTextFormatting = void 0;
30634
- var tslib_1 = __webpack_require__(/*! tslib */ "./node_modules/tslib/tslib.es6.mjs");
30635
- var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
30636
- /**
30637
- * @internal
30638
- */
30639
- function applyTextFormatting(textSegment) {
30640
- var text = textSegment.text;
30641
- // Quick check: if the text contains only formatting markers, return original
30642
- if (isOnlyFormattingMarkers(text)) {
30643
- return [textSegment];
30644
- }
30645
- var textSegments = [];
30646
- var currentState = { bold: false, italic: false, strikethrough: false };
30647
- var currentText = '';
30648
- var i = 0;
30649
- while (i < text.length) {
30650
- var marker = parseMarkerAt(text, i);
30651
- if (marker) {
30652
- // Check if this marker should be treated as formatting or as literal text
30653
- if (shouldToggleFormatting(text, i, marker, currentState)) {
30654
- // If we have accumulated text, create a segment for it
30655
- if (currentText.length > 0) {
30656
- textSegments.push(createFormattedSegment(currentText, textSegment.format, currentState, textSegment.link));
30657
- currentText = '';
30658
- }
30659
- // Toggle the formatting state
30660
- toggleFormatting(currentState, marker.type);
30661
- // Skip the marker characters
30662
- i += marker.length;
30663
- }
30664
- else {
30665
- // Treat as regular text if marker is not valid in this context
30666
- currentText += text[i];
30667
- i++;
30668
- }
30669
- }
30670
- else {
30671
- // Regular character, add to current text
30672
- currentText += text[i];
30673
- i++;
30674
- }
30675
- }
30676
- // Add any remaining text as a final segment
30677
- if (currentText.length > 0) {
30678
- textSegments.push(createFormattedSegment(currentText, textSegment.format, currentState, textSegment.link));
30679
- }
30680
- // If no meaningful formatting was applied, return the original segment
30681
- if (textSegments.length === 0 ||
30682
- (textSegments.length === 1 && textSegments[0].text === textSegment.text)) {
30683
- return [textSegment];
30684
- }
30685
- return textSegments;
30686
- }
30687
- exports.applyTextFormatting = applyTextFormatting;
30688
- function isOnlyFormattingMarkers(text) {
30689
- // Remove all potential formatting markers and see if anything remains
30690
- var remaining = text;
30691
- remaining = remaining.replace(/\*\*/g, ''); // Remove **
30692
- remaining = remaining.replace(/~~/g, ''); // Remove ~~
30693
- remaining = remaining.replace(/\*/g, ''); // Remove *
30694
- // If nothing remains after removing all markers, it was only markers
30695
- return remaining.length === 0;
30696
- }
30697
- function parseMarkerAt(text, index) {
30698
- var remaining = text.substring(index);
30699
- if (remaining.startsWith('~~')) {
30700
- return { type: 'strikethrough', length: 2 };
30701
- }
30702
- if (remaining.startsWith('**')) {
30703
- return { type: 'bold', length: 2 };
30704
- }
30705
- if (remaining.startsWith('*')) {
30706
- return { type: 'italic', length: 1 };
30707
- }
30708
- return null;
30709
- }
30710
- function shouldToggleFormatting(text, index, marker, currentState) {
30711
- var nextChar = index + marker.length < text.length ? text.charAt(index + marker.length) : '';
30712
- var isCurrentlyActive = getCurrentFormatState(currentState, marker.type);
30713
- if (isCurrentlyActive) {
30714
- // We're currently in this format, so any marker can close it
30715
- return true;
30716
- }
30717
- else {
30718
- // We're not in this format, so this marker would open it
30719
- // Opening markers must be followed by non-whitespace
30720
- return nextChar.length > 0 && !isWhitespace(nextChar);
30721
- }
30722
- }
30723
- function isWhitespace(char) {
30724
- return /\s/.test(char);
30725
- }
30726
- function toggleFormatting(state, type) {
30727
- switch (type) {
30728
- case 'bold':
30729
- state.bold = !state.bold;
30730
- break;
30731
- case 'italic':
30732
- state.italic = !state.italic;
30733
- break;
30734
- case 'strikethrough':
30735
- state.strikethrough = !state.strikethrough;
30736
- break;
30737
- }
30738
- }
30739
- function getCurrentFormatState(state, type) {
30740
- switch (type) {
30741
- case 'bold':
30742
- return state.bold;
30743
- case 'italic':
30744
- return state.italic;
30745
- case 'strikethrough':
30746
- return state.strikethrough;
30747
- }
30748
- }
30749
- function createFormattedSegment(text, baseFormat, state, link) {
30750
- var format = (0, tslib_1.__assign)({}, baseFormat);
30751
- if (state.bold) {
30752
- format.fontWeight = 'bold';
30753
- }
30754
- if (state.italic) {
30755
- format.italic = true;
30756
- }
30757
- if (state.strikethrough) {
30758
- format.strikethrough = true;
30759
- }
30760
- return (0, roosterjs_content_model_dom_1.createText)(text, format, link);
30761
- }
30762
-
30763
-
30764
30708
  /***/ },
30765
30709
 
30766
30710
  /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/convertMarkdownToContentModel.ts"
@@ -31358,29 +31302,143 @@ exports.isMarkdownTable = isMarkdownTable;
31358
31302
 
31359
31303
  /***/ },
31360
31304
 
31361
- /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts"
31362
- /*!*******************************************************************************************************!*\
31363
- !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/splitParagraphSegments.ts ***!
31364
- \*******************************************************************************************************/
31365
- (__unused_webpack_module, exports) {
31305
+ /***/ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts"
31306
+ /*!****************************************************************************************************!*\
31307
+ !*** ./packages/roosterjs-content-model-markdown/lib/markdownToModel/utils/parseInlineSegments.ts ***!
31308
+ \****************************************************************************************************/
31309
+ (__unused_webpack_module, exports, __webpack_require__) {
31366
31310
 
31367
31311
  "use strict";
31368
31312
 
31369
31313
  Object.defineProperty(exports, "__esModule", ({ value: true }));
31370
- exports.splitParagraphSegments = void 0;
31371
- // Matches markdown links and images in a string.
31372
- // Group 1 (full link): [text](url) e.g. [Click here](https://example.com)
31373
- // Group 2: link text e.g. "Click here"
31374
- // Group 3: link url e.g. "https://example.com"
31375
- // Group 4 (full image): ![alt](url) e.g. ![Logo](https://example.com/logo.png)
31376
- // Group 5: alt text e.g. "Logo"
31377
- // Group 6: image url e.g. "https://example.com/logo.png"
31378
- var linkRegex = /(\[([^\[]+)\]\(([^\)]+)\))|(\!\[([^\[]+)\]\(([^\)]+)\))/g;
31379
- var isValidUrl = function (url) {
31314
+ exports.parseInlineSegments = void 0;
31315
+ var createImageSegment_1 = __webpack_require__(/*! ../creators/createImageSegment */ "./packages/roosterjs-content-model-markdown/lib/markdownToModel/creators/createImageSegment.ts");
31316
+ var roosterjs_content_model_dom_1 = __webpack_require__(/*! roosterjs-content-model-dom */ "./packages/roosterjs-content-model-dom/lib/index.ts");
31317
+ // Matches a markdown link [text](url) anchored at the start of the input.
31318
+ var linkPattern = /^\[([^\[\]]+)\]\(([^\)]+)\)/;
31319
+ // Matches a markdown image ![alt](url) anchored at the start of the input.
31320
+ var imagePattern = /^!\[([^\[\]]+)\]\(([^\)]+)\)/;
31321
+ /**
31322
+ * @internal
31323
+ * Parse a markdown inline string into Content Model segments. Supports bold/italic/
31324
+ * strikethrough markers, links, and images, and keeps formatting state active across
31325
+ * link boundaries (e.g. **[link](url)**).
31326
+ */
31327
+ function parseInlineSegments(text, segments, state, link) {
31328
+ if (state === void 0) { state = { bold: false, italic: false, strikethrough: false }; }
31329
+ var buffer = '';
31330
+ var i = 0;
31331
+ var flushBuffer = function () {
31332
+ if (buffer.length > 0) {
31333
+ segments.push(createFormattedSegment(buffer, state, link));
31334
+ buffer = '';
31335
+ }
31336
+ };
31337
+ while (i < text.length) {
31338
+ var remaining = text.substring(i);
31339
+ // Image: ![alt](url)
31340
+ var imgMatch = imagePattern.exec(remaining);
31341
+ if (imgMatch && isValidUrl(imgMatch[2])) {
31342
+ flushBuffer();
31343
+ segments.push((0, createImageSegment_1.createImageSegment)(imgMatch[1], imgMatch[2]));
31344
+ i += imgMatch[0].length;
31345
+ continue;
31346
+ }
31347
+ // Link: [text](url) — keep outer formatting state active inside the link
31348
+ var linkMatch = linkPattern.exec(remaining);
31349
+ if (linkMatch && isValidUrl(linkMatch[2])) {
31350
+ flushBuffer();
31351
+ var innerLink = {
31352
+ dataset: {},
31353
+ format: { href: linkMatch[2], underline: true },
31354
+ };
31355
+ parseInlineSegments(linkMatch[1], segments, state, innerLink);
31356
+ i += linkMatch[0].length;
31357
+ continue;
31358
+ }
31359
+ // Formatting marker
31360
+ var marker = parseMarkerAt(text, i);
31361
+ if (marker && shouldToggleFormatting(text, i, marker, state)) {
31362
+ flushBuffer();
31363
+ toggleFormatting(state, marker.type);
31364
+ i += marker.length;
31365
+ continue;
31366
+ }
31367
+ buffer += text[i];
31368
+ i++;
31369
+ }
31370
+ flushBuffer();
31371
+ }
31372
+ exports.parseInlineSegments = parseInlineSegments;
31373
+ function parseMarkerAt(text, index) {
31374
+ var remaining = text.substring(index);
31375
+ if (remaining.startsWith('~~')) {
31376
+ return { type: 'strikethrough', length: 2 };
31377
+ }
31378
+ if (remaining.startsWith('**')) {
31379
+ return { type: 'bold', length: 2 };
31380
+ }
31381
+ if (remaining.startsWith('*')) {
31382
+ return { type: 'italic', length: 1 };
31383
+ }
31384
+ return null;
31385
+ }
31386
+ function shouldToggleFormatting(text, index, marker, currentState) {
31387
+ var isCurrentlyActive = getCurrentFormatState(currentState, marker.type);
31388
+ if (isCurrentlyActive) {
31389
+ return true;
31390
+ }
31391
+ // Opening marker must be followed by a non-whitespace character.
31392
+ var nextIndex = index + marker.length;
31393
+ var nextChar = nextIndex < text.length ? text.charAt(nextIndex) : '';
31394
+ if (nextChar.length === 0 || isWhitespace(nextChar)) {
31395
+ return false;
31396
+ }
31397
+ return true;
31398
+ }
31399
+ function isWhitespace(char) {
31400
+ return /\s/.test(char);
31401
+ }
31402
+ function toggleFormatting(state, type) {
31403
+ switch (type) {
31404
+ case 'bold':
31405
+ state.bold = !state.bold;
31406
+ break;
31407
+ case 'italic':
31408
+ state.italic = !state.italic;
31409
+ break;
31410
+ case 'strikethrough':
31411
+ state.strikethrough = !state.strikethrough;
31412
+ break;
31413
+ }
31414
+ }
31415
+ function getCurrentFormatState(state, type) {
31416
+ switch (type) {
31417
+ case 'bold':
31418
+ return state.bold;
31419
+ case 'italic':
31420
+ return state.italic;
31421
+ case 'strikethrough':
31422
+ return state.strikethrough;
31423
+ }
31424
+ }
31425
+ function createFormattedSegment(text, state, link) {
31426
+ var format = {};
31427
+ if (state.bold) {
31428
+ format.fontWeight = 'bold';
31429
+ }
31430
+ if (state.italic) {
31431
+ format.italic = true;
31432
+ }
31433
+ if (state.strikethrough) {
31434
+ format.strikethrough = true;
31435
+ }
31436
+ return (0, roosterjs_content_model_dom_1.createText)(text, format, link);
31437
+ }
31438
+ function isValidUrl(url) {
31380
31439
  if (!url) {
31381
31440
  return false;
31382
31441
  }
31383
- // Accept common non-http schemes and relative paths
31384
31442
  if (url.startsWith('data:') ||
31385
31443
  url.startsWith('blob:') ||
31386
31444
  url.startsWith('/') ||
@@ -31395,51 +31453,7 @@ var isValidUrl = function (url) {
31395
31453
  catch (_) {
31396
31454
  return false;
31397
31455
  }
31398
- };
31399
- function pushText(result, text) {
31400
- var last = result[result.length - 1];
31401
- if (last && last.type === 'text') {
31402
- last.text += text;
31403
- }
31404
- else {
31405
- result.push({ type: 'text', text: text, url: '' });
31406
- }
31407
- }
31408
- /**
31409
- * @internal
31410
- */
31411
- function splitParagraphSegments(text) {
31412
- var result = [];
31413
- var lastIndex = 0;
31414
- var match = null;
31415
- while ((match = linkRegex.exec(text)) !== null) {
31416
- if (match.index > lastIndex) {
31417
- pushText(result, text.slice(lastIndex, match.index));
31418
- }
31419
- if (match[2] && match[3]) {
31420
- if (isValidUrl(match[3])) {
31421
- result.push({ type: 'link', text: match[2], url: match[3] });
31422
- }
31423
- else {
31424
- pushText(result, match[0]);
31425
- }
31426
- }
31427
- else if (match[5] && match[6]) {
31428
- if (isValidUrl(match[6])) {
31429
- result.push({ type: 'image', text: match[5], url: match[6] });
31430
- }
31431
- else {
31432
- pushText(result, match[0]);
31433
- }
31434
- }
31435
- lastIndex = linkRegex.lastIndex;
31436
- }
31437
- if (lastIndex < text.length) {
31438
- pushText(result, text.slice(lastIndex));
31439
- }
31440
- return result;
31441
31456
  }
31442
- exports.splitParagraphSegments = splitParagraphSegments;
31443
31457
 
31444
31458
 
31445
31459
  /***/ },
@@ -31700,20 +31714,30 @@ function createMarkdownParagraph(paragraph, context) {
31700
31714
  }
31701
31715
  exports.createMarkdownParagraph = createMarkdownParagraph;
31702
31716
  function textProcessor(text) {
31703
- var markdownString = text.text;
31704
- if (text.link) {
31705
- markdownString = "[" + text.text + "](" + text.link.format.href + ")";
31706
- }
31707
- if (text.format.fontWeight == 'bold') {
31708
- markdownString = "**" + markdownString + "**";
31709
- }
31710
- if (text.format.strikethrough) {
31711
- markdownString = "~~" + markdownString + "~~";
31717
+ var _a = text.format, fontWeight = _a.fontWeight, italic = _a.italic, strikethrough = _a.strikethrough;
31718
+ var hasInlineFormat = fontWeight == 'bold' || italic || strikethrough;
31719
+ if (!hasInlineFormat) {
31720
+ return text.link ? "[" + text.text + "](" + text.link.format.href + ")" : text.text;
31721
+ }
31722
+ // Move leading/trailing whitespace outside the markers so the emitted
31723
+ // markdown is valid (CommonMark requires emphasis markers to hug
31724
+ // non-whitespace), e.g. "world " with <b> => " " + "**world**".
31725
+ var match = /^(\s*)([\s\S]*?)(\s*)$/.exec(text.text);
31726
+ var _b = (0, tslib_1.__read)(match ? match : ['', '', text.text, ''], 4), leading = _b[1], core = _b[2], trailing = _b[3];
31727
+ if (!core) {
31728
+ return text.text;
31729
+ }
31730
+ var inner = text.link ? "[" + core + "](" + text.link.format.href + ")" : core;
31731
+ if (fontWeight == 'bold') {
31732
+ inner = "**" + inner + "**";
31733
+ }
31734
+ if (strikethrough) {
31735
+ inner = "~~" + inner + "~~";
31712
31736
  }
31713
- if (text.format.italic) {
31714
- markdownString = "*" + markdownString + "*";
31737
+ if (italic) {
31738
+ inner = "*" + inner + "*";
31715
31739
  }
31716
- return markdownString;
31740
+ return "" + leading + inner + trailing;
31717
31741
  }
31718
31742
 
31719
31743