@atlaskit/editor-plugin-paste 12.1.1 → 12.1.3

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-paste
2
2
 
3
+ ## 12.1.3
4
+
5
+ ### Patch Changes
6
+
7
+ - [`dbe0d03cebcd7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/dbe0d03cebcd7) -
8
+ Fix media copy/paste from rendered comments: only hoist external images from mediaSingle wrappers,
9
+ preserve internal media with valid file references so ProseMirror can reconstruct proper media
10
+ nodes. Remove debug console.log.
11
+ - Updated dependencies
12
+
13
+ ## 12.1.2
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies
18
+
3
19
  ## 12.1.1
4
20
 
5
21
  ### Patch Changes
@@ -146,6 +146,29 @@ function canContainImage(element) {
146
146
  return VALID_TAGS_CONTAINER.indexOf(element.tagName) !== -1;
147
147
  }
148
148
 
149
+ /**
150
+ * Determines whether an `<img>` inside a mediaSingle wrapper should be hoisted
151
+ * out of the wrapper and treated as a standalone external image.
152
+ *
153
+ * This should only happen when:
154
+ * - The image source is external (data-source="external"), and
155
+ * - The media wrapper has no valid file reference (no data-id), meaning
156
+ * ProseMirror cannot reconstruct a proper internal media node from the wrapper.
157
+ *
158
+ * When the wrapper has a valid file reference (e.g. internal media from Jira),
159
+ * we leave the wrapper intact so ProseMirror can reconstruct a proper internal media node with the file reference and fetch fresh auth tokens.
160
+ */
161
+ function shouldHoistFromMediaSingle(imageTag) {
162
+ var _mediaNode$hasAttribu;
163
+ var isExternalSource = imageTag.getAttribute('data-source') === 'external';
164
+ if (!isExternalSource) {
165
+ return false;
166
+ }
167
+ var mediaNode = imageTag.closest('[data-node-type="media"]');
168
+ var hasFileReference = (_mediaNode$hasAttribu = mediaNode === null || mediaNode === void 0 ? void 0 : mediaNode.hasAttribute('data-id')) !== null && _mediaNode$hasAttribu !== void 0 ? _mediaNode$hasAttribu : false;
169
+ return !hasFileReference;
170
+ }
171
+
149
172
  /**
150
173
  * Given a html string, we attempt to hoist any nested `<img>` tags,
151
174
  * not directly wrapped by a `<div>` as ProseMirror no-op's
@@ -188,6 +211,24 @@ var unwrapNestedMediaElements = exports.unwrapNestedMediaElements = function unw
188
211
  return;
189
212
  }
190
213
 
214
+ // when copying a jira comment that contains external image (data-source="external"),
215
+ // the 'media' div is like this:
216
+ // <div data-node-type="media"> has no data-id,
217
+ // But when copying a jira comment that contains image that was uploaded to Jira, the div is like this:
218
+ // <div data-node-type="media" data-type="file" data-id="dce9c14b-b857-41fd-9452-5f4ba3a4f679" data-collection="" data-file-name="Screenshot 2026-05-26 at 4.41.05 pm.png" data-file-size="1354355" data-file-mime-type="image/png">
219
+ // ProseMirror fails to parse the media node because it has no data-id,
220
+ // so we hoist the <img> to replace the entire mediaSingle wrapper
221
+ // so the editor can treat it as a plain external image and render/re-upload it correctly.
222
+ var mediaSingleWrapper = imageTag.closest('[data-node-type="mediaSingle"]');
223
+ if (mediaSingleWrapper && shouldHoistFromMediaSingle(imageTag) && (0, _expValEquals.expValEquals)('fix_copy_paste_external_media_renderer_to_editor', 'isEnabled', true)) {
224
+ // Hoist the <img> to replace the entire mediaSingle wrapper
225
+ if (mediaSingleWrapper.parentElement) {
226
+ mediaSingleWrapper.parentElement.insertBefore(imageTag, mediaSingleWrapper);
227
+ mediaSingleWrapper.remove();
228
+ return;
229
+ }
230
+ }
231
+
191
232
  // If either the parent or the image itself contains styles that would make
192
233
  // them invisible on copy, dont paste them.
193
234
  if (isElementInvisible(mediaParent) || isElementInvisible(imageTag)) {
@@ -142,6 +142,29 @@ function canContainImage(element) {
142
142
  return VALID_TAGS_CONTAINER.indexOf(element.tagName) !== -1;
143
143
  }
144
144
 
145
+ /**
146
+ * Determines whether an `<img>` inside a mediaSingle wrapper should be hoisted
147
+ * out of the wrapper and treated as a standalone external image.
148
+ *
149
+ * This should only happen when:
150
+ * - The image source is external (data-source="external"), and
151
+ * - The media wrapper has no valid file reference (no data-id), meaning
152
+ * ProseMirror cannot reconstruct a proper internal media node from the wrapper.
153
+ *
154
+ * When the wrapper has a valid file reference (e.g. internal media from Jira),
155
+ * we leave the wrapper intact so ProseMirror can reconstruct a proper internal media node with the file reference and fetch fresh auth tokens.
156
+ */
157
+ function shouldHoistFromMediaSingle(imageTag) {
158
+ var _mediaNode$hasAttribu;
159
+ const isExternalSource = imageTag.getAttribute('data-source') === 'external';
160
+ if (!isExternalSource) {
161
+ return false;
162
+ }
163
+ const mediaNode = imageTag.closest('[data-node-type="media"]');
164
+ const hasFileReference = (_mediaNode$hasAttribu = mediaNode === null || mediaNode === void 0 ? void 0 : mediaNode.hasAttribute('data-id')) !== null && _mediaNode$hasAttribu !== void 0 ? _mediaNode$hasAttribu : false;
165
+ return !hasFileReference;
166
+ }
167
+
145
168
  /**
146
169
  * Given a html string, we attempt to hoist any nested `<img>` tags,
147
170
  * not directly wrapped by a `<div>` as ProseMirror no-op's
@@ -184,6 +207,24 @@ export const unwrapNestedMediaElements = html => {
184
207
  return;
185
208
  }
186
209
 
210
+ // when copying a jira comment that contains external image (data-source="external"),
211
+ // the 'media' div is like this:
212
+ // <div data-node-type="media"> has no data-id,
213
+ // But when copying a jira comment that contains image that was uploaded to Jira, the div is like this:
214
+ // <div data-node-type="media" data-type="file" data-id="dce9c14b-b857-41fd-9452-5f4ba3a4f679" data-collection="" data-file-name="Screenshot 2026-05-26 at 4.41.05 pm.png" data-file-size="1354355" data-file-mime-type="image/png">
215
+ // ProseMirror fails to parse the media node because it has no data-id,
216
+ // so we hoist the <img> to replace the entire mediaSingle wrapper
217
+ // so the editor can treat it as a plain external image and render/re-upload it correctly.
218
+ const mediaSingleWrapper = imageTag.closest('[data-node-type="mediaSingle"]');
219
+ if (mediaSingleWrapper && shouldHoistFromMediaSingle(imageTag) && expValEquals('fix_copy_paste_external_media_renderer_to_editor', 'isEnabled', true)) {
220
+ // Hoist the <img> to replace the entire mediaSingle wrapper
221
+ if (mediaSingleWrapper.parentElement) {
222
+ mediaSingleWrapper.parentElement.insertBefore(imageTag, mediaSingleWrapper);
223
+ mediaSingleWrapper.remove();
224
+ return;
225
+ }
226
+ }
227
+
187
228
  // If either the parent or the image itself contains styles that would make
188
229
  // them invisible on copy, dont paste them.
189
230
  if (isElementInvisible(mediaParent) || isElementInvisible(imageTag)) {
@@ -137,6 +137,29 @@ function canContainImage(element) {
137
137
  return VALID_TAGS_CONTAINER.indexOf(element.tagName) !== -1;
138
138
  }
139
139
 
140
+ /**
141
+ * Determines whether an `<img>` inside a mediaSingle wrapper should be hoisted
142
+ * out of the wrapper and treated as a standalone external image.
143
+ *
144
+ * This should only happen when:
145
+ * - The image source is external (data-source="external"), and
146
+ * - The media wrapper has no valid file reference (no data-id), meaning
147
+ * ProseMirror cannot reconstruct a proper internal media node from the wrapper.
148
+ *
149
+ * When the wrapper has a valid file reference (e.g. internal media from Jira),
150
+ * we leave the wrapper intact so ProseMirror can reconstruct a proper internal media node with the file reference and fetch fresh auth tokens.
151
+ */
152
+ function shouldHoistFromMediaSingle(imageTag) {
153
+ var _mediaNode$hasAttribu;
154
+ var isExternalSource = imageTag.getAttribute('data-source') === 'external';
155
+ if (!isExternalSource) {
156
+ return false;
157
+ }
158
+ var mediaNode = imageTag.closest('[data-node-type="media"]');
159
+ var hasFileReference = (_mediaNode$hasAttribu = mediaNode === null || mediaNode === void 0 ? void 0 : mediaNode.hasAttribute('data-id')) !== null && _mediaNode$hasAttribu !== void 0 ? _mediaNode$hasAttribu : false;
160
+ return !hasFileReference;
161
+ }
162
+
140
163
  /**
141
164
  * Given a html string, we attempt to hoist any nested `<img>` tags,
142
165
  * not directly wrapped by a `<div>` as ProseMirror no-op's
@@ -179,6 +202,24 @@ export var unwrapNestedMediaElements = function unwrapNestedMediaElements(html)
179
202
  return;
180
203
  }
181
204
 
205
+ // when copying a jira comment that contains external image (data-source="external"),
206
+ // the 'media' div is like this:
207
+ // <div data-node-type="media"> has no data-id,
208
+ // But when copying a jira comment that contains image that was uploaded to Jira, the div is like this:
209
+ // <div data-node-type="media" data-type="file" data-id="dce9c14b-b857-41fd-9452-5f4ba3a4f679" data-collection="" data-file-name="Screenshot 2026-05-26 at 4.41.05 pm.png" data-file-size="1354355" data-file-mime-type="image/png">
210
+ // ProseMirror fails to parse the media node because it has no data-id,
211
+ // so we hoist the <img> to replace the entire mediaSingle wrapper
212
+ // so the editor can treat it as a plain external image and render/re-upload it correctly.
213
+ var mediaSingleWrapper = imageTag.closest('[data-node-type="mediaSingle"]');
214
+ if (mediaSingleWrapper && shouldHoistFromMediaSingle(imageTag) && expValEquals('fix_copy_paste_external_media_renderer_to_editor', 'isEnabled', true)) {
215
+ // Hoist the <img> to replace the entire mediaSingle wrapper
216
+ if (mediaSingleWrapper.parentElement) {
217
+ mediaSingleWrapper.parentElement.insertBefore(imageTag, mediaSingleWrapper);
218
+ mediaSingleWrapper.remove();
219
+ return;
220
+ }
221
+ }
222
+
182
223
  // If either the parent or the image itself contains styles that would make
183
224
  // them invisible on copy, dont paste them.
184
225
  if (isElementInvisible(mediaParent) || isElementInvisible(imageTag)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-paste",
3
- "version": "12.1.1",
3
+ "version": "12.1.3",
4
4
  "description": "Paste plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -34,29 +34,29 @@
34
34
  "@atlaskit/editor-plugin-analytics": "^11.0.0",
35
35
  "@atlaskit/editor-plugin-annotation": "^11.0.0",
36
36
  "@atlaskit/editor-plugin-better-type-history": "^11.0.0",
37
- "@atlaskit/editor-plugin-card": "^17.1.0",
37
+ "@atlaskit/editor-plugin-card": "^17.3.0",
38
38
  "@atlaskit/editor-plugin-expand": "^12.0.0",
39
39
  "@atlaskit/editor-plugin-feature-flags": "^10.0.0",
40
40
  "@atlaskit/editor-plugin-list": "^13.0.0",
41
- "@atlaskit/editor-plugin-media": "^13.1.0",
42
- "@atlaskit/editor-plugin-mentions": "^13.1.0",
41
+ "@atlaskit/editor-plugin-media": "^13.4.0",
42
+ "@atlaskit/editor-plugin-mentions": "^13.3.0",
43
43
  "@atlaskit/editor-prosemirror": "^7.3.0",
44
44
  "@atlaskit/editor-tables": "^2.10.0",
45
- "@atlaskit/flag": "^17.12.0",
45
+ "@atlaskit/flag": "^17.13.0",
46
46
  "@atlaskit/icon": "^35.4.0",
47
47
  "@atlaskit/insm": "^0.4.0",
48
48
  "@atlaskit/media-client": "^36.3.0",
49
49
  "@atlaskit/media-common": "^13.3.0",
50
50
  "@atlaskit/platform-feature-flags": "^1.1.0",
51
51
  "@atlaskit/prosemirror-history": "^0.2.0",
52
- "@atlaskit/tmp-editor-statsig": "^89.0.0",
53
- "@atlaskit/tokens": "^13.1.0",
52
+ "@atlaskit/tmp-editor-statsig": "^91.0.0",
53
+ "@atlaskit/tokens": "^13.3.0",
54
54
  "@babel/runtime": "^7.0.0",
55
55
  "lodash": "^4.17.21",
56
56
  "uuid": "^3.1.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@atlaskit/editor-common": "^115.2.0",
59
+ "@atlaskit/editor-common": "^115.7.0",
60
60
  "react": "^18.2.0",
61
61
  "react-dom": "^18.2.0",
62
62
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"