@atlaskit/editor-plugin-paste-options-toolbar 11.3.3 → 11.4.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/current-pasted-smart-link/package.json +17 -0
  3. package/dist/cjs/entry-points/current-pasted-smart-link.js +12 -0
  4. package/dist/cjs/pasteOptionsToolbarPlugin.js +29 -8
  5. package/dist/cjs/pm-plugins/util/format-handlers.js +1 -1
  6. package/dist/cjs/ui/on-paste-actions-menu/PasteActionsMenu.js +11 -1
  7. package/dist/cjs/ui/on-paste-actions-menu/PasteMenuComponents.js +1 -1
  8. package/dist/cjs/ui/utils/current-pasted-smart-link.js +56 -0
  9. package/dist/cjs/ui/utils/paste-menu-rules/isNotSingleLink.js +12 -9
  10. package/dist/es2019/entry-points/current-pasted-smart-link.js +2 -0
  11. package/dist/es2019/pasteOptionsToolbarPlugin.js +27 -8
  12. package/dist/es2019/pm-plugins/util/format-handlers.js +2 -2
  13. package/dist/es2019/ui/on-paste-actions-menu/PasteActionsMenu.js +12 -1
  14. package/dist/es2019/ui/on-paste-actions-menu/PasteMenuComponents.js +1 -1
  15. package/dist/es2019/ui/utils/current-pasted-smart-link.js +50 -0
  16. package/dist/es2019/ui/utils/paste-menu-rules/isNotSingleLink.js +10 -9
  17. package/dist/esm/entry-points/current-pasted-smart-link.js +2 -0
  18. package/dist/esm/pasteOptionsToolbarPlugin.js +29 -8
  19. package/dist/esm/pm-plugins/util/format-handlers.js +2 -2
  20. package/dist/esm/ui/on-paste-actions-menu/PasteActionsMenu.js +11 -1
  21. package/dist/esm/ui/on-paste-actions-menu/PasteMenuComponents.js +1 -1
  22. package/dist/esm/ui/utils/current-pasted-smart-link.js +50 -0
  23. package/dist/esm/ui/utils/paste-menu-rules/isNotSingleLink.js +12 -9
  24. package/dist/types/entry-points/current-pasted-smart-link.d.ts +1 -0
  25. package/dist/types/entry-points/paste-options-toolbar-plugin-type.d.ts +2 -1
  26. package/dist/types/pasteOptionsToolbarPluginType.d.ts +27 -19
  27. package/dist/types/ui/utils/current-pasted-smart-link.d.ts +2 -0
  28. package/dist/types-ts4.5/entry-points/current-pasted-smart-link.d.ts +1 -0
  29. package/dist/types-ts4.5/entry-points/paste-options-toolbar-plugin-type.d.ts +2 -1
  30. package/dist/types-ts4.5/pasteOptionsToolbarPluginType.d.ts +27 -19
  31. package/dist/types-ts4.5/ui/utils/current-pasted-smart-link.d.ts +2 -0
  32. package/package.json +5 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @atlaskit/editor-plugin-paste-options-toolbar
2
2
 
3
+ ## 11.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`52a08b0d14e39`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/52a08b0d14e39) -
8
+ Add code block language auto-detection experiment
9
+ - Updated dependencies
10
+
11
+ ## 11.4.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`d2be5b45b39b0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d2be5b45b39b0) -
16
+ Add Smart Link-aware paste actions for the paste actions menu V2 experiment.
17
+
18
+ ### Patch Changes
19
+
20
+ - Updated dependencies
21
+
3
22
  ## 11.3.3
4
23
 
5
24
  ### Patch Changes
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@atlaskit/editor-plugin-paste-options-toolbar/current-pasted-smart-link",
3
+ "main": "../dist/cjs/entry-points/current-pasted-smart-link.js",
4
+ "module": "../dist/esm/entry-points/current-pasted-smart-link.js",
5
+ "module:es2019": "../dist/es2019/entry-points/current-pasted-smart-link.js",
6
+ "sideEffects": [
7
+ "*.compiled.css"
8
+ ],
9
+ "types": "../dist/types/entry-points/current-pasted-smart-link.d.ts",
10
+ "typesVersions": {
11
+ ">=4.5 <5.9": {
12
+ "*": [
13
+ "../dist/types-ts4.5/entry-points/current-pasted-smart-link.d.ts"
14
+ ]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ Object.defineProperty(exports, "getSingleSmartLinkUrlFromSlice", {
7
+ enumerable: true,
8
+ get: function get() {
9
+ return _currentPastedSmartLink.getSingleSmartLinkUrlFromSlice;
10
+ }
11
+ });
12
+ var _currentPastedSmartLink = require("../ui/utils/current-pasted-smart-link");
@@ -9,6 +9,7 @@ var _react = _interopRequireWildcard(require("react"));
9
9
  var _hooks = require("@atlaskit/editor-common/hooks");
10
10
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
11
11
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
12
+ var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
12
13
  var _commands = require("./editor-commands/commands");
13
14
  var _main = require("./pm-plugins/main");
14
15
  var _types = require("./types/types");
@@ -17,6 +18,7 @@ var _exposureV = require("./ui/on-paste-actions-menu/exposure-v2");
17
18
  var _PasteActionsMenu = require("./ui/on-paste-actions-menu/PasteActionsMenu");
18
19
  var _PasteMenuComponents = require("./ui/on-paste-actions-menu/PasteMenuComponents");
19
20
  var _toolbar = require("./ui/toolbar");
21
+ var _currentPastedSmartLink = require("./ui/utils/current-pasted-smart-link");
20
22
  var _rules = require("./ui/utils/paste-menu-rules/rules");
21
23
  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); }
22
24
  var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref) {
@@ -24,6 +26,25 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
24
26
  var config = _ref.config,
25
27
  api = _ref.api;
26
28
  var editorAnalyticsAPI = api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions;
29
+ var getPasteMenuContext = function getPasteMenuContext() {
30
+ return {
31
+ getCurrentPasteRange: function getCurrentPasteRange() {
32
+ var _api$pasteOptionsTool;
33
+ var state = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
34
+ if (!state) {
35
+ return undefined;
36
+ }
37
+ return {
38
+ pasteEndPos: state.pasteEndPos,
39
+ pasteStartPos: state.pasteStartPos
40
+ };
41
+ },
42
+ getCurrentPastedUrl: function getCurrentPastedUrl() {
43
+ var _api$paste;
44
+ return (0, _currentPastedSmartLink.getSingleSmartLinkUrlFromSlice)(api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 || (_api$paste = _api$paste.sharedState.currentState()) === null || _api$paste === void 0 || (_api$paste = _api$paste.lastContentPasted) === null || _api$paste === void 0 ? void 0 : _api$paste.pastedSlice);
45
+ }
46
+ };
47
+ };
27
48
  if (config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_paste_actions_menu', 'isEnabled', true)) {
28
49
  var _api$uiControlRegistr;
29
50
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register((0, _PasteMenuComponents.getPasteMenuComponents)({
@@ -34,9 +55,9 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
34
55
  var _api$uiControlRegistr2;
35
56
  var rules = function () {
36
57
  var getContext = function getContext() {
37
- var _api$pasteOptionsTool, _api$paste;
38
- var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
39
- var pasteState = api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
58
+ var _api$pasteOptionsTool2, _api$paste2;
59
+ var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
60
+ var pasteState = api === null || api === void 0 || (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
40
61
  return {
41
62
  getPlaintextLength: function getPlaintextLength() {
42
63
  var _pasteOptsState$plain;
@@ -65,7 +86,7 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
65
86
  };
66
87
  return (0, _rules.createPasteMenuRuleFactories)(getContext);
67
88
  }();
68
- var productButtons = config.pasteMenuButtonsFactory(rules);
89
+ var productButtons = config.pasteMenuButtonsFactory(rules, getPasteMenuContext());
69
90
  api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 || _api$uiControlRegistr2.actions.register(productButtons);
70
91
  }
71
92
  return {
@@ -73,9 +94,9 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
73
94
  actions: {
74
95
  getPasteMenuRules: function getPasteMenuRules() {
75
96
  var getContext = function getContext() {
76
- var _api$pasteOptionsTool2, _api$paste2;
77
- var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
78
- var pasteState = api === null || api === void 0 || (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
97
+ var _api$pasteOptionsTool3, _api$paste3;
98
+ var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool3 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool3 === void 0 ? void 0 : _api$pasteOptionsTool3.sharedState.currentState();
99
+ var pasteState = api === null || api === void 0 || (_api$paste3 = api.paste) === null || _api$paste3 === void 0 ? void 0 : _api$paste3.sharedState.currentState();
79
100
  return {
80
101
  getPlaintextLength: function getPlaintextLength() {
81
102
  var _pasteOptsState$plain2;
@@ -159,7 +180,7 @@ var pasteOptionsToolbarPlugin = exports.pasteOptionsToolbarPlugin = function pas
159
180
  popupsMountPoint = _ref3.popupsMountPoint,
160
181
  popupsBoundariesElement = _ref3.popupsBoundariesElement,
161
182
  popupsScrollableElement = _ref3.popupsScrollableElement;
162
- if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_paste_actions_menu', 'isEnabled', true)) || !editorView) {
183
+ if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_paste_actions_menu', 'isEnabled', true) || ['hasSpellingAndGrammar', 'hasAltAiActions'].includes((0, _expVal.expValNoExposure)('platform_editor_paste_actions_menu_v2', 'variant', 'control')))) || !editorView) {
163
184
  return null;
164
185
  }
165
186
  return /*#__PURE__*/_react.default.createElement(_PasteActionsMenu.PasteActionsMenu, {
@@ -174,7 +174,7 @@ function getMarkdownSlice(text, schema, selection) {
174
174
  var $end = _state.Selection.atEnd(doc).$from;
175
175
  var openStart = canMergeOpenStart ? $start.depth : 0;
176
176
  var openEnd = canMergeOpenEnd ? $end.depth : 0;
177
- return (0, _codeBlock.defaultWrapForMarkdownCodeBlocksInSlice)(new _model.Slice(doc.content, openStart, openEnd), schema);
177
+ return (0, _codeBlock.normalizeMarkdownCodeBlockAttrsInSlice)(new _model.Slice(doc.content, openStart, openEnd), schema);
178
178
  } catch (error) {
179
179
  (0, _monitoring.logException)(error, {
180
180
  location: 'editor-plugin-paste-options-toolbar/util'
@@ -357,7 +357,17 @@ var PasteActionsMenu = exports.PasteActionsMenu = function PasteActionsMenu(_ref
357
357
  // Choose positioning strategy based on whether the menu appears inline
358
358
  // (right of cursor for short pastes) or anchored to the block ancestor
359
359
  // (right side of content area for longer pastes / AI actions).
360
- var positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
360
+ var positionCalculator;
361
+ try {
362
+ positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
363
+ } catch (error) {
364
+ positionCalculator = function positionCalculator(position) {
365
+ var _position$top3;
366
+ return _objectSpread(_objectSpread({}, position), {}, {
367
+ top: (_position$top3 = position.top) !== null && _position$top3 !== void 0 ? _position$top3 : 0
368
+ });
369
+ };
370
+ }
361
371
  return /*#__PURE__*/_react.default.createElement(PopupWithListeners, {
362
372
  target: target,
363
373
  mountTo: mountTo,
@@ -167,7 +167,7 @@ var getPasteMenuComponents = exports.getPasteMenuComponents = function getPasteM
167
167
  var hasVisibleAiActions = getHasVisibleAiActions(api);
168
168
  if (!hasVisibleAiActions) {
169
169
  return /*#__PURE__*/_react.default.createElement(_compiled.Box, {
170
- padding: "space.050"
170
+ padding: "space.100"
171
171
  }, props.children);
172
172
  }
173
173
  return /*#__PURE__*/_react.default.createElement(_editorToolbar.ToolbarDropdownItemSection, {
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getSingleSmartLinkUrlFromSlice = void 0;
7
+ var _isNotSingleLink = require("./paste-menu-rules/isNotSingleLink");
8
+ var getUrlFromTextLinkNode = function getUrlFromTextLinkNode(node) {
9
+ var _node$marks$0$attrs;
10
+ if (!node.isText || node.marks.length !== 1 || node.marks[0].type.name !== 'link') {
11
+ return undefined;
12
+ }
13
+ var href = (_node$marks$0$attrs = node.marks[0].attrs) === null || _node$marks$0$attrs === void 0 ? void 0 : _node$marks$0$attrs.href;
14
+ return typeof href === 'string' && node.text === href ? href : undefined;
15
+ };
16
+ var getUrlFromInlineCardNode = function getUrlFromInlineCardNode(node) {
17
+ var _node$attrs;
18
+ if (node.type.name !== 'inlineCard') {
19
+ return undefined;
20
+ }
21
+ var url = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.url;
22
+ return typeof url === 'string' ? url : undefined;
23
+ };
24
+ var significantChildren = function significantChildren(node) {
25
+ var children = [];
26
+ node.content.forEach(function (child) {
27
+ var _child$text;
28
+ if (child.isText && ((_child$text = child.text) === null || _child$text === void 0 ? void 0 : _child$text.trim()) === '') {
29
+ return;
30
+ }
31
+ children.push(child);
32
+ });
33
+ return children;
34
+ };
35
+ var getSingleSmartLinkUrlFromSlice = exports.getSingleSmartLinkUrlFromSlice = function getSingleSmartLinkUrlFromSlice(slice) {
36
+ var _getUrlFromTextLinkNo, _getUrlFromTextLinkNo2;
37
+ if (!slice || (0, _isNotSingleLink.isNotSingleLink)(slice)) {
38
+ return undefined;
39
+ }
40
+ if (slice.content.childCount !== 1) {
41
+ return undefined;
42
+ }
43
+ var topNode = slice.content.child(0);
44
+ var topNodeUrl = (_getUrlFromTextLinkNo = getUrlFromTextLinkNode(topNode)) !== null && _getUrlFromTextLinkNo !== void 0 ? _getUrlFromTextLinkNo : getUrlFromInlineCardNode(topNode);
45
+ if (topNodeUrl) {
46
+ return topNodeUrl;
47
+ }
48
+ if (topNode.type.name !== 'paragraph') {
49
+ return undefined;
50
+ }
51
+ var children = significantChildren(topNode);
52
+ if (children.length !== 1) {
53
+ return undefined;
54
+ }
55
+ return (_getUrlFromTextLinkNo2 = getUrlFromTextLinkNode(children[0])) !== null && _getUrlFromTextLinkNo2 !== void 0 ? _getUrlFromTextLinkNo2 : getUrlFromInlineCardNode(children[0]);
56
+ };
@@ -21,30 +21,33 @@ var isUrlOnlyLinkTextNode = function isUrlOnlyLinkTextNode(node) {
21
21
  };
22
22
 
23
23
  /**
24
- * Returns true if the slice represents a single inline card (smartlink) node.
24
+ * Returns true if the slice represents a single smart-link card node.
25
25
  * Handles two shapes:
26
- * - paragraph > inlineCard (smartlink from editor/renderer, wrapped in a paragraph)
27
- * - inlineCard (top-level inlineCard with no paragraph wrapper)
26
+ * - paragraph > inlineCard|blockCard (smartlink from editor/renderer, wrapped in a paragraph)
27
+ * - inlineCard|blockCard (top-level card with no paragraph wrapper)
28
28
  */
29
- var isSingleInlineCard = function isSingleInlineCard(slice) {
29
+ var isSingleSmartLinkCard = function isSingleSmartLinkCard(slice) {
30
+ var isSupportedCard = function isSupportedCard(node) {
31
+ return node.type.name === 'inlineCard' || node.type.name === 'blockCard';
32
+ };
30
33
  if (slice.content.childCount !== 1) {
31
34
  return false;
32
35
  }
33
36
  var topNode = slice.content.child(0);
34
37
 
35
- // Top-level inlineCard (no paragraph wrapper)
36
- if (topNode.type.name === 'inlineCard') {
38
+ // Top-level inlineCard/blockCard (no paragraph wrapper)
39
+ if (isSupportedCard(topNode)) {
37
40
  return true;
38
41
  }
39
42
 
40
- // paragraph > inlineCard
43
+ // paragraph > inlineCard/blockCard
41
44
  if (topNode.type.name !== 'paragraph') {
42
45
  return false;
43
46
  }
44
47
  if (topNode.childCount !== 1) {
45
48
  return false;
46
49
  }
47
- return topNode.child(0).type.name === 'inlineCard';
50
+ return isSupportedCard(topNode.child(0));
48
51
  };
49
52
 
50
53
  /**
@@ -113,5 +116,5 @@ var isNotSingleLink = exports.isNotSingleLink = function isNotSingleLink(slice)
113
116
  // No rich-text slice → plain text paste, not a single link in the relevant sense
114
117
  return true;
115
118
  }
116
- return !isSingleBareLink(slice) && !isSingleInlineCard(slice);
119
+ return !isSingleBareLink(slice) && !isSingleSmartLinkCard(slice);
117
120
  };
@@ -0,0 +1,2 @@
1
+ /* eslint-disable @atlaskit/editor/no-re-export */
2
+ export { getSingleSmartLinkUrlFromSlice } from '../ui/utils/current-pasted-smart-link';
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
3
  import { fg } from '@atlaskit/platform-feature-flags';
4
4
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
5
+ import { expValNoExposure } from '@atlaskit/tmp-editor-statsig/expVal';
5
6
  import { hideToolbar, showToolbar } from './editor-commands/commands';
6
7
  import { createPlugin } from './pm-plugins/main';
7
8
  import { pasteOptionsPluginKey, ToolbarDropdownOption } from './types/types';
@@ -10,6 +11,7 @@ import { firePasteActionsMenuV2ExperimentExposure } from './ui/on-paste-actions-
10
11
  import { PasteActionsMenu } from './ui/on-paste-actions-menu/PasteActionsMenu';
11
12
  import { getPasteMenuComponents } from './ui/on-paste-actions-menu/PasteMenuComponents';
12
13
  import { buildToolbar, isToolbarVisible } from './ui/toolbar';
14
+ import { getSingleSmartLinkUrlFromSlice } from './ui/utils/current-pasted-smart-link';
13
15
  import { createPasteMenuRuleFactories } from './ui/utils/paste-menu-rules/rules';
14
16
  export const pasteOptionsToolbarPlugin = ({
15
17
  config,
@@ -17,6 +19,23 @@ export const pasteOptionsToolbarPlugin = ({
17
19
  }) => {
18
20
  var _api$analytics;
19
21
  const editorAnalyticsAPI = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions;
22
+ const getPasteMenuContext = () => ({
23
+ getCurrentPasteRange: () => {
24
+ var _api$pasteOptionsTool;
25
+ const state = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
26
+ if (!state) {
27
+ return undefined;
28
+ }
29
+ return {
30
+ pasteEndPos: state.pasteEndPos,
31
+ pasteStartPos: state.pasteStartPos
32
+ };
33
+ },
34
+ getCurrentPastedUrl: () => {
35
+ var _api$paste, _api$paste$sharedStat, _api$paste$sharedStat2;
36
+ return getSingleSmartLinkUrlFromSlice(api === null || api === void 0 ? void 0 : (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : (_api$paste$sharedStat = _api$paste.sharedState.currentState()) === null || _api$paste$sharedStat === void 0 ? void 0 : (_api$paste$sharedStat2 = _api$paste$sharedStat.lastContentPasted) === null || _api$paste$sharedStat2 === void 0 ? void 0 : _api$paste$sharedStat2.pastedSlice);
37
+ }
38
+ });
20
39
  if (config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true)) {
21
40
  var _api$uiControlRegistr;
22
41
  api === null || api === void 0 ? void 0 : (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 ? void 0 : _api$uiControlRegistr.actions.register(getPasteMenuComponents({
@@ -27,9 +46,9 @@ export const pasteOptionsToolbarPlugin = ({
27
46
  var _api$uiControlRegistr2;
28
47
  const rules = (() => {
29
48
  const getContext = () => {
30
- var _api$pasteOptionsTool, _api$paste;
31
- const pasteOptsState = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
32
- const pasteState = api === null || api === void 0 ? void 0 : (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
49
+ var _api$pasteOptionsTool2, _api$paste2;
50
+ const pasteOptsState = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
51
+ const pasteState = api === null || api === void 0 ? void 0 : (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
33
52
  return {
34
53
  getPlaintextLength: () => {
35
54
  var _pasteOptsState$plain;
@@ -56,7 +75,7 @@ export const pasteOptionsToolbarPlugin = ({
56
75
  };
57
76
  return createPasteMenuRuleFactories(getContext);
58
77
  })();
59
- const productButtons = config.pasteMenuButtonsFactory(rules);
78
+ const productButtons = config.pasteMenuButtonsFactory(rules, getPasteMenuContext());
60
79
  api === null || api === void 0 ? void 0 : (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 ? void 0 : _api$uiControlRegistr2.actions.register(productButtons);
61
80
  }
62
81
  return {
@@ -64,9 +83,9 @@ export const pasteOptionsToolbarPlugin = ({
64
83
  actions: {
65
84
  getPasteMenuRules: () => {
66
85
  const getContext = () => {
67
- var _api$pasteOptionsTool2, _api$paste2;
68
- const pasteOptsState = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
69
- const pasteState = api === null || api === void 0 ? void 0 : (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
86
+ var _api$pasteOptionsTool3, _api$paste3;
87
+ const pasteOptsState = api === null || api === void 0 ? void 0 : (_api$pasteOptionsTool3 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool3 === void 0 ? void 0 : _api$pasteOptionsTool3.sharedState.currentState();
88
+ const pasteState = api === null || api === void 0 ? void 0 : (_api$paste3 = api.paste) === null || _api$paste3 === void 0 ? void 0 : _api$paste3.sharedState.currentState();
70
89
  return {
71
90
  getPlaintextLength: () => {
72
91
  var _pasteOptsState$plain2;
@@ -148,7 +167,7 @@ export const pasteOptionsToolbarPlugin = ({
148
167
  popupsBoundariesElement,
149
168
  popupsScrollableElement
150
169
  }) {
151
- if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true)) || !editorView) {
170
+ if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && (expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true) || ['hasSpellingAndGrammar', 'hasAltAiActions'].includes(expValNoExposure('platform_editor_paste_actions_menu_v2', 'variant', 'control')))) || !editorView) {
152
171
  return null;
153
172
  }
154
173
  return /*#__PURE__*/React.createElement(PasteActionsMenu, {
@@ -1,4 +1,4 @@
1
- import { defaultWrapForMarkdownCodeBlocksInSlice } from '@atlaskit/editor-common/code-block';
1
+ import { normalizeMarkdownCodeBlockAttrsInSlice } from '@atlaskit/editor-common/code-block';
2
2
  import { logException } from '@atlaskit/editor-common/monitoring';
3
3
  import { md } from '@atlaskit/editor-common/paste';
4
4
  import { MarkdownTransformer } from '@atlaskit/editor-markdown-transformer';
@@ -169,7 +169,7 @@ export function getMarkdownSlice(text, schema, selection) {
169
169
  const $end = Selection.atEnd(doc).$from;
170
170
  const openStart = canMergeOpenStart ? $start.depth : 0;
171
171
  const openEnd = canMergeOpenEnd ? $end.depth : 0;
172
- return defaultWrapForMarkdownCodeBlocksInSlice(new Slice(doc.content, openStart, openEnd), schema);
172
+ return normalizeMarkdownCodeBlockAttrsInSlice(new Slice(doc.content, openStart, openEnd), schema);
173
173
  } catch (error) {
174
174
  logException(error, {
175
175
  location: 'editor-plugin-paste-options-toolbar/util'
@@ -342,7 +342,18 @@ export const PasteActionsMenu = ({
342
342
  // Choose positioning strategy based on whether the menu appears inline
343
343
  // (right of cursor for short pastes) or anchored to the block ancestor
344
344
  // (right side of content area for longer pastes / AI actions).
345
- const positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
345
+ let positionCalculator;
346
+ try {
347
+ positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
348
+ } catch (error) {
349
+ positionCalculator = position => {
350
+ var _position$top3;
351
+ return {
352
+ ...position,
353
+ top: (_position$top3 = position.top) !== null && _position$top3 !== void 0 ? _position$top3 : 0
354
+ };
355
+ };
356
+ }
346
357
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
347
358
  target: target,
348
359
  mountTo: mountTo,
@@ -160,7 +160,7 @@ export const getPasteMenuComponents = ({
160
160
  const hasVisibleAiActions = getHasVisibleAiActions(api);
161
161
  if (!hasVisibleAiActions) {
162
162
  return /*#__PURE__*/React.createElement(Box, {
163
- padding: "space.050"
163
+ padding: "space.100"
164
164
  }, props.children);
165
165
  }
166
166
  return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
@@ -0,0 +1,50 @@
1
+ import { isNotSingleLink } from './paste-menu-rules/isNotSingleLink';
2
+ const getUrlFromTextLinkNode = node => {
3
+ var _node$marks$0$attrs;
4
+ if (!node.isText || node.marks.length !== 1 || node.marks[0].type.name !== 'link') {
5
+ return undefined;
6
+ }
7
+ const href = (_node$marks$0$attrs = node.marks[0].attrs) === null || _node$marks$0$attrs === void 0 ? void 0 : _node$marks$0$attrs.href;
8
+ return typeof href === 'string' && node.text === href ? href : undefined;
9
+ };
10
+ const getUrlFromInlineCardNode = node => {
11
+ var _node$attrs;
12
+ if (node.type.name !== 'inlineCard') {
13
+ return undefined;
14
+ }
15
+ const url = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.url;
16
+ return typeof url === 'string' ? url : undefined;
17
+ };
18
+ const significantChildren = node => {
19
+ const children = [];
20
+ node.content.forEach(child => {
21
+ var _child$text;
22
+ if (child.isText && ((_child$text = child.text) === null || _child$text === void 0 ? void 0 : _child$text.trim()) === '') {
23
+ return;
24
+ }
25
+ children.push(child);
26
+ });
27
+ return children;
28
+ };
29
+ export const getSingleSmartLinkUrlFromSlice = slice => {
30
+ var _getUrlFromTextLinkNo, _getUrlFromTextLinkNo2;
31
+ if (!slice || isNotSingleLink(slice)) {
32
+ return undefined;
33
+ }
34
+ if (slice.content.childCount !== 1) {
35
+ return undefined;
36
+ }
37
+ const topNode = slice.content.child(0);
38
+ const topNodeUrl = (_getUrlFromTextLinkNo = getUrlFromTextLinkNode(topNode)) !== null && _getUrlFromTextLinkNo !== void 0 ? _getUrlFromTextLinkNo : getUrlFromInlineCardNode(topNode);
39
+ if (topNodeUrl) {
40
+ return topNodeUrl;
41
+ }
42
+ if (topNode.type.name !== 'paragraph') {
43
+ return undefined;
44
+ }
45
+ const children = significantChildren(topNode);
46
+ if (children.length !== 1) {
47
+ return undefined;
48
+ }
49
+ return (_getUrlFromTextLinkNo2 = getUrlFromTextLinkNode(children[0])) !== null && _getUrlFromTextLinkNo2 !== void 0 ? _getUrlFromTextLinkNo2 : getUrlFromInlineCardNode(children[0]);
50
+ };
@@ -15,30 +15,31 @@ const isUrlOnlyLinkTextNode = node => {
15
15
  };
16
16
 
17
17
  /**
18
- * Returns true if the slice represents a single inline card (smartlink) node.
18
+ * Returns true if the slice represents a single smart-link card node.
19
19
  * Handles two shapes:
20
- * - paragraph > inlineCard (smartlink from editor/renderer, wrapped in a paragraph)
21
- * - inlineCard (top-level inlineCard with no paragraph wrapper)
20
+ * - paragraph > inlineCard|blockCard (smartlink from editor/renderer, wrapped in a paragraph)
21
+ * - inlineCard|blockCard (top-level card with no paragraph wrapper)
22
22
  */
23
- const isSingleInlineCard = slice => {
23
+ const isSingleSmartLinkCard = slice => {
24
+ const isSupportedCard = node => node.type.name === 'inlineCard' || node.type.name === 'blockCard';
24
25
  if (slice.content.childCount !== 1) {
25
26
  return false;
26
27
  }
27
28
  const topNode = slice.content.child(0);
28
29
 
29
- // Top-level inlineCard (no paragraph wrapper)
30
- if (topNode.type.name === 'inlineCard') {
30
+ // Top-level inlineCard/blockCard (no paragraph wrapper)
31
+ if (isSupportedCard(topNode)) {
31
32
  return true;
32
33
  }
33
34
 
34
- // paragraph > inlineCard
35
+ // paragraph > inlineCard/blockCard
35
36
  if (topNode.type.name !== 'paragraph') {
36
37
  return false;
37
38
  }
38
39
  if (topNode.childCount !== 1) {
39
40
  return false;
40
41
  }
41
- return topNode.child(0).type.name === 'inlineCard';
42
+ return isSupportedCard(topNode.child(0));
42
43
  };
43
44
 
44
45
  /**
@@ -107,5 +108,5 @@ export const isNotSingleLink = slice => {
107
108
  // No rich-text slice → plain text paste, not a single link in the relevant sense
108
109
  return true;
109
110
  }
110
- return !isSingleBareLink(slice) && !isSingleInlineCard(slice);
111
+ return !isSingleBareLink(slice) && !isSingleSmartLinkCard(slice);
111
112
  };
@@ -0,0 +1,2 @@
1
+ /* eslint-disable @atlaskit/editor/no-re-export */
2
+ export { getSingleSmartLinkUrlFromSlice } from '../ui/utils/current-pasted-smart-link';
@@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
2
2
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
3
3
  import { fg } from '@atlaskit/platform-feature-flags';
4
4
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
5
+ import { expValNoExposure } from '@atlaskit/tmp-editor-statsig/expVal';
5
6
  import { hideToolbar, showToolbar } from './editor-commands/commands';
6
7
  import { createPlugin } from './pm-plugins/main';
7
8
  import { pasteOptionsPluginKey, ToolbarDropdownOption } from './types/types';
@@ -10,12 +11,32 @@ import { firePasteActionsMenuV2ExperimentExposure } from './ui/on-paste-actions-
10
11
  import { PasteActionsMenu } from './ui/on-paste-actions-menu/PasteActionsMenu';
11
12
  import { getPasteMenuComponents } from './ui/on-paste-actions-menu/PasteMenuComponents';
12
13
  import { buildToolbar, isToolbarVisible } from './ui/toolbar';
14
+ import { getSingleSmartLinkUrlFromSlice } from './ui/utils/current-pasted-smart-link';
13
15
  import { createPasteMenuRuleFactories } from './ui/utils/paste-menu-rules/rules';
14
16
  export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref) {
15
17
  var _api$analytics;
16
18
  var config = _ref.config,
17
19
  api = _ref.api;
18
20
  var editorAnalyticsAPI = api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions;
21
+ var getPasteMenuContext = function getPasteMenuContext() {
22
+ return {
23
+ getCurrentPasteRange: function getCurrentPasteRange() {
24
+ var _api$pasteOptionsTool;
25
+ var state = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
26
+ if (!state) {
27
+ return undefined;
28
+ }
29
+ return {
30
+ pasteEndPos: state.pasteEndPos,
31
+ pasteStartPos: state.pasteStartPos
32
+ };
33
+ },
34
+ getCurrentPastedUrl: function getCurrentPastedUrl() {
35
+ var _api$paste;
36
+ return getSingleSmartLinkUrlFromSlice(api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 || (_api$paste = _api$paste.sharedState.currentState()) === null || _api$paste === void 0 || (_api$paste = _api$paste.lastContentPasted) === null || _api$paste === void 0 ? void 0 : _api$paste.pastedSlice);
37
+ }
38
+ };
39
+ };
19
40
  if (config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true)) {
20
41
  var _api$uiControlRegistr;
21
42
  api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register(getPasteMenuComponents({
@@ -26,9 +47,9 @@ export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref)
26
47
  var _api$uiControlRegistr2;
27
48
  var rules = function () {
28
49
  var getContext = function getContext() {
29
- var _api$pasteOptionsTool, _api$paste;
30
- var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool === void 0 ? void 0 : _api$pasteOptionsTool.sharedState.currentState();
31
- var pasteState = api === null || api === void 0 || (_api$paste = api.paste) === null || _api$paste === void 0 ? void 0 : _api$paste.sharedState.currentState();
50
+ var _api$pasteOptionsTool2, _api$paste2;
51
+ var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
52
+ var pasteState = api === null || api === void 0 || (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
32
53
  return {
33
54
  getPlaintextLength: function getPlaintextLength() {
34
55
  var _pasteOptsState$plain;
@@ -57,7 +78,7 @@ export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref)
57
78
  };
58
79
  return createPasteMenuRuleFactories(getContext);
59
80
  }();
60
- var productButtons = config.pasteMenuButtonsFactory(rules);
81
+ var productButtons = config.pasteMenuButtonsFactory(rules, getPasteMenuContext());
61
82
  api === null || api === void 0 || (_api$uiControlRegistr2 = api.uiControlRegistry) === null || _api$uiControlRegistr2 === void 0 || _api$uiControlRegistr2.actions.register(productButtons);
62
83
  }
63
84
  return {
@@ -65,9 +86,9 @@ export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref)
65
86
  actions: {
66
87
  getPasteMenuRules: function getPasteMenuRules() {
67
88
  var getContext = function getContext() {
68
- var _api$pasteOptionsTool2, _api$paste2;
69
- var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool2 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool2 === void 0 ? void 0 : _api$pasteOptionsTool2.sharedState.currentState();
70
- var pasteState = api === null || api === void 0 || (_api$paste2 = api.paste) === null || _api$paste2 === void 0 ? void 0 : _api$paste2.sharedState.currentState();
89
+ var _api$pasteOptionsTool3, _api$paste3;
90
+ var pasteOptsState = api === null || api === void 0 || (_api$pasteOptionsTool3 = api.pasteOptionsToolbarPlugin) === null || _api$pasteOptionsTool3 === void 0 ? void 0 : _api$pasteOptionsTool3.sharedState.currentState();
91
+ var pasteState = api === null || api === void 0 || (_api$paste3 = api.paste) === null || _api$paste3 === void 0 ? void 0 : _api$paste3.sharedState.currentState();
71
92
  return {
72
93
  getPlaintextLength: function getPlaintextLength() {
73
94
  var _pasteOptsState$plain2;
@@ -151,7 +172,7 @@ export var pasteOptionsToolbarPlugin = function pasteOptionsToolbarPlugin(_ref)
151
172
  popupsMountPoint = _ref3.popupsMountPoint,
152
173
  popupsBoundariesElement = _ref3.popupsBoundariesElement,
153
174
  popupsScrollableElement = _ref3.popupsScrollableElement;
154
- if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true)) || !editorView) {
175
+ if (!(config !== null && config !== void 0 && config.usePopupBasedPasteActionsMenu && (expValEqualsNoExposure('platform_editor_paste_actions_menu', 'isEnabled', true) || ['hasSpellingAndGrammar', 'hasAltAiActions'].includes(expValNoExposure('platform_editor_paste_actions_menu_v2', 'variant', 'control')))) || !editorView) {
155
176
  return null;
156
177
  }
157
178
  return /*#__PURE__*/React.createElement(PasteActionsMenu, {
@@ -1,4 +1,4 @@
1
- import { defaultWrapForMarkdownCodeBlocksInSlice } from '@atlaskit/editor-common/code-block';
1
+ import { normalizeMarkdownCodeBlockAttrsInSlice } from '@atlaskit/editor-common/code-block';
2
2
  import { logException } from '@atlaskit/editor-common/monitoring';
3
3
  import { md } from '@atlaskit/editor-common/paste';
4
4
  import { MarkdownTransformer } from '@atlaskit/editor-markdown-transformer';
@@ -167,7 +167,7 @@ export function getMarkdownSlice(text, schema, selection) {
167
167
  var $end = Selection.atEnd(doc).$from;
168
168
  var openStart = canMergeOpenStart ? $start.depth : 0;
169
169
  var openEnd = canMergeOpenEnd ? $end.depth : 0;
170
- return defaultWrapForMarkdownCodeBlocksInSlice(new Slice(doc.content, openStart, openEnd), schema);
170
+ return normalizeMarkdownCodeBlockAttrsInSlice(new Slice(doc.content, openStart, openEnd), schema);
171
171
  } catch (error) {
172
172
  logException(error, {
173
173
  location: 'editor-plugin-paste-options-toolbar/util'
@@ -342,7 +342,17 @@ export var PasteActionsMenu = function PasteActionsMenu(_ref) {
342
342
  // Choose positioning strategy based on whether the menu appears inline
343
343
  // (right of cursor for short pastes) or anchored to the block ancestor
344
344
  // (right side of content area for longer pastes / AI actions).
345
- var positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
345
+ var positionCalculator;
346
+ try {
347
+ positionCalculator = useInlinePosition ? onInlinePositionCalculated(editorView, pasteEndPos, target, popupContentRef) : onPositionCalculated(editorView, pasteStartPos, pasteEndPos, target, effectiveScrollableElement);
348
+ } catch (error) {
349
+ positionCalculator = function positionCalculator(position) {
350
+ var _position$top3;
351
+ return _objectSpread(_objectSpread({}, position), {}, {
352
+ top: (_position$top3 = position.top) !== null && _position$top3 !== void 0 ? _position$top3 : 0
353
+ });
354
+ };
355
+ }
346
356
  return /*#__PURE__*/React.createElement(PopupWithListeners, {
347
357
  target: target,
348
358
  mountTo: mountTo,
@@ -158,7 +158,7 @@ export var getPasteMenuComponents = function getPasteMenuComponents(_ref3) {
158
158
  var hasVisibleAiActions = getHasVisibleAiActions(api);
159
159
  if (!hasVisibleAiActions) {
160
160
  return /*#__PURE__*/React.createElement(Box, {
161
- padding: "space.050"
161
+ padding: "space.100"
162
162
  }, props.children);
163
163
  }
164
164
  return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
@@ -0,0 +1,50 @@
1
+ import { isNotSingleLink } from './paste-menu-rules/isNotSingleLink';
2
+ var getUrlFromTextLinkNode = function getUrlFromTextLinkNode(node) {
3
+ var _node$marks$0$attrs;
4
+ if (!node.isText || node.marks.length !== 1 || node.marks[0].type.name !== 'link') {
5
+ return undefined;
6
+ }
7
+ var href = (_node$marks$0$attrs = node.marks[0].attrs) === null || _node$marks$0$attrs === void 0 ? void 0 : _node$marks$0$attrs.href;
8
+ return typeof href === 'string' && node.text === href ? href : undefined;
9
+ };
10
+ var getUrlFromInlineCardNode = function getUrlFromInlineCardNode(node) {
11
+ var _node$attrs;
12
+ if (node.type.name !== 'inlineCard') {
13
+ return undefined;
14
+ }
15
+ var url = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.url;
16
+ return typeof url === 'string' ? url : undefined;
17
+ };
18
+ var significantChildren = function significantChildren(node) {
19
+ var children = [];
20
+ node.content.forEach(function (child) {
21
+ var _child$text;
22
+ if (child.isText && ((_child$text = child.text) === null || _child$text === void 0 ? void 0 : _child$text.trim()) === '') {
23
+ return;
24
+ }
25
+ children.push(child);
26
+ });
27
+ return children;
28
+ };
29
+ export var getSingleSmartLinkUrlFromSlice = function getSingleSmartLinkUrlFromSlice(slice) {
30
+ var _getUrlFromTextLinkNo, _getUrlFromTextLinkNo2;
31
+ if (!slice || isNotSingleLink(slice)) {
32
+ return undefined;
33
+ }
34
+ if (slice.content.childCount !== 1) {
35
+ return undefined;
36
+ }
37
+ var topNode = slice.content.child(0);
38
+ var topNodeUrl = (_getUrlFromTextLinkNo = getUrlFromTextLinkNode(topNode)) !== null && _getUrlFromTextLinkNo !== void 0 ? _getUrlFromTextLinkNo : getUrlFromInlineCardNode(topNode);
39
+ if (topNodeUrl) {
40
+ return topNodeUrl;
41
+ }
42
+ if (topNode.type.name !== 'paragraph') {
43
+ return undefined;
44
+ }
45
+ var children = significantChildren(topNode);
46
+ if (children.length !== 1) {
47
+ return undefined;
48
+ }
49
+ return (_getUrlFromTextLinkNo2 = getUrlFromTextLinkNode(children[0])) !== null && _getUrlFromTextLinkNo2 !== void 0 ? _getUrlFromTextLinkNo2 : getUrlFromInlineCardNode(children[0]);
50
+ };
@@ -15,30 +15,33 @@ var isUrlOnlyLinkTextNode = function isUrlOnlyLinkTextNode(node) {
15
15
  };
16
16
 
17
17
  /**
18
- * Returns true if the slice represents a single inline card (smartlink) node.
18
+ * Returns true if the slice represents a single smart-link card node.
19
19
  * Handles two shapes:
20
- * - paragraph > inlineCard (smartlink from editor/renderer, wrapped in a paragraph)
21
- * - inlineCard (top-level inlineCard with no paragraph wrapper)
20
+ * - paragraph > inlineCard|blockCard (smartlink from editor/renderer, wrapped in a paragraph)
21
+ * - inlineCard|blockCard (top-level card with no paragraph wrapper)
22
22
  */
23
- var isSingleInlineCard = function isSingleInlineCard(slice) {
23
+ var isSingleSmartLinkCard = function isSingleSmartLinkCard(slice) {
24
+ var isSupportedCard = function isSupportedCard(node) {
25
+ return node.type.name === 'inlineCard' || node.type.name === 'blockCard';
26
+ };
24
27
  if (slice.content.childCount !== 1) {
25
28
  return false;
26
29
  }
27
30
  var topNode = slice.content.child(0);
28
31
 
29
- // Top-level inlineCard (no paragraph wrapper)
30
- if (topNode.type.name === 'inlineCard') {
32
+ // Top-level inlineCard/blockCard (no paragraph wrapper)
33
+ if (isSupportedCard(topNode)) {
31
34
  return true;
32
35
  }
33
36
 
34
- // paragraph > inlineCard
37
+ // paragraph > inlineCard/blockCard
35
38
  if (topNode.type.name !== 'paragraph') {
36
39
  return false;
37
40
  }
38
41
  if (topNode.childCount !== 1) {
39
42
  return false;
40
43
  }
41
- return topNode.child(0).type.name === 'inlineCard';
44
+ return isSupportedCard(topNode.child(0));
42
45
  };
43
46
 
44
47
  /**
@@ -107,5 +110,5 @@ export var isNotSingleLink = function isNotSingleLink(slice) {
107
110
  // No rich-text slice → plain text paste, not a single link in the relevant sense
108
111
  return true;
109
112
  }
110
- return !isSingleBareLink(slice) && !isSingleInlineCard(slice);
113
+ return !isSingleBareLink(slice) && !isSingleSmartLinkCard(slice);
111
114
  };
@@ -0,0 +1 @@
1
+ export { getSingleSmartLinkUrlFromSlice } from '../ui/utils/current-pasted-smart-link';
@@ -1 +1,2 @@
1
- export type { PasteOptionsToolbarPlugin, PasteOptionsToolbarPluginDependencies, PasteOptionsToolbarSharedState, } from '../pasteOptionsToolbarPluginType';
1
+ export type { PasteOptionsToolbarPlugin, PasteOptionsToolbarPluginConfiguration, PasteOptionsToolbarPluginDependencies, PasteOptionsToolbarPasteMenuContext, PasteOptionsToolbarSharedState, } from '../pasteOptionsToolbarPluginType';
2
+ export type { PasteMenuRuleFactories } from '../ui/utils/paste-menu-rules/types';
@@ -20,6 +20,32 @@ export interface PasteOptionsToolbarSharedState {
20
20
  showLegacyOptions: boolean;
21
21
  showToolbar: boolean;
22
22
  }
23
+ export type PasteOptionsToolbarPasteMenuContext = {
24
+ getCurrentPastedUrl: () => string | undefined;
25
+ getCurrentPasteRange: () => {
26
+ pasteEndPos: number;
27
+ pasteStartPos: number;
28
+ } | undefined;
29
+ };
30
+ export type PasteOptionsToolbarPluginConfiguration = {
31
+ /**
32
+ * Optional factory for composing product-specific paste menu buttons.
33
+ * Called with the pre-bound rule factories so products can compose
34
+ * `isHidden` callbacks before plugin setup.
35
+ *
36
+ * @example
37
+ * pasteMenuButtonsFactory: (rules) => [
38
+ * {
39
+ * type: 'menu-item',
40
+ * key: 'my-product-button',
41
+ * isHidden: rules.allRules(rules.notProseRule, rules.minCharsRule(100)),
42
+ * component: () => <MyProductButton />,
43
+ * },
44
+ * ]
45
+ */
46
+ pasteMenuButtonsFactory?: (rules: PasteMenuRuleFactories, context?: PasteOptionsToolbarPasteMenuContext) => RegisterComponent[];
47
+ usePopupBasedPasteActionsMenu?: boolean;
48
+ };
23
49
  export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlugin', {
24
50
  actions: {
25
51
  /**
@@ -35,24 +61,6 @@ export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlu
35
61
  getPasteMenuRules: () => PasteMenuRuleFactories;
36
62
  };
37
63
  dependencies: PasteOptionsToolbarPluginDependencies;
38
- pluginConfiguration?: {
39
- /**
40
- * Optional factory for composing product-specific paste menu buttons.
41
- * Called with the pre-bound rule factories so products can compose
42
- * `isHidden` callbacks before plugin setup.
43
- *
44
- * @example
45
- * pasteMenuButtonsFactory: (rules) => [
46
- * {
47
- * type: 'menu-item',
48
- * key: 'my-product-button',
49
- * isHidden: rules.allRules(rules.notProseRule, rules.minCharsRule(100)),
50
- * component: () => <MyProductButton />,
51
- * },
52
- * ]
53
- */
54
- pasteMenuButtonsFactory?: (rules: PasteMenuRuleFactories) => RegisterComponent[];
55
- usePopupBasedPasteActionsMenu?: boolean;
56
- };
64
+ pluginConfiguration?: PasteOptionsToolbarPluginConfiguration;
57
65
  sharedState: PasteOptionsToolbarSharedState;
58
66
  }>;
@@ -0,0 +1,2 @@
1
+ import type { Slice } from '@atlaskit/editor-prosemirror/model';
2
+ export declare const getSingleSmartLinkUrlFromSlice: (slice: Slice | undefined) => string | undefined;
@@ -0,0 +1 @@
1
+ export { getSingleSmartLinkUrlFromSlice } from '../ui/utils/current-pasted-smart-link';
@@ -1 +1,2 @@
1
- export type { PasteOptionsToolbarPlugin, PasteOptionsToolbarPluginDependencies, PasteOptionsToolbarSharedState, } from '../pasteOptionsToolbarPluginType';
1
+ export type { PasteOptionsToolbarPlugin, PasteOptionsToolbarPluginConfiguration, PasteOptionsToolbarPluginDependencies, PasteOptionsToolbarPasteMenuContext, PasteOptionsToolbarSharedState, } from '../pasteOptionsToolbarPluginType';
2
+ export type { PasteMenuRuleFactories } from '../ui/utils/paste-menu-rules/types';
@@ -20,6 +20,32 @@ export interface PasteOptionsToolbarSharedState {
20
20
  showLegacyOptions: boolean;
21
21
  showToolbar: boolean;
22
22
  }
23
+ export type PasteOptionsToolbarPasteMenuContext = {
24
+ getCurrentPastedUrl: () => string | undefined;
25
+ getCurrentPasteRange: () => {
26
+ pasteEndPos: number;
27
+ pasteStartPos: number;
28
+ } | undefined;
29
+ };
30
+ export type PasteOptionsToolbarPluginConfiguration = {
31
+ /**
32
+ * Optional factory for composing product-specific paste menu buttons.
33
+ * Called with the pre-bound rule factories so products can compose
34
+ * `isHidden` callbacks before plugin setup.
35
+ *
36
+ * @example
37
+ * pasteMenuButtonsFactory: (rules) => [
38
+ * {
39
+ * type: 'menu-item',
40
+ * key: 'my-product-button',
41
+ * isHidden: rules.allRules(rules.notProseRule, rules.minCharsRule(100)),
42
+ * component: () => <MyProductButton />,
43
+ * },
44
+ * ]
45
+ */
46
+ pasteMenuButtonsFactory?: (rules: PasteMenuRuleFactories, context?: PasteOptionsToolbarPasteMenuContext) => RegisterComponent[];
47
+ usePopupBasedPasteActionsMenu?: boolean;
48
+ };
23
49
  export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlugin', {
24
50
  actions: {
25
51
  /**
@@ -35,24 +61,6 @@ export type PasteOptionsToolbarPlugin = NextEditorPlugin<'pasteOptionsToolbarPlu
35
61
  getPasteMenuRules: () => PasteMenuRuleFactories;
36
62
  };
37
63
  dependencies: PasteOptionsToolbarPluginDependencies;
38
- pluginConfiguration?: {
39
- /**
40
- * Optional factory for composing product-specific paste menu buttons.
41
- * Called with the pre-bound rule factories so products can compose
42
- * `isHidden` callbacks before plugin setup.
43
- *
44
- * @example
45
- * pasteMenuButtonsFactory: (rules) => [
46
- * {
47
- * type: 'menu-item',
48
- * key: 'my-product-button',
49
- * isHidden: rules.allRules(rules.notProseRule, rules.minCharsRule(100)),
50
- * component: () => <MyProductButton />,
51
- * },
52
- * ]
53
- */
54
- pasteMenuButtonsFactory?: (rules: PasteMenuRuleFactories) => RegisterComponent[];
55
- usePopupBasedPasteActionsMenu?: boolean;
56
- };
64
+ pluginConfiguration?: PasteOptionsToolbarPluginConfiguration;
57
65
  sharedState: PasteOptionsToolbarSharedState;
58
66
  }>;
@@ -0,0 +1,2 @@
1
+ import type { Slice } from '@atlaskit/editor-prosemirror/model';
2
+ export declare const getSingleSmartLinkUrlFromSlice: (slice: Slice | undefined) => string | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste-options-toolbar",
3
- "version": "11.3.3",
3
+ "version": "11.4.1",
4
4
  "description": "Paste options toolbar for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -30,26 +30,26 @@
30
30
  "atlaskit:src": "src/index.ts",
31
31
  "dependencies": {
32
32
  "@atlaskit/css": "^0.19.0",
33
- "@atlaskit/dropdown-menu": "^16.9.0",
33
+ "@atlaskit/dropdown-menu": "^16.10.0",
34
34
  "@atlaskit/editor-markdown-transformer": "^5.21.0",
35
35
  "@atlaskit/editor-plugin-analytics": "^10.1.0",
36
36
  "@atlaskit/editor-plugin-paste": "^11.2.0",
37
37
  "@atlaskit/editor-plugin-ui-control-registry": "^4.1.0",
38
38
  "@atlaskit/editor-prosemirror": "^7.3.0",
39
39
  "@atlaskit/editor-shared-styles": "^3.11.0",
40
- "@atlaskit/editor-toolbar": "^1.8.0",
40
+ "@atlaskit/editor-toolbar": "^1.9.0",
41
41
  "@atlaskit/editor-ui-control-model": "^1.2.0",
42
42
  "@atlaskit/icon": "^35.3.0",
43
43
  "@atlaskit/platform-feature-flags": "^1.1.0",
44
44
  "@atlaskit/primitives": "^19.0.0",
45
- "@atlaskit/tmp-editor-statsig": "^85.0.0",
45
+ "@atlaskit/tmp-editor-statsig": "^87.0.0",
46
46
  "@atlaskit/tokens": "^13.1.0",
47
47
  "@babel/runtime": "^7.0.0",
48
48
  "@compiled/react": "^0.20.0",
49
49
  "@emotion/react": "^11.7.1"
50
50
  },
51
51
  "peerDependencies": {
52
- "@atlaskit/editor-common": "^114.50.0",
52
+ "@atlaskit/editor-common": "^114.55.0",
53
53
  "react": "^18.2.0",
54
54
  "react-dom": "^18.2.0",
55
55
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"