@atlaskit/editor-plugin-media 12.1.5 → 12.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/dist/cjs/mediaPlugin.js +9 -0
- package/dist/cjs/nodeviews/mediaNodeView/index.js +9 -1
- package/dist/cjs/nodeviews/mediaNodeView/media.js +2 -1
- package/dist/cjs/pm-plugins/ai-generating-decoration.js +180 -0
- package/dist/cjs/pm-plugins/commands.js +27 -1
- package/dist/es2019/mediaPlugin.js +8 -1
- package/dist/es2019/nodeviews/mediaNodeView/index.js +9 -1
- package/dist/es2019/nodeviews/mediaNodeView/media.js +2 -1
- package/dist/es2019/pm-plugins/ai-generating-decoration.js +166 -0
- package/dist/es2019/pm-plugins/commands.js +20 -0
- package/dist/esm/mediaPlugin.js +10 -1
- package/dist/esm/nodeviews/mediaNodeView/index.js +9 -1
- package/dist/esm/nodeviews/mediaNodeView/media.js +2 -1
- package/dist/esm/pm-plugins/ai-generating-decoration.js +170 -0
- package/dist/esm/pm-plugins/commands.js +26 -0
- package/dist/types/mediaPluginType.d.ts +13 -0
- package/dist/types/nodeviews/mediaNodeView/index.d.ts +1 -0
- package/dist/types/nodeviews/mediaNodeView/media.d.ts +1 -0
- package/dist/types/pm-plugins/ai-generating-decoration.d.ts +47 -0
- package/dist/types/pm-plugins/commands.d.ts +13 -0
- package/dist/types-ts4.5/mediaPluginType.d.ts +13 -0
- package/dist/types-ts4.5/nodeviews/mediaNodeView/index.d.ts +1 -0
- package/dist/types-ts4.5/nodeviews/mediaNodeView/media.d.ts +1 -0
- package/dist/types-ts4.5/pm-plugins/ai-generating-decoration.d.ts +47 -0
- package/dist/types-ts4.5/pm-plugins/commands.d.ts +13 -0
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-media
|
|
2
2
|
|
|
3
|
+
## 12.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
|
|
9
|
+
## 12.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [`a0b1822615d7e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a0b1822615d7e) -
|
|
14
|
+
Refactor inline editor AI image generation loading state to use ProseMirror decorations:
|
|
15
|
+
- Add AI generating decoration plugin to editor-plugin-media for transient visual state tracking
|
|
16
|
+
via ProseMirror decorations instead of ADF schema attributes
|
|
17
|
+
- Remove \_\_isAIGenerating transient attribute from ADF media node schema
|
|
18
|
+
- Update editor-rovo-bridge to dispatch decoration meta instead of mutating node attributes
|
|
19
|
+
- Media NodeView reads decoration state and passes isAIGenerating prop to media-card
|
|
20
|
+
- AIBorder component with pulsing gradient border and translucent blanket during AI image
|
|
21
|
+
generation
|
|
22
|
+
- Internationalized AI generating progress bar aria label
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies
|
|
27
|
+
|
|
3
28
|
## 12.1.5
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
package/dist/cjs/mediaPlugin.js
CHANGED
|
@@ -27,6 +27,7 @@ var _media = require("./nodeviews/toDOM-fixes/media");
|
|
|
27
27
|
var _mediaGroup = require("./nodeviews/toDOM-fixes/mediaGroup");
|
|
28
28
|
var _mediaInline = require("./nodeviews/toDOM-fixes/mediaInline");
|
|
29
29
|
var _mediaSingle2 = require("./nodeviews/toDOM-fixes/mediaSingle");
|
|
30
|
+
var _aiGeneratingDecoration = require("./pm-plugins/ai-generating-decoration");
|
|
30
31
|
var _altText = require("./pm-plugins/alt-text");
|
|
31
32
|
var _keymap = _interopRequireDefault(require("./pm-plugins/alt-text/keymap"));
|
|
32
33
|
var _commands = require("./pm-plugins/commands");
|
|
@@ -180,6 +181,8 @@ var mediaPlugin = exports.mediaPlugin = function mediaPlugin(_ref3) {
|
|
|
180
181
|
showMediaViewer: _commands.showMediaViewer,
|
|
181
182
|
hideMediaViewer: _commands.hideMediaViewer,
|
|
182
183
|
trackMediaPaste: _commands.trackMediaPaste,
|
|
184
|
+
setAIGenerating: _commands.setAIGenerating,
|
|
185
|
+
clearAIGenerating: _commands.clearAIGenerating,
|
|
183
186
|
insertMediaSingle: (0, _commands.insertMediaAsMediaSingleCommand)(api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.allowPixelResizing)
|
|
184
187
|
},
|
|
185
188
|
nodes: function nodes() {
|
|
@@ -305,6 +308,12 @@ var mediaPlugin = exports.mediaPlugin = function mediaPlugin(_ref3) {
|
|
|
305
308
|
plugin: _pixelResizing.createPlugin
|
|
306
309
|
});
|
|
307
310
|
}
|
|
311
|
+
pmPlugins.push({
|
|
312
|
+
name: 'mediaAIGeneratingDecoration',
|
|
313
|
+
plugin: function plugin() {
|
|
314
|
+
return (0, _aiGeneratingDecoration.createAIGeneratingDecorationPlugin)();
|
|
315
|
+
}
|
|
316
|
+
});
|
|
308
317
|
pmPlugins.push({
|
|
309
318
|
name: 'mediaSelectionHandler',
|
|
310
319
|
plugin: function plugin() {
|
|
@@ -23,6 +23,7 @@ var _useSharedPluginStateSelector = require("@atlaskit/editor-common/use-shared-
|
|
|
23
23
|
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
|
|
24
24
|
var _mediaClient = require("@atlaskit/media-client");
|
|
25
25
|
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
|
|
26
|
+
var _aiGeneratingDecoration = require("../../pm-plugins/ai-generating-decoration");
|
|
26
27
|
var _helpers = require("../../pm-plugins/commands/helpers");
|
|
27
28
|
var _mediaCommon = require("../../pm-plugins/utils/media-common");
|
|
28
29
|
var _media = _interopRequireDefault(require("./media"));
|
|
@@ -62,6 +63,7 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
62
63
|
}
|
|
63
64
|
_this = _callSuper(this, MediaNodeView, [].concat(args));
|
|
64
65
|
(0, _defineProperty2.default)(_this, "isSelected", false);
|
|
66
|
+
(0, _defineProperty2.default)(_this, "isAIGenerating", false);
|
|
65
67
|
(0, _defineProperty2.default)(_this, "hasBeenResized", false);
|
|
66
68
|
(0, _defineProperty2.default)(_this, "hasResizedListener", function () {
|
|
67
69
|
if (!_this.hasBeenResized) {
|
|
@@ -178,7 +180,8 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
178
180
|
mediaOptions: mediaOptions,
|
|
179
181
|
onExternalImageLoaded: _this.onExternalImageLoaded,
|
|
180
182
|
isViewOnly: ((_this$reactComponentP = _this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.editorViewMode) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.sharedState.currentState()) === null || _this$reactComponentP === void 0 ? void 0 : _this$reactComponentP.mode) === 'view',
|
|
181
|
-
pluginInjectionApi: _this.reactComponentProps.pluginInjectionApi
|
|
183
|
+
pluginInjectionApi: _this.reactComponentProps.pluginInjectionApi,
|
|
184
|
+
isAIGenerating: _this.isAIGenerating
|
|
182
185
|
});
|
|
183
186
|
};
|
|
184
187
|
});
|
|
@@ -268,6 +271,11 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
268
271
|
this.isSelected = hasMediaNodeSelectedDecoration;
|
|
269
272
|
return true;
|
|
270
273
|
}
|
|
274
|
+
var aiGenerating = (0, _aiGeneratingDecoration.hasAIGeneratingDecoration)(decorations);
|
|
275
|
+
if (this.isAIGenerating !== aiGenerating) {
|
|
276
|
+
this.isAIGenerating = aiGenerating;
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
271
279
|
if (this.node.attrs !== nextNode.attrs) {
|
|
272
280
|
return true;
|
|
273
281
|
}
|
|
@@ -181,7 +181,7 @@ var MediaNode = exports.MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
181
181
|
value: function shouldComponentUpdate(nextProps, nextState) {
|
|
182
182
|
var hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
|
|
183
183
|
var hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
|
|
184
|
-
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || (0, _expValEquals.expValEquals)('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
184
|
+
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.isAIGenerating !== nextProps.isAIGenerating || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || (0, _expValEquals.expValEquals)('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
185
185
|
return true;
|
|
186
186
|
}
|
|
187
187
|
return false;
|
|
@@ -391,6 +391,7 @@ var MediaNode = exports.MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
391
391
|
videoControlsWrapperRef: this.videoControlsWrapperRef,
|
|
392
392
|
ssr: ssr,
|
|
393
393
|
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig),
|
|
394
|
+
isAIGenerating: !!this.props.isAIGenerating,
|
|
394
395
|
onError: (0, _expValEquals.expValEquals)('platform_editor_media_error_analytics', 'isEnabled', true) ? this.onError : undefined
|
|
395
396
|
})));
|
|
396
397
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.aiGeneratingDecorationPluginKey = void 0;
|
|
8
|
+
exports.clearAIGeneratingMeta = clearAIGeneratingMeta;
|
|
9
|
+
exports.createAIGeneratingDecorationPlugin = createAIGeneratingDecorationPlugin;
|
|
10
|
+
exports.hasAIGeneratingDecoration = hasAIGeneratingDecoration;
|
|
11
|
+
exports.setAIGeneratingMeta = setAIGeneratingMeta;
|
|
12
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
|
+
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
|
|
14
|
+
var _state = require("@atlaskit/editor-prosemirror/state");
|
|
15
|
+
var _view = require("@atlaskit/editor-prosemirror/view");
|
|
16
|
+
var _featureGateJsClient = _interopRequireDefault(require("@atlaskit/feature-gate-js-client"));
|
|
17
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
18
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
19
|
+
/**
|
|
20
|
+
* ProseMirror plugin that manages AI-generating decorations on media nodes.
|
|
21
|
+
*
|
|
22
|
+
* Instead of storing transient `__isAIGenerating` attributes in the ADF schema
|
|
23
|
+
* (which pollutes the document model and undo history), this plugin uses
|
|
24
|
+
* ProseMirror decorations — a view-layer-only mechanism that never affects the
|
|
25
|
+
* document content, serialization, or undo/redo stack.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// ── Plugin Key ──────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
var aiGeneratingDecorationPluginKey = exports.aiGeneratingDecorationPluginKey = new _state.PluginKey('aiGeneratingDecoration');
|
|
31
|
+
|
|
32
|
+
// ── Types ───────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
var AI_GENERATING_DECORATION_TYPE = 'ai-generating';
|
|
35
|
+
|
|
36
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build a DecorationSet containing a Decoration.node for every media node
|
|
40
|
+
* whose `id` is in the given set.
|
|
41
|
+
*/
|
|
42
|
+
function buildDecorationSet(doc, mediaIds) {
|
|
43
|
+
if (mediaIds.size === 0) {
|
|
44
|
+
return _view.DecorationSet.empty;
|
|
45
|
+
}
|
|
46
|
+
var decorations = [];
|
|
47
|
+
doc.descendants(function (node, pos) {
|
|
48
|
+
if (node.type.name === 'media' && mediaIds.has(node.attrs.id)) {
|
|
49
|
+
decorations.push(_view.Decoration.node(pos, pos + node.nodeSize, {},
|
|
50
|
+
// no DOM attrs needed — the NodeView reads the decoration spec
|
|
51
|
+
{
|
|
52
|
+
type: AI_GENERATING_DECORATION_TYPE,
|
|
53
|
+
mediaId: node.attrs.id
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return _view.DecorationSet.create(doc, decorations);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ── Public utilities ────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns `true` if the given decorations array contains an AI-generating
|
|
64
|
+
* decoration. Call this from a NodeView's `update()` / `viewShouldUpdate()`
|
|
65
|
+
* to determine whether to render the AI border.
|
|
66
|
+
*/
|
|
67
|
+
function hasAIGeneratingDecoration(decorations) {
|
|
68
|
+
return decorations.some(function (d) {
|
|
69
|
+
return d.spec.type === AI_GENERATING_DECORATION_TYPE;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Dispatch a transaction that sets the AI-generating decoration on a media
|
|
75
|
+
* node identified by `mediaId`.
|
|
76
|
+
*
|
|
77
|
+
* Usage from the editor bridge:
|
|
78
|
+
* ```
|
|
79
|
+
* editorAPI.core.actions.execute(({ tr }) =>
|
|
80
|
+
* setAIGeneratingMeta(tr, mediaId),
|
|
81
|
+
* );
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
function setAIGeneratingMeta(tr, mediaId) {
|
|
85
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
86
|
+
type: 'SET_GENERATING',
|
|
87
|
+
mediaId: mediaId
|
|
88
|
+
}).setMeta('addToHistory', false);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Dispatch a transaction that clears the AI-generating decoration for a
|
|
93
|
+
* specific media node.
|
|
94
|
+
*/
|
|
95
|
+
function clearAIGeneratingMeta(tr, mediaId) {
|
|
96
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
97
|
+
type: 'CLEAR_GENERATING',
|
|
98
|
+
mediaId: mediaId
|
|
99
|
+
}).setMeta('addToHistory', false);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Plugin ──────────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
/** Creates the ProseMirror plugin that manages AI-generating decorations on media nodes. */
|
|
105
|
+
function createAIGeneratingDecorationPlugin() {
|
|
106
|
+
return new _safePlugin.SafePlugin({
|
|
107
|
+
key: aiGeneratingDecorationPluginKey,
|
|
108
|
+
state: {
|
|
109
|
+
init: function init() {
|
|
110
|
+
return {
|
|
111
|
+
generatingMediaIds: new Set(),
|
|
112
|
+
decorationSet: _view.DecorationSet.empty
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
apply: function apply(tr, pluginState, _oldState, newState) {
|
|
116
|
+
// Killswitch — if active, clear any existing decorations and stop
|
|
117
|
+
// eslint-disable-next-line @atlaskit/platform/use-recommended-utils -- dynamic config killswitch, not a standard feature gate
|
|
118
|
+
if (_featureGateJsClient.default.getExperimentValue('maui_ai_border_killswitch', 'value', false)) {
|
|
119
|
+
if (pluginState.generatingMediaIds.size > 0) {
|
|
120
|
+
return {
|
|
121
|
+
generatingMediaIds: new Set(),
|
|
122
|
+
decorationSet: _view.DecorationSet.empty
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return pluginState;
|
|
126
|
+
}
|
|
127
|
+
var meta = tr.getMeta(aiGeneratingDecorationPluginKey);
|
|
128
|
+
if (meta) {
|
|
129
|
+
switch (meta.type) {
|
|
130
|
+
case 'SET_GENERATING':
|
|
131
|
+
{
|
|
132
|
+
var ids = new Set(pluginState.generatingMediaIds);
|
|
133
|
+
ids.add(meta.mediaId);
|
|
134
|
+
return {
|
|
135
|
+
generatingMediaIds: ids,
|
|
136
|
+
decorationSet: buildDecorationSet(newState.doc, ids)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case 'CLEAR_GENERATING':
|
|
140
|
+
{
|
|
141
|
+
var _ids = new Set(pluginState.generatingMediaIds);
|
|
142
|
+
_ids.delete(meta.mediaId);
|
|
143
|
+
return {
|
|
144
|
+
generatingMediaIds: _ids,
|
|
145
|
+
decorationSet: buildDecorationSet(newState.doc, _ids)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
case 'CLEAR_ALL':
|
|
149
|
+
return {
|
|
150
|
+
generatingMediaIds: new Set(),
|
|
151
|
+
decorationSet: _view.DecorationSet.empty
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Map decorations through document changes so positions stay in sync
|
|
157
|
+
if (tr.docChanged && pluginState.decorationSet !== _view.DecorationSet.empty) {
|
|
158
|
+
try {
|
|
159
|
+
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
160
|
+
decorationSet: pluginState.decorationSet.map(tr.mapping, newState.doc)
|
|
161
|
+
});
|
|
162
|
+
} catch (_unused) {
|
|
163
|
+
// Collaborative editing edge case — reset
|
|
164
|
+
return {
|
|
165
|
+
generatingMediaIds: new Set(),
|
|
166
|
+
decorationSet: _view.DecorationSet.empty
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return pluginState;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
props: {
|
|
174
|
+
decorations: function decorations(state) {
|
|
175
|
+
var _aiGeneratingDecorati, _aiGeneratingDecorati2;
|
|
176
|
+
return (_aiGeneratingDecorati = (_aiGeneratingDecorati2 = aiGeneratingDecorationPluginKey.getState(state)) === null || _aiGeneratingDecorati2 === void 0 ? void 0 : _aiGeneratingDecorati2.decorationSet) !== null && _aiGeneratingDecorati !== void 0 ? _aiGeneratingDecorati : _view.DecorationSet.empty;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.trackMediaPaste = exports.showMediaViewer = exports.insertMediaAsMediaSingleCommand = exports.hideMediaViewer = void 0;
|
|
6
|
+
exports.trackMediaPaste = exports.showMediaViewer = exports.setAIGenerating = exports.insertMediaAsMediaSingleCommand = exports.hideMediaViewer = exports.clearAIGenerating = void 0;
|
|
7
7
|
var _actions = require("../pm-plugins/actions");
|
|
8
|
+
var _aiGeneratingDecoration = require("../pm-plugins/ai-generating-decoration");
|
|
8
9
|
var _pluginKey = require("../pm-plugins/plugin-key");
|
|
9
10
|
var _mediaCommon = require("../pm-plugins/utils/media-common");
|
|
10
11
|
var _mediaSingle = require("./utils/media-single");
|
|
@@ -39,6 +40,31 @@ var trackMediaPaste = exports.trackMediaPaste = function trackMediaPaste(attrs)
|
|
|
39
40
|
return tr;
|
|
40
41
|
};
|
|
41
42
|
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
46
|
+
* The decoration triggers the AI border visual on the media's NodeView.
|
|
47
|
+
*
|
|
48
|
+
* Decorations live in the view layer only and never affect the document model
|
|
49
|
+
* or undo/redo history.
|
|
50
|
+
*/
|
|
51
|
+
var setAIGenerating = exports.setAIGenerating = function setAIGenerating(mediaId) {
|
|
52
|
+
return function (_ref4) {
|
|
53
|
+
var tr = _ref4.tr;
|
|
54
|
+
return (0, _aiGeneratingDecoration.setAIGeneratingMeta)(tr, mediaId);
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
60
|
+
* `mediaId`. Removes the AI border visual from that media's NodeView.
|
|
61
|
+
*/
|
|
62
|
+
var clearAIGenerating = exports.clearAIGenerating = function clearAIGenerating(mediaId) {
|
|
63
|
+
return function (_ref5) {
|
|
64
|
+
var tr = _ref5.tr;
|
|
65
|
+
return (0, _aiGeneratingDecoration.clearAIGeneratingMeta)(tr, mediaId);
|
|
66
|
+
};
|
|
67
|
+
};
|
|
42
68
|
var insertMediaAsMediaSingleCommand = exports.insertMediaAsMediaSingleCommand = function insertMediaAsMediaSingleCommand(editorAnalyticsAPI, allowPixelResizing) {
|
|
43
69
|
return function (mediaAttrs, inputMethod, insertMediaVia) {
|
|
44
70
|
return (0, _mediaSingle.createInsertMediaAsMediaSingleCommand)(mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing);
|
|
@@ -18,9 +18,10 @@ import { mediaSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/media';
|
|
|
18
18
|
import { mediaGroupSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaGroup';
|
|
19
19
|
import { mediaInlineSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaInline';
|
|
20
20
|
import { mediaSingleSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaSingle';
|
|
21
|
+
import { createAIGeneratingDecorationPlugin } from './pm-plugins/ai-generating-decoration';
|
|
21
22
|
import { createPlugin as createMediaAltTextPlugin } from './pm-plugins/alt-text';
|
|
22
23
|
import keymapMediaAltTextPlugin from './pm-plugins/alt-text/keymap';
|
|
23
|
-
import { hideMediaViewer, insertMediaAsMediaSingleCommand, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
|
|
24
|
+
import { clearAIGenerating, hideMediaViewer, insertMediaAsMediaSingleCommand, setAIGenerating, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
|
|
24
25
|
import keymapPlugin from './pm-plugins/keymap';
|
|
25
26
|
import keymapMediaSinglePlugin from './pm-plugins/keymap-media';
|
|
26
27
|
import linkingPlugin from './pm-plugins/linking';
|
|
@@ -173,6 +174,8 @@ export const mediaPlugin = ({
|
|
|
173
174
|
showMediaViewer,
|
|
174
175
|
hideMediaViewer,
|
|
175
176
|
trackMediaPaste,
|
|
177
|
+
setAIGenerating,
|
|
178
|
+
clearAIGenerating,
|
|
176
179
|
insertMediaSingle: insertMediaAsMediaSingleCommand(api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.allowPixelResizing)
|
|
177
180
|
},
|
|
178
181
|
nodes() {
|
|
@@ -296,6 +299,10 @@ export const mediaPlugin = ({
|
|
|
296
299
|
plugin: createMediaPixelResizingPlugin
|
|
297
300
|
});
|
|
298
301
|
}
|
|
302
|
+
pmPlugins.push({
|
|
303
|
+
name: 'mediaAIGeneratingDecoration',
|
|
304
|
+
plugin: () => createAIGeneratingDecorationPlugin()
|
|
305
|
+
});
|
|
299
306
|
pmPlugins.push({
|
|
300
307
|
name: 'mediaSelectionHandler',
|
|
301
308
|
plugin: () => {
|
|
@@ -9,6 +9,7 @@ import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared
|
|
|
9
9
|
import { akEditorFullWidthLayoutWidth, akEditorDefaultLayoutWidth, akEditorCalculatedWideLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
10
10
|
import { getAttrsFromUrl } from '@atlaskit/media-client';
|
|
11
11
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
12
|
+
import { hasAIGeneratingDecoration } from '../../pm-plugins/ai-generating-decoration';
|
|
12
13
|
import { updateCurrentMediaNodeAttrs } from '../../pm-plugins/commands/helpers';
|
|
13
14
|
import { isMediaBlobUrlFromAttrs } from '../../pm-plugins/utils/media-common';
|
|
14
15
|
// Ignored via go/ees005
|
|
@@ -40,6 +41,7 @@ class MediaNodeView extends SelectionBasedNodeView {
|
|
|
40
41
|
constructor(...args) {
|
|
41
42
|
super(...args);
|
|
42
43
|
_defineProperty(this, "isSelected", false);
|
|
44
|
+
_defineProperty(this, "isAIGenerating", false);
|
|
43
45
|
_defineProperty(this, "hasBeenResized", false);
|
|
44
46
|
_defineProperty(this, "hasResizedListener", () => {
|
|
45
47
|
if (!this.hasBeenResized) {
|
|
@@ -167,7 +169,8 @@ class MediaNodeView extends SelectionBasedNodeView {
|
|
|
167
169
|
mediaOptions: mediaOptions,
|
|
168
170
|
onExternalImageLoaded: this.onExternalImageLoaded,
|
|
169
171
|
isViewOnly: ((_this$reactComponentP = this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP === void 0 ? void 0 : (_this$reactComponentP2 = _this$reactComponentP.editorViewMode) === null || _this$reactComponentP2 === void 0 ? void 0 : (_this$reactComponentP3 = _this$reactComponentP2.sharedState.currentState()) === null || _this$reactComponentP3 === void 0 ? void 0 : _this$reactComponentP3.mode) === 'view',
|
|
170
|
-
pluginInjectionApi: this.reactComponentProps.pluginInjectionApi
|
|
172
|
+
pluginInjectionApi: this.reactComponentProps.pluginInjectionApi,
|
|
173
|
+
isAIGenerating: this.isAIGenerating
|
|
171
174
|
});
|
|
172
175
|
};
|
|
173
176
|
});
|
|
@@ -248,6 +251,11 @@ class MediaNodeView extends SelectionBasedNodeView {
|
|
|
248
251
|
this.isSelected = hasMediaNodeSelectedDecoration;
|
|
249
252
|
return true;
|
|
250
253
|
}
|
|
254
|
+
const aiGenerating = hasAIGeneratingDecoration(decorations);
|
|
255
|
+
if (this.isAIGenerating !== aiGenerating) {
|
|
256
|
+
this.isAIGenerating = aiGenerating;
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
251
259
|
if (this.node.attrs !== nextNode.attrs) {
|
|
252
260
|
return true;
|
|
253
261
|
}
|
|
@@ -150,7 +150,7 @@ export class MediaNode extends Component {
|
|
|
150
150
|
shouldComponentUpdate(nextProps, nextState) {
|
|
151
151
|
const hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
|
|
152
152
|
const hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
|
|
153
|
-
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
153
|
+
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.isAIGenerating !== nextProps.isAIGenerating || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
154
154
|
return true;
|
|
155
155
|
}
|
|
156
156
|
return false;
|
|
@@ -332,6 +332,7 @@ export class MediaNode extends Component {
|
|
|
332
332
|
videoControlsWrapperRef: this.videoControlsWrapperRef,
|
|
333
333
|
ssr: ssr,
|
|
334
334
|
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig),
|
|
335
|
+
isAIGenerating: !!this.props.isAIGenerating,
|
|
335
336
|
onError: expValEquals('platform_editor_media_error_analytics', 'isEnabled', true) ? this.onError : undefined
|
|
336
337
|
})));
|
|
337
338
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
4
|
+
import FeatureGates from '@atlaskit/feature-gate-js-client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* ProseMirror plugin that manages AI-generating decorations on media nodes.
|
|
8
|
+
*
|
|
9
|
+
* Instead of storing transient `__isAIGenerating` attributes in the ADF schema
|
|
10
|
+
* (which pollutes the document model and undo history), this plugin uses
|
|
11
|
+
* ProseMirror decorations — a view-layer-only mechanism that never affects the
|
|
12
|
+
* document content, serialization, or undo/redo stack.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ── Plugin Key ──────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export const aiGeneratingDecorationPluginKey = new PluginKey('aiGeneratingDecoration');
|
|
18
|
+
|
|
19
|
+
// ── Types ───────────────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const AI_GENERATING_DECORATION_TYPE = 'ai-generating';
|
|
22
|
+
|
|
23
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Build a DecorationSet containing a Decoration.node for every media node
|
|
27
|
+
* whose `id` is in the given set.
|
|
28
|
+
*/
|
|
29
|
+
function buildDecorationSet(doc, mediaIds) {
|
|
30
|
+
if (mediaIds.size === 0) {
|
|
31
|
+
return DecorationSet.empty;
|
|
32
|
+
}
|
|
33
|
+
const decorations = [];
|
|
34
|
+
doc.descendants((node, pos) => {
|
|
35
|
+
if (node.type.name === 'media' && mediaIds.has(node.attrs.id)) {
|
|
36
|
+
decorations.push(Decoration.node(pos, pos + node.nodeSize, {},
|
|
37
|
+
// no DOM attrs needed — the NodeView reads the decoration spec
|
|
38
|
+
{
|
|
39
|
+
type: AI_GENERATING_DECORATION_TYPE,
|
|
40
|
+
mediaId: node.attrs.id
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return DecorationSet.create(doc, decorations);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Public utilities ────────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Returns `true` if the given decorations array contains an AI-generating
|
|
51
|
+
* decoration. Call this from a NodeView's `update()` / `viewShouldUpdate()`
|
|
52
|
+
* to determine whether to render the AI border.
|
|
53
|
+
*/
|
|
54
|
+
export function hasAIGeneratingDecoration(decorations) {
|
|
55
|
+
return decorations.some(d => d.spec.type === AI_GENERATING_DECORATION_TYPE);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Dispatch a transaction that sets the AI-generating decoration on a media
|
|
60
|
+
* node identified by `mediaId`.
|
|
61
|
+
*
|
|
62
|
+
* Usage from the editor bridge:
|
|
63
|
+
* ```
|
|
64
|
+
* editorAPI.core.actions.execute(({ tr }) =>
|
|
65
|
+
* setAIGeneratingMeta(tr, mediaId),
|
|
66
|
+
* );
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function setAIGeneratingMeta(tr, mediaId) {
|
|
70
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
71
|
+
type: 'SET_GENERATING',
|
|
72
|
+
mediaId
|
|
73
|
+
}).setMeta('addToHistory', false);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Dispatch a transaction that clears the AI-generating decoration for a
|
|
78
|
+
* specific media node.
|
|
79
|
+
*/
|
|
80
|
+
export function clearAIGeneratingMeta(tr, mediaId) {
|
|
81
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
82
|
+
type: 'CLEAR_GENERATING',
|
|
83
|
+
mediaId
|
|
84
|
+
}).setMeta('addToHistory', false);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ── Plugin ──────────────────────────────────────────────────────────────────
|
|
88
|
+
|
|
89
|
+
/** Creates the ProseMirror plugin that manages AI-generating decorations on media nodes. */
|
|
90
|
+
export function createAIGeneratingDecorationPlugin() {
|
|
91
|
+
return new SafePlugin({
|
|
92
|
+
key: aiGeneratingDecorationPluginKey,
|
|
93
|
+
state: {
|
|
94
|
+
init() {
|
|
95
|
+
return {
|
|
96
|
+
generatingMediaIds: new Set(),
|
|
97
|
+
decorationSet: DecorationSet.empty
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
apply(tr, pluginState, _oldState, newState) {
|
|
101
|
+
// Killswitch — if active, clear any existing decorations and stop
|
|
102
|
+
// eslint-disable-next-line @atlaskit/platform/use-recommended-utils -- dynamic config killswitch, not a standard feature gate
|
|
103
|
+
if (FeatureGates.getExperimentValue('maui_ai_border_killswitch', 'value', false)) {
|
|
104
|
+
if (pluginState.generatingMediaIds.size > 0) {
|
|
105
|
+
return {
|
|
106
|
+
generatingMediaIds: new Set(),
|
|
107
|
+
decorationSet: DecorationSet.empty
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return pluginState;
|
|
111
|
+
}
|
|
112
|
+
const meta = tr.getMeta(aiGeneratingDecorationPluginKey);
|
|
113
|
+
if (meta) {
|
|
114
|
+
switch (meta.type) {
|
|
115
|
+
case 'SET_GENERATING':
|
|
116
|
+
{
|
|
117
|
+
const ids = new Set(pluginState.generatingMediaIds);
|
|
118
|
+
ids.add(meta.mediaId);
|
|
119
|
+
return {
|
|
120
|
+
generatingMediaIds: ids,
|
|
121
|
+
decorationSet: buildDecorationSet(newState.doc, ids)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
case 'CLEAR_GENERATING':
|
|
125
|
+
{
|
|
126
|
+
const ids = new Set(pluginState.generatingMediaIds);
|
|
127
|
+
ids.delete(meta.mediaId);
|
|
128
|
+
return {
|
|
129
|
+
generatingMediaIds: ids,
|
|
130
|
+
decorationSet: buildDecorationSet(newState.doc, ids)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
case 'CLEAR_ALL':
|
|
134
|
+
return {
|
|
135
|
+
generatingMediaIds: new Set(),
|
|
136
|
+
decorationSet: DecorationSet.empty
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Map decorations through document changes so positions stay in sync
|
|
142
|
+
if (tr.docChanged && pluginState.decorationSet !== DecorationSet.empty) {
|
|
143
|
+
try {
|
|
144
|
+
return {
|
|
145
|
+
...pluginState,
|
|
146
|
+
decorationSet: pluginState.decorationSet.map(tr.mapping, newState.doc)
|
|
147
|
+
};
|
|
148
|
+
} catch {
|
|
149
|
+
// Collaborative editing edge case — reset
|
|
150
|
+
return {
|
|
151
|
+
generatingMediaIds: new Set(),
|
|
152
|
+
decorationSet: DecorationSet.empty
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return pluginState;
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
props: {
|
|
160
|
+
decorations(state) {
|
|
161
|
+
var _aiGeneratingDecorati, _aiGeneratingDecorati2;
|
|
162
|
+
return (_aiGeneratingDecorati = (_aiGeneratingDecorati2 = aiGeneratingDecorationPluginKey.getState(state)) === null || _aiGeneratingDecorati2 === void 0 ? void 0 : _aiGeneratingDecorati2.decorationSet) !== null && _aiGeneratingDecorati !== void 0 ? _aiGeneratingDecorati : DecorationSet.empty;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ACTIONS } from '../pm-plugins/actions';
|
|
2
|
+
import { setAIGeneratingMeta, clearAIGeneratingMeta } from '../pm-plugins/ai-generating-decoration';
|
|
2
3
|
import { stateKey } from '../pm-plugins/plugin-key';
|
|
3
4
|
import { getIdentifier } from '../pm-plugins/utils/media-common';
|
|
4
5
|
import { createInsertMediaAsMediaSingleCommand } from './utils/media-single';
|
|
@@ -32,4 +33,23 @@ export const trackMediaPaste = attrs => ({
|
|
|
32
33
|
});
|
|
33
34
|
return tr;
|
|
34
35
|
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
39
|
+
* The decoration triggers the AI border visual on the media's NodeView.
|
|
40
|
+
*
|
|
41
|
+
* Decorations live in the view layer only and never affect the document model
|
|
42
|
+
* or undo/redo history.
|
|
43
|
+
*/
|
|
44
|
+
export const setAIGenerating = mediaId => ({
|
|
45
|
+
tr
|
|
46
|
+
}) => setAIGeneratingMeta(tr, mediaId);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
50
|
+
* `mediaId`. Removes the AI border visual from that media's NodeView.
|
|
51
|
+
*/
|
|
52
|
+
export const clearAIGenerating = mediaId => ({
|
|
53
|
+
tr
|
|
54
|
+
}) => clearAIGeneratingMeta(tr, mediaId);
|
|
35
55
|
export const insertMediaAsMediaSingleCommand = (editorAnalyticsAPI, allowPixelResizing) => (mediaAttrs, inputMethod, insertMediaVia) => createInsertMediaAsMediaSingleCommand(mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing);
|
package/dist/esm/mediaPlugin.js
CHANGED
|
@@ -21,9 +21,10 @@ import { mediaSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/media';
|
|
|
21
21
|
import { mediaGroupSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaGroup';
|
|
22
22
|
import { mediaInlineSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaInline';
|
|
23
23
|
import { mediaSingleSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/mediaSingle';
|
|
24
|
+
import { createAIGeneratingDecorationPlugin } from './pm-plugins/ai-generating-decoration';
|
|
24
25
|
import { createPlugin as createMediaAltTextPlugin } from './pm-plugins/alt-text';
|
|
25
26
|
import keymapMediaAltTextPlugin from './pm-plugins/alt-text/keymap';
|
|
26
|
-
import { hideMediaViewer, insertMediaAsMediaSingleCommand, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
|
|
27
|
+
import { clearAIGenerating, hideMediaViewer, insertMediaAsMediaSingleCommand, setAIGenerating, showMediaViewer, trackMediaPaste } from './pm-plugins/commands';
|
|
27
28
|
import keymapPlugin from './pm-plugins/keymap';
|
|
28
29
|
import keymapMediaSinglePlugin from './pm-plugins/keymap-media';
|
|
29
30
|
import linkingPlugin from './pm-plugins/linking';
|
|
@@ -171,6 +172,8 @@ export var mediaPlugin = function mediaPlugin(_ref3) {
|
|
|
171
172
|
showMediaViewer: showMediaViewer,
|
|
172
173
|
hideMediaViewer: hideMediaViewer,
|
|
173
174
|
trackMediaPaste: trackMediaPaste,
|
|
175
|
+
setAIGenerating: setAIGenerating,
|
|
176
|
+
clearAIGenerating: clearAIGenerating,
|
|
174
177
|
insertMediaSingle: insertMediaAsMediaSingleCommand(api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions, options.allowPixelResizing)
|
|
175
178
|
},
|
|
176
179
|
nodes: function nodes() {
|
|
@@ -296,6 +299,12 @@ export var mediaPlugin = function mediaPlugin(_ref3) {
|
|
|
296
299
|
plugin: createMediaPixelResizingPlugin
|
|
297
300
|
});
|
|
298
301
|
}
|
|
302
|
+
pmPlugins.push({
|
|
303
|
+
name: 'mediaAIGeneratingDecoration',
|
|
304
|
+
plugin: function plugin() {
|
|
305
|
+
return createAIGeneratingDecorationPlugin();
|
|
306
|
+
}
|
|
307
|
+
});
|
|
299
308
|
pmPlugins.push({
|
|
300
309
|
name: 'mediaSelectionHandler',
|
|
301
310
|
plugin: function plugin() {
|
|
@@ -22,6 +22,7 @@ import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared
|
|
|
22
22
|
import { akEditorFullWidthLayoutWidth, akEditorDefaultLayoutWidth, akEditorCalculatedWideLayoutWidth } from '@atlaskit/editor-shared-styles';
|
|
23
23
|
import { getAttrsFromUrl } from '@atlaskit/media-client';
|
|
24
24
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
25
|
+
import { hasAIGeneratingDecoration } from '../../pm-plugins/ai-generating-decoration';
|
|
25
26
|
import { updateCurrentMediaNodeAttrs } from '../../pm-plugins/commands/helpers';
|
|
26
27
|
import { isMediaBlobUrlFromAttrs } from '../../pm-plugins/utils/media-common';
|
|
27
28
|
// Ignored via go/ees005
|
|
@@ -56,6 +57,7 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
56
57
|
}
|
|
57
58
|
_this = _callSuper(this, MediaNodeView, [].concat(args));
|
|
58
59
|
_defineProperty(_this, "isSelected", false);
|
|
60
|
+
_defineProperty(_this, "isAIGenerating", false);
|
|
59
61
|
_defineProperty(_this, "hasBeenResized", false);
|
|
60
62
|
_defineProperty(_this, "hasResizedListener", function () {
|
|
61
63
|
if (!_this.hasBeenResized) {
|
|
@@ -172,7 +174,8 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
172
174
|
mediaOptions: mediaOptions,
|
|
173
175
|
onExternalImageLoaded: _this.onExternalImageLoaded,
|
|
174
176
|
isViewOnly: ((_this$reactComponentP = _this.reactComponentProps.pluginInjectionApi) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.editorViewMode) === null || _this$reactComponentP === void 0 || (_this$reactComponentP = _this$reactComponentP.sharedState.currentState()) === null || _this$reactComponentP === void 0 ? void 0 : _this$reactComponentP.mode) === 'view',
|
|
175
|
-
pluginInjectionApi: _this.reactComponentProps.pluginInjectionApi
|
|
177
|
+
pluginInjectionApi: _this.reactComponentProps.pluginInjectionApi,
|
|
178
|
+
isAIGenerating: _this.isAIGenerating
|
|
176
179
|
});
|
|
177
180
|
};
|
|
178
181
|
});
|
|
@@ -262,6 +265,11 @@ var MediaNodeView = /*#__PURE__*/function (_SelectionBasedNodeVi) {
|
|
|
262
265
|
this.isSelected = hasMediaNodeSelectedDecoration;
|
|
263
266
|
return true;
|
|
264
267
|
}
|
|
268
|
+
var aiGenerating = hasAIGeneratingDecoration(decorations);
|
|
269
|
+
if (this.isAIGenerating !== aiGenerating) {
|
|
270
|
+
this.isAIGenerating = aiGenerating;
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
265
273
|
if (this.node.attrs !== nextNode.attrs) {
|
|
266
274
|
return true;
|
|
267
275
|
}
|
|
@@ -173,7 +173,7 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
173
173
|
value: function shouldComponentUpdate(nextProps, nextState) {
|
|
174
174
|
var hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
|
|
175
175
|
var hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
|
|
176
|
-
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
176
|
+
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection || this.props.isAIGenerating !== nextProps.isAIGenerating || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.mediaProvider !== nextProps.mediaProvider || expValEquals('platform_editor_media_vc_fixes', 'isEnabled', true) && this.props.syncProvider !== nextProps.syncProvider || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
177
177
|
return true;
|
|
178
178
|
}
|
|
179
179
|
return false;
|
|
@@ -383,6 +383,7 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
383
383
|
videoControlsWrapperRef: this.videoControlsWrapperRef,
|
|
384
384
|
ssr: ssr,
|
|
385
385
|
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig),
|
|
386
|
+
isAIGenerating: !!this.props.isAIGenerating,
|
|
386
387
|
onError: expValEquals('platform_editor_media_error_analytics', 'isEnabled', true) ? this.onError : undefined
|
|
387
388
|
})));
|
|
388
389
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
3
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
4
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
5
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
6
|
+
import { Decoration, DecorationSet } from '@atlaskit/editor-prosemirror/view';
|
|
7
|
+
import FeatureGates from '@atlaskit/feature-gate-js-client';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* ProseMirror plugin that manages AI-generating decorations on media nodes.
|
|
11
|
+
*
|
|
12
|
+
* Instead of storing transient `__isAIGenerating` attributes in the ADF schema
|
|
13
|
+
* (which pollutes the document model and undo history), this plugin uses
|
|
14
|
+
* ProseMirror decorations — a view-layer-only mechanism that never affects the
|
|
15
|
+
* document content, serialization, or undo/redo stack.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ── Plugin Key ──────────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
export var aiGeneratingDecorationPluginKey = new PluginKey('aiGeneratingDecoration');
|
|
21
|
+
|
|
22
|
+
// ── Types ───────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
var AI_GENERATING_DECORATION_TYPE = 'ai-generating';
|
|
25
|
+
|
|
26
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Build a DecorationSet containing a Decoration.node for every media node
|
|
30
|
+
* whose `id` is in the given set.
|
|
31
|
+
*/
|
|
32
|
+
function buildDecorationSet(doc, mediaIds) {
|
|
33
|
+
if (mediaIds.size === 0) {
|
|
34
|
+
return DecorationSet.empty;
|
|
35
|
+
}
|
|
36
|
+
var decorations = [];
|
|
37
|
+
doc.descendants(function (node, pos) {
|
|
38
|
+
if (node.type.name === 'media' && mediaIds.has(node.attrs.id)) {
|
|
39
|
+
decorations.push(Decoration.node(pos, pos + node.nodeSize, {},
|
|
40
|
+
// no DOM attrs needed — the NodeView reads the decoration spec
|
|
41
|
+
{
|
|
42
|
+
type: AI_GENERATING_DECORATION_TYPE,
|
|
43
|
+
mediaId: node.attrs.id
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return DecorationSet.create(doc, decorations);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── Public utilities ────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns `true` if the given decorations array contains an AI-generating
|
|
54
|
+
* decoration. Call this from a NodeView's `update()` / `viewShouldUpdate()`
|
|
55
|
+
* to determine whether to render the AI border.
|
|
56
|
+
*/
|
|
57
|
+
export function hasAIGeneratingDecoration(decorations) {
|
|
58
|
+
return decorations.some(function (d) {
|
|
59
|
+
return d.spec.type === AI_GENERATING_DECORATION_TYPE;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Dispatch a transaction that sets the AI-generating decoration on a media
|
|
65
|
+
* node identified by `mediaId`.
|
|
66
|
+
*
|
|
67
|
+
* Usage from the editor bridge:
|
|
68
|
+
* ```
|
|
69
|
+
* editorAPI.core.actions.execute(({ tr }) =>
|
|
70
|
+
* setAIGeneratingMeta(tr, mediaId),
|
|
71
|
+
* );
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function setAIGeneratingMeta(tr, mediaId) {
|
|
75
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
76
|
+
type: 'SET_GENERATING',
|
|
77
|
+
mediaId: mediaId
|
|
78
|
+
}).setMeta('addToHistory', false);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Dispatch a transaction that clears the AI-generating decoration for a
|
|
83
|
+
* specific media node.
|
|
84
|
+
*/
|
|
85
|
+
export function clearAIGeneratingMeta(tr, mediaId) {
|
|
86
|
+
return tr.setMeta(aiGeneratingDecorationPluginKey, {
|
|
87
|
+
type: 'CLEAR_GENERATING',
|
|
88
|
+
mediaId: mediaId
|
|
89
|
+
}).setMeta('addToHistory', false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Plugin ──────────────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
/** Creates the ProseMirror plugin that manages AI-generating decorations on media nodes. */
|
|
95
|
+
export function createAIGeneratingDecorationPlugin() {
|
|
96
|
+
return new SafePlugin({
|
|
97
|
+
key: aiGeneratingDecorationPluginKey,
|
|
98
|
+
state: {
|
|
99
|
+
init: function init() {
|
|
100
|
+
return {
|
|
101
|
+
generatingMediaIds: new Set(),
|
|
102
|
+
decorationSet: DecorationSet.empty
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
apply: function apply(tr, pluginState, _oldState, newState) {
|
|
106
|
+
// Killswitch — if active, clear any existing decorations and stop
|
|
107
|
+
// eslint-disable-next-line @atlaskit/platform/use-recommended-utils -- dynamic config killswitch, not a standard feature gate
|
|
108
|
+
if (FeatureGates.getExperimentValue('maui_ai_border_killswitch', 'value', false)) {
|
|
109
|
+
if (pluginState.generatingMediaIds.size > 0) {
|
|
110
|
+
return {
|
|
111
|
+
generatingMediaIds: new Set(),
|
|
112
|
+
decorationSet: DecorationSet.empty
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return pluginState;
|
|
116
|
+
}
|
|
117
|
+
var meta = tr.getMeta(aiGeneratingDecorationPluginKey);
|
|
118
|
+
if (meta) {
|
|
119
|
+
switch (meta.type) {
|
|
120
|
+
case 'SET_GENERATING':
|
|
121
|
+
{
|
|
122
|
+
var ids = new Set(pluginState.generatingMediaIds);
|
|
123
|
+
ids.add(meta.mediaId);
|
|
124
|
+
return {
|
|
125
|
+
generatingMediaIds: ids,
|
|
126
|
+
decorationSet: buildDecorationSet(newState.doc, ids)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
case 'CLEAR_GENERATING':
|
|
130
|
+
{
|
|
131
|
+
var _ids = new Set(pluginState.generatingMediaIds);
|
|
132
|
+
_ids.delete(meta.mediaId);
|
|
133
|
+
return {
|
|
134
|
+
generatingMediaIds: _ids,
|
|
135
|
+
decorationSet: buildDecorationSet(newState.doc, _ids)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
case 'CLEAR_ALL':
|
|
139
|
+
return {
|
|
140
|
+
generatingMediaIds: new Set(),
|
|
141
|
+
decorationSet: DecorationSet.empty
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Map decorations through document changes so positions stay in sync
|
|
147
|
+
if (tr.docChanged && pluginState.decorationSet !== DecorationSet.empty) {
|
|
148
|
+
try {
|
|
149
|
+
return _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
150
|
+
decorationSet: pluginState.decorationSet.map(tr.mapping, newState.doc)
|
|
151
|
+
});
|
|
152
|
+
} catch (_unused) {
|
|
153
|
+
// Collaborative editing edge case — reset
|
|
154
|
+
return {
|
|
155
|
+
generatingMediaIds: new Set(),
|
|
156
|
+
decorationSet: DecorationSet.empty
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return pluginState;
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
props: {
|
|
164
|
+
decorations: function decorations(state) {
|
|
165
|
+
var _aiGeneratingDecorati, _aiGeneratingDecorati2;
|
|
166
|
+
return (_aiGeneratingDecorati = (_aiGeneratingDecorati2 = aiGeneratingDecorationPluginKey.getState(state)) === null || _aiGeneratingDecorati2 === void 0 ? void 0 : _aiGeneratingDecorati2.decorationSet) !== null && _aiGeneratingDecorati !== void 0 ? _aiGeneratingDecorati : DecorationSet.empty;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ACTIONS } from '../pm-plugins/actions';
|
|
2
|
+
import { setAIGeneratingMeta, clearAIGeneratingMeta } from '../pm-plugins/ai-generating-decoration';
|
|
2
3
|
import { stateKey } from '../pm-plugins/plugin-key';
|
|
3
4
|
import { getIdentifier } from '../pm-plugins/utils/media-common';
|
|
4
5
|
import { createInsertMediaAsMediaSingleCommand } from './utils/media-single';
|
|
@@ -33,6 +34,31 @@ export var trackMediaPaste = function trackMediaPaste(attrs) {
|
|
|
33
34
|
return tr;
|
|
34
35
|
};
|
|
35
36
|
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
40
|
+
* The decoration triggers the AI border visual on the media's NodeView.
|
|
41
|
+
*
|
|
42
|
+
* Decorations live in the view layer only and never affect the document model
|
|
43
|
+
* or undo/redo history.
|
|
44
|
+
*/
|
|
45
|
+
export var setAIGenerating = function setAIGenerating(mediaId) {
|
|
46
|
+
return function (_ref4) {
|
|
47
|
+
var tr = _ref4.tr;
|
|
48
|
+
return setAIGeneratingMeta(tr, mediaId);
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
54
|
+
* `mediaId`. Removes the AI border visual from that media's NodeView.
|
|
55
|
+
*/
|
|
56
|
+
export var clearAIGenerating = function clearAIGenerating(mediaId) {
|
|
57
|
+
return function (_ref5) {
|
|
58
|
+
var tr = _ref5.tr;
|
|
59
|
+
return clearAIGeneratingMeta(tr, mediaId);
|
|
60
|
+
};
|
|
61
|
+
};
|
|
36
62
|
export var insertMediaAsMediaSingleCommand = function insertMediaAsMediaSingleCommand(editorAnalyticsAPI, allowPixelResizing) {
|
|
37
63
|
return function (mediaAttrs, inputMethod, insertMediaVia) {
|
|
38
64
|
return createInsertMediaAsMediaSingleCommand(mediaAttrs, inputMethod, editorAnalyticsAPI, insertMediaVia, allowPixelResizing);
|
|
@@ -69,6 +69,11 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
|
|
|
69
69
|
setProvider: (provider: Promise<MediaProvider>) => boolean;
|
|
70
70
|
};
|
|
71
71
|
commands: {
|
|
72
|
+
/**
|
|
73
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
74
|
+
* `mediaId`. Removes the AI border from that media node.
|
|
75
|
+
*/
|
|
76
|
+
clearAIGenerating: (mediaId: string) => EditorCommand;
|
|
72
77
|
hideMediaViewer: EditorCommand;
|
|
73
78
|
/**
|
|
74
79
|
* Inserts a media node as a media single.
|
|
@@ -79,6 +84,14 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
|
|
|
79
84
|
* @param insertMediaVia - Optional parameter indicating how the media was inserted
|
|
80
85
|
*/
|
|
81
86
|
insertMediaSingle: (attrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
|
|
87
|
+
/**
|
|
88
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
89
|
+
* Renders an AI border around the media node while AI is generating/replacing it.
|
|
90
|
+
*
|
|
91
|
+
* Decorations live in the view layer only and never affect the document model
|
|
92
|
+
* or undo/redo history.
|
|
93
|
+
*/
|
|
94
|
+
setAIGenerating: (mediaId: string) => EditorCommand;
|
|
82
95
|
showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
|
|
83
96
|
trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
|
|
84
97
|
};
|
|
@@ -17,6 +17,7 @@ interface MediaNodeWithPluginStateComponentProps {
|
|
|
17
17
|
}
|
|
18
18
|
declare class MediaNodeView extends SelectionBasedNodeView<MediaNodeViewProps> {
|
|
19
19
|
private isSelected;
|
|
20
|
+
private isAIGenerating;
|
|
20
21
|
private hasBeenResized;
|
|
21
22
|
private resizeListenerBinding?;
|
|
22
23
|
getMediaSingleNode(getPos: getPosHandlerNode): PMNode | null;
|
|
@@ -15,6 +15,7 @@ export interface MediaNodeProps extends ReactNodeProps, ImageLoaderProps {
|
|
|
15
15
|
api?: ExtractInjectionAPI<MediaNextEditorPluginType>;
|
|
16
16
|
contextIdentifierProvider?: Promise<ContextIdentifierProvider>;
|
|
17
17
|
getPos: ProsemirrorGetPosHandler;
|
|
18
|
+
isAIGenerating?: boolean;
|
|
18
19
|
isLoading?: boolean;
|
|
19
20
|
isMediaSingle?: boolean;
|
|
20
21
|
isViewOnly?: boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
/**
|
|
6
|
+
* ProseMirror plugin that manages AI-generating decorations on media nodes.
|
|
7
|
+
*
|
|
8
|
+
* Instead of storing transient `__isAIGenerating` attributes in the ADF schema
|
|
9
|
+
* (which pollutes the document model and undo history), this plugin uses
|
|
10
|
+
* ProseMirror decorations — a view-layer-only mechanism that never affects the
|
|
11
|
+
* document content, serialization, or undo/redo stack.
|
|
12
|
+
*/
|
|
13
|
+
export declare const aiGeneratingDecorationPluginKey: PluginKey;
|
|
14
|
+
export type AIGeneratingAction = {
|
|
15
|
+
mediaId: string;
|
|
16
|
+
type: 'SET_GENERATING';
|
|
17
|
+
} | {
|
|
18
|
+
mediaId: string;
|
|
19
|
+
type: 'CLEAR_GENERATING';
|
|
20
|
+
} | {
|
|
21
|
+
type: 'CLEAR_ALL';
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns `true` if the given decorations array contains an AI-generating
|
|
25
|
+
* decoration. Call this from a NodeView's `update()` / `viewShouldUpdate()`
|
|
26
|
+
* to determine whether to render the AI border.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hasAIGeneratingDecoration(decorations: readonly Decoration[]): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Dispatch a transaction that sets the AI-generating decoration on a media
|
|
31
|
+
* node identified by `mediaId`.
|
|
32
|
+
*
|
|
33
|
+
* Usage from the editor bridge:
|
|
34
|
+
* ```
|
|
35
|
+
* editorAPI.core.actions.execute(({ tr }) =>
|
|
36
|
+
* setAIGeneratingMeta(tr, mediaId),
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function setAIGeneratingMeta(tr: Transaction, mediaId: string): Transaction;
|
|
41
|
+
/**
|
|
42
|
+
* Dispatch a transaction that clears the AI-generating decoration for a
|
|
43
|
+
* specific media node.
|
|
44
|
+
*/
|
|
45
|
+
export declare function clearAIGeneratingMeta(tr: Transaction, mediaId: string): Transaction;
|
|
46
|
+
/** Creates the ProseMirror plugin that manages AI-generating decorations on media nodes. */
|
|
47
|
+
export declare function createAIGeneratingDecorationPlugin(): SafePlugin;
|
|
@@ -4,4 +4,17 @@ import type { EditorCommand } from '@atlaskit/editor-common/types';
|
|
|
4
4
|
export declare const showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
|
|
5
5
|
export declare const hideMediaViewer: EditorCommand;
|
|
6
6
|
export declare const trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
|
|
7
|
+
/**
|
|
8
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
9
|
+
* The decoration triggers the AI border visual on the media's NodeView.
|
|
10
|
+
*
|
|
11
|
+
* Decorations live in the view layer only and never affect the document model
|
|
12
|
+
* or undo/redo history.
|
|
13
|
+
*/
|
|
14
|
+
export declare const setAIGenerating: (mediaId: string) => EditorCommand;
|
|
15
|
+
/**
|
|
16
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
17
|
+
* `mediaId`. Removes the AI border visual from that media's NodeView.
|
|
18
|
+
*/
|
|
19
|
+
export declare const clearAIGenerating: (mediaId: string) => EditorCommand;
|
|
7
20
|
export declare const insertMediaAsMediaSingleCommand: (editorAnalyticsAPI?: EditorAnalyticsAPI, allowPixelResizing?: boolean) => (mediaAttrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
|
|
@@ -69,6 +69,11 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
|
|
|
69
69
|
setProvider: (provider: Promise<MediaProvider>) => boolean;
|
|
70
70
|
};
|
|
71
71
|
commands: {
|
|
72
|
+
/**
|
|
73
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
74
|
+
* `mediaId`. Removes the AI border from that media node.
|
|
75
|
+
*/
|
|
76
|
+
clearAIGenerating: (mediaId: string) => EditorCommand;
|
|
72
77
|
hideMediaViewer: EditorCommand;
|
|
73
78
|
/**
|
|
74
79
|
* Inserts a media node as a media single.
|
|
@@ -79,6 +84,14 @@ export type MediaNextEditorPluginType = NextEditorPlugin<'media', {
|
|
|
79
84
|
* @param insertMediaVia - Optional parameter indicating how the media was inserted
|
|
80
85
|
*/
|
|
81
86
|
insertMediaSingle: (attrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
|
|
87
|
+
/**
|
|
88
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
89
|
+
* Renders an AI border around the media node while AI is generating/replacing it.
|
|
90
|
+
*
|
|
91
|
+
* Decorations live in the view layer only and never affect the document model
|
|
92
|
+
* or undo/redo history.
|
|
93
|
+
*/
|
|
94
|
+
setAIGenerating: (mediaId: string) => EditorCommand;
|
|
82
95
|
showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
|
|
83
96
|
trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
|
|
84
97
|
};
|
|
@@ -17,6 +17,7 @@ interface MediaNodeWithPluginStateComponentProps {
|
|
|
17
17
|
}
|
|
18
18
|
declare class MediaNodeView extends SelectionBasedNodeView<MediaNodeViewProps> {
|
|
19
19
|
private isSelected;
|
|
20
|
+
private isAIGenerating;
|
|
20
21
|
private hasBeenResized;
|
|
21
22
|
private resizeListenerBinding?;
|
|
22
23
|
getMediaSingleNode(getPos: getPosHandlerNode): PMNode | null;
|
|
@@ -15,6 +15,7 @@ export interface MediaNodeProps extends ReactNodeProps, ImageLoaderProps {
|
|
|
15
15
|
api?: ExtractInjectionAPI<MediaNextEditorPluginType>;
|
|
16
16
|
contextIdentifierProvider?: Promise<ContextIdentifierProvider>;
|
|
17
17
|
getPos: ProsemirrorGetPosHandler;
|
|
18
|
+
isAIGenerating?: boolean;
|
|
18
19
|
isLoading?: boolean;
|
|
19
20
|
isMediaSingle?: boolean;
|
|
20
21
|
isViewOnly?: boolean;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
2
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
3
|
+
import type { Transaction } from '@atlaskit/editor-prosemirror/state';
|
|
4
|
+
import { Decoration } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
/**
|
|
6
|
+
* ProseMirror plugin that manages AI-generating decorations on media nodes.
|
|
7
|
+
*
|
|
8
|
+
* Instead of storing transient `__isAIGenerating` attributes in the ADF schema
|
|
9
|
+
* (which pollutes the document model and undo history), this plugin uses
|
|
10
|
+
* ProseMirror decorations — a view-layer-only mechanism that never affects the
|
|
11
|
+
* document content, serialization, or undo/redo stack.
|
|
12
|
+
*/
|
|
13
|
+
export declare const aiGeneratingDecorationPluginKey: PluginKey;
|
|
14
|
+
export type AIGeneratingAction = {
|
|
15
|
+
mediaId: string;
|
|
16
|
+
type: 'SET_GENERATING';
|
|
17
|
+
} | {
|
|
18
|
+
mediaId: string;
|
|
19
|
+
type: 'CLEAR_GENERATING';
|
|
20
|
+
} | {
|
|
21
|
+
type: 'CLEAR_ALL';
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Returns `true` if the given decorations array contains an AI-generating
|
|
25
|
+
* decoration. Call this from a NodeView's `update()` / `viewShouldUpdate()`
|
|
26
|
+
* to determine whether to render the AI border.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hasAIGeneratingDecoration(decorations: readonly Decoration[]): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Dispatch a transaction that sets the AI-generating decoration on a media
|
|
31
|
+
* node identified by `mediaId`.
|
|
32
|
+
*
|
|
33
|
+
* Usage from the editor bridge:
|
|
34
|
+
* ```
|
|
35
|
+
* editorAPI.core.actions.execute(({ tr }) =>
|
|
36
|
+
* setAIGeneratingMeta(tr, mediaId),
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function setAIGeneratingMeta(tr: Transaction, mediaId: string): Transaction;
|
|
41
|
+
/**
|
|
42
|
+
* Dispatch a transaction that clears the AI-generating decoration for a
|
|
43
|
+
* specific media node.
|
|
44
|
+
*/
|
|
45
|
+
export declare function clearAIGeneratingMeta(tr: Transaction, mediaId: string): Transaction;
|
|
46
|
+
/** Creates the ProseMirror plugin that manages AI-generating decorations on media nodes. */
|
|
47
|
+
export declare function createAIGeneratingDecorationPlugin(): SafePlugin;
|
|
@@ -4,4 +4,17 @@ import type { EditorCommand } from '@atlaskit/editor-common/types';
|
|
|
4
4
|
export declare const showMediaViewer: (media: MediaADFAttrs) => EditorCommand;
|
|
5
5
|
export declare const hideMediaViewer: EditorCommand;
|
|
6
6
|
export declare const trackMediaPaste: (attrs: MediaADFAttrs) => EditorCommand;
|
|
7
|
+
/**
|
|
8
|
+
* Sets the AI-generating decoration on a media node identified by `mediaId`.
|
|
9
|
+
* The decoration triggers the AI border visual on the media's NodeView.
|
|
10
|
+
*
|
|
11
|
+
* Decorations live in the view layer only and never affect the document model
|
|
12
|
+
* or undo/redo history.
|
|
13
|
+
*/
|
|
14
|
+
export declare const setAIGenerating: (mediaId: string) => EditorCommand;
|
|
15
|
+
/**
|
|
16
|
+
* Clears the AI-generating decoration for a specific media node identified by
|
|
17
|
+
* `mediaId`. Removes the AI border visual from that media's NodeView.
|
|
18
|
+
*/
|
|
19
|
+
export declare const clearAIGenerating: (mediaId: string) => EditorCommand;
|
|
7
20
|
export declare const insertMediaAsMediaSingleCommand: (editorAnalyticsAPI?: EditorAnalyticsAPI, allowPixelResizing?: boolean) => (mediaAttrs: MediaADFAttrs, inputMethod: InputMethodInsertMedia, insertMediaVia?: InsertMediaVia) => EditorCommand;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-media",
|
|
3
|
-
"version": "12.1
|
|
3
|
+
"version": "12.2.1",
|
|
4
4
|
"description": "Media plugin for @atlaskit/editor-core",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"atlaskit:src": "src/index.ts",
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@atlaskit/adf-schema": "^52.
|
|
32
|
+
"@atlaskit/adf-schema": "^52.6.0",
|
|
33
33
|
"@atlaskit/analytics-namespaced-context": "^7.2.0",
|
|
34
34
|
"@atlaskit/analytics-next": "^11.2.0",
|
|
35
35
|
"@atlaskit/button": "^23.11.0",
|
|
@@ -51,10 +51,11 @@
|
|
|
51
51
|
"@atlaskit/editor-prosemirror": "^7.3.0",
|
|
52
52
|
"@atlaskit/editor-shared-styles": "^3.10.0",
|
|
53
53
|
"@atlaskit/editor-tables": "^2.9.0",
|
|
54
|
+
"@atlaskit/feature-gate-js-client": "5.5.11",
|
|
54
55
|
"@atlaskit/form": "^15.5.0",
|
|
55
56
|
"@atlaskit/icon": "^34.3.0",
|
|
56
57
|
"@atlaskit/icon-lab": "^6.6.0",
|
|
57
|
-
"@atlaskit/media-card": "^80.
|
|
58
|
+
"@atlaskit/media-card": "^80.2.0",
|
|
58
59
|
"@atlaskit/media-client": "^36.0.0",
|
|
59
60
|
"@atlaskit/media-client-react": "^5.0.0",
|
|
60
61
|
"@atlaskit/media-common": "^13.0.0",
|
|
@@ -65,7 +66,7 @@
|
|
|
65
66
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
66
67
|
"@atlaskit/primitives": "^19.0.0",
|
|
67
68
|
"@atlaskit/textfield": "^8.3.0",
|
|
68
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
69
|
+
"@atlaskit/tmp-editor-statsig": "^69.0.0",
|
|
69
70
|
"@atlaskit/tokens": "^13.0.0",
|
|
70
71
|
"@atlaskit/tooltip": "^21.2.0",
|
|
71
72
|
"@babel/runtime": "^7.0.0",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"uuid": "^3.1.0"
|
|
78
79
|
},
|
|
79
80
|
"peerDependencies": {
|
|
80
|
-
"@atlaskit/editor-common": "^114.
|
|
81
|
+
"@atlaskit/editor-common": "^114.10.0",
|
|
81
82
|
"@atlaskit/media-core": "^37.0.0",
|
|
82
83
|
"react": "^18.2.0",
|
|
83
84
|
"react-dom": "^18.2.0",
|