@atlaskit/editor-common 114.16.0 → 114.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/cjs/extensibility/extensionNodeView.js +91 -22
  3. package/dist/cjs/messages/markdown-mode.js +5 -5
  4. package/dist/cjs/messages/media-insert.js +1 -1
  5. package/dist/cjs/messages/mentions.js +5 -0
  6. package/dist/cjs/monitoring/error.js +1 -1
  7. package/dist/cjs/ui/DropList/index.js +1 -1
  8. package/dist/cjs/ui/Expand/index.js +5 -0
  9. package/dist/cjs/ui/MediaSingle/grid.js +2 -10
  10. package/dist/cjs/ui/MediaSingle/styled.js +1 -13
  11. package/dist/es2019/extensibility/extensionNodeView.js +88 -22
  12. package/dist/es2019/messages/markdown-mode.js +5 -5
  13. package/dist/es2019/messages/media-insert.js +1 -1
  14. package/dist/es2019/messages/mentions.js +5 -0
  15. package/dist/es2019/monitoring/error.js +1 -1
  16. package/dist/es2019/ui/DropList/index.js +1 -1
  17. package/dist/es2019/ui/Expand/index.js +5 -0
  18. package/dist/es2019/ui/MediaSingle/grid.js +2 -10
  19. package/dist/es2019/ui/MediaSingle/styled.js +1 -13
  20. package/dist/esm/extensibility/extensionNodeView.js +91 -22
  21. package/dist/esm/messages/markdown-mode.js +5 -5
  22. package/dist/esm/messages/media-insert.js +1 -1
  23. package/dist/esm/messages/mentions.js +5 -0
  24. package/dist/esm/monitoring/error.js +1 -1
  25. package/dist/esm/ui/DropList/index.js +1 -1
  26. package/dist/esm/ui/Expand/index.js +5 -0
  27. package/dist/esm/ui/MediaSingle/grid.js +2 -10
  28. package/dist/esm/ui/MediaSingle/styled.js +1 -13
  29. package/dist/types/extensibility/extensionNodeView.d.ts +6 -13
  30. package/dist/types/messages/mentions.d.ts +16 -11
  31. package/dist/types/ui/Expand/index.d.ts +13 -8
  32. package/dist/types-ts4.5/extensibility/extensionNodeView.d.ts +6 -13
  33. package/dist/types-ts4.5/messages/mentions.d.ts +16 -11
  34. package/dist/types-ts4.5/ui/Expand/index.d.ts +13 -8
  35. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # @atlaskit/editor-common
2
2
 
3
+ ## 114.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`a759f33417d9b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a759f33417d9b) -
8
+ [PIRA-1311](https://opsj.atlassian.net/browse/PIRA-1311) - clean up platform_editor_media_vc_fixes
9
+
10
+ ### Patch Changes
11
+
12
+ - [`7043ace1c45f2`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/7043ace1c45f2) -
13
+ [ux] reduces slipperiness and improves clarity of inline invites for editor mentions
14
+ - Updated dependencies
15
+
16
+ ## 114.17.0
17
+
18
+ ### Minor Changes
19
+
20
+ - [`9c750ab0192db`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9c750ab0192db) -
21
+ Add role=group and aria-label to expand content div so screen readers announce the body of an
22
+ expand node
23
+ - [`09301dc1b0c55`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/09301dc1b0c55) -
24
+ [ux] Switches the order of the image generation tab in the media insert picker to the first
25
+ position and renames the tab to Create. All changes behind an experiment.
26
+
27
+ ### Patch Changes
28
+
29
+ - [`42fa4f485424b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/42fa4f485424b) -
30
+ [ux] Fix Table of Contents extension becoming invisible after drag-and-drop or layout resize when
31
+ the platform_editor_hydration_skip_react_portal experiment is enabled (EDITOR-6613). The SSR
32
+ DOM-reuse optimization is now restricted to the very first ExtensionNode init for each
33
+ (extensionKey, localId) identity within an EditorView. Subsequent ExtensionNodes for the same
34
+ identity (e.g. those created when ProseMirror constructs the new node view before destroying the
35
+ old one during drag-and-drop or layout resize) take the normal React render path instead of
36
+ mistakenly reusing the previous instance's still-mounted React-rendered domRef as if it were SSR
37
+ output. Per-identity scoping preserves the optimization for pages with multiple TOC extensions.
38
+ - Updated dependencies
39
+
3
40
  ## 114.16.0
4
41
 
5
42
  ### Minor Changes
@@ -23,6 +23,66 @@ var _ExtensionNodeWrapper = require("./ExtensionNodeWrapper");
23
23
  function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
24
24
  function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
25
25
  function _superPropGet(t, o, e, r) { var p = (0, _get2.default)((0, _getPrototypeOf2.default)(1 & r ? t.prototype : t), o, e); return 2 & r && "function" == typeof p ? function (t) { return p.apply(e, t); } : p; }
26
+ /**
27
+ * Allowlists of extension keys + layout
28
+ * Currently, only toc with default layout allows to skip React render.
29
+ * Extensions NOT in this list always follow the normal React render path.
30
+ */
31
+ var ssrHydrationExtensionAllowlist = ['toc'];
32
+ var ssrHydrationLayoutAllowlist = ['default'];
33
+ var isSSRHydrationEligible = function isSSRHydrationEligible(node) {
34
+ var _node$attrs;
35
+ if (node.type.name !== 'extension') {
36
+ return false;
37
+ }
38
+ var _ref = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {},
39
+ extensionKey = _ref.extensionKey,
40
+ layout = _ref.layout;
41
+ if (!ssrHydrationExtensionAllowlist.includes(extensionKey)) {
42
+ return false;
43
+ }
44
+ // Treat a missing layout attr as `default` (the schema default).
45
+ var effectiveLayout = layout !== null && layout !== void 0 ? layout : 'default';
46
+ return ssrHydrationLayoutAllowlist.includes(effectiveLayout);
47
+ };
48
+
49
+ /**
50
+ * Per-(EditorView, extensionKey + localId) record of which extension identities
51
+ * have already had their initial-hydration init pass. SSR DOM reuse is only valid
52
+ * the very first time an ExtensionNode init runs for a given identity in a given
53
+ * editor — that is the only moment a real SSR-rendered element for that identity
54
+ * can exist in the editor DOM.
55
+ *
56
+ * After the first init, any element matching the SSR selector is the previous
57
+ * node view's React-rendered domRef that ProseMirror has not yet detached (e.g.
58
+ * during DnD / layout resize, where ProseMirror constructs the new node view
59
+ * BEFORE destroying the old one). Reusing it as if it were SSR DOM causes the
60
+ * new node view to skip React rendering and leaves the extension invisible
61
+ * (EDITOR-6613).
62
+ */
63
+ var consumedHydrationIdentitiesByEditor = new WeakMap();
64
+ var _getHydrationIdentityKey = function getHydrationIdentityKey(extensionKey, localId) {
65
+ if (typeof extensionKey !== 'string' || typeof localId !== 'string') {
66
+ return null;
67
+ }
68
+ if (extensionKey === '' || localId === '') {
69
+ return null;
70
+ }
71
+ return "".concat(extensionKey, "::").concat(localId);
72
+ };
73
+ var hasHydrationIdentityBeenConsumed = function hasHydrationIdentityBeenConsumed(view, identityKey) {
74
+ var consumed = consumedHydrationIdentitiesByEditor.get(view);
75
+ return consumed ? consumed.has(identityKey) : false;
76
+ };
77
+ var markHydrationIdentityAsConsumed = function markHydrationIdentityAsConsumed(view, identityKey) {
78
+ var consumed = consumedHydrationIdentitiesByEditor.get(view);
79
+ if (!consumed) {
80
+ consumed = new Set();
81
+ consumedHydrationIdentitiesByEditor.set(view, consumed);
82
+ }
83
+ consumed.add(identityKey);
84
+ };
85
+
26
86
  // getInlineNodeViewProducer is a new api to use instead of ReactNodeView
27
87
  // when creating inline node views, however, it is difficult to test the impact
28
88
  // on selections when migrating inlineExtension to use the new api.
@@ -36,16 +96,8 @@ var ExtensionNode = exports.ExtensionNode = /*#__PURE__*/function (_ReactNodeVie
36
96
  args[_key] = arguments[_key];
37
97
  }
38
98
  _this = _callSuper(this, ExtensionNode, [].concat(args));
39
- /**
40
- * Track whether we found and are reusing SSR'd DOM.
41
- * When true, we skip React Portal rendering on first init to preserve SSR content.
42
- */
99
+ /** True between SSR DOM adoption in `createDomRef` and the SSR→React handoff in `update`. */
43
100
  (0, _defineProperty2.default)(_this, "didReuseSsrDom", false);
44
- /**
45
- * Track whether this is the first init call.
46
- * SSR content preservation only happens on the very first init.
47
- */
48
- (0, _defineProperty2.default)(_this, "isFirstInit", true);
49
101
  return _this;
50
102
  }
51
103
  (0, _inherits2.default)(ExtensionNode, _ReactNodeView);
@@ -59,14 +111,32 @@ var ExtensionNode = exports.ExtensionNode = /*#__PURE__*/function (_ReactNodeVie
59
111
  return this.node.type.isAtom || mutation.type !== 'selection' && mutation.attributeName !== 'data-layout';
60
112
  }
61
113
 
114
+ /** See {@link consumedHydrationIdentitiesByEditor}. Null when attrs are missing → SSR reuse skipped. */
115
+ }, {
116
+ key: "getHydrationIdentityKey",
117
+ value: function getHydrationIdentityKey() {
118
+ var _this$node$attrs, _this$node$attrs2;
119
+ return _getHydrationIdentityKey((_this$node$attrs = this.node.attrs) === null || _this$node$attrs === void 0 ? void 0 : _this$node$attrs.extensionKey, (_this$node$attrs2 = this.node.attrs) === null || _this$node$attrs2 === void 0 ? void 0 : _this$node$attrs2.localId);
120
+ }
121
+
122
+ /** True only for the first ExtensionNode of this identity in this editor. See {@link consumedHydrationIdentitiesByEditor}. */
123
+ }, {
124
+ key: "isInInitialHydrationWindow",
125
+ value: function isInInitialHydrationWindow() {
126
+ var identityKey = this.getHydrationIdentityKey();
127
+ if (identityKey === null) {
128
+ return false;
129
+ }
130
+ return !hasHydrationIdentityBeenConsumed(this.view, identityKey);
131
+ }
132
+
62
133
  // Reserve height by setting a minimum height for the extension node view element
63
134
  }, {
64
135
  key: "createDomRef",
65
136
  value: function createDomRef() {
66
137
  if (!(0, _platformFeatureFlags.fg)('confluence_connect_macro_preset_height')) {
67
- // Try to reuse SSR'd DOM node on first init only
68
- // This preserves SSR content and avoids TTVC mutations during hydration
69
- if (!(0, _coreUtils.isSSR)() && this.isFirstInit && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc' && (0, _expValEquals.expValEquals)('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
138
+ // SSR DOM reuse see {@link consumedHydrationIdentitiesByEditor}.
139
+ if (!(0, _coreUtils.isSSR)() && isSSRHydrationEligible(this.node) && this.isInInitialHydrationWindow() && (0, _expValEquals.expValEquals)('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
70
140
  var ssrElement = this.findSSRElement();
71
141
  if (ssrElement) {
72
142
  this.didReuseSsrDom = true;
@@ -121,22 +191,22 @@ var ExtensionNode = exports.ExtensionNode = /*#__PURE__*/function (_ReactNodeVie
121
191
  return null;
122
192
  }
123
193
 
124
- /**
125
- * Override init() to skip React Portal rendering on first init if we're reusing SSR'd DOM.
126
- * This preserves the SSR content without React unnecessarily re-rendering it.
127
- */
194
+ /** Skip React Portal render on first init when reusing SSR DOM. See {@link consumedHydrationIdentitiesByEditor}. */
128
195
  }, {
129
196
  key: "init",
130
197
  value: function init() {
131
198
  if (!(0, _expValEquals.expValEquals)('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
132
199
  _superPropGet(ExtensionNode, "init", this, 3)([]);
133
200
  } else {
134
- if (!(0, _coreUtils.isSSR)() && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc') {
201
+ var isEligibleForSsrReuse = !(0, _coreUtils.isSSR)() && isSSRHydrationEligible(this.node);
202
+ if (isEligibleForSsrReuse && this.isInInitialHydrationWindow()) {
135
203
  var ssrElement = this.findSSRElement();
136
204
  var shouldSkipInitRender = ssrElement !== null;
137
205
  _superPropGet(ExtensionNode, "init", this, 3)([shouldSkipInitRender]);
138
- if (shouldSkipInitRender) {
139
- this.isFirstInit = false;
206
+ var identityKey = this.getHydrationIdentityKey();
207
+ if (identityKey !== null) {
208
+ // Close the hydration window — see {@link consumedHydrationIdentitiesByEditor}.
209
+ markHydrationIdentityAsConsumed(this.view, identityKey);
140
210
  }
141
211
  } else {
142
212
  _superPropGet(ExtensionNode, "init", this, 3)([]);
@@ -218,9 +288,8 @@ var ExtensionNode = exports.ExtensionNode = /*#__PURE__*/function (_ReactNodeVie
218
288
  key: "render",
219
289
  value: function render(props, forwardRef) {
220
290
  var _props$extensionNodeV;
221
- // If we reused SSR'd DOM on first init, don't render React Portal
222
- // The SSR content is already perfect and doesn't need re-rendering
223
- if (this.didReuseSsrDom && this.isFirstInit) {
291
+ // While sitting on SSR DOM, skip the React portal see {@link didReuseSsrDom}.
292
+ if (this.didReuseSsrDom) {
224
293
  return null;
225
294
  }
226
295
  return /*#__PURE__*/_react.default.createElement(_ExtensionNodeWrapper.ExtensionNodeWrapper, {
@@ -22,18 +22,18 @@ var markdownModeMessages = exports.markdownModeMessages = (0, _reactIntl.defineM
22
22
  },
23
23
  source: {
24
24
  id: 'fabric.editor.markdownMode.viewToggle.source',
25
- defaultMessage: 'Markdown source',
26
- description: 'Tooltip and accessible label for the toggle button that switches the editor into raw markdown source view.'
25
+ defaultMessage: 'Syntax editor',
26
+ description: 'Tooltip and accessible label for the toggle button that switches the editor into raw markdown source (syntax) view.'
27
27
  },
28
28
  wysiwyg: {
29
29
  id: 'fabric.editor.markdownMode.viewToggle.wysiwyg',
30
- defaultMessage: 'WYSIWYG',
30
+ defaultMessage: 'WYSIWYG editor',
31
31
  description: 'Tooltip and accessible label for the toggle button that switches the editor into the WYSIWYG markdown editing view (the default).'
32
32
  },
33
33
  preview: {
34
34
  id: 'fabric.editor.markdownMode.viewToggle.preview',
35
- defaultMessage: 'Preview',
36
- description: 'Tooltip and accessible label for the toggle button that switches the editor into a read-only rendered markdown preview.'
35
+ defaultMessage: 'Split view (coming soon)',
36
+ description: 'Tooltip and accessible label for the toggle button that will switch the editor into a split view. This feature is not yet available.'
37
37
  },
38
38
  sourceAriaLabel: {
39
39
  id: 'fabric.editor.markdownMode.sourceView.ariaLabel',
@@ -68,7 +68,7 @@ var mediaInsertMessages = exports.mediaInsertMessages = (0, _reactIntl.defineMes
68
68
  },
69
69
  generateTabTitle: {
70
70
  id: 'fabric.editor.media.insert.generateTabTitle',
71
- defaultMessage: 'Generate with AI',
71
+ defaultMessage: 'Create',
72
72
  description: 'Title of the navigation tab that allows users to generate an image through AI'
73
73
  },
74
74
  mediaPickerPopupAriaLabel: {
@@ -40,5 +40,10 @@ var mentionMessages = exports.mentionMessages = (0, _reactIntl.defineMessages)({
40
40
  id: 'fabric.editor.inviteItem.sendInvite',
41
41
  defaultMessage: 'Send request to invite teammate',
42
42
  description: 'By line text for send request to invite teammate option shown in mentions.'
43
+ },
44
+ inviteButton: {
45
+ id: 'fabric.editor.inviteItem.inviteButton',
46
+ defaultMessage: 'Invite',
47
+ description: 'Label for the invite button shown in the mention typeahead invite item.'
43
48
  }
44
49
  });
@@ -19,7 +19,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
19
19
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
20
20
  var SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
21
21
  var packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
22
- var packageVersion = "114.15.0";
22
+ var packageVersion = "114.17.0";
23
23
  var sanitiseSentryEvents = function sanitiseSentryEvents(data, _hint) {
24
24
  // Remove URL as it has UGC
25
25
  // Ignored via go/ees007
@@ -24,7 +24,7 @@ function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.
24
24
  * @jsx jsx
25
25
  */ // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
26
26
  var packageName = "@atlaskit/editor-common";
27
- var packageVersion = "114.15.0";
27
+ var packageVersion = "114.17.0";
28
28
  var halfFocusRing = 1;
29
29
  var dropOffset = '0, 8';
30
30
  var fadeIn = (0, _react2.keyframes)({
@@ -50,6 +50,11 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
50
50
  id: 'fabric.editor.expandAriaLabel',
51
51
  defaultMessage: 'Give this expand a title',
52
52
  description: 'aria label for an expand node title input field'
53
+ },
54
+ expandBodyAriaLabel: {
55
+ id: 'fabric.editor.expandBodyAriaLabel',
56
+ defaultMessage: 'Expand body content',
57
+ description: 'Aria label for the body content of an expand node'
53
58
  }
54
59
  });
55
60
  var ExpandIconWrapper = exports.ExpandIconWrapper = function ExpandIconWrapper(_ref) {
@@ -11,7 +11,6 @@ exports.calcPxFromColumns = calcPxFromColumns;
11
11
  exports.calcPxFromPct = calcPxFromPct;
12
12
  exports.wrappedLayouts = exports.snapToGrid = exports.layoutSupportsWidth = void 0;
13
13
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
14
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
15
14
  var _constants = require("../../media-single/constants");
16
15
  var validWidthModes = ['center', 'wrap-left', 'wrap-right', 'align-start', 'align-end'];
17
16
  var layoutSupportsWidth = exports.layoutSupportsWidth = function layoutSupportsWidth(layout) {
@@ -67,10 +66,7 @@ var calcMediaPxWidth = exports.calcMediaPxWidth = function calcMediaPxWidth(opts
67
66
  }
68
67
  return calculatedPctWidth;
69
68
  }
70
- if ((0, _expValEquals.expValEquals)('platform_editor_media_vc_fixes', 'isEnabled', true)) {
71
- return calculatedPctWidth;
72
- }
73
- return Math.min(calculatedPctWidth, origWidth);
69
+ return calculatedPctWidth;
74
70
  }
75
71
  if (calculatedResizedPctWidth) {
76
72
  return calculatedResizedPctWidth;
@@ -84,11 +80,7 @@ var calcMediaPxWidth = exports.calcMediaPxWidth = function calcMediaPxWidth(opts
84
80
  } else if (layout && wrappedLayouts.indexOf(layout) !== -1) {
85
81
  // when layout is wrap-left, wrap-right, align-start, align-end
86
82
  // but no pctWidth is defined
87
- if ((0, _expValEquals.expValEquals)('platform_editor_media_vc_fixes', 'isEnabled', true)) {
88
- return Math.min(calcPxFromPct(0.5, lineLength || width), origWidth);
89
- }
90
- var halfLineLength = Math.ceil((lineLength || width) / 2);
91
- return origWidth <= halfLineLength ? origWidth : halfLineLength;
83
+ return Math.min(calcPxFromPct(0.5, lineLength || width), origWidth);
92
84
  }
93
85
  return origWidth;
94
86
  };
@@ -16,7 +16,6 @@ var _react = _interopRequireDefault(require("react"));
16
16
  var _react2 = require("@emotion/react");
17
17
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
18
18
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
19
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
20
19
  var _utils = require("../../utils");
21
20
  var _breakout = require("../../utils/breakout");
22
21
  var _excluded = ["children"];
@@ -137,16 +136,6 @@ function calcResizedWidth(layout, width) {
137
136
  return "".concat(width, "px");
138
137
  }
139
138
  }
140
- function calcMaxWidth(layout, containerWidth) {
141
- switch (layout) {
142
- case 'wide':
143
- return (0, _breakout.calcWideWidth)(containerWidth);
144
- case 'full-width':
145
- return (0, _breakout.calcBreakoutWidth)(layout, containerWidth);
146
- default:
147
- return '100%';
148
- }
149
- }
150
139
  function calcMargin(layout) {
151
140
  switch (layout) {
152
141
  case 'wrap-right':
@@ -231,7 +220,6 @@ var MediaSingleDimensionHelper = exports.MediaSingleDimensionHelper = function M
231
220
  _ref$isInRenderer = _ref.isInRenderer,
232
221
  isInRenderer = _ref$isInRenderer === void 0 ? false : _ref$isInRenderer;
233
222
  var calculatedWidth = roundToClosestEvenPxValue(isExtendedResizeExperienceOn ? "".concat(mediaSingleWidth || width, "px") : mediaSingleWidth ? calcResizedWidth(layout, width || 0, containerWidth) : calcLegacyWidth(layout, width || 0, containerWidth, fullWidthMode, isResized, isInsideOfInlineExtension));
234
- var calculatedMaxWidth = roundToClosestEvenPxValue(isExtendedResizeExperienceOn ? "".concat(containerWidth, "px") : calcMaxWidth(layout, containerWidth));
235
223
  var cssMaxWidth = isExtendedResizeExperienceOn ? 'var(--ak-editor-max-container-width)' : calcMaxCssForPercentageTypeMedia(layout);
236
224
 
237
225
  // jest warning: JSDOM version (22) doesn't support the new @container CSS rule
@@ -247,7 +235,7 @@ var MediaSingleDimensionHelper = exports.MediaSingleDimensionHelper = function M
247
235
  '@container ak-renderer-wrapper (min-width: 1px)': {
248
236
  maxWidth: '100cqw'
249
237
  }
250
- }) : (0, _expValEquals.expValEquals)('platform_editor_media_vc_fixes', 'isEnabled', true) ? "max-width: ".concat(cssMaxWidth, ";") : "max-width: ".concat(calculatedMaxWidth, ";"), isExtendedResizeExperienceOn && "&[class*='is-resizing'] {\n .new-file-experience-wrapper {\n box-shadow: none !important;\n }\n\n ".concat(!isNestedNode && _utils.nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", "\n }"), float(layout), calcMargin(layout), isNestedNode ? /* Make nested node appear responsive when resizing table cell */"max-width: 100%;" : _utils.nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", isImageAligned(layout));
238
+ }) : "max-width: ".concat(cssMaxWidth, ";"), isExtendedResizeExperienceOn && "&[class*='is-resizing'] {\n .new-file-experience-wrapper {\n box-shadow: none !important;\n }\n\n ".concat(!isNestedNode && _utils.nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", "\n }"), float(layout), calcMargin(layout), isNestedNode ? /* Make nested node appear responsive when resizing table cell */"max-width: 100%;" : _utils.nonWrappedLayouts.includes(layout) && "margin-left: 50%;\n transform: translateX(-50%);", isImageAligned(layout));
251
239
  };
252
240
  var RenderFallbackContainer = function RenderFallbackContainer(_ref2) {
253
241
  var hasFallbackContainer = _ref2.hasFallbackContainer,
@@ -6,6 +6,67 @@ import { isSSR } from '../core-utils';
6
6
  import ReactNodeView from '../react-node-view';
7
7
  import { Extension } from './Extension';
8
8
  import { ExtensionNodeWrapper } from './ExtensionNodeWrapper';
9
+ /**
10
+ * Allowlists of extension keys + layout
11
+ * Currently, only toc with default layout allows to skip React render.
12
+ * Extensions NOT in this list always follow the normal React render path.
13
+ */
14
+ const ssrHydrationExtensionAllowlist = ['toc'];
15
+ const ssrHydrationLayoutAllowlist = ['default'];
16
+ const isSSRHydrationEligible = node => {
17
+ var _node$attrs;
18
+ if (node.type.name !== 'extension') {
19
+ return false;
20
+ }
21
+ const {
22
+ extensionKey,
23
+ layout
24
+ } = (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 ? _node$attrs : {};
25
+ if (!ssrHydrationExtensionAllowlist.includes(extensionKey)) {
26
+ return false;
27
+ }
28
+ // Treat a missing layout attr as `default` (the schema default).
29
+ const effectiveLayout = layout !== null && layout !== void 0 ? layout : 'default';
30
+ return ssrHydrationLayoutAllowlist.includes(effectiveLayout);
31
+ };
32
+
33
+ /**
34
+ * Per-(EditorView, extensionKey + localId) record of which extension identities
35
+ * have already had their initial-hydration init pass. SSR DOM reuse is only valid
36
+ * the very first time an ExtensionNode init runs for a given identity in a given
37
+ * editor — that is the only moment a real SSR-rendered element for that identity
38
+ * can exist in the editor DOM.
39
+ *
40
+ * After the first init, any element matching the SSR selector is the previous
41
+ * node view's React-rendered domRef that ProseMirror has not yet detached (e.g.
42
+ * during DnD / layout resize, where ProseMirror constructs the new node view
43
+ * BEFORE destroying the old one). Reusing it as if it were SSR DOM causes the
44
+ * new node view to skip React rendering and leaves the extension invisible
45
+ * (EDITOR-6613).
46
+ */
47
+ const consumedHydrationIdentitiesByEditor = new WeakMap();
48
+ const getHydrationIdentityKey = (extensionKey, localId) => {
49
+ if (typeof extensionKey !== 'string' || typeof localId !== 'string') {
50
+ return null;
51
+ }
52
+ if (extensionKey === '' || localId === '') {
53
+ return null;
54
+ }
55
+ return `${extensionKey}::${localId}`;
56
+ };
57
+ const hasHydrationIdentityBeenConsumed = (view, identityKey) => {
58
+ const consumed = consumedHydrationIdentitiesByEditor.get(view);
59
+ return consumed ? consumed.has(identityKey) : false;
60
+ };
61
+ const markHydrationIdentityAsConsumed = (view, identityKey) => {
62
+ let consumed = consumedHydrationIdentitiesByEditor.get(view);
63
+ if (!consumed) {
64
+ consumed = new Set();
65
+ consumedHydrationIdentitiesByEditor.set(view, consumed);
66
+ }
67
+ consumed.add(identityKey);
68
+ };
69
+
9
70
  // getInlineNodeViewProducer is a new api to use instead of ReactNodeView
10
71
  // when creating inline node views, however, it is difficult to test the impact
11
72
  // on selections when migrating inlineExtension to use the new api.
@@ -14,16 +75,8 @@ import { ExtensionNodeWrapper } from './ExtensionNodeWrapper';
14
75
  export class ExtensionNode extends ReactNodeView {
15
76
  constructor(...args) {
16
77
  super(...args);
17
- /**
18
- * Track whether we found and are reusing SSR'd DOM.
19
- * When true, we skip React Portal rendering on first init to preserve SSR content.
20
- */
78
+ /** True between SSR DOM adoption in `createDomRef` and the SSR→React handoff in `update`. */
21
79
  _defineProperty(this, "didReuseSsrDom", false);
22
- /**
23
- * Track whether this is the first init call.
24
- * SSR content preservation only happens on the very first init.
25
- */
26
- _defineProperty(this, "isFirstInit", true);
27
80
  }
28
81
  ignoreMutation(mutation) {
29
82
  // Extensions can perform async operations that will change the DOM.
@@ -33,12 +86,26 @@ export class ExtensionNode extends ReactNodeView {
33
86
  return this.node.type.isAtom || mutation.type !== 'selection' && mutation.attributeName !== 'data-layout';
34
87
  }
35
88
 
89
+ /** See {@link consumedHydrationIdentitiesByEditor}. Null when attrs are missing → SSR reuse skipped. */
90
+ getHydrationIdentityKey() {
91
+ var _this$node$attrs, _this$node$attrs2;
92
+ return getHydrationIdentityKey((_this$node$attrs = this.node.attrs) === null || _this$node$attrs === void 0 ? void 0 : _this$node$attrs.extensionKey, (_this$node$attrs2 = this.node.attrs) === null || _this$node$attrs2 === void 0 ? void 0 : _this$node$attrs2.localId);
93
+ }
94
+
95
+ /** True only for the first ExtensionNode of this identity in this editor. See {@link consumedHydrationIdentitiesByEditor}. */
96
+ isInInitialHydrationWindow() {
97
+ const identityKey = this.getHydrationIdentityKey();
98
+ if (identityKey === null) {
99
+ return false;
100
+ }
101
+ return !hasHydrationIdentityBeenConsumed(this.view, identityKey);
102
+ }
103
+
36
104
  // Reserve height by setting a minimum height for the extension node view element
37
105
  createDomRef() {
38
106
  if (!fg('confluence_connect_macro_preset_height')) {
39
- // Try to reuse SSR'd DOM node on first init only
40
- // This preserves SSR content and avoids TTVC mutations during hydration
41
- if (!isSSR() && this.isFirstInit && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc' && expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
107
+ // SSR DOM reuse see {@link consumedHydrationIdentitiesByEditor}.
108
+ if (!isSSR() && isSSRHydrationEligible(this.node) && this.isInInitialHydrationWindow() && expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
42
109
  const ssrElement = this.findSSRElement();
43
110
  if (ssrElement) {
44
111
  this.didReuseSsrDom = true;
@@ -91,20 +158,20 @@ export class ExtensionNode extends ReactNodeView {
91
158
  return null;
92
159
  }
93
160
 
94
- /**
95
- * Override init() to skip React Portal rendering on first init if we're reusing SSR'd DOM.
96
- * This preserves the SSR content without React unnecessarily re-rendering it.
97
- */
161
+ /** Skip React Portal render on first init when reusing SSR DOM. See {@link consumedHydrationIdentitiesByEditor}. */
98
162
  init() {
99
163
  if (!expValEquals('platform_editor_hydration_skip_react_portal', 'isEnabled', true)) {
100
164
  super.init();
101
165
  } else {
102
- if (!isSSR() && this.node.type.name === 'extension' && this.node.attrs.extensionKey === 'toc') {
166
+ const isEligibleForSsrReuse = !isSSR() && isSSRHydrationEligible(this.node);
167
+ if (isEligibleForSsrReuse && this.isInInitialHydrationWindow()) {
103
168
  const ssrElement = this.findSSRElement();
104
169
  const shouldSkipInitRender = ssrElement !== null;
105
170
  super.init(shouldSkipInitRender);
106
- if (shouldSkipInitRender) {
107
- this.isFirstInit = false;
171
+ const identityKey = this.getHydrationIdentityKey();
172
+ if (identityKey !== null) {
173
+ // Close the hydration window — see {@link consumedHydrationIdentitiesByEditor}.
174
+ markHydrationIdentityAsConsumed(this.view, identityKey);
108
175
  }
109
176
  } else {
110
177
  super.init();
@@ -175,9 +242,8 @@ export class ExtensionNode extends ReactNodeView {
175
242
  }
176
243
  render(props, forwardRef) {
177
244
  var _props$extensionNodeV;
178
- // If we reused SSR'd DOM on first init, don't render React Portal
179
- // The SSR content is already perfect and doesn't need re-rendering
180
- if (this.didReuseSsrDom && this.isFirstInit) {
245
+ // While sitting on SSR DOM, skip the React portal see {@link didReuseSsrDom}.
246
+ if (this.didReuseSsrDom) {
181
247
  return null;
182
248
  }
183
249
  return /*#__PURE__*/React.createElement(ExtensionNodeWrapper, {
@@ -17,18 +17,18 @@ export const markdownModeMessages = defineMessages({
17
17
  },
18
18
  source: {
19
19
  id: 'fabric.editor.markdownMode.viewToggle.source',
20
- defaultMessage: 'Markdown source',
21
- description: 'Tooltip and accessible label for the toggle button that switches the editor into raw markdown source view.'
20
+ defaultMessage: 'Syntax editor',
21
+ description: 'Tooltip and accessible label for the toggle button that switches the editor into raw markdown source (syntax) view.'
22
22
  },
23
23
  wysiwyg: {
24
24
  id: 'fabric.editor.markdownMode.viewToggle.wysiwyg',
25
- defaultMessage: 'WYSIWYG',
25
+ defaultMessage: 'WYSIWYG editor',
26
26
  description: 'Tooltip and accessible label for the toggle button that switches the editor into the WYSIWYG markdown editing view (the default).'
27
27
  },
28
28
  preview: {
29
29
  id: 'fabric.editor.markdownMode.viewToggle.preview',
30
- defaultMessage: 'Preview',
31
- description: 'Tooltip and accessible label for the toggle button that switches the editor into a read-only rendered markdown preview.'
30
+ defaultMessage: 'Split view (coming soon)',
31
+ description: 'Tooltip and accessible label for the toggle button that will switch the editor into a split view. This feature is not yet available.'
32
32
  },
33
33
  sourceAriaLabel: {
34
34
  id: 'fabric.editor.markdownMode.sourceView.ariaLabel',
@@ -62,7 +62,7 @@ export const mediaInsertMessages = defineMessages({
62
62
  },
63
63
  generateTabTitle: {
64
64
  id: 'fabric.editor.media.insert.generateTabTitle',
65
- defaultMessage: 'Generate with AI',
65
+ defaultMessage: 'Create',
66
66
  description: 'Title of the navigation tab that allows users to generate an image through AI'
67
67
  },
68
68
  mediaPickerPopupAriaLabel: {
@@ -34,5 +34,10 @@ export const mentionMessages = defineMessages({
34
34
  id: 'fabric.editor.inviteItem.sendInvite',
35
35
  defaultMessage: 'Send request to invite teammate',
36
36
  description: 'By line text for send request to invite teammate option shown in mentions.'
37
+ },
38
+ inviteButton: {
39
+ id: 'fabric.editor.inviteItem.inviteButton',
40
+ defaultMessage: 'Invite',
41
+ description: 'Label for the invite button shown in the mention typeahead invite item.'
37
42
  }
38
43
  });
@@ -4,7 +4,7 @@ import { isFedRamp } from './environment';
4
4
  import { normaliseSentryBreadcrumbs, SERIALIZABLE_ATTRIBUTES } from './normalise-sentry-breadcrumbs';
5
5
  const SENTRY_DSN = 'https://0b10c8e02fb44d8796c047b102c9bee8@o55978.ingest.sentry.io/4505129224110080';
6
6
  const packageName = 'editor-common'; // Sentry doesn't accept '/' in its releases https://docs.sentry.io/platforms/javascript/configuration/releases/
7
- const packageVersion = "114.15.0";
7
+ const packageVersion = "114.17.0";
8
8
  const sanitiseSentryEvents = (data, _hint) => {
9
9
  // Remove URL as it has UGC
10
10
  // Ignored via go/ees007
@@ -14,7 +14,7 @@ import withAnalyticsEvents from '@atlaskit/analytics-next/withAnalyticsEvents';
14
14
  import { fg } from '@atlaskit/platform-feature-flags';
15
15
  import Layer from '../Layer';
16
16
  const packageName = "@atlaskit/editor-common";
17
- const packageVersion = "114.15.0";
17
+ const packageVersion = "114.17.0";
18
18
  const halfFocusRing = 1;
19
19
  const dropOffset = '0, 8';
20
20
  const fadeIn = keyframes({
@@ -41,6 +41,11 @@ export const messages = defineMessages({
41
41
  id: 'fabric.editor.expandAriaLabel',
42
42
  defaultMessage: 'Give this expand a title',
43
43
  description: 'aria label for an expand node title input field'
44
+ },
45
+ expandBodyAriaLabel: {
46
+ id: 'fabric.editor.expandBodyAriaLabel',
47
+ defaultMessage: 'Expand body content',
48
+ description: 'Aria label for the body content of an expand node'
44
49
  }
45
50
  });
46
51
  export const ExpandIconWrapper = ({
@@ -1,5 +1,4 @@
1
1
  import { akEditorBreakoutPadding, breakoutWideScaleRatio } from '@atlaskit/editor-shared-styles';
2
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
3
2
  import { MEDIA_SINGLE_GUTTER_SIZE } from '../../media-single/constants';
4
3
  const validWidthModes = ['center', 'wrap-left', 'wrap-right', 'align-start', 'align-end'];
5
4
  export const layoutSupportsWidth = layout => validWidthModes.indexOf(layout) > -1;
@@ -55,10 +54,7 @@ export const calcMediaPxWidth = opts => {
55
54
  }
56
55
  return calculatedPctWidth;
57
56
  }
58
- if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
59
- return calculatedPctWidth;
60
- }
61
- return Math.min(calculatedPctWidth, origWidth);
57
+ return calculatedPctWidth;
62
58
  }
63
59
  if (calculatedResizedPctWidth) {
64
60
  return calculatedResizedPctWidth;
@@ -72,11 +68,7 @@ export const calcMediaPxWidth = opts => {
72
68
  } else if (layout && wrappedLayouts.indexOf(layout) !== -1) {
73
69
  // when layout is wrap-left, wrap-right, align-start, align-end
74
70
  // but no pctWidth is defined
75
- if (expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true)) {
76
- return Math.min(calcPxFromPct(0.5, lineLength || width), origWidth);
77
- }
78
- const halfLineLength = Math.ceil((lineLength || width) / 2);
79
- return origWidth <= halfLineLength ? origWidth : halfLineLength;
71
+ return Math.min(calcPxFromPct(0.5, lineLength || width), origWidth);
80
72
  }
81
73
  return origWidth;
82
74
  };