@atlaskit/editor-plugin-code-block 14.0.0 → 14.0.2

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,21 @@
1
1
  # @atlaskit/editor-plugin-code-block
2
2
 
3
+ ## 14.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
9
+ ## 14.0.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [`4543a53261907`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/4543a53261907) -
14
+ Fix code block language picker behaviour for auto-detection state, cap the picker trigger width,
15
+ keep detected labels stable across toolbar updates, and avoid selecting the whole code block when
16
+ dragging across multiple code lines.
17
+ - Updated dependencies
18
+
3
19
  ## 14.0.0
4
20
 
5
21
  ### Major Changes
@@ -117,7 +117,9 @@ var detectLanguage = exports.detectLanguage = function detectLanguage() {
117
117
  }
118
118
  var autoDetectState = _autoDetectState.autoDetectPluginKey.getState(state);
119
119
  var previousEntry = autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId];
120
- var entry = (0, _autoDetectState2.createAutoDetectEntry)(node, pos, (0, _autoDetectState2.hasEnoughTextForAutoDetection)(node.textContent), previousEntry);
120
+ var entry = (0, _autoDetectState2.createAutoDetectEntry)(node, pos, (0, _autoDetectState2.hasEnoughTextForAutoDetection)(node.textContent), previousEntry, {
121
+ preserveDetectionResult: false
122
+ });
121
123
  var tr = state.tr.setNodeMarkup(pos, state.schema.nodes.codeBlock, _objectSpread(_objectSpread({}, node.attrs), {}, {
122
124
  language: null
123
125
  })).setMeta(_autoDetectState.autoDetectPluginKey, {
@@ -117,7 +117,7 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
117
117
  // Updates mapping position of all existing decorations to new positions
118
118
  // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
119
119
  var updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
120
- if (codeBlockNodes) {
120
+ if (codeBlockNodes.length) {
121
121
  (0, _codeBlock.updateCodeBlockWrappedStateNodeKeys)(codeBlockNodes, _oldState);
122
122
  // Disabled when using advanced code block for performance reasons
123
123
  // @ts-expect-error Code block advanced cannot depend on code block
@@ -32,33 +32,53 @@ var _pluginKey = require("./plugin-key");
32
32
  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); }
33
33
  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; }
34
34
  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; }
35
+ var getDetectLanguageOption = function getDetectLanguageOption(formatMessage) {
36
+ return {
37
+ alias: [],
38
+ label: formatMessage(_messages.codeBlockButtonMessages.detectLanguage),
39
+ value: _languagePickerOptions.DETECT_LANGUAGE_VALUE
40
+ };
41
+ };
42
+ var getNoneDetectedOption = function getNoneDetectedOption(formatMessage) {
43
+ return {
44
+ alias: [],
45
+ label: formatMessage(_messages.codeBlockButtonMessages.noneDetected),
46
+ value: _languagePickerOptions.NONE_LANGUAGE_VALUE
47
+ };
48
+ };
49
+ var getDefaultLanguagePickerValue = function getDefaultLanguagePickerValue(language, languagePickerOptions) {
50
+ return language ? languagePickerOptions.find(function (option) {
51
+ return language === _languagePickerOptions.NONE_LANGUAGE_VALUE ? option.value === _languagePickerOptions.PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language);
52
+ }) : undefined;
53
+ };
35
54
  var getAutoDetectPickerValue = function getAutoDetectPickerValue(_ref) {
36
55
  var autoDetectEntry = _ref.autoDetectEntry,
37
56
  formatMessage = _ref.formatMessage,
38
57
  language = _ref.language,
39
58
  languagePickerOptions = _ref.languagePickerOptions;
40
- var defaultPickerValue = language ? languagePickerOptions.find(function (option) {
41
- return language === _languagePickerOptions.NONE_LANGUAGE_VALUE ? option.value === _languagePickerOptions.PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language);
42
- }) : undefined;
59
+ var defaultPickerValue = getDefaultLanguagePickerValue(language, languagePickerOptions);
43
60
 
44
61
  // A weak re-detection records noneDetected but can leave a previously auto-detected
45
62
  // language on the node. Keep showing "(detected)" only while that preserved language
46
63
  // still matches the node language, so manual language changes do not inherit the label.
47
- if (defaultPickerValue && ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'detected' || (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && autoDetectEntry.autoDetectedLanguage === language)) {
64
+ var shouldShowDetectedLabel = Boolean(defaultPickerValue) && (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.autoDetectedLanguage) === language && Boolean(autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult);
65
+ if (shouldShowDetectedLabel && defaultPickerValue) {
48
66
  return _objectSpread(_objectSpread({}, defaultPickerValue), {}, {
49
67
  label: formatMessage(_messages.codeBlockButtonMessages.detectedLanguage, {
50
68
  language: defaultPickerValue.label
51
69
  })
52
70
  });
53
71
  }
54
- if ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && !language) {
55
- return {
56
- alias: [],
57
- label: formatMessage(_messages.codeBlockButtonMessages.noneDetected),
58
- value: _languagePickerOptions.NONE_LANGUAGE_VALUE
59
- };
72
+ if (language || !autoDetectEntry) {
73
+ return defaultPickerValue;
74
+ }
75
+ if (autoDetectEntry.detectionResult === 'noneDetected') {
76
+ return getNoneDetectedOption(formatMessage);
77
+ }
78
+ if (!autoDetectEntry.detectionResult) {
79
+ return getDetectLanguageOption(formatMessage);
60
80
  }
61
- return defaultPickerValue;
81
+ return undefined;
62
82
  };
63
83
  var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
64
84
  var allowCopyToClipboard = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
@@ -3,10 +3,10 @@
3
3
  ._mqm2ia51{border-block-start:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
4
4
  ._18m915vq{overflow-y:hidden}
5
5
  ._1bsb1osq{width:100%}
6
- ._1bsbuuw1{width:200px}
7
6
  ._1bto1l2s{text-overflow:ellipsis}
8
7
  ._1reo15vq{overflow-x:hidden}
9
8
  ._1xw9glyw button:after{content:none}
10
9
  ._ficf1e5h button{text-align:left}
11
10
  ._o5721q9c{white-space:nowrap}
11
+ ._p12fuuw1{max-width:200px}
12
12
  ._vwz478tn{line-height:1.714}
@@ -25,7 +25,7 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
25
25
  var pickerOptionStyles = null;
26
26
  var styles = {
27
27
  divider: "_h7alglyw _179rglyw _mqm2ia51 _1bsb1osq",
28
- trigger: "_1reo15vq _18m915vq _1bsbuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
28
+ trigger: "_1reo15vq _18m915vq _p12fuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
29
29
  };
30
30
  var CustomGroup = function CustomGroup(props) {
31
31
  var allGroups = props.selectProps.options;
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.updateAutoDetectState = exports.shouldTriggerLargeChangeDetection = exports.removeAutoDetection = exports.queueAutoDetection = exports.hasEnoughTextForAutoDetection = exports.getLocalId = exports.getFirstLine = exports.createAutoDetectEntry = void 0;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
9
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
11
  var _codeBlock = require("@atlaskit/editor-common/code-block");
11
- var _utils = require("../pm-plugins/utils");
12
+ var _utils = require("@atlaskit/editor-common/utils");
13
+ var _utils2 = require("../pm-plugins/utils");
12
14
  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; }
13
15
  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; }
14
16
  var MIN_AUTO_DETECT_TEXT_LENGTH = 20;
@@ -29,11 +31,12 @@ var getLocalId = exports.getLocalId = function getLocalId(node) {
29
31
  return typeof node.attrs.localId === 'string' ? node.attrs.localId : null;
30
32
  };
31
33
  var createAutoDetectEntry = exports.createAutoDetectEntry = function createAutoDetectEntry(node, pos, isPending, previous) {
34
+ var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
32
35
  return {
33
36
  lastObservedText: node.textContent,
34
37
  lastObservedFirstLine: getFirstLine(node.textContent),
35
38
  isPending: isPending,
36
- detectionResult: previous === null || previous === void 0 ? void 0 : previous.detectionResult,
39
+ detectionResult: options.preserveDetectionResult === false ? undefined : previous === null || previous === void 0 ? void 0 : previous.detectionResult,
37
40
  autoDetectedLanguage: previous === null || previous === void 0 ? void 0 : previous.autoDetectedLanguage,
38
41
  pos: pos
39
42
  };
@@ -80,6 +83,9 @@ var getCodeBlockLocalIdsRemovedFromChangedRanges = function getCodeBlockLocalIds
80
83
  });
81
84
  return localIds;
82
85
  };
86
+ var isHistoryMeta = function isHistoryMeta(meta) {
87
+ return (0, _typeof2.default)(meta) === 'object' && meta !== null && 'redo' in meta;
88
+ };
83
89
  var getCodeBlockTransactionChanges = function getCodeBlockTransactionChanges(tr, codeBlockType) {
84
90
  var insertedNodesWithPos = (0, _codeBlock.getInsertedCodeBlocksInTransaction)(tr, codeBlockType);
85
91
  var removedFromChangedRangesLocalIds = getCodeBlockLocalIdsRemovedFromChangedRanges(tr, codeBlockType);
@@ -116,6 +122,8 @@ var updateAutoDetectState = exports.updateAutoDetectState = function updateAutoD
116
122
  var codeBlock = tr.doc.type.schema.nodes.codeBlock;
117
123
  var isPaste = tr.getMeta('paste') === true || tr.getMeta('uiEvent') === 'paste';
118
124
  var isExternalContentChange = tr.getMeta('replaceDocument') === true || tr.getMeta('isRemote') === true;
125
+ var historyMeta = tr.getMeta(_utils.pmHistoryPluginKey);
126
+ var isUndo = isHistoryMeta(historyMeta) && historyMeta.redo === false;
119
127
  var hasTrackedEntries = Object.keys(pluginState.languageDetectionMap).length > 0;
120
128
 
121
129
  // Page loads and remote edits should not start auto-detection for code blocks.
@@ -124,7 +132,7 @@ var updateAutoDetectState = exports.updateAutoDetectState = function updateAutoD
124
132
  }
125
133
 
126
134
  // Existing entries still need mapping/deletion cleanup, but external edits should not refresh text.
127
- var changedCodeBlockNodes = isExternalContentChange ? [] : (0, _utils.getAllChangedCodeBlocksInTransaction)(tr);
135
+ var changedCodeBlockNodes = isExternalContentChange ? [] : (0, _utils2.getAllChangedCodeBlocksInTransaction)(tr);
128
136
  if (!hasTrackedEntries && !changedCodeBlockNodes.length) {
129
137
  return pluginState.languageDetectionMap;
130
138
  }
@@ -151,6 +159,7 @@ var updateAutoDetectState = exports.updateAutoDetectState = function updateAutoD
151
159
  languageDetectionMap = queueAutoDetection(languageDetectionMap, node, pos, hasEnoughTextForAutoDetection(node.textContent));
152
160
  });
153
161
  changedCodeBlockNodes.forEach(function (_ref5) {
162
+ var _tr$before$nodeAt;
154
163
  var node = _ref5.node,
155
164
  pos = _ref5.pos;
156
165
  var localId = getLocalId(node);
@@ -159,7 +168,10 @@ var updateAutoDetectState = exports.updateAutoDetectState = function updateAutoD
159
168
  }
160
169
  var currentLanguage = node.attrs.language;
161
170
  var previousEntry = languageDetectionMap[localId];
162
- if (currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
171
+ // Undo metadata does not identify the changed node; compare the pre/post language
172
+ // so text undo inside auto-detection mode keeps the entry.
173
+ var previousLanguage = isUndo ? (_tr$before$nodeAt = tr.before.nodeAt(tr.mapping.invert().map(pos))) === null || _tr$before$nodeAt === void 0 ? void 0 : _tr$before$nodeAt.attrs.language : undefined;
174
+ if (isUndo && previousLanguage && !currentLanguage || currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
163
175
  languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
164
176
  return;
165
177
  }
@@ -76,7 +76,8 @@ var runPendingDetection = function runPendingDetection(view, localId, api) {
76
76
  },
77
77
  eventType: _analytics.EVENT_TYPE.TRACK
78
78
  })(tr);
79
- return tr;
79
+ // Language detection runs in the background and should not move the viewport.
80
+ return tr.setMeta('scrollIntoView', false);
80
81
  });
81
82
  };
82
83
  var clearTimer = function clearTimer(timers, localId) {
@@ -105,7 +105,9 @@ export const detectLanguage = () => (state, dispatch) => {
105
105
  }
106
106
  const autoDetectState = autoDetectPluginKey.getState(state);
107
107
  const previousEntry = autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId];
108
- const entry = createAutoDetectEntry(node, pos, hasEnoughTextForAutoDetection(node.textContent), previousEntry);
108
+ const entry = createAutoDetectEntry(node, pos, hasEnoughTextForAutoDetection(node.textContent), previousEntry, {
109
+ preserveDetectionResult: false
110
+ });
109
111
  const tr = state.tr.setNodeMarkup(pos, state.schema.nodes.codeBlock, {
110
112
  ...node.attrs,
111
113
  language: null
@@ -105,7 +105,7 @@ export const createPlugin = ({
105
105
  // Updates mapping position of all existing decorations to new positions
106
106
  // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
107
107
  let updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
108
- if (codeBlockNodes) {
108
+ if (codeBlockNodes.length) {
109
109
  updateCodeBlockWrappedStateNodeKeys(codeBlockNodes, _oldState);
110
110
  // Disabled when using advanced code block for performance reasons
111
111
  // @ts-expect-error Code block advanced cannot depend on code block
@@ -13,24 +13,36 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
13
  import { changeLanguage, copyContentToClipboardWithAnalytics, createFormatCodeOnClick, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
14
14
  import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
15
15
  import { WrapIcon } from '../ui/icons/WrapIcon';
16
- import { NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
16
+ import { DETECT_LANGUAGE_VALUE, NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
17
17
  import { preloadFormatterOnIntent } from '../utils/format-code/formatter';
18
18
  import { autoDetectPluginKey } from './auto-detect-state';
19
19
  import { provideVisualFeedbackForCopyButton, removeVisualFeedbackForCopyButton } from './codeBlockCopySelectionPlugin';
20
20
  import { createLanguageList, DEFAULT_LANGUAGES, getLanguageIdentifier } from './language-list';
21
21
  import { pluginKey } from './plugin-key';
22
+ const getDetectLanguageOption = formatMessage => ({
23
+ alias: [],
24
+ label: formatMessage(codeBlockButtonMessages.detectLanguage),
25
+ value: DETECT_LANGUAGE_VALUE
26
+ });
27
+ const getNoneDetectedOption = formatMessage => ({
28
+ alias: [],
29
+ label: formatMessage(codeBlockButtonMessages.noneDetected),
30
+ value: NONE_LANGUAGE_VALUE
31
+ });
32
+ const getDefaultLanguagePickerValue = (language, languagePickerOptions) => language ? languagePickerOptions.find(option => language === NONE_LANGUAGE_VALUE ? option.value === PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language)) : undefined;
22
33
  const getAutoDetectPickerValue = ({
23
34
  autoDetectEntry,
24
35
  formatMessage,
25
36
  language,
26
37
  languagePickerOptions
27
38
  }) => {
28
- const defaultPickerValue = language ? languagePickerOptions.find(option => language === NONE_LANGUAGE_VALUE ? option.value === PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language)) : undefined;
39
+ const defaultPickerValue = getDefaultLanguagePickerValue(language, languagePickerOptions);
29
40
 
30
41
  // A weak re-detection records noneDetected but can leave a previously auto-detected
31
42
  // language on the node. Keep showing "(detected)" only while that preserved language
32
43
  // still matches the node language, so manual language changes do not inherit the label.
33
- if (defaultPickerValue && ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'detected' || (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && autoDetectEntry.autoDetectedLanguage === language)) {
44
+ const shouldShowDetectedLabel = Boolean(defaultPickerValue) && (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.autoDetectedLanguage) === language && Boolean(autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult);
45
+ if (shouldShowDetectedLabel && defaultPickerValue) {
34
46
  return {
35
47
  ...defaultPickerValue,
36
48
  label: formatMessage(codeBlockButtonMessages.detectedLanguage, {
@@ -38,14 +50,16 @@ const getAutoDetectPickerValue = ({
38
50
  })
39
51
  };
40
52
  }
41
- if ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && !language) {
42
- return {
43
- alias: [],
44
- label: formatMessage(codeBlockButtonMessages.noneDetected),
45
- value: NONE_LANGUAGE_VALUE
46
- };
53
+ if (language || !autoDetectEntry) {
54
+ return defaultPickerValue;
55
+ }
56
+ if (autoDetectEntry.detectionResult === 'noneDetected') {
57
+ return getNoneDetectedOption(formatMessage);
58
+ }
59
+ if (!autoDetectEntry.detectionResult) {
60
+ return getDetectLanguageOption(formatMessage);
47
61
  }
48
- return defaultPickerValue;
62
+ return undefined;
49
63
  };
50
64
  export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLanguageName = undefined, formatCodeProvider = undefined) => {
51
65
  const languageList = createLanguageList(overrideLanguageName ? DEFAULT_LANGUAGES.map(languageOption => ({
@@ -3,10 +3,10 @@
3
3
  ._mqm2ia51{border-block-start:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
4
4
  ._18m915vq{overflow-y:hidden}
5
5
  ._1bsb1osq{width:100%}
6
- ._1bsbuuw1{width:200px}
7
6
  ._1bto1l2s{text-overflow:ellipsis}
8
7
  ._1reo15vq{overflow-x:hidden}
9
8
  ._1xw9glyw button:after{content:none}
10
9
  ._ficf1e5h button{text-align:left}
11
10
  ._o5721q9c{white-space:nowrap}
11
+ ._p12fuuw1{max-width:200px}
12
12
  ._vwz478tn{line-height:1.714}
@@ -12,7 +12,7 @@ import { createGroupedLanguageOptions } from './language-picker-options';
12
12
  const pickerOptionStyles = null;
13
13
  const styles = {
14
14
  divider: "_h7alglyw _179rglyw _mqm2ia51 _1bsb1osq",
15
- trigger: "_1reo15vq _18m915vq _1bsbuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
15
+ trigger: "_1reo15vq _18m915vq _p12fuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
16
16
  };
17
17
  const CustomGroup = props => {
18
18
  const allGroups = props.selectProps.options;
@@ -1,4 +1,5 @@
1
1
  import { getInsertedCodeBlocksInTransaction } from '@atlaskit/editor-common/code-block';
2
+ import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
2
3
  import { getAllChangedCodeBlocksInTransaction } from '../pm-plugins/utils';
3
4
  const MIN_AUTO_DETECT_TEXT_LENGTH = 20;
4
5
  export const shouldTriggerLargeChangeDetection = (lastObservedText, text) => {
@@ -13,11 +14,11 @@ export const getFirstLine = text => {
13
14
  };
14
15
  export const hasEnoughTextForAutoDetection = text => text.trim().length >= MIN_AUTO_DETECT_TEXT_LENGTH;
15
16
  export const getLocalId = node => typeof node.attrs.localId === 'string' ? node.attrs.localId : null;
16
- export const createAutoDetectEntry = (node, pos, isPending, previous) => ({
17
+ export const createAutoDetectEntry = (node, pos, isPending, previous, options = {}) => ({
17
18
  lastObservedText: node.textContent,
18
19
  lastObservedFirstLine: getFirstLine(node.textContent),
19
20
  isPending,
20
- detectionResult: previous === null || previous === void 0 ? void 0 : previous.detectionResult,
21
+ detectionResult: options.preserveDetectionResult === false ? undefined : previous === null || previous === void 0 ? void 0 : previous.detectionResult,
21
22
  autoDetectedLanguage: previous === null || previous === void 0 ? void 0 : previous.autoDetectedLanguage,
22
23
  pos
23
24
  });
@@ -68,6 +69,7 @@ const getCodeBlockLocalIdsRemovedFromChangedRanges = (tr, codeBlockType) => {
68
69
  });
69
70
  return localIds;
70
71
  };
72
+ const isHistoryMeta = meta => typeof meta === 'object' && meta !== null && 'redo' in meta;
71
73
  const getCodeBlockTransactionChanges = (tr, codeBlockType) => {
72
74
  const insertedNodesWithPos = getInsertedCodeBlocksInTransaction(tr, codeBlockType);
73
75
  const removedFromChangedRangesLocalIds = getCodeBlockLocalIdsRemovedFromChangedRanges(tr, codeBlockType);
@@ -107,6 +109,8 @@ export const updateAutoDetectState = (tr, pluginState) => {
107
109
  } = tr.doc.type.schema.nodes;
108
110
  const isPaste = tr.getMeta('paste') === true || tr.getMeta('uiEvent') === 'paste';
109
111
  const isExternalContentChange = tr.getMeta('replaceDocument') === true || tr.getMeta('isRemote') === true;
112
+ const historyMeta = tr.getMeta(pmHistoryPluginKey);
113
+ const isUndo = isHistoryMeta(historyMeta) && historyMeta.redo === false;
110
114
  const hasTrackedEntries = Object.keys(pluginState.languageDetectionMap).length > 0;
111
115
 
112
116
  // Page loads and remote edits should not start auto-detection for code blocks.
@@ -143,13 +147,17 @@ export const updateAutoDetectState = (tr, pluginState) => {
143
147
  node,
144
148
  pos
145
149
  }) => {
150
+ var _tr$before$nodeAt;
146
151
  const localId = getLocalId(node);
147
152
  if (!localId || !languageDetectionMap[localId]) {
148
153
  return;
149
154
  }
150
155
  const currentLanguage = node.attrs.language;
151
156
  const previousEntry = languageDetectionMap[localId];
152
- if (currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
157
+ // Undo metadata does not identify the changed node; compare the pre/post language
158
+ // so text undo inside auto-detection mode keeps the entry.
159
+ const previousLanguage = isUndo ? (_tr$before$nodeAt = tr.before.nodeAt(tr.mapping.invert().map(pos))) === null || _tr$before$nodeAt === void 0 ? void 0 : _tr$before$nodeAt.attrs.language : undefined;
160
+ if (isUndo && previousLanguage && !currentLanguage || currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
153
161
  languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
154
162
  return;
155
163
  }
@@ -68,7 +68,8 @@ const runPendingDetection = (view, localId, api) => {
68
68
  },
69
69
  eventType: EVENT_TYPE.TRACK
70
70
  })(tr);
71
- return tr;
71
+ // Language detection runs in the background and should not move the viewport.
72
+ return tr.setMeta('scrollIntoView', false);
72
73
  });
73
74
  };
74
75
  const clearTimer = (timers, localId) => {
@@ -106,7 +106,9 @@ export var detectLanguage = function detectLanguage() {
106
106
  }
107
107
  var autoDetectState = autoDetectPluginKey.getState(state);
108
108
  var previousEntry = autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId];
109
- var entry = createAutoDetectEntry(node, pos, hasEnoughTextForAutoDetection(node.textContent), previousEntry);
109
+ var entry = createAutoDetectEntry(node, pos, hasEnoughTextForAutoDetection(node.textContent), previousEntry, {
110
+ preserveDetectionResult: false
111
+ });
110
112
  var tr = state.tr.setNodeMarkup(pos, state.schema.nodes.codeBlock, _objectSpread(_objectSpread({}, node.attrs), {}, {
111
113
  language: null
112
114
  })).setMeta(autoDetectPluginKey, {
@@ -110,7 +110,7 @@ export var createPlugin = function createPlugin(_ref) {
110
110
  // Updates mapping position of all existing decorations to new positions
111
111
  // specifically used for updating word wrap node decorators (does not cover drag & drop, validateWordWrappedDecorators does).
112
112
  var updatedDecorationSet = pluginState.decorations.map(tr.mapping, tr.doc);
113
- if (codeBlockNodes) {
113
+ if (codeBlockNodes.length) {
114
114
  updateCodeBlockWrappedStateNodeKeys(codeBlockNodes, _oldState);
115
115
  // Disabled when using advanced code block for performance reasons
116
116
  // @ts-expect-error Code block advanced cannot depend on code block
@@ -17,39 +17,59 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
17
17
  import { changeLanguage, copyContentToClipboardWithAnalytics, createFormatCodeOnClick, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
18
18
  import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
19
19
  import { WrapIcon } from '../ui/icons/WrapIcon';
20
- import { NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
20
+ import { DETECT_LANGUAGE_VALUE, NONE_LANGUAGE_VALUE, PLAIN_TEXT_LANGUAGE_VALUE } from '../ui/language-picker-options';
21
21
  import { preloadFormatterOnIntent } from '../utils/format-code/formatter';
22
22
  import { autoDetectPluginKey } from './auto-detect-state';
23
23
  import { provideVisualFeedbackForCopyButton, removeVisualFeedbackForCopyButton } from './codeBlockCopySelectionPlugin';
24
24
  import { createLanguageList, DEFAULT_LANGUAGES, getLanguageIdentifier } from './language-list';
25
25
  import { pluginKey } from './plugin-key';
26
+ var getDetectLanguageOption = function getDetectLanguageOption(formatMessage) {
27
+ return {
28
+ alias: [],
29
+ label: formatMessage(codeBlockButtonMessages.detectLanguage),
30
+ value: DETECT_LANGUAGE_VALUE
31
+ };
32
+ };
33
+ var getNoneDetectedOption = function getNoneDetectedOption(formatMessage) {
34
+ return {
35
+ alias: [],
36
+ label: formatMessage(codeBlockButtonMessages.noneDetected),
37
+ value: NONE_LANGUAGE_VALUE
38
+ };
39
+ };
40
+ var getDefaultLanguagePickerValue = function getDefaultLanguagePickerValue(language, languagePickerOptions) {
41
+ return language ? languagePickerOptions.find(function (option) {
42
+ return language === NONE_LANGUAGE_VALUE ? option.value === PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language);
43
+ }) : undefined;
44
+ };
26
45
  var getAutoDetectPickerValue = function getAutoDetectPickerValue(_ref) {
27
46
  var autoDetectEntry = _ref.autoDetectEntry,
28
47
  formatMessage = _ref.formatMessage,
29
48
  language = _ref.language,
30
49
  languagePickerOptions = _ref.languagePickerOptions;
31
- var defaultPickerValue = language ? languagePickerOptions.find(function (option) {
32
- return language === NONE_LANGUAGE_VALUE ? option.value === PLAIN_TEXT_LANGUAGE_VALUE : option.value === language || option.alias.includes(language);
33
- }) : undefined;
50
+ var defaultPickerValue = getDefaultLanguagePickerValue(language, languagePickerOptions);
34
51
 
35
52
  // A weak re-detection records noneDetected but can leave a previously auto-detected
36
53
  // language on the node. Keep showing "(detected)" only while that preserved language
37
54
  // still matches the node language, so manual language changes do not inherit the label.
38
- if (defaultPickerValue && ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'detected' || (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && autoDetectEntry.autoDetectedLanguage === language)) {
55
+ var shouldShowDetectedLabel = Boolean(defaultPickerValue) && (autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.autoDetectedLanguage) === language && Boolean(autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult);
56
+ if (shouldShowDetectedLabel && defaultPickerValue) {
39
57
  return _objectSpread(_objectSpread({}, defaultPickerValue), {}, {
40
58
  label: formatMessage(codeBlockButtonMessages.detectedLanguage, {
41
59
  language: defaultPickerValue.label
42
60
  })
43
61
  });
44
62
  }
45
- if ((autoDetectEntry === null || autoDetectEntry === void 0 ? void 0 : autoDetectEntry.detectionResult) === 'noneDetected' && !language) {
46
- return {
47
- alias: [],
48
- label: formatMessage(codeBlockButtonMessages.noneDetected),
49
- value: NONE_LANGUAGE_VALUE
50
- };
63
+ if (language || !autoDetectEntry) {
64
+ return defaultPickerValue;
65
+ }
66
+ if (autoDetectEntry.detectionResult === 'noneDetected') {
67
+ return getNoneDetectedOption(formatMessage);
68
+ }
69
+ if (!autoDetectEntry.detectionResult) {
70
+ return getDetectLanguageOption(formatMessage);
51
71
  }
52
- return defaultPickerValue;
72
+ return undefined;
53
73
  };
54
74
  export var getToolbarConfig = function getToolbarConfig() {
55
75
  var allowCopyToClipboard = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
@@ -3,10 +3,10 @@
3
3
  ._mqm2ia51{border-block-start:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
4
4
  ._18m915vq{overflow-y:hidden}
5
5
  ._1bsb1osq{width:100%}
6
- ._1bsbuuw1{width:200px}
7
6
  ._1bto1l2s{text-overflow:ellipsis}
8
7
  ._1reo15vq{overflow-x:hidden}
9
8
  ._1xw9glyw button:after{content:none}
10
9
  ._ficf1e5h button{text-align:left}
11
10
  ._o5721q9c{white-space:nowrap}
11
+ ._p12fuuw1{max-width:200px}
12
12
  ._vwz478tn{line-height:1.714}
@@ -16,7 +16,7 @@ import { createGroupedLanguageOptions } from './language-picker-options';
16
16
  var pickerOptionStyles = null;
17
17
  var styles = {
18
18
  divider: "_h7alglyw _179rglyw _mqm2ia51 _1bsb1osq",
19
- trigger: "_1reo15vq _18m915vq _1bsbuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
19
+ trigger: "_1reo15vq _18m915vq _p12fuuw1 _1bto1l2s _o5721q9c _ficf1e5h _1xw9glyw"
20
20
  };
21
21
  var CustomGroup = function CustomGroup(props) {
22
22
  var allGroups = props.selectProps.options;
@@ -1,8 +1,10 @@
1
1
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import _typeof from "@babel/runtime/helpers/typeof";
2
3
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
4
  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
5
  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
6
  import { getInsertedCodeBlocksInTransaction } from '@atlaskit/editor-common/code-block';
7
+ import { pmHistoryPluginKey } from '@atlaskit/editor-common/utils';
6
8
  import { getAllChangedCodeBlocksInTransaction } from '../pm-plugins/utils';
7
9
  var MIN_AUTO_DETECT_TEXT_LENGTH = 20;
8
10
  export var shouldTriggerLargeChangeDetection = function shouldTriggerLargeChangeDetection(lastObservedText, text) {
@@ -22,11 +24,12 @@ export var getLocalId = function getLocalId(node) {
22
24
  return typeof node.attrs.localId === 'string' ? node.attrs.localId : null;
23
25
  };
24
26
  export var createAutoDetectEntry = function createAutoDetectEntry(node, pos, isPending, previous) {
27
+ var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
25
28
  return {
26
29
  lastObservedText: node.textContent,
27
30
  lastObservedFirstLine: getFirstLine(node.textContent),
28
31
  isPending: isPending,
29
- detectionResult: previous === null || previous === void 0 ? void 0 : previous.detectionResult,
32
+ detectionResult: options.preserveDetectionResult === false ? undefined : previous === null || previous === void 0 ? void 0 : previous.detectionResult,
30
33
  autoDetectedLanguage: previous === null || previous === void 0 ? void 0 : previous.autoDetectedLanguage,
31
34
  pos: pos
32
35
  };
@@ -73,6 +76,9 @@ var getCodeBlockLocalIdsRemovedFromChangedRanges = function getCodeBlockLocalIds
73
76
  });
74
77
  return localIds;
75
78
  };
79
+ var isHistoryMeta = function isHistoryMeta(meta) {
80
+ return _typeof(meta) === 'object' && meta !== null && 'redo' in meta;
81
+ };
76
82
  var getCodeBlockTransactionChanges = function getCodeBlockTransactionChanges(tr, codeBlockType) {
77
83
  var insertedNodesWithPos = getInsertedCodeBlocksInTransaction(tr, codeBlockType);
78
84
  var removedFromChangedRangesLocalIds = getCodeBlockLocalIdsRemovedFromChangedRanges(tr, codeBlockType);
@@ -109,6 +115,8 @@ export var updateAutoDetectState = function updateAutoDetectState(tr, pluginStat
109
115
  var codeBlock = tr.doc.type.schema.nodes.codeBlock;
110
116
  var isPaste = tr.getMeta('paste') === true || tr.getMeta('uiEvent') === 'paste';
111
117
  var isExternalContentChange = tr.getMeta('replaceDocument') === true || tr.getMeta('isRemote') === true;
118
+ var historyMeta = tr.getMeta(pmHistoryPluginKey);
119
+ var isUndo = isHistoryMeta(historyMeta) && historyMeta.redo === false;
112
120
  var hasTrackedEntries = Object.keys(pluginState.languageDetectionMap).length > 0;
113
121
 
114
122
  // Page loads and remote edits should not start auto-detection for code blocks.
@@ -144,6 +152,7 @@ export var updateAutoDetectState = function updateAutoDetectState(tr, pluginStat
144
152
  languageDetectionMap = queueAutoDetection(languageDetectionMap, node, pos, hasEnoughTextForAutoDetection(node.textContent));
145
153
  });
146
154
  changedCodeBlockNodes.forEach(function (_ref5) {
155
+ var _tr$before$nodeAt;
147
156
  var node = _ref5.node,
148
157
  pos = _ref5.pos;
149
158
  var localId = getLocalId(node);
@@ -152,7 +161,10 @@ export var updateAutoDetectState = function updateAutoDetectState(tr, pluginStat
152
161
  }
153
162
  var currentLanguage = node.attrs.language;
154
163
  var previousEntry = languageDetectionMap[localId];
155
- if (currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
164
+ // Undo metadata does not identify the changed node; compare the pre/post language
165
+ // so text undo inside auto-detection mode keeps the entry.
166
+ var previousLanguage = isUndo ? (_tr$before$nodeAt = tr.before.nodeAt(tr.mapping.invert().map(pos))) === null || _tr$before$nodeAt === void 0 ? void 0 : _tr$before$nodeAt.attrs.language : undefined;
167
+ if (isUndo && previousLanguage && !currentLanguage || currentLanguage && currentLanguage !== previousEntry.autoDetectedLanguage) {
156
168
  languageDetectionMap = removeAutoDetection(languageDetectionMap, localId);
157
169
  return;
158
170
  }
@@ -69,7 +69,8 @@ var runPendingDetection = function runPendingDetection(view, localId, api) {
69
69
  },
70
70
  eventType: EVENT_TYPE.TRACK
71
71
  })(tr);
72
- return tr;
72
+ // Language detection runs in the background and should not move the viewport.
73
+ return tr.setMeta('scrollIntoView', false);
73
74
  });
74
75
  };
75
76
  var clearTimer = function clearTimer(timers, localId) {
@@ -5,7 +5,11 @@ export declare const shouldTriggerLargeChangeDetection: (lastObservedText: strin
5
5
  export declare const getFirstLine: (text: string) => string;
6
6
  export declare const hasEnoughTextForAutoDetection: (text: string) => boolean;
7
7
  export declare const getLocalId: (node: PMNode) => string | null;
8
- export declare const createAutoDetectEntry: (node: PMNode, pos: number, isPending: boolean, previous?: AutoDetectEntry) => AutoDetectEntry;
8
+ type CreateAutoDetectEntryOptions = {
9
+ preserveDetectionResult?: boolean;
10
+ };
11
+ export declare const createAutoDetectEntry: (node: PMNode, pos: number, isPending: boolean, previous?: AutoDetectEntry, options?: CreateAutoDetectEntryOptions) => AutoDetectEntry;
9
12
  export declare const queueAutoDetection: (languageDetectionMap: AutoDetectState["languageDetectionMap"], node: PMNode, pos: number, isPending: boolean) => AutoDetectState["languageDetectionMap"];
10
13
  export declare const removeAutoDetection: (languageDetectionMap: AutoDetectState["languageDetectionMap"], localId: string) => AutoDetectState["languageDetectionMap"];
11
14
  export declare const updateAutoDetectState: (tr: ReadonlyTransaction, pluginState: AutoDetectState) => AutoDetectState["languageDetectionMap"];
15
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-code-block",
3
- "version": "14.0.0",
3
+ "version": "14.0.2",
4
4
  "description": "Code block plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -42,13 +42,13 @@
42
42
  "@atlaskit/primitives": "^20.0.0",
43
43
  "@atlaskit/prosemirror-input-rules": "^4.0.0",
44
44
  "@atlaskit/select": "^22.0.0",
45
- "@atlaskit/tmp-editor-statsig": "^104.0.0",
45
+ "@atlaskit/tmp-editor-statsig": "^106.0.0",
46
46
  "@atlaskit/tokens": "^14.0.0",
47
47
  "@babel/runtime": "^7.0.0",
48
48
  "@compiled/react": "^0.20.0"
49
49
  },
50
50
  "peerDependencies": {
51
- "@atlaskit/editor-common": "^116.0.0",
51
+ "@atlaskit/editor-common": "^116.2.0",
52
52
  "react": "^18.2.0",
53
53
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
54
54
  },