@atlaskit/editor-common 112.8.4 → 112.9.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 112.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`9b33b26d69865`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9b33b26d69865) -
8
+ Narrow list replacement range during indent/outdent for collab-friendly cursor preservation
9
+ - [`3f6f3c13a6033`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3f6f3c13a6033) -
10
+ Fix fontSize mark preservation during list type conversions (bullet/ordered ↔ task list) and
11
+ blockTaskItem handling when toggling task list off
12
+ - Updated dependencies
13
+
14
+ ## 112.9.0
15
+
16
+ ### Minor Changes
17
+
18
+ - [`9f5fba61d4c9d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9f5fba61d4c9d) -
19
+ do not show external badge for images hosted on bitbucket.org
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies
24
+
3
25
  ## 112.8.4
4
26
 
5
27
  ### Patch Changes
@@ -99,6 +99,12 @@ Object.defineProperty(exports, "moveTargetIntoList", {
99
99
  return _replaceContent.moveTargetIntoList;
100
100
  }
101
101
  });
102
+ Object.defineProperty(exports, "narrowReplacementRange", {
103
+ enumerable: true,
104
+ get: function get() {
105
+ return _narrowReplacementRange.narrowReplacementRange;
106
+ }
107
+ });
102
108
  Object.defineProperty(exports, "normalizeListItemsSelection", {
103
109
  enumerable: true,
104
110
  get: function get() {
@@ -124,6 +130,7 @@ var _analytics = require("./analytics");
124
130
  var _indentation = require("./indentation");
125
131
  var _restoreSelection = require("./restore-selection");
126
132
  var _buildReplacementFragment = require("./build-replacement-fragment");
133
+ var _narrowReplacementRange = require("./narrow-replacement-range");
127
134
  var _flattenList = require("./flatten-list");
128
135
  var _utils = require("../utils");
129
136
  var _messages = require("./messages");
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.narrowReplacementRange = narrowReplacementRange;
7
+ var _model = require("@atlaskit/editor-prosemirror/model");
8
+ /**
9
+ * Narrows a full-list replacement to the minimal changed range.
10
+ *
11
+ * Compares the old root list node with the new replacement fragment
12
+ * from both ends to find the first and last positions where they differ,
13
+ * then returns only the changed subrange.
14
+ *
15
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
16
+ * on unchanged items are preserved during collaborative editing.
17
+ */
18
+ function narrowReplacementRange(doc, rootListStart, rootListEnd, fragment, contentStartOffsets) {
19
+ var oldNode = doc.nodeAt(rootListStart);
20
+ var newNode = fragment.childCount === 1 ? fragment.firstChild : null;
21
+ if (!oldNode || !newNode || newNode.type !== oldNode.type) {
22
+ return {
23
+ start: rootListStart,
24
+ end: rootListEnd,
25
+ fragment: fragment,
26
+ adjustedContentStartOffsets: contentStartOffsets
27
+ };
28
+ }
29
+ var minChildCount = Math.min(oldNode.childCount, newNode.childCount);
30
+ var commonPrefixChildren = 0;
31
+ var prefixSize = 0;
32
+ for (var i = 0; i < minChildCount; i++) {
33
+ var oldChild = oldNode.child(i);
34
+ var newChild = newNode.child(i);
35
+ if (oldChild.eq(newChild)) {
36
+ commonPrefixChildren++;
37
+ prefixSize += oldChild.nodeSize;
38
+ } else {
39
+ break;
40
+ }
41
+ }
42
+ var commonSuffixChildren = 0;
43
+ var suffixSize = 0;
44
+ for (var _i = 0; _i < minChildCount - commonPrefixChildren; _i++) {
45
+ var _oldChild = oldNode.child(oldNode.childCount - 1 - _i);
46
+ var _newChild = newNode.child(newNode.childCount - 1 - _i);
47
+ if (_oldChild.eq(_newChild)) {
48
+ commonSuffixChildren++;
49
+ suffixSize += _oldChild.nodeSize;
50
+ } else {
51
+ break;
52
+ }
53
+ }
54
+ var totalCommon = commonPrefixChildren + commonSuffixChildren;
55
+ if (totalCommon >= oldNode.childCount && totalCommon >= newNode.childCount) {
56
+ return {
57
+ start: rootListStart,
58
+ end: rootListStart,
59
+ fragment: _model.Fragment.empty,
60
+ adjustedContentStartOffsets: contentStartOffsets
61
+ };
62
+ }
63
+ var narrowedStart = rootListStart + 1 + prefixSize;
64
+ var narrowedEnd = rootListEnd - 1 - suffixSize;
65
+ var changedChildStart = commonPrefixChildren;
66
+ var changedChildEnd = newNode.childCount - commonSuffixChildren;
67
+ var changedNodes = [];
68
+ for (var _i2 = changedChildStart; _i2 < changedChildEnd; _i2++) {
69
+ changedNodes.push(newNode.child(_i2));
70
+ }
71
+ var narrowedFragment = _model.Fragment.from(changedNodes);
72
+ var prefixOffset = 1 + prefixSize;
73
+ var adjustedContentStartOffsets = contentStartOffsets.map(function (offset) {
74
+ return offset - prefixOffset;
75
+ });
76
+ return {
77
+ start: narrowedStart,
78
+ end: narrowedEnd,
79
+ fragment: narrowedFragment,
80
+ adjustedContentStartOffsets: adjustedContentStartOffsets
81
+ };
82
+ }
@@ -9,6 +9,7 @@ var _react = _interopRequireDefault(require("react"));
9
9
  var _reactIntlNext = require("react-intl-next");
10
10
  var _statusInformation = _interopRequireDefault(require("@atlaskit/icon/core/status-information"));
11
11
  var _primitives = require("@atlaskit/primitives");
12
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
12
13
  var _tooltip = _interopRequireDefault(require("@atlaskit/tooltip"));
13
14
  var _media = require("../media");
14
15
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
@@ -21,6 +22,7 @@ var baseStyles = (0, _primitives.xcss)({
21
22
  cursor: 'pointer'
22
23
  });
23
24
  var NO_EXTERNAL_BADGE_HOSTS = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com'];
25
+ var NO_EXTERNAL_BADGE_HOSTS_NEW = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com', 'bitbucket.org'];
24
26
  var isUnbadgedUrl = exports.isUnbadgedUrl = function isUnbadgedUrl(url) {
25
27
  if (!url) {
26
28
  return false;
@@ -38,6 +40,11 @@ var isUnbadgedUrl = exports.isUnbadgedUrl = function isUnbadgedUrl(url) {
38
40
  if (protocol === 'data:') {
39
41
  return pathname === null || pathname === void 0 ? void 0 : pathname.startsWith('image/');
40
42
  }
43
+ if ((0, _expValEquals.expValEquals)('platform_editor_media_external_badge_bbc_fix', 'isEnable', true)) {
44
+ return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS_NEW.some(function (host) {
45
+ return hostname === host || hostname.endsWith(".".concat(host));
46
+ }));
47
+ }
41
48
  return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS.some(function (host) {
42
49
  return hostname === host || hostname.endsWith(".".concat(host));
43
50
  }));
@@ -19,7 +19,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
19
19
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
20
20
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
21
21
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
22
- var packageVersion = "112.8.3";
22
+ var packageVersion = "0.0.0-development";
23
23
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
24
24
  // Remove URL as it has UGC
25
25
  // Ignored via go/ees007
@@ -9,7 +9,11 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _model = require("@atlaskit/editor-prosemirror/model");
11
11
  var _utils = require("@atlaskit/editor-prosemirror/utils");
12
+ var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
12
13
  var _listUtils = require("./list-utils");
14
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
15
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
16
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
13
17
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14
18
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15
19
  var getContentSupportChecker = function getContentSupportChecker(targetNodeType) {
@@ -21,6 +25,16 @@ var getContentSupportChecker = function getContentSupportChecker(targetNodeType)
21
25
  }
22
26
  };
23
27
  };
28
+ var createBlockTaskItemWithMarks = function createBlockTaskItemWithMarks(content, marks, schema) {
29
+ var _schema$nodes = schema.nodes,
30
+ blockTaskItem = _schema$nodes.blockTaskItem,
31
+ paragraph = _schema$nodes.paragraph;
32
+ var allowedMarks = marks.filter(function (mark) {
33
+ return blockTaskItem.allowsMarkType(mark.type);
34
+ });
35
+ var newParagraph = paragraph.create(null, content.length > 0 ? content : null, allowedMarks);
36
+ return blockTaskItem.create(null, newParagraph);
37
+ };
24
38
  var _transformListRecursively = exports.transformListRecursively = function transformListRecursively(props, onhandleUnsupportedContent) {
25
39
  var transformedItems = [];
26
40
  var listNode = props.listNode,
@@ -31,17 +45,36 @@ var _transformListRecursively = exports.transformListRecursively = function tran
31
45
  supportedListTypes = props.supportedListTypes,
32
46
  schema = props.schema,
33
47
  targetNodeType = props.targetNodeType;
34
- var _schema$nodes = schema.nodes,
35
- taskList = _schema$nodes.taskList,
36
- listItem = _schema$nodes.listItem,
37
- taskItem = _schema$nodes.taskItem,
38
- paragraph = _schema$nodes.paragraph;
48
+ var _schema$nodes2 = schema.nodes,
49
+ taskList = _schema$nodes2.taskList,
50
+ listItem = _schema$nodes2.listItem,
51
+ taskItem = _schema$nodes2.taskItem,
52
+ paragraph = _schema$nodes2.paragraph,
53
+ blockTaskItem = _schema$nodes2.blockTaskItem;
54
+
55
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
56
+ // but keep this solution general
57
+ var isBlockTaskEnabled = !!blockTaskItem && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
58
+
59
+ /**
60
+ * Extracts paragraph children from a blockTaskItem, preserving their marks.
61
+ */
62
+ var extractParagraphsFromBlockTaskItem = function extractParagraphsFromBlockTaskItem(node) {
63
+ var paragraphs = [];
64
+ node.forEach(function (child) {
65
+ if (child.type === paragraph) {
66
+ paragraphs.push(child);
67
+ }
68
+ });
69
+ return paragraphs;
70
+ };
39
71
  listNode.forEach(function (child) {
40
72
  if (isSourceBulletOrOrdered && isTargetTask) {
41
73
  // Convert bullet/ordered => task
42
74
  if (child.type === listItem) {
43
75
  var inlineContent = [];
44
76
  var nestedTaskLists = [];
77
+ var blockMarks = [];
45
78
  child.forEach(function (grandChild) {
46
79
  if (supportedListTypes.has(grandChild.type) && grandChild.type !== taskList) {
47
80
  nestedTaskLists.push(_transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
@@ -50,18 +83,39 @@ var _transformListRecursively = exports.transformListRecursively = function tran
50
83
  } else if (!getContentSupportChecker(taskItem)(grandChild) && !grandChild.isTextblock) {
51
84
  onhandleUnsupportedContent === null || onhandleUnsupportedContent === void 0 || onhandleUnsupportedContent(grandChild);
52
85
  } else {
86
+ if (isBlockTaskEnabled && grandChild.type === paragraph && grandChild.marks.length > 0) {
87
+ blockMarks = grandChild.marks;
88
+ }
53
89
  inlineContent.push.apply(inlineContent, (0, _toConsumableArray2.default)((0, _listUtils.convertBlockToInlineContent)(grandChild, schema)));
54
90
  }
55
91
  });
56
- transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
92
+ if (isBlockTaskEnabled && blockMarks.length > 0) {
93
+ transformedItems.push(createBlockTaskItemWithMarks(inlineContent, blockMarks, schema));
94
+ } else {
95
+ transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
96
+ }
57
97
  transformedItems.push.apply(transformedItems, nestedTaskLists);
58
98
  }
59
99
  } else if (isSourceTask && isTargetBulletOrOrdered) {
60
100
  // Convert task => bullet/ordered
61
101
  if (child.type === taskItem) {
62
102
  var _inlineContent = (0, _toConsumableArray2.default)(child.content.content);
63
- var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null);
103
+
104
+ // Transfer taskItem's block marks to the paragraph.
105
+ // Use listItem.allowsMarkType since the paragraph will be inside a listItem
106
+ // (which uses ParagraphWithFontSizeStage0 that allows fontSize).
107
+ var paragraphMarks = isBlockTaskEnabled && child.marks.length > 0 ? child.marks.filter(function (mark) {
108
+ return listItem.allowsMarkType(mark.type);
109
+ }) : undefined;
110
+ var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null, paragraphMarks);
64
111
  transformedItems.push(listItem.create(null, [paragraphNode]));
112
+ } else if (isBlockTaskEnabled && child.type === blockTaskItem) {
113
+ // blockTaskItem wraps content in paragraphs — extract them directly,
114
+ // preserving their fontSize marks
115
+ var paragraphs = extractParagraphsFromBlockTaskItem(child);
116
+ if (paragraphs.length > 0) {
117
+ transformedItems.push(listItem.create(null, paragraphs));
118
+ }
65
119
  } else if (child.type === taskList) {
66
120
  var transformedNestedList = _transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
67
121
  listNode: child
@@ -195,21 +249,27 @@ var transformBetweenListTypes = exports.transformBetweenListTypes = function tra
195
249
  */
196
250
  var transformToTaskList = exports.transformToTaskList = function transformToTaskList(tr, range, targetNodeType, targetAttrs, nodes) {
197
251
  try {
198
- var taskItem = nodes.taskItem;
252
+ var taskItem = nodes.taskItem,
253
+ paragraph = nodes.paragraph,
254
+ blockTaskItem = nodes.blockTaskItem;
255
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
256
+ // but keep this solution general
257
+ var isBlockTaskItemEnabled = !!blockTaskItem && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
199
258
  var listItems = [];
200
259
 
201
260
  // Process each block in the range
202
261
  tr.doc.nodesBetween(range.start, range.end, function (node) {
203
262
  if (node.isBlock) {
204
- // For block nodes like paragraphs, directly use their inline content
205
263
  var inlineContent = (0, _toConsumableArray2.default)(node.content.content);
206
264
  if (inlineContent.length > 0) {
207
- // Create task item with inline content directly
208
- var listItem = taskItem.create(targetAttrs, inlineContent);
209
- listItems.push(listItem);
265
+ if (isBlockTaskItemEnabled && node.type === paragraph && node.marks.length > 0) {
266
+ listItems.push(createBlockTaskItemWithMarks(inlineContent, node.marks, tr.doc.type.schema));
267
+ } else {
268
+ listItems.push(taskItem.create(targetAttrs, inlineContent));
269
+ }
210
270
  }
211
271
  }
212
- return false; // Don't traverse into children
272
+ return false;
213
273
  });
214
274
  if (listItems.length === 0) {
215
275
  return null;
@@ -233,6 +293,44 @@ var transformTaskListToBlockNodes = exports.transformTaskListToBlockNodes = func
233
293
  sourcePos = context.sourcePos;
234
294
  var selection = tr.selection;
235
295
  var schema = selection.$from.doc.type.schema;
296
+ var blockTaskItem = schema.nodes.blockTaskItem;
297
+
298
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
299
+ // but keep this solution general
300
+ var isBlockTaskItemEnabled = !!blockTaskItem && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
301
+ if (isBlockTaskItemEnabled) {
302
+ var blockTaskItemsResult = (0, _utils.findChildrenByType)(sourceNode, blockTaskItem);
303
+ if (blockTaskItemsResult.length > 0 && targetNodeType === schema.nodes.paragraph) {
304
+ // blockTaskItem content is (paragraph | extension)+
305
+ // Extract paragraph children directly — they may carry block marks (e.g. fontSize)
306
+ var _targetNodes = [];
307
+ var _iterator = _createForOfIteratorHelper(blockTaskItemsResult),
308
+ _step;
309
+ try {
310
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
311
+ var blockItem = _step.value.node;
312
+ blockItem.forEach(function (child) {
313
+ if (child.type === schema.nodes.paragraph) {
314
+ _targetNodes.push(child);
315
+ }
316
+ });
317
+ }
318
+ } catch (err) {
319
+ _iterator.e(err);
320
+ } finally {
321
+ _iterator.f();
322
+ }
323
+ if (_targetNodes.length === 0) {
324
+ return null;
325
+ }
326
+ var _slice = new _model.Slice(_model.Fragment.fromArray(_targetNodes), 0, 0);
327
+ var _rangeStart = sourcePos !== null ? sourcePos : selection.from;
328
+ tr.replaceRange(_rangeStart, _rangeStart + sourceNode.nodeSize, _slice);
329
+ return tr;
330
+ }
331
+ }
332
+
333
+ // Original logic for regular taskItem children
236
334
  var taskItemsResult = (0, _utils.findChildrenByType)(sourceNode, schema.nodes.taskItem);
237
335
  var taskItems = taskItemsResult.map(function (item) {
238
336
  return item.node;
@@ -280,6 +378,10 @@ var getFormattedNode = exports.getFormattedNode = function getFormattedNode(tr)
280
378
  var selection = tr.selection;
281
379
  var nodes = tr.doc.type.schema.nodes;
282
380
 
381
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
382
+ // but keep this solution general
383
+ var isBlockTaskItemEnabled = !!nodes.blockTaskItem && (0, _expValEquals.expValEquals)('platform_editor_small_font_size', 'isEnabled', true);
384
+
283
385
  // Find the node to format from the current selection
284
386
  var nodeToFormat;
285
387
  var nodePos = selection.from;
@@ -291,13 +393,17 @@ var getFormattedNode = exports.getFormattedNode = function getFormattedNode(tr)
291
393
  nodePos = selectedNode.pos;
292
394
  } else {
293
395
  // Try to find parent node (including list parents)
294
- var parentNode = (0, _utils.findParentNodeOfType)([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
396
+ var parentNodeTypes = [nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection];
397
+ if (isBlockTaskItemEnabled) {
398
+ parentNodeTypes.push(nodes.blockTaskItem);
399
+ }
400
+ var parentNode = (0, _utils.findParentNodeOfType)(parentNodeTypes)(selection);
295
401
  if (parentNode) {
296
402
  nodeToFormat = parentNode.node;
297
403
  nodePos = parentNode.pos;
298
404
  var paragraphOrHeadingNode = (0, _utils.findParentNodeOfType)([nodes.paragraph, nodes.heading])(selection);
299
- // Special case: if we found a listItem, check if we need the parent list instead
300
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
405
+ // Special case: if we found a listItem/taskItem/blockTaskItem, check if we need the parent list instead
406
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem || isBlockTaskItemEnabled && parentNode.node.type === nodes.blockTaskItem) {
301
407
  var listParent = (0, _utils.findParentNodeOfType)([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
302
408
  if (listParent) {
303
409
  // For list transformations, we want the list parent, not the listItem
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "112.8.3";
27
+ var packageVersion = "0.0.0-development";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -8,6 +8,7 @@ export { getCommonListAnalyticsAttributes, countListItemsInSelection } from './a
8
8
  export { hasValidListIndentationLevel } from './indentation';
9
9
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
10
10
  export { buildReplacementFragment } from './build-replacement-fragment';
11
+ export { narrowReplacementRange } from './narrow-replacement-range';
11
12
  export { flattenList } from './flatten-list';
12
13
  export { isListNode, isListItemNode, isBulletList, isParagraphNode } from '../utils';
13
14
  export { messages } from './messages';
@@ -0,0 +1,74 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ /**
3
+ * Narrows a full-list replacement to the minimal changed range.
4
+ *
5
+ * Compares the old root list node with the new replacement fragment
6
+ * from both ends to find the first and last positions where they differ,
7
+ * then returns only the changed subrange.
8
+ *
9
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
10
+ * on unchanged items are preserved during collaborative editing.
11
+ */
12
+ export function narrowReplacementRange(doc, rootListStart, rootListEnd, fragment, contentStartOffsets) {
13
+ const oldNode = doc.nodeAt(rootListStart);
14
+ const newNode = fragment.childCount === 1 ? fragment.firstChild : null;
15
+ if (!oldNode || !newNode || newNode.type !== oldNode.type) {
16
+ return {
17
+ start: rootListStart,
18
+ end: rootListEnd,
19
+ fragment,
20
+ adjustedContentStartOffsets: contentStartOffsets
21
+ };
22
+ }
23
+ const minChildCount = Math.min(oldNode.childCount, newNode.childCount);
24
+ let commonPrefixChildren = 0;
25
+ let prefixSize = 0;
26
+ for (let i = 0; i < minChildCount; i++) {
27
+ const oldChild = oldNode.child(i);
28
+ const newChild = newNode.child(i);
29
+ if (oldChild.eq(newChild)) {
30
+ commonPrefixChildren++;
31
+ prefixSize += oldChild.nodeSize;
32
+ } else {
33
+ break;
34
+ }
35
+ }
36
+ let commonSuffixChildren = 0;
37
+ let suffixSize = 0;
38
+ for (let i = 0; i < minChildCount - commonPrefixChildren; i++) {
39
+ const oldChild = oldNode.child(oldNode.childCount - 1 - i);
40
+ const newChild = newNode.child(newNode.childCount - 1 - i);
41
+ if (oldChild.eq(newChild)) {
42
+ commonSuffixChildren++;
43
+ suffixSize += oldChild.nodeSize;
44
+ } else {
45
+ break;
46
+ }
47
+ }
48
+ const totalCommon = commonPrefixChildren + commonSuffixChildren;
49
+ if (totalCommon >= oldNode.childCount && totalCommon >= newNode.childCount) {
50
+ return {
51
+ start: rootListStart,
52
+ end: rootListStart,
53
+ fragment: Fragment.empty,
54
+ adjustedContentStartOffsets: contentStartOffsets
55
+ };
56
+ }
57
+ const narrowedStart = rootListStart + 1 + prefixSize;
58
+ const narrowedEnd = rootListEnd - 1 - suffixSize;
59
+ const changedChildStart = commonPrefixChildren;
60
+ const changedChildEnd = newNode.childCount - commonSuffixChildren;
61
+ const changedNodes = [];
62
+ for (let i = changedChildStart; i < changedChildEnd; i++) {
63
+ changedNodes.push(newNode.child(i));
64
+ }
65
+ const narrowedFragment = Fragment.from(changedNodes);
66
+ const prefixOffset = 1 + prefixSize;
67
+ const adjustedContentStartOffsets = contentStartOffsets.map(offset => offset - prefixOffset);
68
+ return {
69
+ start: narrowedStart,
70
+ end: narrowedEnd,
71
+ fragment: narrowedFragment,
72
+ adjustedContentStartOffsets
73
+ };
74
+ }
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import InfoIcon from '@atlaskit/icon/core/status-information';
4
4
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
5
5
  import { Box, xcss } from '@atlaskit/primitives';
6
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
6
7
  import Tooltip from '@atlaskit/tooltip';
7
8
  import { externalMediaMessages } from '../media';
8
9
  const baseStyles = xcss({
@@ -13,6 +14,7 @@ const baseStyles = xcss({
13
14
  cursor: 'pointer'
14
15
  });
15
16
  const NO_EXTERNAL_BADGE_HOSTS = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com'];
17
+ const NO_EXTERNAL_BADGE_HOSTS_NEW = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com', 'bitbucket.org'];
16
18
  export const isUnbadgedUrl = url => {
17
19
  if (!url) {
18
20
  return false;
@@ -32,6 +34,9 @@ export const isUnbadgedUrl = url => {
32
34
  if (protocol === 'data:') {
33
35
  return pathname === null || pathname === void 0 ? void 0 : pathname.startsWith('image/');
34
36
  }
37
+ if (expValEquals('platform_editor_media_external_badge_bbc_fix', 'isEnable', true)) {
38
+ return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS_NEW.some(host => hostname === host || hostname.endsWith(`.${host}`)));
39
+ }
35
40
  return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS.some(host => hostname === host || hostname.endsWith(`.${host}`)));
36
41
  };
37
42
  export const ExternalImageBadge = ({
@@ -4,7 +4,7 @@ import { isFedRamp } from './environment';
4
4
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
5
5
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
6
6
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
7
- const packageVersion = "112.8.3";
7
+ const packageVersion = "0.0.0-development";
8
8
  const sanitiseSentryEvents = (data, _hint) => {
9
9
  // Remove URL as it has UGC
10
10
  // Ignored via go/ees007
@@ -1,5 +1,6 @@
1
1
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
2
2
  import { findChildrenByType, findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
3
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
4
  import { getSupportedListTypesSet, isBulletOrOrderedList, isTaskList, convertBlockToInlineContent } from './list-utils';
4
5
  const getContentSupportChecker = targetNodeType => {
5
6
  return node => {
@@ -10,6 +11,15 @@ const getContentSupportChecker = targetNodeType => {
10
11
  }
11
12
  };
12
13
  };
14
+ const createBlockTaskItemWithMarks = (content, marks, schema) => {
15
+ const {
16
+ blockTaskItem,
17
+ paragraph
18
+ } = schema.nodes;
19
+ const allowedMarks = marks.filter(mark => blockTaskItem.allowsMarkType(mark.type));
20
+ const newParagraph = paragraph.create(null, content.length > 0 ? content : null, allowedMarks);
21
+ return blockTaskItem.create(null, newParagraph);
22
+ };
13
23
  export const transformListRecursively = (props, onhandleUnsupportedContent) => {
14
24
  const transformedItems = [];
15
25
  const {
@@ -26,14 +36,33 @@ export const transformListRecursively = (props, onhandleUnsupportedContent) => {
26
36
  taskList,
27
37
  listItem,
28
38
  taskItem,
29
- paragraph
39
+ paragraph,
40
+ blockTaskItem
30
41
  } = schema.nodes;
42
+
43
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
44
+ // but keep this solution general
45
+ const isBlockTaskEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
46
+
47
+ /**
48
+ * Extracts paragraph children from a blockTaskItem, preserving their marks.
49
+ */
50
+ const extractParagraphsFromBlockTaskItem = node => {
51
+ const paragraphs = [];
52
+ node.forEach(child => {
53
+ if (child.type === paragraph) {
54
+ paragraphs.push(child);
55
+ }
56
+ });
57
+ return paragraphs;
58
+ };
31
59
  listNode.forEach(child => {
32
60
  if (isSourceBulletOrOrdered && isTargetTask) {
33
61
  // Convert bullet/ordered => task
34
62
  if (child.type === listItem) {
35
63
  const inlineContent = [];
36
64
  const nestedTaskLists = [];
65
+ let blockMarks = [];
37
66
  child.forEach(grandChild => {
38
67
  if (supportedListTypes.has(grandChild.type) && grandChild.type !== taskList) {
39
68
  nestedTaskLists.push(transformListRecursively({
@@ -43,18 +72,37 @@ export const transformListRecursively = (props, onhandleUnsupportedContent) => {
43
72
  } else if (!getContentSupportChecker(taskItem)(grandChild) && !grandChild.isTextblock) {
44
73
  onhandleUnsupportedContent === null || onhandleUnsupportedContent === void 0 ? void 0 : onhandleUnsupportedContent(grandChild);
45
74
  } else {
75
+ if (isBlockTaskEnabled && grandChild.type === paragraph && grandChild.marks.length > 0) {
76
+ blockMarks = grandChild.marks;
77
+ }
46
78
  inlineContent.push(...convertBlockToInlineContent(grandChild, schema));
47
79
  }
48
80
  });
49
- transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
81
+ if (isBlockTaskEnabled && blockMarks.length > 0) {
82
+ transformedItems.push(createBlockTaskItemWithMarks(inlineContent, blockMarks, schema));
83
+ } else {
84
+ transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
85
+ }
50
86
  transformedItems.push(...nestedTaskLists);
51
87
  }
52
88
  } else if (isSourceTask && isTargetBulletOrOrdered) {
53
89
  // Convert task => bullet/ordered
54
90
  if (child.type === taskItem) {
55
91
  const inlineContent = [...child.content.content];
56
- const paragraphNode = paragraph.create(null, inlineContent.length > 0 ? inlineContent : null);
92
+
93
+ // Transfer taskItem's block marks to the paragraph.
94
+ // Use listItem.allowsMarkType since the paragraph will be inside a listItem
95
+ // (which uses ParagraphWithFontSizeStage0 that allows fontSize).
96
+ const paragraphMarks = isBlockTaskEnabled && child.marks.length > 0 ? child.marks.filter(mark => listItem.allowsMarkType(mark.type)) : undefined;
97
+ const paragraphNode = paragraph.create(null, inlineContent.length > 0 ? inlineContent : null, paragraphMarks);
57
98
  transformedItems.push(listItem.create(null, [paragraphNode]));
99
+ } else if (isBlockTaskEnabled && child.type === blockTaskItem) {
100
+ // blockTaskItem wraps content in paragraphs — extract them directly,
101
+ // preserving their fontSize marks
102
+ const paragraphs = extractParagraphsFromBlockTaskItem(child);
103
+ if (paragraphs.length > 0) {
104
+ transformedItems.push(listItem.create(null, paragraphs));
105
+ }
58
106
  } else if (child.type === taskList) {
59
107
  const transformedNestedList = transformListRecursively({
60
108
  ...props,
@@ -199,22 +247,28 @@ export const transformBetweenListTypes = context => {
199
247
  export const transformToTaskList = (tr, range, targetNodeType, targetAttrs, nodes) => {
200
248
  try {
201
249
  const {
202
- taskItem
250
+ taskItem,
251
+ paragraph,
252
+ blockTaskItem
203
253
  } = nodes;
254
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
255
+ // but keep this solution general
256
+ const isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
204
257
  const listItems = [];
205
258
 
206
259
  // Process each block in the range
207
260
  tr.doc.nodesBetween(range.start, range.end, node => {
208
261
  if (node.isBlock) {
209
- // For block nodes like paragraphs, directly use their inline content
210
262
  const inlineContent = [...node.content.content];
211
263
  if (inlineContent.length > 0) {
212
- // Create task item with inline content directly
213
- const listItem = taskItem.create(targetAttrs, inlineContent);
214
- listItems.push(listItem);
264
+ if (isBlockTaskItemEnabled && node.type === paragraph && node.marks.length > 0) {
265
+ listItems.push(createBlockTaskItemWithMarks(inlineContent, node.marks, tr.doc.type.schema));
266
+ } else {
267
+ listItems.push(taskItem.create(targetAttrs, inlineContent));
268
+ }
215
269
  }
216
270
  }
217
- return false; // Don't traverse into children
271
+ return false;
218
272
  });
219
273
  if (listItems.length === 0) {
220
274
  return null;
@@ -242,6 +296,39 @@ export const transformTaskListToBlockNodes = context => {
242
296
  selection
243
297
  } = tr;
244
298
  const schema = selection.$from.doc.type.schema;
299
+ const {
300
+ blockTaskItem
301
+ } = schema.nodes;
302
+
303
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
304
+ // but keep this solution general
305
+ const isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
306
+ if (isBlockTaskItemEnabled) {
307
+ const blockTaskItemsResult = findChildrenByType(sourceNode, blockTaskItem);
308
+ if (blockTaskItemsResult.length > 0 && targetNodeType === schema.nodes.paragraph) {
309
+ // blockTaskItem content is (paragraph | extension)+
310
+ // Extract paragraph children directly — they may carry block marks (e.g. fontSize)
311
+ const targetNodes = [];
312
+ for (const {
313
+ node: blockItem
314
+ } of blockTaskItemsResult) {
315
+ blockItem.forEach(child => {
316
+ if (child.type === schema.nodes.paragraph) {
317
+ targetNodes.push(child);
318
+ }
319
+ });
320
+ }
321
+ if (targetNodes.length === 0) {
322
+ return null;
323
+ }
324
+ const slice = new Slice(Fragment.fromArray(targetNodes), 0, 0);
325
+ const rangeStart = sourcePos !== null ? sourcePos : selection.from;
326
+ tr.replaceRange(rangeStart, rangeStart + sourceNode.nodeSize, slice);
327
+ return tr;
328
+ }
329
+ }
330
+
331
+ // Original logic for regular taskItem children
245
332
  const taskItemsResult = findChildrenByType(sourceNode, schema.nodes.taskItem);
246
333
  const taskItems = taskItemsResult.map(item => item.node);
247
334
  const taskItemFragments = taskItems.map(taskItem => taskItem.content);
@@ -283,6 +370,10 @@ export const getFormattedNode = tr => {
283
370
  nodes
284
371
  } = tr.doc.type.schema;
285
372
 
373
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
374
+ // but keep this solution general
375
+ const isBlockTaskItemEnabled = !!nodes.blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
376
+
286
377
  // Find the node to format from the current selection
287
378
  let nodeToFormat;
288
379
  let nodePos = selection.from;
@@ -294,13 +385,17 @@ export const getFormattedNode = tr => {
294
385
  nodePos = selectedNode.pos;
295
386
  } else {
296
387
  // Try to find parent node (including list parents)
297
- const parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
388
+ const parentNodeTypes = [nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection];
389
+ if (isBlockTaskItemEnabled) {
390
+ parentNodeTypes.push(nodes.blockTaskItem);
391
+ }
392
+ const parentNode = findParentNodeOfType(parentNodeTypes)(selection);
298
393
  if (parentNode) {
299
394
  nodeToFormat = parentNode.node;
300
395
  nodePos = parentNode.pos;
301
396
  const paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
302
- // Special case: if we found a listItem, check if we need the parent list instead
303
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
397
+ // Special case: if we found a listItem/taskItem/blockTaskItem, check if we need the parent list instead
398
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem || isBlockTaskItemEnabled && parentNode.node.type === nodes.blockTaskItem) {
304
399
  const listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
305
400
  if (listParent) {
306
401
  // For list transformations, we want the list parent, not the listItem
@@ -14,7 +14,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import Layer from '../Layer';
16
16
  const packageName = "@atlaskit/editor-common";
17
- const packageVersion = "112.8.3";
17
+ const packageVersion = "0.0.0-development";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -8,6 +8,7 @@ export { getCommonListAnalyticsAttributes, countListItemsInSelection } from './a
8
8
  export { hasValidListIndentationLevel } from './indentation';
9
9
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
10
10
  export { buildReplacementFragment } from './build-replacement-fragment';
11
+ export { narrowReplacementRange } from './narrow-replacement-range';
11
12
  export { flattenList } from './flatten-list';
12
13
  export { isListNode, isListItemNode, isBulletList, isParagraphNode } from '../utils';
13
14
  export { messages } from './messages';
@@ -0,0 +1,76 @@
1
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
2
+ /**
3
+ * Narrows a full-list replacement to the minimal changed range.
4
+ *
5
+ * Compares the old root list node with the new replacement fragment
6
+ * from both ends to find the first and last positions where they differ,
7
+ * then returns only the changed subrange.
8
+ *
9
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
10
+ * on unchanged items are preserved during collaborative editing.
11
+ */
12
+ export function narrowReplacementRange(doc, rootListStart, rootListEnd, fragment, contentStartOffsets) {
13
+ var oldNode = doc.nodeAt(rootListStart);
14
+ var newNode = fragment.childCount === 1 ? fragment.firstChild : null;
15
+ if (!oldNode || !newNode || newNode.type !== oldNode.type) {
16
+ return {
17
+ start: rootListStart,
18
+ end: rootListEnd,
19
+ fragment: fragment,
20
+ adjustedContentStartOffsets: contentStartOffsets
21
+ };
22
+ }
23
+ var minChildCount = Math.min(oldNode.childCount, newNode.childCount);
24
+ var commonPrefixChildren = 0;
25
+ var prefixSize = 0;
26
+ for (var i = 0; i < minChildCount; i++) {
27
+ var oldChild = oldNode.child(i);
28
+ var newChild = newNode.child(i);
29
+ if (oldChild.eq(newChild)) {
30
+ commonPrefixChildren++;
31
+ prefixSize += oldChild.nodeSize;
32
+ } else {
33
+ break;
34
+ }
35
+ }
36
+ var commonSuffixChildren = 0;
37
+ var suffixSize = 0;
38
+ for (var _i = 0; _i < minChildCount - commonPrefixChildren; _i++) {
39
+ var _oldChild = oldNode.child(oldNode.childCount - 1 - _i);
40
+ var _newChild = newNode.child(newNode.childCount - 1 - _i);
41
+ if (_oldChild.eq(_newChild)) {
42
+ commonSuffixChildren++;
43
+ suffixSize += _oldChild.nodeSize;
44
+ } else {
45
+ break;
46
+ }
47
+ }
48
+ var totalCommon = commonPrefixChildren + commonSuffixChildren;
49
+ if (totalCommon >= oldNode.childCount && totalCommon >= newNode.childCount) {
50
+ return {
51
+ start: rootListStart,
52
+ end: rootListStart,
53
+ fragment: Fragment.empty,
54
+ adjustedContentStartOffsets: contentStartOffsets
55
+ };
56
+ }
57
+ var narrowedStart = rootListStart + 1 + prefixSize;
58
+ var narrowedEnd = rootListEnd - 1 - suffixSize;
59
+ var changedChildStart = commonPrefixChildren;
60
+ var changedChildEnd = newNode.childCount - commonSuffixChildren;
61
+ var changedNodes = [];
62
+ for (var _i2 = changedChildStart; _i2 < changedChildEnd; _i2++) {
63
+ changedNodes.push(newNode.child(_i2));
64
+ }
65
+ var narrowedFragment = Fragment.from(changedNodes);
66
+ var prefixOffset = 1 + prefixSize;
67
+ var adjustedContentStartOffsets = contentStartOffsets.map(function (offset) {
68
+ return offset - prefixOffset;
69
+ });
70
+ return {
71
+ start: narrowedStart,
72
+ end: narrowedEnd,
73
+ fragment: narrowedFragment,
74
+ adjustedContentStartOffsets: adjustedContentStartOffsets
75
+ };
76
+ }
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl-next';
3
3
  import InfoIcon from '@atlaskit/icon/core/status-information';
4
4
  // eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
5
5
  import { Box, xcss } from '@atlaskit/primitives';
6
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
6
7
  import Tooltip from '@atlaskit/tooltip';
7
8
  import { externalMediaMessages } from '../media';
8
9
  var baseStyles = xcss({
@@ -13,6 +14,7 @@ var baseStyles = xcss({
13
14
  cursor: 'pointer'
14
15
  });
15
16
  var NO_EXTERNAL_BADGE_HOSTS = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com'];
17
+ var NO_EXTERNAL_BADGE_HOSTS_NEW = ['atlassian.com', 'loom.com', 'dam-cdn.atl.orangelogic.com', 'bitbucket.org'];
16
18
  export var isUnbadgedUrl = function isUnbadgedUrl(url) {
17
19
  if (!url) {
18
20
  return false;
@@ -30,6 +32,11 @@ export var isUnbadgedUrl = function isUnbadgedUrl(url) {
30
32
  if (protocol === 'data:') {
31
33
  return pathname === null || pathname === void 0 ? void 0 : pathname.startsWith('image/');
32
34
  }
35
+ if (expValEquals('platform_editor_media_external_badge_bbc_fix', 'isEnable', true)) {
36
+ return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS_NEW.some(function (host) {
37
+ return hostname === host || hostname.endsWith(".".concat(host));
38
+ }));
39
+ }
33
40
  return Boolean(hostname && NO_EXTERNAL_BADGE_HOSTS.some(function (host) {
34
41
  return hostname === host || hostname.endsWith(".".concat(host));
35
42
  }));
@@ -10,7 +10,7 @@ import { isFedRamp } from './environment';
10
10
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
11
11
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
12
12
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
13
- var packageVersion = "112.8.3";
13
+ var packageVersion = "0.0.0-development";
14
14
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
15
15
  // Remove URL as it has UGC
16
16
  // Ignored via go/ees007
@@ -1,9 +1,13 @@
1
1
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
2
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
4
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
5
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
3
6
  function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
4
7
  function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
5
8
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
6
9
  import { findChildrenByType, findParentNodeOfType, findSelectedNodeOfType } from '@atlaskit/editor-prosemirror/utils';
10
+ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
11
  import { getSupportedListTypesSet, isBulletOrOrderedList, isTaskList, convertBlockToInlineContent } from './list-utils';
8
12
  var getContentSupportChecker = function getContentSupportChecker(targetNodeType) {
9
13
  return function (node) {
@@ -14,6 +18,16 @@ var getContentSupportChecker = function getContentSupportChecker(targetNodeType)
14
18
  }
15
19
  };
16
20
  };
21
+ var createBlockTaskItemWithMarks = function createBlockTaskItemWithMarks(content, marks, schema) {
22
+ var _schema$nodes = schema.nodes,
23
+ blockTaskItem = _schema$nodes.blockTaskItem,
24
+ paragraph = _schema$nodes.paragraph;
25
+ var allowedMarks = marks.filter(function (mark) {
26
+ return blockTaskItem.allowsMarkType(mark.type);
27
+ });
28
+ var newParagraph = paragraph.create(null, content.length > 0 ? content : null, allowedMarks);
29
+ return blockTaskItem.create(null, newParagraph);
30
+ };
17
31
  var _transformListRecursively = function transformListRecursively(props, onhandleUnsupportedContent) {
18
32
  var transformedItems = [];
19
33
  var listNode = props.listNode,
@@ -24,17 +38,36 @@ var _transformListRecursively = function transformListRecursively(props, onhandl
24
38
  supportedListTypes = props.supportedListTypes,
25
39
  schema = props.schema,
26
40
  targetNodeType = props.targetNodeType;
27
- var _schema$nodes = schema.nodes,
28
- taskList = _schema$nodes.taskList,
29
- listItem = _schema$nodes.listItem,
30
- taskItem = _schema$nodes.taskItem,
31
- paragraph = _schema$nodes.paragraph;
41
+ var _schema$nodes2 = schema.nodes,
42
+ taskList = _schema$nodes2.taskList,
43
+ listItem = _schema$nodes2.listItem,
44
+ taskItem = _schema$nodes2.taskItem,
45
+ paragraph = _schema$nodes2.paragraph,
46
+ blockTaskItem = _schema$nodes2.blockTaskItem;
47
+
48
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
49
+ // but keep this solution general
50
+ var isBlockTaskEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
51
+
52
+ /**
53
+ * Extracts paragraph children from a blockTaskItem, preserving their marks.
54
+ */
55
+ var extractParagraphsFromBlockTaskItem = function extractParagraphsFromBlockTaskItem(node) {
56
+ var paragraphs = [];
57
+ node.forEach(function (child) {
58
+ if (child.type === paragraph) {
59
+ paragraphs.push(child);
60
+ }
61
+ });
62
+ return paragraphs;
63
+ };
32
64
  listNode.forEach(function (child) {
33
65
  if (isSourceBulletOrOrdered && isTargetTask) {
34
66
  // Convert bullet/ordered => task
35
67
  if (child.type === listItem) {
36
68
  var inlineContent = [];
37
69
  var nestedTaskLists = [];
70
+ var blockMarks = [];
38
71
  child.forEach(function (grandChild) {
39
72
  if (supportedListTypes.has(grandChild.type) && grandChild.type !== taskList) {
40
73
  nestedTaskLists.push(_transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
@@ -43,18 +76,39 @@ var _transformListRecursively = function transformListRecursively(props, onhandl
43
76
  } else if (!getContentSupportChecker(taskItem)(grandChild) && !grandChild.isTextblock) {
44
77
  onhandleUnsupportedContent === null || onhandleUnsupportedContent === void 0 || onhandleUnsupportedContent(grandChild);
45
78
  } else {
79
+ if (isBlockTaskEnabled && grandChild.type === paragraph && grandChild.marks.length > 0) {
80
+ blockMarks = grandChild.marks;
81
+ }
46
82
  inlineContent.push.apply(inlineContent, _toConsumableArray(convertBlockToInlineContent(grandChild, schema)));
47
83
  }
48
84
  });
49
- transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
85
+ if (isBlockTaskEnabled && blockMarks.length > 0) {
86
+ transformedItems.push(createBlockTaskItemWithMarks(inlineContent, blockMarks, schema));
87
+ } else {
88
+ transformedItems.push(taskItem.create(null, inlineContent.length > 0 ? inlineContent : null));
89
+ }
50
90
  transformedItems.push.apply(transformedItems, nestedTaskLists);
51
91
  }
52
92
  } else if (isSourceTask && isTargetBulletOrOrdered) {
53
93
  // Convert task => bullet/ordered
54
94
  if (child.type === taskItem) {
55
95
  var _inlineContent = _toConsumableArray(child.content.content);
56
- var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null);
96
+
97
+ // Transfer taskItem's block marks to the paragraph.
98
+ // Use listItem.allowsMarkType since the paragraph will be inside a listItem
99
+ // (which uses ParagraphWithFontSizeStage0 that allows fontSize).
100
+ var paragraphMarks = isBlockTaskEnabled && child.marks.length > 0 ? child.marks.filter(function (mark) {
101
+ return listItem.allowsMarkType(mark.type);
102
+ }) : undefined;
103
+ var paragraphNode = paragraph.create(null, _inlineContent.length > 0 ? _inlineContent : null, paragraphMarks);
57
104
  transformedItems.push(listItem.create(null, [paragraphNode]));
105
+ } else if (isBlockTaskEnabled && child.type === blockTaskItem) {
106
+ // blockTaskItem wraps content in paragraphs — extract them directly,
107
+ // preserving their fontSize marks
108
+ var paragraphs = extractParagraphsFromBlockTaskItem(child);
109
+ if (paragraphs.length > 0) {
110
+ transformedItems.push(listItem.create(null, paragraphs));
111
+ }
58
112
  } else if (child.type === taskList) {
59
113
  var transformedNestedList = _transformListRecursively(_objectSpread(_objectSpread({}, props), {}, {
60
114
  listNode: child
@@ -189,21 +243,27 @@ export var transformBetweenListTypes = function transformBetweenListTypes(contex
189
243
  */
190
244
  export var transformToTaskList = function transformToTaskList(tr, range, targetNodeType, targetAttrs, nodes) {
191
245
  try {
192
- var taskItem = nodes.taskItem;
246
+ var taskItem = nodes.taskItem,
247
+ paragraph = nodes.paragraph,
248
+ blockTaskItem = nodes.blockTaskItem;
249
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
250
+ // but keep this solution general
251
+ var isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
193
252
  var listItems = [];
194
253
 
195
254
  // Process each block in the range
196
255
  tr.doc.nodesBetween(range.start, range.end, function (node) {
197
256
  if (node.isBlock) {
198
- // For block nodes like paragraphs, directly use their inline content
199
257
  var inlineContent = _toConsumableArray(node.content.content);
200
258
  if (inlineContent.length > 0) {
201
- // Create task item with inline content directly
202
- var listItem = taskItem.create(targetAttrs, inlineContent);
203
- listItems.push(listItem);
259
+ if (isBlockTaskItemEnabled && node.type === paragraph && node.marks.length > 0) {
260
+ listItems.push(createBlockTaskItemWithMarks(inlineContent, node.marks, tr.doc.type.schema));
261
+ } else {
262
+ listItems.push(taskItem.create(targetAttrs, inlineContent));
263
+ }
204
264
  }
205
265
  }
206
- return false; // Don't traverse into children
266
+ return false;
207
267
  });
208
268
  if (listItems.length === 0) {
209
269
  return null;
@@ -227,6 +287,44 @@ export var transformTaskListToBlockNodes = function transformTaskListToBlockNode
227
287
  sourcePos = context.sourcePos;
228
288
  var selection = tr.selection;
229
289
  var schema = selection.$from.doc.type.schema;
290
+ var blockTaskItem = schema.nodes.blockTaskItem;
291
+
292
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
293
+ // but keep this solution general
294
+ var isBlockTaskItemEnabled = !!blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
295
+ if (isBlockTaskItemEnabled) {
296
+ var blockTaskItemsResult = findChildrenByType(sourceNode, blockTaskItem);
297
+ if (blockTaskItemsResult.length > 0 && targetNodeType === schema.nodes.paragraph) {
298
+ // blockTaskItem content is (paragraph | extension)+
299
+ // Extract paragraph children directly — they may carry block marks (e.g. fontSize)
300
+ var _targetNodes = [];
301
+ var _iterator = _createForOfIteratorHelper(blockTaskItemsResult),
302
+ _step;
303
+ try {
304
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
305
+ var blockItem = _step.value.node;
306
+ blockItem.forEach(function (child) {
307
+ if (child.type === schema.nodes.paragraph) {
308
+ _targetNodes.push(child);
309
+ }
310
+ });
311
+ }
312
+ } catch (err) {
313
+ _iterator.e(err);
314
+ } finally {
315
+ _iterator.f();
316
+ }
317
+ if (_targetNodes.length === 0) {
318
+ return null;
319
+ }
320
+ var _slice = new Slice(Fragment.fromArray(_targetNodes), 0, 0);
321
+ var _rangeStart = sourcePos !== null ? sourcePos : selection.from;
322
+ tr.replaceRange(_rangeStart, _rangeStart + sourceNode.nodeSize, _slice);
323
+ return tr;
324
+ }
325
+ }
326
+
327
+ // Original logic for regular taskItem children
230
328
  var taskItemsResult = findChildrenByType(sourceNode, schema.nodes.taskItem);
231
329
  var taskItems = taskItemsResult.map(function (item) {
232
330
  return item.node;
@@ -274,6 +372,10 @@ export var getFormattedNode = function getFormattedNode(tr) {
274
372
  var selection = tr.selection;
275
373
  var nodes = tr.doc.type.schema.nodes;
276
374
 
375
+ // gating behind platform_editor_small_font_size to support task lists with font size applied,
376
+ // but keep this solution general
377
+ var isBlockTaskItemEnabled = !!nodes.blockTaskItem && expValEquals('platform_editor_small_font_size', 'isEnabled', true);
378
+
277
379
  // Find the node to format from the current selection
278
380
  var nodeToFormat;
279
381
  var nodePos = selection.from;
@@ -285,13 +387,17 @@ export var getFormattedNode = function getFormattedNode(tr) {
285
387
  nodePos = selectedNode.pos;
286
388
  } else {
287
389
  // Try to find parent node (including list parents)
288
- var parentNode = findParentNodeOfType([nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection])(selection);
390
+ var parentNodeTypes = [nodes.blockquote, nodes.panel, nodes.expand, nodes.codeBlock, nodes.listItem, nodes.taskItem, nodes.layoutSection];
391
+ if (isBlockTaskItemEnabled) {
392
+ parentNodeTypes.push(nodes.blockTaskItem);
393
+ }
394
+ var parentNode = findParentNodeOfType(parentNodeTypes)(selection);
289
395
  if (parentNode) {
290
396
  nodeToFormat = parentNode.node;
291
397
  nodePos = parentNode.pos;
292
398
  var paragraphOrHeadingNode = findParentNodeOfType([nodes.paragraph, nodes.heading])(selection);
293
- // Special case: if we found a listItem, check if we need the parent list instead
294
- if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem) {
399
+ // Special case: if we found a listItem/taskItem/blockTaskItem, check if we need the parent list instead
400
+ if (parentNode.node.type === nodes.listItem || parentNode.node.type === nodes.taskItem || isBlockTaskItemEnabled && parentNode.node.type === nodes.blockTaskItem) {
295
401
  var listParent = findParentNodeOfType([nodes.bulletList, nodes.orderedList, nodes.taskList])(selection);
296
402
  if (listParent) {
297
403
  // For list transformations, we want the list parent, not the listItem
@@ -21,7 +21,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
21
21
  import { fg } from '@atlaskit/platform-feature-flags';
22
22
  import Layer from '../Layer';
23
23
  var packageName = "@atlaskit/editor-common";
24
- var packageVersion = "112.8.3";
24
+ var packageVersion = "0.0.0-development";
25
25
  var halfFocusRing = 1;
26
26
  var dropOffset = '0, 8';
27
27
  var fadeIn = keyframes({
@@ -6,6 +6,8 @@ export { hasValidListIndentationLevel } from './indentation';
6
6
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
7
7
  export { buildReplacementFragment } from './build-replacement-fragment';
8
8
  export type { BuildResult } from './build-replacement-fragment';
9
+ export { narrowReplacementRange } from './narrow-replacement-range';
10
+ export type { NarrowedReplacement } from './narrow-replacement-range';
9
11
  export type { FlattenedItem } from './flatten-list';
10
12
  export { flattenList } from './flatten-list';
11
13
  export type { FlattenListOptions, FlattenListResult } from './flatten-list';
@@ -0,0 +1,19 @@
1
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
+ export type NarrowedReplacement = {
4
+ adjustedContentStartOffsets: number[];
5
+ end: number;
6
+ fragment: Fragment;
7
+ start: number;
8
+ };
9
+ /**
10
+ * Narrows a full-list replacement to the minimal changed range.
11
+ *
12
+ * Compares the old root list node with the new replacement fragment
13
+ * from both ends to find the first and last positions where they differ,
14
+ * then returns only the changed subrange.
15
+ *
16
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
17
+ * on unchanged items are preserved during collaborative editing.
18
+ */
19
+ export declare function narrowReplacementRange(doc: PMNode, rootListStart: number, rootListEnd: number, fragment: Fragment, contentStartOffsets: number[]): NarrowedReplacement;
@@ -6,6 +6,8 @@ export { hasValidListIndentationLevel } from './indentation';
6
6
  export { restoreSelection, computeSelectionOffsets } from './restore-selection';
7
7
  export { buildReplacementFragment } from './build-replacement-fragment';
8
8
  export type { BuildResult } from './build-replacement-fragment';
9
+ export { narrowReplacementRange } from './narrow-replacement-range';
10
+ export type { NarrowedReplacement } from './narrow-replacement-range';
9
11
  export type { FlattenedItem } from './flatten-list';
10
12
  export { flattenList } from './flatten-list';
11
13
  export type { FlattenListOptions, FlattenListResult } from './flatten-list';
@@ -0,0 +1,19 @@
1
+ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
2
+ import { Fragment } from '@atlaskit/editor-prosemirror/model';
3
+ export type NarrowedReplacement = {
4
+ adjustedContentStartOffsets: number[];
5
+ end: number;
6
+ fragment: Fragment;
7
+ start: number;
8
+ };
9
+ /**
10
+ * Narrows a full-list replacement to the minimal changed range.
11
+ *
12
+ * Compares the old root list node with the new replacement fragment
13
+ * from both ends to find the first and last positions where they differ,
14
+ * then returns only the changed subrange.
15
+ *
16
+ * This reduces the scope of `tr.replaceWith()` so that remote cursors
17
+ * on unchanged items are preserved during collaborative editing.
18
+ */
19
+ export declare function narrowReplacementRange(doc: PMNode, rootListStart: number, rootListEnd: number, fragment: Fragment, contentStartOffsets: number[]): NarrowedReplacement;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-common",
3
- "version": "112.8.4",
3
+ "version": "112.9.1",
4
4
  "description": "A package that contains common classes and components for editor and renderer",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -35,14 +35,14 @@
35
35
  "@atlaskit/afm-i18n-platform-editor-editor-common": "2.18.0",
36
36
  "@atlaskit/analytics-listeners": "^10.0.0",
37
37
  "@atlaskit/analytics-namespaced-context": "^7.2.0",
38
- "@atlaskit/analytics-next": "^11.1.0",
38
+ "@atlaskit/analytics-next": "^11.2.0",
39
39
  "@atlaskit/atlassian-context": "^0.6.0",
40
40
  "@atlaskit/browser-apis": "^0.0.1",
41
41
  "@atlaskit/button": "^23.10.0",
42
42
  "@atlaskit/codemod-utils": "^4.2.0",
43
43
  "@atlaskit/css": "^0.19.0",
44
44
  "@atlaskit/custom-steps": "^0.16.0",
45
- "@atlaskit/dropdown-menu": "^16.7.0",
45
+ "@atlaskit/dropdown-menu": "^16.8.0",
46
46
  "@atlaskit/editor-json-transformer": "^8.31.0",
47
47
  "@atlaskit/editor-palette": "^2.1.0",
48
48
  "@atlaskit/editor-prosemirror": "^7.3.0",
@@ -70,7 +70,7 @@
70
70
  "@atlaskit/platform-feature-flags": "^1.1.0",
71
71
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
72
72
  "@atlaskit/popper": "^7.1.0",
73
- "@atlaskit/primitives": "^18.0.0",
73
+ "@atlaskit/primitives": "^18.1.0",
74
74
  "@atlaskit/profilecard": "^24.44.0",
75
75
  "@atlaskit/prosemirror-history": "^0.2.0",
76
76
  "@atlaskit/react-ufo": "^5.5.0",
@@ -81,7 +81,7 @@
81
81
  "@atlaskit/status": "^3.1.0",
82
82
  "@atlaskit/task-decision": "^19.3.0",
83
83
  "@atlaskit/textfield": "^8.2.0",
84
- "@atlaskit/tmp-editor-statsig": "^45.0.0",
84
+ "@atlaskit/tmp-editor-statsig": "^46.0.0",
85
85
  "@atlaskit/tokens": "^11.2.0",
86
86
  "@atlaskit/tooltip": "^21.0.0",
87
87
  "@atlaskit/width-detector": "^5.0.0",