@atlaskit/editor-plugin-synced-block 10.1.10 → 10.2.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @atlaskit/editor-plugin-synced-block
2
2
 
3
+ ## 10.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`c0b0b98789f7a`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/c0b0b98789f7a) -
8
+ Add analytics for synced block source creation: record the creation input method and whether the
9
+ block was created empty, and emit a one-off event the first time an empty source block gains
10
+ content.
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies
15
+
3
16
  ## 10.1.10
4
17
 
5
18
  ### Patch Changes
@@ -78,11 +78,17 @@ var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_
78
78
  var tr = _ref.tr,
79
79
  syncBlockStore = _ref.syncBlockStore,
80
80
  typeAheadInsert = _ref.typeAheadInsert,
81
- fireAnalyticsEvent = _ref.fireAnalyticsEvent;
81
+ fireAnalyticsEvent = _ref.fireAnalyticsEvent,
82
+ inputMethod = _ref.inputMethod;
82
83
  var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
83
84
  bodiedSyncBlock = _tr$doc$type$schema$n.bodiedSyncBlock,
84
85
  paragraph = _tr$doc$type$schema$n.paragraph;
85
86
 
87
+ // Capture createdEmpty before any insertion mutates the selection. The meta is
88
+ // set on the final transaction before returning (see below), since the
89
+ // typeahead path may reassign `tr` and drop meta set here.
90
+ var createdEmpty = tr.selection.empty;
91
+
86
92
  // If the selection is empty, we want to insert the sync block on a new line
87
93
  if (tr.selection.empty) {
88
94
  var attrs = syncBlockStore.sourceManager.generateBodiedSyncBlockAttrs();
@@ -149,6 +155,14 @@ var createSyncedBlock = exports.createSyncedBlock = function createSyncedBlock(_
149
155
  tr.setSelection(_state.TextSelection.create(tr.doc, conversionInfo.from));
150
156
  }
151
157
  }
158
+
159
+ // Stash creation-type signals on the final transaction (after any typeahead
160
+ // reassignment). Set unconditionally — the store manager only reads it behind
161
+ // the feature gate.
162
+ tr.setMeta(_main.creationMetaKey, {
163
+ createdEmpty: createdEmpty,
164
+ inputMethod: inputMethod
165
+ });
152
166
  return tr;
153
167
  };
154
168
  var copySyncedBlockReferenceToClipboardEditorCommand = exports.copySyncedBlockReferenceToClipboardEditorCommand = function copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api) {
@@ -4,7 +4,7 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
4
4
  Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
- exports.syncedBlockPluginKey = exports.getDeleteMechanism = exports.deleteMechanismMetaKey = exports.createPlugin = void 0;
7
+ exports.syncedBlockPluginKey = exports.getDeleteMechanism = exports.deleteMechanismMetaKey = exports.creationMetaKey = exports.createPlugin = void 0;
8
8
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
9
9
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
10
10
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
@@ -53,6 +53,16 @@ var syncedBlockPluginKey = exports.syncedBlockPluginKey = new _state.PluginKey('
53
53
  * `platform_editor_blocks_patch_4`.
54
54
  */
55
55
  var deleteMechanismMetaKey = exports.deleteMechanismMetaKey = new _state.PluginKey('syncedBlockDeleteMechanism');
56
+
57
+ /**
58
+ * Creation-type signals set by {@link createSyncedBlock} on the creating
59
+ * transaction. The async creation handler forwards them to the store manager,
60
+ * which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
61
+ * this transient signal isolated from plugin state. Consumed behind
62
+ * `platform_editor_blocks_patch_4`.
63
+ */
64
+
65
+ var creationMetaKey = exports.creationMetaKey = new _state.PluginKey('syncedBlockCreationMeta');
56
66
  var mapRetryCreationPosMap = function mapRetryCreationPosMap(oldMap, newRetryCreationPos, mapPos, isPerfExperimentOn) {
57
67
  var resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
58
68
 
@@ -182,6 +192,43 @@ var getDeleteMechanism = exports.getDeleteMechanism = function getDeleteMechanis
182
192
  var isNodeSelected = selection instanceof _state.NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock';
183
193
  return isNodeSelected ? 'selectionReplaced' : 'keyboardDelete';
184
194
  };
195
+ /**
196
+ * True when a source `bodiedSyncBlock` holds user content — i.e. not just the
197
+ * single empty paragraph it is created with (used to detect empty→content).
198
+ */
199
+ var bodiedSyncBlockHasContent = function bodiedSyncBlockHasContent(node) {
200
+ if (node.childCount === 0) {
201
+ return false;
202
+ }
203
+ if (node.childCount === 1) {
204
+ var child = node.firstChild;
205
+ // A freshly-created block is a single empty paragraph; anything else
206
+ // (non-empty paragraph, or a different/first block) counts as content.
207
+ return !((child === null || child === void 0 ? void 0 : child.type.name) === 'paragraph' && child.content.size === 0);
208
+ }
209
+ return true;
210
+ };
211
+
212
+ /**
213
+ * Ask the store manager to fire the one-shot first-content-added event for every
214
+ * populated source block. The manager only emits for blocks it created empty
215
+ * this session and dedupes per block, so iterating all populated blocks is safe
216
+ * and self-limiting. Called only when `hasEditInSyncBlock` flagged an in-block
217
+ * edit, so this walk runs rarely.
218
+ */
219
+ var emitFirstContentAddedForEditedBlocks = function emitFirstContentAddedForEditedBlocks(tr, state, syncBlockStore) {
220
+ var bodiedSyncBlock = state.schema.nodes.bodiedSyncBlock;
221
+ tr.doc.descendants(function (node) {
222
+ if (node.type === bodiedSyncBlock) {
223
+ if (bodiedSyncBlockHasContent(node)) {
224
+ syncBlockStore.sourceManager.maybeEmitFirstContentAdded(node.attrs.resourceId, node.attrs.localId);
225
+ }
226
+ // bodiedSyncBlocks are top-level and not nested, so no need to descend.
227
+ return false;
228
+ }
229
+ return true;
230
+ });
231
+ };
185
232
  var filterTransactionOnline = function filterTransactionOnline(_ref3) {
186
233
  var _tr$getMeta;
187
234
  var tr = _ref3.tr,
@@ -262,9 +309,20 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
262
309
  if (isPasteOrDrop && (0, _platformFeatureFlags.fg)('platform_synced_block_patch_13')) {
263
310
  return true;
264
311
  }
265
- (0, _handleBodiedSyncBlockCreation.handleBodiedSyncBlockCreation)(bodiedSyncBlockAdded, state, api);
312
+
313
+ // Forward creation-type signals captured by createSyncedBlock onto the async
314
+ // `syncedBlockCreate` success event.
315
+ var creationMeta = tr.getMeta(creationMetaKey);
316
+ (0, _handleBodiedSyncBlockCreation.handleBodiedSyncBlockCreation)(bodiedSyncBlockAdded, state, api, creationMeta);
266
317
  return true;
267
318
  }
319
+
320
+ // On an in-block edit, let the store manager fire the one-shot first-content
321
+ // event (dedupe + empty→content scoping live there). After the add/remove
322
+ // branches so it never runs for the creation transaction itself.
323
+ if ((0, _trackSyncBlocks7.hasEditInSyncBlock)(tr, state)) {
324
+ emitFirstContentAddedForEditedBlocks(tr, state, syncBlockStore);
325
+ }
268
326
  showExtensionInSyncBlockWarningIfNeeded(tr, state, api, extensionFlagShown);
269
327
  return true;
270
328
  };
@@ -74,9 +74,12 @@ var buildRevertCreationTr = function buildRevertCreationTr(tr, pos) {
74
74
 
75
75
  /**
76
76
  *
77
- * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow
77
+ * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
78
+ *
79
+ * @param enrichment optional creation-type signals from `createSyncedBlock`,
80
+ * forwarded to the store manager to attach to the `syncedBlockCreate` event.
78
81
  */
79
- var handleBodiedSyncBlockCreation = exports.handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, editorState, api) {
82
+ var handleBodiedSyncBlockCreation = exports.handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, editorState, api, enrichment) {
80
83
  var syncBlockStore = _main.syncedBlockPluginKey.getState(editorState).syncBlockStore;
81
84
  bodiedSyncBlockAdded.forEach(function (node) {
82
85
  if (node.from === undefined || node.to === undefined) {
@@ -137,6 +140,6 @@ var handleBodiedSyncBlockCreation = exports.handleBodiedSyncBlockCreation = func
137
140
  });
138
141
  });
139
142
  }
140
- });
143
+ }, enrichment);
141
144
  });
142
145
  };
@@ -107,7 +107,7 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
107
107
  copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard(inputMethod) {
108
108
  return (0, _editorCommands.copySyncedBlockReferenceToClipboardEditorCommand)(syncBlockStore, inputMethod, api);
109
109
  },
110
- insertSyncedBlock: function insertSyncedBlock() {
110
+ insertSyncedBlock: function insertSyncedBlock(inputMethod) {
111
111
  return function (_ref3) {
112
112
  var _api$analytics3;
113
113
  var tr = _ref3.tr;
@@ -117,7 +117,8 @@ var syncedBlockPlugin = exports.syncedBlockPlugin = function syncedBlockPlugin(_
117
117
  return (0, _editorCommands.createSyncedBlock)({
118
118
  tr: tr,
119
119
  syncBlockStore: syncBlockStore,
120
- fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent
120
+ fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent,
121
+ inputMethod: inputMethod
121
122
  }) || null;
122
123
  };
123
124
  }
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", {
8
8
  exports.CreateSyncedBlockButton = void 0;
9
9
  var _react = _interopRequireWildcard(require("react"));
10
10
  var _reactIntl = require("react-intl");
11
+ var _analytics = require("@atlaskit/editor-common/analytics");
11
12
  var _hooks = require("@atlaskit/editor-common/hooks");
12
13
  var _messages = require("@atlaskit/editor-common/messages");
13
14
  var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
@@ -51,7 +52,7 @@ var CreateSyncedBlockButton = exports.CreateSyncedBlockButton = function CreateS
51
52
  var dispatched = api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(function (_ref2) {
52
53
  var _api$blockControls;
53
54
  var tr = _ref2.tr;
54
- var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
55
+ var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(_analytics.INPUT_METHOD.SYNCED_BLOCK_TB)({
55
56
  tr: tr
56
57
  });
57
58
  if (!result) {
@@ -70,7 +71,7 @@ var CreateSyncedBlockButton = exports.CreateSyncedBlockButton = function CreateS
70
71
  }
71
72
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref3) {
72
73
  var tr = _ref3.tr;
73
- return api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
74
+ return api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(_analytics.INPUT_METHOD.SYNCED_BLOCK_TB)({
74
75
  tr: tr
75
76
  });
76
77
  });
@@ -65,7 +65,7 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref2
65
65
  // stopping selection preservation — otherwise we would dispatch an
66
66
  // effectively-empty transaction and close the menu despite nothing
67
67
  // having been inserted.
68
- var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
68
+ var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(_analytics.INPUT_METHOD.BLOCK_MENU)({
69
69
  tr: tr
70
70
  });
71
71
  if (!result) {
@@ -90,7 +90,7 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref2
90
90
 
91
91
  // Legacy behaviour: insert then close the block menu as two separate executes
92
92
  // (batching them caused selection collisions — EDITOR-2751).
93
- api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock());
93
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(_analytics.INPUT_METHOD.BLOCK_MENU));
94
94
  api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || (_api$blockControls3 = _api$blockControls3.commands) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.toggleBlockMenu({
95
95
  closeMenu: true
96
96
  }));
@@ -10,6 +10,7 @@ exports.getQuickInsertConfig = void 0;
10
10
  require("./quick-insert.compiled.css");
11
11
  var React = _interopRequireWildcard(require("react"));
12
12
  var _runtime = require("@compiled/react/runtime");
13
+ var _analytics = require("@atlaskit/editor-common/analytics");
13
14
  var _messages = require("@atlaskit/editor-common/messages");
14
15
  var _quickInsert = require("@atlaskit/editor-common/quick-insert");
15
16
  var _lozenge = _interopRequireDefault(require("@atlaskit/lozenge"));
@@ -49,7 +50,8 @@ var getQuickInsertConfig = exports.getQuickInsertConfig = function getQuickInser
49
50
  tr: state.tr,
50
51
  syncBlockStore: syncBlockStore,
51
52
  typeAheadInsert: insert,
52
- fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent
53
+ fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent,
54
+ inputMethod: _analytics.INPUT_METHOD.QUICK_INSERT
53
55
  });
54
56
  },
55
57
  testId: _types.SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate
@@ -5,7 +5,7 @@ import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
5
5
  import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
6
6
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
- import { deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
8
+ import { creationMetaKey, deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
9
9
  import { canBeConvertedToSyncBlock, deferDispatch, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
10
10
  import { FLAG_ID } from '../types';
11
11
  import { findBodiedSyncBlockByLocalId, pasteSyncBlockHTMLContent } from './utils';
@@ -68,7 +68,8 @@ export const createSyncedBlock = ({
68
68
  tr,
69
69
  syncBlockStore,
70
70
  typeAheadInsert,
71
- fireAnalyticsEvent
71
+ fireAnalyticsEvent,
72
+ inputMethod
72
73
  }) => {
73
74
  const {
74
75
  schema: {
@@ -79,6 +80,11 @@ export const createSyncedBlock = ({
79
80
  }
80
81
  } = tr.doc.type;
81
82
 
83
+ // Capture createdEmpty before any insertion mutates the selection. The meta is
84
+ // set on the final transaction before returning (see below), since the
85
+ // typeahead path may reassign `tr` and drop meta set here.
86
+ const createdEmpty = tr.selection.empty;
87
+
82
88
  // If the selection is empty, we want to insert the sync block on a new line
83
89
  if (tr.selection.empty) {
84
90
  const attrs = syncBlockStore.sourceManager.generateBodiedSyncBlockAttrs();
@@ -145,6 +151,14 @@ export const createSyncedBlock = ({
145
151
  tr.setSelection(TextSelection.create(tr.doc, conversionInfo.from));
146
152
  }
147
153
  }
154
+
155
+ // Stash creation-type signals on the final transaction (after any typeahead
156
+ // reassignment). Set unconditionally — the store manager only reads it behind
157
+ // the feature gate.
158
+ tr.setMeta(creationMetaKey, {
159
+ createdEmpty,
160
+ inputMethod
161
+ });
148
162
  return tr;
149
163
  };
150
164
  export const copySyncedBlockReferenceToClipboardEditorCommand = (syncBlockStore, inputMethod, api) => ({
@@ -37,6 +37,16 @@ export const syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
37
37
  * `platform_editor_blocks_patch_4`.
38
38
  */
39
39
  export const deleteMechanismMetaKey = new PluginKey('syncedBlockDeleteMechanism');
40
+
41
+ /**
42
+ * Creation-type signals set by {@link createSyncedBlock} on the creating
43
+ * transaction. The async creation handler forwards them to the store manager,
44
+ * which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
45
+ * this transient signal isolated from plugin state. Consumed behind
46
+ * `platform_editor_blocks_patch_4`.
47
+ */
48
+
49
+ export const creationMetaKey = new PluginKey('syncedBlockCreationMeta');
40
50
  const mapRetryCreationPosMap = (oldMap, newRetryCreationPos, mapPos, isPerfExperimentOn) => {
41
51
  const resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
42
52
 
@@ -153,6 +163,45 @@ export const getDeleteMechanism = (tr, state) => {
153
163
  const isNodeSelected = selection instanceof NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock';
154
164
  return isNodeSelected ? 'selectionReplaced' : 'keyboardDelete';
155
165
  };
166
+ /**
167
+ * True when a source `bodiedSyncBlock` holds user content — i.e. not just the
168
+ * single empty paragraph it is created with (used to detect empty→content).
169
+ */
170
+ const bodiedSyncBlockHasContent = node => {
171
+ if (node.childCount === 0) {
172
+ return false;
173
+ }
174
+ if (node.childCount === 1) {
175
+ const child = node.firstChild;
176
+ // A freshly-created block is a single empty paragraph; anything else
177
+ // (non-empty paragraph, or a different/first block) counts as content.
178
+ return !((child === null || child === void 0 ? void 0 : child.type.name) === 'paragraph' && child.content.size === 0);
179
+ }
180
+ return true;
181
+ };
182
+
183
+ /**
184
+ * Ask the store manager to fire the one-shot first-content-added event for every
185
+ * populated source block. The manager only emits for blocks it created empty
186
+ * this session and dedupes per block, so iterating all populated blocks is safe
187
+ * and self-limiting. Called only when `hasEditInSyncBlock` flagged an in-block
188
+ * edit, so this walk runs rarely.
189
+ */
190
+ const emitFirstContentAddedForEditedBlocks = (tr, state, syncBlockStore) => {
191
+ const {
192
+ bodiedSyncBlock
193
+ } = state.schema.nodes;
194
+ tr.doc.descendants(node => {
195
+ if (node.type === bodiedSyncBlock) {
196
+ if (bodiedSyncBlockHasContent(node)) {
197
+ syncBlockStore.sourceManager.maybeEmitFirstContentAdded(node.attrs.resourceId, node.attrs.localId);
198
+ }
199
+ // bodiedSyncBlocks are top-level and not nested, so no need to descend.
200
+ return false;
201
+ }
202
+ return true;
203
+ });
204
+ };
156
205
  const filterTransactionOnline = ({
157
206
  tr,
158
207
  state,
@@ -235,9 +284,20 @@ const filterTransactionOnline = ({
235
284
  if (isPasteOrDrop && fg('platform_synced_block_patch_13')) {
236
285
  return true;
237
286
  }
238
- handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
287
+
288
+ // Forward creation-type signals captured by createSyncedBlock onto the async
289
+ // `syncedBlockCreate` success event.
290
+ const creationMeta = tr.getMeta(creationMetaKey);
291
+ handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api, creationMeta);
239
292
  return true;
240
293
  }
294
+
295
+ // On an in-block edit, let the store manager fire the one-shot first-content
296
+ // event (dedupe + empty→content scoping live there). After the add/remove
297
+ // branches so it never runs for the creation transaction itself.
298
+ if (hasEditInSyncBlock(tr, state)) {
299
+ emitFirstContentAddedForEditedBlocks(tr, state, syncBlockStore);
300
+ }
241
301
  showExtensionInSyncBlockWarningIfNeeded(tr, state, api, extensionFlagShown);
242
302
  return true;
243
303
  };
@@ -65,9 +65,12 @@ const buildRevertCreationTr = (tr, pos) => {
65
65
 
66
66
  /**
67
67
  *
68
- * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow
68
+ * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
69
+ *
70
+ * @param enrichment optional creation-type signals from `createSyncedBlock`,
71
+ * forwarded to the store manager to attach to the `syncedBlockCreate` event.
69
72
  */
70
- export const handleBodiedSyncBlockCreation = (bodiedSyncBlockAdded, editorState, api) => {
73
+ export const handleBodiedSyncBlockCreation = (bodiedSyncBlockAdded, editorState, api, enrichment) => {
71
74
  const syncBlockStore = syncedBlockPluginKey.getState(editorState).syncBlockStore;
72
75
  bodiedSyncBlockAdded.forEach(node => {
73
76
  if (node.from === undefined || node.to === undefined) {
@@ -130,6 +133,6 @@ export const handleBodiedSyncBlockCreation = (bodiedSyncBlockAdded, editorState,
130
133
  });
131
134
  });
132
135
  }
133
- });
136
+ }, enrichment);
134
137
  });
135
138
  };
@@ -97,7 +97,7 @@ export const syncedBlockPlugin = ({
97
97
  },
98
98
  commands: {
99
99
  copySyncedBlockReferenceToClipboard: inputMethod => copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api),
100
- insertSyncedBlock: () => ({
100
+ insertSyncedBlock: inputMethod => ({
101
101
  tr
102
102
  }) => {
103
103
  var _api$analytics3;
@@ -107,7 +107,8 @@ export const syncedBlockPlugin = ({
107
107
  return createSyncedBlock({
108
108
  tr,
109
109
  syncBlockStore,
110
- fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent
110
+ fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent,
111
+ inputMethod
111
112
  }) || null;
112
113
  }
113
114
  },
@@ -1,5 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
4
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
5
  import { syncBlockMessages } from '@atlaskit/editor-common/messages';
5
6
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
@@ -45,7 +46,7 @@ export const CreateSyncedBlockButton = ({
45
46
  tr
46
47
  }) => {
47
48
  var _api$blockControls, _api$blockControls$co;
48
- const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
49
+ const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
49
50
  tr
50
51
  });
51
52
  if (!result) {
@@ -64,7 +65,7 @@ export const CreateSyncedBlockButton = ({
64
65
  }
65
66
  api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(({
66
67
  tr
67
- }) => api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
68
+ }) => api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
68
69
  tr
69
70
  }));
70
71
  api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.focus();
@@ -57,7 +57,7 @@ const CreateSyncedBlockDropdownItem = ({
57
57
  // stopping selection preservation — otherwise we would dispatch an
58
58
  // effectively-empty transaction and close the menu despite nothing
59
59
  // having been inserted.
60
- const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
60
+ const result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU)({
61
61
  tr
62
62
  });
63
63
  if (!result) {
@@ -82,7 +82,7 @@ const CreateSyncedBlockDropdownItem = ({
82
82
 
83
83
  // Legacy behaviour: insert then close the block menu as two separate executes
84
84
  // (batching them caused selection collisions — EDITOR-2751).
85
- api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock());
85
+ api === null || api === void 0 ? void 0 : (_api$core3 = api.core) === null || _api$core3 === void 0 ? void 0 : _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU));
86
86
  api === null || api === void 0 ? void 0 : (_api$core4 = api.core) === null || _api$core4 === void 0 ? void 0 : _api$core4.actions.execute(api === null || api === void 0 ? void 0 : (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 ? void 0 : (_api$blockControls3$c = _api$blockControls3.commands) === null || _api$blockControls3$c === void 0 ? void 0 : _api$blockControls3$c.toggleBlockMenu({
87
87
  closeMenu: true
88
88
  }));
@@ -2,6 +2,7 @@
2
2
  import "./quick-insert.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
6
  import { blockTypeMessages } from '@atlaskit/editor-common/messages';
6
7
  import { IconSyncBlock } from '@atlaskit/editor-common/quick-insert';
7
8
  import Lozenge from '@atlaskit/lozenge';
@@ -39,7 +40,8 @@ export const getQuickInsertConfig = (config, api, syncBlockStore) => {
39
40
  tr: state.tr,
40
41
  syncBlockStore,
41
42
  typeAheadInsert: insert,
42
- fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent
43
+ fireAnalyticsEvent: api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent,
44
+ inputMethod: INPUT_METHOD.QUICK_INSERT
43
45
  });
44
46
  },
45
47
  testId: SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate
@@ -8,7 +8,7 @@ import { DOMSerializer, Fragment } from '@atlaskit/editor-prosemirror/model';
8
8
  import { NodeSelection, Selection, TextSelection } from '@atlaskit/editor-prosemirror/state';
9
9
  import { findSelectedNodeOfType, removeParentNodeOfType, removeSelectedNode, safeInsert } from '@atlaskit/editor-prosemirror/utils';
10
10
  import { fg } from '@atlaskit/platform-feature-flags';
11
- import { deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
11
+ import { creationMetaKey, deleteMechanismMetaKey, syncedBlockPluginKey } from '../pm-plugins/main';
12
12
  import { canBeConvertedToSyncBlock, deferDispatch, findSyncBlock, findSyncBlockOrBodiedSyncBlock, isBodiedSyncBlockNode } from '../pm-plugins/utils/utils';
13
13
  import { FLAG_ID } from '../types';
14
14
  import { findBodiedSyncBlockByLocalId, pasteSyncBlockHTMLContent } from './utils';
@@ -71,11 +71,17 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
71
71
  var tr = _ref.tr,
72
72
  syncBlockStore = _ref.syncBlockStore,
73
73
  typeAheadInsert = _ref.typeAheadInsert,
74
- fireAnalyticsEvent = _ref.fireAnalyticsEvent;
74
+ fireAnalyticsEvent = _ref.fireAnalyticsEvent,
75
+ inputMethod = _ref.inputMethod;
75
76
  var _tr$doc$type$schema$n = tr.doc.type.schema.nodes,
76
77
  bodiedSyncBlock = _tr$doc$type$schema$n.bodiedSyncBlock,
77
78
  paragraph = _tr$doc$type$schema$n.paragraph;
78
79
 
80
+ // Capture createdEmpty before any insertion mutates the selection. The meta is
81
+ // set on the final transaction before returning (see below), since the
82
+ // typeahead path may reassign `tr` and drop meta set here.
83
+ var createdEmpty = tr.selection.empty;
84
+
79
85
  // If the selection is empty, we want to insert the sync block on a new line
80
86
  if (tr.selection.empty) {
81
87
  var attrs = syncBlockStore.sourceManager.generateBodiedSyncBlockAttrs();
@@ -142,6 +148,14 @@ export var createSyncedBlock = function createSyncedBlock(_ref) {
142
148
  tr.setSelection(TextSelection.create(tr.doc, conversionInfo.from));
143
149
  }
144
150
  }
151
+
152
+ // Stash creation-type signals on the final transaction (after any typeahead
153
+ // reassignment). Set unconditionally — the store manager only reads it behind
154
+ // the feature gate.
155
+ tr.setMeta(creationMetaKey, {
156
+ createdEmpty: createdEmpty,
157
+ inputMethod: inputMethod
158
+ });
145
159
  return tr;
146
160
  };
147
161
  export var copySyncedBlockReferenceToClipboardEditorCommand = function copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api) {
@@ -46,6 +46,16 @@ export var syncedBlockPluginKey = new PluginKey('syncedBlockPlugin');
46
46
  * `platform_editor_blocks_patch_4`.
47
47
  */
48
48
  export var deleteMechanismMetaKey = new PluginKey('syncedBlockDeleteMechanism');
49
+
50
+ /**
51
+ * Creation-type signals set by {@link createSyncedBlock} on the creating
52
+ * transaction. The async creation handler forwards them to the store manager,
53
+ * which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
54
+ * this transient signal isolated from plugin state. Consumed behind
55
+ * `platform_editor_blocks_patch_4`.
56
+ */
57
+
58
+ export var creationMetaKey = new PluginKey('syncedBlockCreationMeta');
49
59
  var mapRetryCreationPosMap = function mapRetryCreationPosMap(oldMap, newRetryCreationPos, mapPos, isPerfExperimentOn) {
50
60
  var resourceId = newRetryCreationPos === null || newRetryCreationPos === void 0 ? void 0 : newRetryCreationPos.resourceId;
51
61
 
@@ -175,6 +185,43 @@ export var getDeleteMechanism = function getDeleteMechanism(tr, state) {
175
185
  var isNodeSelected = selection instanceof NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type.name) === 'bodiedSyncBlock';
176
186
  return isNodeSelected ? 'selectionReplaced' : 'keyboardDelete';
177
187
  };
188
+ /**
189
+ * True when a source `bodiedSyncBlock` holds user content — i.e. not just the
190
+ * single empty paragraph it is created with (used to detect empty→content).
191
+ */
192
+ var bodiedSyncBlockHasContent = function bodiedSyncBlockHasContent(node) {
193
+ if (node.childCount === 0) {
194
+ return false;
195
+ }
196
+ if (node.childCount === 1) {
197
+ var child = node.firstChild;
198
+ // A freshly-created block is a single empty paragraph; anything else
199
+ // (non-empty paragraph, or a different/first block) counts as content.
200
+ return !((child === null || child === void 0 ? void 0 : child.type.name) === 'paragraph' && child.content.size === 0);
201
+ }
202
+ return true;
203
+ };
204
+
205
+ /**
206
+ * Ask the store manager to fire the one-shot first-content-added event for every
207
+ * populated source block. The manager only emits for blocks it created empty
208
+ * this session and dedupes per block, so iterating all populated blocks is safe
209
+ * and self-limiting. Called only when `hasEditInSyncBlock` flagged an in-block
210
+ * edit, so this walk runs rarely.
211
+ */
212
+ var emitFirstContentAddedForEditedBlocks = function emitFirstContentAddedForEditedBlocks(tr, state, syncBlockStore) {
213
+ var bodiedSyncBlock = state.schema.nodes.bodiedSyncBlock;
214
+ tr.doc.descendants(function (node) {
215
+ if (node.type === bodiedSyncBlock) {
216
+ if (bodiedSyncBlockHasContent(node)) {
217
+ syncBlockStore.sourceManager.maybeEmitFirstContentAdded(node.attrs.resourceId, node.attrs.localId);
218
+ }
219
+ // bodiedSyncBlocks are top-level and not nested, so no need to descend.
220
+ return false;
221
+ }
222
+ return true;
223
+ });
224
+ };
178
225
  var filterTransactionOnline = function filterTransactionOnline(_ref3) {
179
226
  var _tr$getMeta;
180
227
  var tr = _ref3.tr,
@@ -255,9 +302,20 @@ var filterTransactionOnline = function filterTransactionOnline(_ref3) {
255
302
  if (isPasteOrDrop && fg('platform_synced_block_patch_13')) {
256
303
  return true;
257
304
  }
258
- handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api);
305
+
306
+ // Forward creation-type signals captured by createSyncedBlock onto the async
307
+ // `syncedBlockCreate` success event.
308
+ var creationMeta = tr.getMeta(creationMetaKey);
309
+ handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, state, api, creationMeta);
259
310
  return true;
260
311
  }
312
+
313
+ // On an in-block edit, let the store manager fire the one-shot first-content
314
+ // event (dedupe + empty→content scoping live there). After the add/remove
315
+ // branches so it never runs for the creation transaction itself.
316
+ if (hasEditInSyncBlock(tr, state)) {
317
+ emitFirstContentAddedForEditedBlocks(tr, state, syncBlockStore);
318
+ }
261
319
  showExtensionInSyncBlockWarningIfNeeded(tr, state, api, extensionFlagShown);
262
320
  return true;
263
321
  };
@@ -67,9 +67,12 @@ var buildRevertCreationTr = function buildRevertCreationTr(tr, pos) {
67
67
 
68
68
  /**
69
69
  *
70
- * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow
70
+ * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
71
+ *
72
+ * @param enrichment optional creation-type signals from `createSyncedBlock`,
73
+ * forwarded to the store manager to attach to the `syncedBlockCreate` event.
71
74
  */
72
- export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, editorState, api) {
75
+ export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreation(bodiedSyncBlockAdded, editorState, api, enrichment) {
73
76
  var syncBlockStore = syncedBlockPluginKey.getState(editorState).syncBlockStore;
74
77
  bodiedSyncBlockAdded.forEach(function (node) {
75
78
  if (node.from === undefined || node.to === undefined) {
@@ -130,6 +133,6 @@ export var handleBodiedSyncBlockCreation = function handleBodiedSyncBlockCreatio
130
133
  });
131
134
  });
132
135
  }
133
- });
136
+ }, enrichment);
134
137
  });
135
138
  };
@@ -101,7 +101,7 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref2) {
101
101
  copySyncedBlockReferenceToClipboard: function copySyncedBlockReferenceToClipboard(inputMethod) {
102
102
  return copySyncedBlockReferenceToClipboardEditorCommand(syncBlockStore, inputMethod, api);
103
103
  },
104
- insertSyncedBlock: function insertSyncedBlock() {
104
+ insertSyncedBlock: function insertSyncedBlock(inputMethod) {
105
105
  return function (_ref3) {
106
106
  var _api$analytics3;
107
107
  var tr = _ref3.tr;
@@ -111,7 +111,8 @@ export var syncedBlockPlugin = function syncedBlockPlugin(_ref2) {
111
111
  return createSyncedBlock({
112
112
  tr: tr,
113
113
  syncBlockStore: syncBlockStore,
114
- fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent
114
+ fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.fireAnalyticsEvent,
115
+ inputMethod: inputMethod
115
116
  }) || null;
116
117
  };
117
118
  }
@@ -1,5 +1,6 @@
1
1
  import React, { useCallback } from 'react';
2
2
  import { useIntl } from 'react-intl';
3
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
3
4
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
4
5
  import { syncBlockMessages } from '@atlaskit/editor-common/messages';
5
6
  import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
@@ -42,7 +43,7 @@ export var CreateSyncedBlockButton = function CreateSyncedBlockButton(_ref) {
42
43
  var dispatched = api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 ? void 0 : _api$core.actions.execute(function (_ref2) {
43
44
  var _api$blockControls;
44
45
  var tr = _ref2.tr;
45
- var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
46
+ var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
46
47
  tr: tr
47
48
  });
48
49
  if (!result) {
@@ -61,7 +62,7 @@ export var CreateSyncedBlockButton = function CreateSyncedBlockButton(_ref) {
61
62
  }
62
63
  api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(function (_ref3) {
63
64
  var tr = _ref3.tr;
64
- return api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
65
+ return api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.SYNCED_BLOCK_TB)({
65
66
  tr: tr
66
67
  });
67
68
  });
@@ -56,7 +56,7 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref2
56
56
  // stopping selection preservation — otherwise we would dispatch an
57
57
  // effectively-empty transaction and close the menu despite nothing
58
58
  // having been inserted.
59
- var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock()({
59
+ var result = api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU)({
60
60
  tr: tr
61
61
  });
62
62
  if (!result) {
@@ -81,7 +81,7 @@ var CreateSyncedBlockDropdownItem = function CreateSyncedBlockDropdownItem(_ref2
81
81
 
82
82
  // Legacy behaviour: insert then close the block menu as two separate executes
83
83
  // (batching them caused selection collisions — EDITOR-2751).
84
- api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock());
84
+ api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 ? void 0 : api.syncedBlock.commands.insertSyncedBlock(INPUT_METHOD.BLOCK_MENU));
85
85
  api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$blockControls3 = api.blockControls) === null || _api$blockControls3 === void 0 || (_api$blockControls3 = _api$blockControls3.commands) === null || _api$blockControls3 === void 0 ? void 0 : _api$blockControls3.toggleBlockMenu({
86
86
  closeMenu: true
87
87
  }));
@@ -2,6 +2,7 @@
2
2
  import "./quick-insert.compiled.css";
3
3
  import * as React from 'react';
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
+ import { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
5
6
  import { blockTypeMessages } from '@atlaskit/editor-common/messages';
6
7
  import { IconSyncBlock } from '@atlaskit/editor-common/quick-insert';
7
8
  import Lozenge from '@atlaskit/lozenge';
@@ -40,7 +41,8 @@ export var getQuickInsertConfig = function getQuickInsertConfig(config, api, syn
40
41
  tr: state.tr,
41
42
  syncBlockStore: syncBlockStore,
42
43
  typeAheadInsert: insert,
43
- fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent
44
+ fireAnalyticsEvent: api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions.fireAnalyticsEvent,
45
+ inputMethod: INPUT_METHOD.QUICK_INSERT
44
46
  });
45
47
  },
46
48
  testId: SYNCED_BLOCK_BUTTON_TEST_ID.quickInsertCreate
@@ -6,11 +6,16 @@ import type { SyncBlockStoreManager } from '@atlaskit/editor-synced-block-provid
6
6
  import type { SyncedBlockPlugin } from '../syncedBlockPluginType';
7
7
  type createSyncedBlockProps = {
8
8
  fireAnalyticsEvent?: DispatchAnalyticsEvent;
9
+ /**
10
+ * Creating surface, stashed on the transaction to attach to the async
11
+ * `syncedBlockCreate` event. Optional so legacy callers still type-check.
12
+ */
13
+ inputMethod?: INPUT_METHOD;
9
14
  syncBlockStore: SyncBlockStoreManager;
10
15
  tr: Transaction;
11
16
  typeAheadInsert?: TypeAheadInsert;
12
17
  };
13
- export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, fireAnalyticsEvent, }: createSyncedBlockProps) => false | Transaction;
18
+ export declare const createSyncedBlock: ({ tr, syncBlockStore, typeAheadInsert, fireAnalyticsEvent, inputMethod, }: createSyncedBlockProps) => false | Transaction;
14
19
  export declare const copySyncedBlockReferenceToClipboardEditorCommand: (syncBlockStore: SyncBlockStoreManager, inputMethod: INPUT_METHOD, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => EditorCommand;
15
20
  export declare const copySyncedBlockReferenceToClipboard: (syncBlockStore: SyncBlockStoreManager, inputMethod: INPUT_METHOD, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
16
21
  export declare const editSyncedBlockSource: (syncBlockStore: SyncBlockStoreManager, api?: ExtractInjectionAPI<SyncedBlockPlugin>) => Command;
@@ -1,3 +1,4 @@
1
+ import type { INPUT_METHOD } from '@atlaskit/editor-common/analytics';
1
2
  import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
2
3
  import type { ExtractInjectionAPI, PMPluginFactoryParams } from '@atlaskit/editor-common/types';
3
4
  import type { EditorState, Transaction } from '@atlaskit/editor-prosemirror/state';
@@ -18,6 +19,18 @@ export declare const syncedBlockPluginKey: PluginKey;
18
19
  * `platform_editor_blocks_patch_4`.
19
20
  */
20
21
  export declare const deleteMechanismMetaKey: PluginKey<DeletionMechanism>;
22
+ /**
23
+ * Creation-type signals set by {@link createSyncedBlock} on the creating
24
+ * transaction. The async creation handler forwards them to the store manager,
25
+ * which attaches them to the `syncedBlockCreate` event. A dedicated key keeps
26
+ * this transient signal isolated from plugin state. Consumed behind
27
+ * `platform_editor_blocks_patch_4`.
28
+ */
29
+ export type SyncedBlockCreationMeta = {
30
+ createdEmpty?: boolean;
31
+ inputMethod?: INPUT_METHOD;
32
+ };
33
+ export declare const creationMetaKey: PluginKey<SyncedBlockCreationMeta>;
21
34
  type SyncedBlockPluginState = {
22
35
  activeFlag: ActiveFlag;
23
36
  bodiedSyncBlockDeletionStatus?: BodiedSyncBlockDeletionStatus;
@@ -1,9 +1,13 @@
1
1
  import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
2
2
  import type { EditorState } from '@atlaskit/editor-prosemirror/state';
3
+ import type { CreateSuccessEnrichment } from '@atlaskit/editor-synced-block-provider/errorHandling';
3
4
  import type { SyncedBlockPlugin } from '../../syncedBlockPluginType';
4
5
  import type { SyncBlockInfo } from '../../types';
5
6
  /**
6
7
  *
7
- * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow
8
+ * Save the new bodiedSyncBlock to backend with empty content and handles revert (if failed) and retry flow.
9
+ *
10
+ * @param enrichment optional creation-type signals from `createSyncedBlock`,
11
+ * forwarded to the store manager to attach to the `syncedBlockCreate` event.
8
12
  */
9
- export declare const handleBodiedSyncBlockCreation: (bodiedSyncBlockAdded: SyncBlockInfo[], editorState: EditorState, api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined) => void;
13
+ export declare const handleBodiedSyncBlockCreation: (bodiedSyncBlockAdded: SyncBlockInfo[], editorState: EditorState, api: ExtractInjectionAPI<SyncedBlockPlugin> | undefined, enrichment?: CreateSuccessEnrichment) => void;
@@ -85,7 +85,12 @@ export type SyncedBlockPlugin = NextEditorPlugin<'syncedBlock', {
85
85
  };
86
86
  commands: {
87
87
  copySyncedBlockReferenceToClipboard: (inputMethod: INPUT_METHOD) => EditorCommand;
88
- insertSyncedBlock: () => EditorCommand;
88
+ /**
89
+ * Insert a new source synced block. `inputMethod` records the creating
90
+ * surface for the `syncedBlockCreate` event; optional so existing callers
91
+ * keep working.
92
+ */
93
+ insertSyncedBlock: (inputMethod?: INPUT_METHOD) => EditorCommand;
89
94
  };
90
95
  dependencies: [
91
96
  SelectionPlugin,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-synced-block",
3
- "version": "10.1.10",
3
+ "version": "10.2.0",
4
4
  "description": "SyncedBlock plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,7 @@
35
35
  "@atlaskit/editor-plugin-user-intent": "^10.0.0",
36
36
  "@atlaskit/editor-prosemirror": "^8.0.0",
37
37
  "@atlaskit/editor-shared-styles": "^4.0.0",
38
- "@atlaskit/editor-synced-block-provider": "^8.5.0",
38
+ "@atlaskit/editor-synced-block-provider": "^8.6.0",
39
39
  "@atlaskit/editor-toolbar": "^2.1.0",
40
40
  "@atlaskit/flag": "^18.1.0",
41
41
  "@atlaskit/icon": "36.2.0",
@@ -46,7 +46,7 @@
46
46
  "@atlaskit/platform-feature-flags": "^2.0.0",
47
47
  "@atlaskit/primitives": "^20.5.0",
48
48
  "@atlaskit/spinner": "20.1.0",
49
- "@atlaskit/tmp-editor-statsig": "^124.0.0",
49
+ "@atlaskit/tmp-editor-statsig": "^124.1.0",
50
50
  "@atlaskit/tokens": "15.5.0",
51
51
  "@atlaskit/tooltip": "^23.1.0",
52
52
  "@atlaskit/visually-hidden": "^4.1.0",
@@ -56,7 +56,7 @@
56
56
  "date-fns": "^2.17.0"
57
57
  },
58
58
  "peerDependencies": {
59
- "@atlaskit/editor-common": "^116.24.0",
59
+ "@atlaskit/editor-common": "^116.25.0",
60
60
  "react": "^18.2.0",
61
61
  "react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
62
62
  },