@atlaskit/editor-core 216.10.1 → 216.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/afm-cc/tsconfig.json +0 -1
  3. package/afm-jira/tsconfig.json +0 -1
  4. package/afm-products/tsconfig.json +0 -1
  5. package/dist/cjs/composable-editor/editor-internal.js +1 -0
  6. package/dist/cjs/composable-editor/hooks/useMemoEditorProps.js +2 -1
  7. package/dist/cjs/create-editor/ReactEditorView.js +92 -21
  8. package/dist/cjs/ui/Appearance/FullPage/FullPage.js +10 -2
  9. package/dist/cjs/utils/getNodesCount.js +1 -0
  10. package/dist/cjs/utils/getNodesCountWithExtensionKeys.js +32 -0
  11. package/dist/cjs/version-wrapper.js +1 -1
  12. package/dist/es2019/composable-editor/editor-internal.js +1 -0
  13. package/dist/es2019/composable-editor/hooks/useMemoEditorProps.js +2 -1
  14. package/dist/es2019/create-editor/ReactEditorView.js +82 -17
  15. package/dist/es2019/ui/Appearance/FullPage/FullPage.js +10 -2
  16. package/dist/es2019/utils/getNodesCount.js +1 -0
  17. package/dist/es2019/utils/getNodesCountWithExtensionKeys.js +26 -0
  18. package/dist/es2019/version-wrapper.js +1 -1
  19. package/dist/esm/composable-editor/editor-internal.js +1 -0
  20. package/dist/esm/composable-editor/hooks/useMemoEditorProps.js +2 -1
  21. package/dist/esm/create-editor/ReactEditorView.js +93 -22
  22. package/dist/esm/ui/Appearance/FullPage/FullPage.js +10 -2
  23. package/dist/esm/utils/getNodesCount.js +1 -0
  24. package/dist/esm/utils/getNodesCountWithExtensionKeys.js +26 -0
  25. package/dist/esm/version-wrapper.js +1 -1
  26. package/dist/types/create-editor/ReactEditorView.d.ts +5 -0
  27. package/dist/types/types/editor-appearance-component.d.ts +5 -0
  28. package/dist/types/types/editor-props.d.ts +26 -0
  29. package/dist/types/utils/getNodesCountWithExtensionKeys.d.ts +12 -0
  30. package/dist/types-ts4.5/create-editor/ReactEditorView.d.ts +5 -0
  31. package/dist/types-ts4.5/types/editor-appearance-component.d.ts +5 -0
  32. package/dist/types-ts4.5/types/editor-props.d.ts +26 -0
  33. package/dist/types-ts4.5/utils/getNodesCountWithExtensionKeys.d.ts +12 -0
  34. package/package.json +6 -3
@@ -8,6 +8,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
8
8
  import { jsx } from '@emotion/react';
9
9
  import { browser as browserLegacy, getBrowserInfo } from '@atlaskit/editor-common/browser';
10
10
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
11
+ import { SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
11
12
  import { ContextPanelWidthProvider } from '@atlaskit/editor-common/ui';
12
13
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
13
14
  import { FULL_PAGE_EDITOR_TOOLBAR_HEIGHT } from '@atlaskit/editor-shared-styles';
@@ -19,6 +20,7 @@ import { FullPageContentArea } from './FullPageContentArea';
19
20
  import { FullPageToolbar } from './FullPageToolbar';
20
21
  import { FullPageToolbarNext } from './FullPageToolbarNext';
21
22
  import { fullPageEditorWrapper } from './StyledComponents';
23
+ const SSR_TRACE_SEGMENT_NAME = 'reactEditorView/fullPageAppearance';
22
24
  const useShowKeyline = contentAreaRef => {
23
25
  const [showKeyline, setShowKeyline] = useState(false);
24
26
  useEffect(() => {
@@ -55,6 +57,8 @@ const hasCustomComponents = components => {
55
57
  };
56
58
  export const FullPageEditor = props => {
57
59
  var _scrollContentContain, _toolbarDockingPositi, _scrollContentContain2, _scrollContentContain3, _wrapperElementRef$cu;
60
+ // Should be always the first statement in the component
61
+ const firstRenderStartTimestampRef = useRef(performance.now());
58
62
  const wrapperElementRef = useMemo(() => props.innerRef, [props.innerRef]);
59
63
  const scrollContentContainerRef = useRef(null);
60
64
  const showKeyline = useShowKeyline(scrollContentContainerRef);
@@ -129,7 +133,11 @@ export const FullPageEditor = props => {
129
133
  }
130
134
  const isToolbarAIFCEnabled = Boolean(editorAPI === null || editorAPI === void 0 ? void 0 : editorAPI.toolbar);
131
135
  const popupsBoundariesElement = props.popupsBoundariesElement || (scrollContentContainerRef === null || scrollContentContainerRef === void 0 ? void 0 : (_scrollContentContain = scrollContentContainerRef.current) === null || _scrollContentContain === void 0 ? void 0 : _scrollContentContain.containerArea) || undefined;
132
- return jsx(ContextPanelWidthProvider, null, jsx("div", {
136
+ return jsx(SSRRenderMeasure, {
137
+ segmentName: SSR_TRACE_SEGMENT_NAME,
138
+ startTimestampRef: firstRenderStartTimestampRef,
139
+ onSSRMeasure: fg('platform_editor_better_editor_ssr_spans') ? props.onSSRMeasure : undefined
140
+ }, jsx(ContextPanelWidthProvider, null, jsx("div", {
133
141
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766
134
142
  css: fullPageEditorWrapper
135
143
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
@@ -206,5 +214,5 @@ export const FullPageEditor = props => {
206
214
  viewMode: state.editorViewMode,
207
215
  hasHadInteraction: hasHadInteraction,
208
216
  contentMode: props.contentMode
209
- })));
217
+ }))));
210
218
  };
@@ -1,3 +1,4 @@
1
+ // When experiment platform_editor_prosemirror_rendered_data is removed, review whether this helper is still needed.
1
2
  export function getNodesCount(node) {
2
3
  const count = {};
3
4
  node.nodesBetween(0, node.nodeSize - 2, node => {
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Counts node types and extension keys in a single traversal.
3
+ * We exclude the end-of-doc token by iterating to nodeSize - 2,
4
+ * which matches the typical ProseMirror pattern for traversing all child nodes.
5
+ */
6
+ export function getNodesCountWithExtensionKeys(node) {
7
+ const nodes = {};
8
+ const extensionKeys = {};
9
+ const extensionNodeNames = ['extension', 'bodiedExtension', 'inlineExtension', 'multiBodiedExtension'];
10
+ node.nodesBetween(0, node.nodeSize - 2, currentNode => {
11
+ var _currentNode$attrs;
12
+ nodes[currentNode.type.name] = (nodes[currentNode.type.name] || 0) + 1;
13
+ if (!extensionNodeNames.includes(currentNode.type.name)) {
14
+ return;
15
+ }
16
+ const extensionKey = (_currentNode$attrs = currentNode.attrs) === null || _currentNode$attrs === void 0 ? void 0 : _currentNode$attrs.extensionKey;
17
+ if (!extensionKey) {
18
+ return;
19
+ }
20
+ extensionKeys[extensionKey] = (extensionKeys[extensionKey] || 0) + 1;
21
+ });
22
+ return {
23
+ nodes,
24
+ extensionKeys
25
+ };
26
+ }
@@ -1,2 +1,2 @@
1
1
  export const name = "@atlaskit/editor-core";
2
- export const version = "216.10.0";
2
+ export const version = "216.11.1";
@@ -125,6 +125,7 @@ export var EditorInternal = /*#__PURE__*/memo(function (_ref) {
125
125
  minHeight: props.minHeight,
126
126
  onSave: props.onSave ? handleSave : undefined,
127
127
  onCancel: props.onCancel,
128
+ onSSRMeasure: props.onSSRMeasure,
128
129
  popupsMountPoint: props.popupsMountPoint,
129
130
  popupsBoundariesElement: props.popupsBoundariesElement,
130
131
  popupsScrollableElement: props.popupsScrollableElement,
@@ -87,6 +87,7 @@ export var useMemoEditorProps = function useMemoEditorProps(passedProps) {
87
87
  mention: passedProps.mention,
88
88
  mentionInsertDisplayName: passedProps.mentionInsertDisplayName,
89
89
  uploadErrorHandler: passedProps.uploadErrorHandler,
90
+ onSSRMeasure: passedProps.onSSRMeasure,
90
91
  waitForMediaUpload: passedProps.waitForMediaUpload,
91
92
  extensionHandlers: passedProps.extensionHandlers,
92
93
  allowTextColor: passedProps.allowTextColor,
@@ -111,7 +112,7 @@ export var useMemoEditorProps = function useMemoEditorProps(passedProps) {
111
112
  };
112
113
  var nextProps = _objectSpread(_objectSpread({}, defaultProps), allProps);
113
114
  return nextProps;
114
- }, [passedProps.preset, passedProps.appearance, passedProps.contentMode, passedProps.contentComponents, passedProps.primaryToolbarIconBefore, passedProps.secondaryToolbarComponents, passedProps.persistScrollGutter, passedProps.quickInsert, passedProps.shouldFocus, passedProps.disabled, passedProps.contextPanel, passedProps.errorReporterHandler, passedProps.contentTransformerProvider, passedProps.maxHeight, passedProps.minHeight, passedProps.placeholder, passedProps.placeholderBracketHint, passedProps.performanceTracking, passedProps.inputSamplingLimit, passedProps.defaultValue, passedProps.assistiveLabel, passedProps.assistiveDescribedBy, passedProps.popupsMountPoint, passedProps.popupsBoundariesElement, passedProps.popupsScrollableElement, passedProps.editorActions, passedProps.onEditorReady, passedProps.onDestroy, passedProps.onChange, passedProps.onCancel, passedProps.extensionProviders, passedProps.UNSAFE_useAnalyticsContext, passedProps.useStickyToolbar, passedProps.featureFlags, passedProps.onSave, passedProps.sanitizePrivateContent, passedProps.media, passedProps.collabEdit, passedProps.primaryToolbarComponents, passedProps.allowUndoRedoButtons, passedProps.linking, passedProps.activityProvider, passedProps.searchProvider, passedProps.annotationProviders, passedProps.collabEditProvider, passedProps.presenceProvider, passedProps.emojiProvider, passedProps.taskDecisionProvider, passedProps.legacyImageUploadProvider, passedProps.mentionProvider, passedProps.autoformattingProvider, passedProps.macroProvider, passedProps.contextIdentifierProvider, passedProps.allowExpand, passedProps.allowNestedTasks, passedProps.allowBlockType, passedProps.allowTasksAndDecisions, passedProps.allowBreakout, passedProps.allowRule, passedProps.allowHelpDialog, passedProps.allowPanel, passedProps.allowExtension, passedProps.allowConfluenceInlineComment, passedProps.allowTemplatePlaceholders, passedProps.allowDate, passedProps.allowLayouts, passedProps.allowStatus, passedProps.allowTextAlignment, passedProps.allowIndentation, passedProps.showIndentationButtons, passedProps.allowFindReplace, passedProps.allowBorderMark, passedProps.allowFragmentMark, passedProps.autoScrollIntoView, passedProps.elementBrowser, passedProps.maxContentSize, passedProps.saveOnEnter, passedProps.feedbackInfo, passedProps.mention, passedProps.mentionInsertDisplayName, passedProps.uploadErrorHandler, passedProps.waitForMediaUpload, passedProps.extensionHandlers, passedProps.allowTextColor, passedProps.allowTables, passedProps.insertMenuItems, passedProps.UNSAFE_cards, passedProps.smartLinks, passedProps.allowAnalyticsGASV3, passedProps.codeBlock, passedProps.textFormatting, passedProps.__livePage, passedProps.AppearanceComponent, passedProps.skipValidation, passedProps.syncBlock, passedProps.syncedBlockProvider, passedProps.pasteWarningOptions]);
115
+ }, [passedProps.preset, passedProps.appearance, passedProps.contentMode, passedProps.contentComponents, passedProps.primaryToolbarIconBefore, passedProps.secondaryToolbarComponents, passedProps.persistScrollGutter, passedProps.quickInsert, passedProps.shouldFocus, passedProps.disabled, passedProps.contextPanel, passedProps.errorReporterHandler, passedProps.contentTransformerProvider, passedProps.maxHeight, passedProps.minHeight, passedProps.placeholder, passedProps.placeholderBracketHint, passedProps.performanceTracking, passedProps.inputSamplingLimit, passedProps.defaultValue, passedProps.assistiveLabel, passedProps.assistiveDescribedBy, passedProps.popupsMountPoint, passedProps.popupsBoundariesElement, passedProps.popupsScrollableElement, passedProps.editorActions, passedProps.onEditorReady, passedProps.onDestroy, passedProps.onChange, passedProps.onCancel, passedProps.onSSRMeasure, passedProps.extensionProviders, passedProps.UNSAFE_useAnalyticsContext, passedProps.useStickyToolbar, passedProps.featureFlags, passedProps.onSave, passedProps.sanitizePrivateContent, passedProps.media, passedProps.collabEdit, passedProps.primaryToolbarComponents, passedProps.allowUndoRedoButtons, passedProps.linking, passedProps.activityProvider, passedProps.searchProvider, passedProps.annotationProviders, passedProps.collabEditProvider, passedProps.presenceProvider, passedProps.emojiProvider, passedProps.taskDecisionProvider, passedProps.legacyImageUploadProvider, passedProps.mentionProvider, passedProps.autoformattingProvider, passedProps.macroProvider, passedProps.contextIdentifierProvider, passedProps.allowExpand, passedProps.allowNestedTasks, passedProps.allowBlockType, passedProps.allowTasksAndDecisions, passedProps.allowBreakout, passedProps.allowRule, passedProps.allowHelpDialog, passedProps.allowPanel, passedProps.allowExtension, passedProps.allowConfluenceInlineComment, passedProps.allowTemplatePlaceholders, passedProps.allowDate, passedProps.allowLayouts, passedProps.allowStatus, passedProps.allowTextAlignment, passedProps.allowIndentation, passedProps.showIndentationButtons, passedProps.allowFindReplace, passedProps.allowBorderMark, passedProps.allowFragmentMark, passedProps.autoScrollIntoView, passedProps.elementBrowser, passedProps.maxContentSize, passedProps.saveOnEnter, passedProps.feedbackInfo, passedProps.mention, passedProps.mentionInsertDisplayName, passedProps.uploadErrorHandler, passedProps.waitForMediaUpload, passedProps.extensionHandlers, passedProps.allowTextColor, passedProps.allowTables, passedProps.insertMenuItems, passedProps.UNSAFE_cards, passedProps.smartLinks, passedProps.allowAnalyticsGASV3, passedProps.codeBlock, passedProps.textFormatting, passedProps.__livePage, passedProps.AppearanceComponent, passedProps.skipValidation, passedProps.syncBlock, passedProps.syncedBlockProvider, passedProps.pasteWarningOptions]);
115
116
  return memodProps;
116
117
  };
117
118
  export default useMemoEditorProps;
@@ -14,10 +14,12 @@ import { ACTION, ACTION_SUBJECT, EVENT_TYPE, fireAnalyticsEvent, PLATFORMS } fro
14
14
  import { isSSR } from '@atlaskit/editor-common/core-utils';
15
15
  import { createDispatch, EventDispatcher } from '@atlaskit/editor-common/event-dispatcher';
16
16
  import { useConstructor, usePreviousState } from '@atlaskit/editor-common/hooks';
17
+ import { isPerformanceAPIAvailable } from '@atlaskit/editor-common/is-performance-api-available';
17
18
  import { nodeVisibilityManager } from '@atlaskit/editor-common/node-visibility';
18
19
  import { getEnabledFeatureFlagKeys } from '@atlaskit/editor-common/normalize-feature-flags';
19
20
  import { measureRender } from '@atlaskit/editor-common/performance/measure-render';
20
- import { getResponseEndTime } from '@atlaskit/editor-common/performance/navigation';
21
+ import { getRequestToResponseTime, getResponseEndTime } from '@atlaskit/editor-common/performance/navigation';
22
+ import { profileSSROperation, SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
21
23
  import { EditorPluginInjectionAPI } from '@atlaskit/editor-common/preset';
22
24
  import { processRawValue, processRawValueWithoutValidation } from '@atlaskit/editor-common/process-raw-value';
23
25
  import { ReactEditorViewContext } from '@atlaskit/editor-common/ui-react';
@@ -27,12 +29,14 @@ import { EditorState, Selection, TextSelection } from '@atlaskit/editor-prosemir
27
29
  import { EditorView } from '@atlaskit/editor-prosemirror/view';
28
30
  import { EditorSSRRenderer } from '@atlaskit/editor-ssr-renderer';
29
31
  import { fg } from '@atlaskit/platform-feature-flags';
32
+ import { getInteractionId } from '@atlaskit/react-ufo/interaction-id-context';
30
33
  import { abortAll, getActiveInteraction } from '@atlaskit/react-ufo/interaction-metrics';
31
34
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
32
35
  import { expVal } from '@atlaskit/tmp-editor-statsig/expVal';
33
36
  import { useProviders } from '../composable-editor/hooks/useProviders';
34
37
  import { createFeatureFlagsFromProps } from '../utils/feature-flags-from-props';
35
38
  import { getNodesCount } from '../utils/getNodesCount';
39
+ import { getNodesCountWithExtensionKeys } from '../utils/getNodesCountWithExtensionKeys';
36
40
  import { getNodesVisibleInViewport } from '../utils/getNodesVisibleInViewport';
37
41
  import { isChromeless } from '../utils/is-chromeless';
38
42
  import { isFullPage } from '../utils/is-full-page';
@@ -49,10 +53,15 @@ import { handleEditorFocus } from './ReactEditorView/handleEditorFocus';
49
53
  import { useDispatchTransaction } from './ReactEditorView/useDispatchTransaction';
50
54
  import { useFireFullWidthEvent } from './ReactEditorView/useFireFullWidthEvent';
51
55
  var EDIT_AREA_ID = 'ak-editor-textarea';
56
+ var SSR_TRACE_SEGMENT_NAME = 'reactEditorView';
57
+ var bootStartTime = isPerformanceAPIAvailable() ? performance.now() : undefined;
52
58
  export function ReactEditorView(props) {
53
59
  var _pluginInjectionAPI$c, _media, _linking, _document$querySelect, _props$render, _props$render2;
60
+ // Should be always the first statement in the component
61
+ var firstRenderStartTimestampRef = useRef(performance.now());
54
62
  var preset = props.preset,
55
63
  _props$editorProps = props.editorProps,
64
+ onSSRMeasure = _props$editorProps.onSSRMeasure,
56
65
  nextAppearance = _props$editorProps.appearance,
57
66
  disabled = _props$editorProps.disabled,
58
67
  editorPropFeatureFlags = _props$editorProps.featureFlags,
@@ -458,9 +467,11 @@ export function ReactEditorView(props) {
458
467
  distortedDuration = _ref3.distortedDuration;
459
468
  var proseMirrorRenderedSeverity = getAnalyticsEventSeverity(duration, PROSEMIRROR_RENDERED_NORMAL_SEVERITY_THRESHOLD, PROSEMIRROR_RENDERED_DEGRADED_SEVERITY_THRESHOLD);
460
469
  if (viewRef.current) {
461
- var _pluginInjectionAPI$c2;
462
- var nodes = getNodesCount(viewRef.current.state.doc);
470
+ var _nodesAndExtensionKey, _pluginInjectionAPI$c2;
471
+ var nodesAndExtensionKeys = expValEquals('platform_editor_prosemirror_rendered_data', 'isEnabled', true) ? getNodesCountWithExtensionKeys(viewRef.current.state.doc) : undefined;
472
+ var nodes = (_nodesAndExtensionKey = nodesAndExtensionKeys === null || nodesAndExtensionKeys === void 0 ? void 0 : nodesAndExtensionKeys.nodes) !== null && _nodesAndExtensionKey !== void 0 ? _nodesAndExtensionKey : getNodesCount(viewRef.current.state.doc);
463
473
  var ttfb = getResponseEndTime();
474
+ var requestToResponseTime = getRequestToResponseTime();
464
475
  var contextIdentifier = (_pluginInjectionAPI$c2 = pluginInjectionAPI.current.api().base) === null || _pluginInjectionAPI$c2 === void 0 ? void 0 : _pluginInjectionAPI$c2.sharedState.currentState();
465
476
  var nodesInViewport = getNodesVisibleInViewport(viewRef.current.dom);
466
477
  var nodeSize = viewRef.current.state.doc.nodeSize;
@@ -488,10 +499,51 @@ export function ReactEditorView(props) {
488
499
  } : {},
489
500
  totalNodes = _ref4.totalNodes,
490
501
  nodeSizeBucket = _ref4.nodeSizeBucket;
491
- dispatchAnalyticsEvent({
492
- action: ACTION.PROSEMIRROR_RENDERED,
493
- actionSubject: ACTION_SUBJECT.EDITOR,
494
- attributes: {
502
+ if (expValEquals('platform_editor_prosemirror_rendered_data', 'isEnabled', true)) {
503
+ var _nodesAndExtensionKey2;
504
+ var extensionKeys = (_nodesAndExtensionKey2 = nodesAndExtensionKeys === null || nodesAndExtensionKeys === void 0 ? void 0 : nodesAndExtensionKeys.extensionKeys) !== null && _nodesAndExtensionKey2 !== void 0 ? _nodesAndExtensionKey2 : {};
505
+ var interaction = getActiveInteraction();
506
+ var pageLoadType = interaction === null || interaction === void 0 ? void 0 : interaction.type;
507
+ var pageType = interaction === null || interaction === void 0 ? void 0 : interaction.routeName;
508
+ var timings = function () {
509
+ if (requestToResponseTime === undefined && bootStartTime === undefined) {
510
+ return undefined;
511
+ }
512
+ var timingValues = {};
513
+ if (requestToResponseTime !== undefined) {
514
+ timingValues['requestStart->responseEnd'] = Math.round(requestToResponseTime);
515
+ }
516
+ if (bootStartTime !== undefined) {
517
+ timingValues.bootToRender = Math.round(startTime - bootStartTime);
518
+ }
519
+ return timingValues;
520
+ }();
521
+ var attributes = {
522
+ duration: duration,
523
+ startTime: startTime,
524
+ nodes: nodes,
525
+ nodesInViewport: nodesInViewport,
526
+ nodeSize: nodeSize,
527
+ nodeSizeBucket: nodeSizeBucket,
528
+ totalNodes: totalNodes,
529
+ ttfb: ttfb,
530
+ severity: proseMirrorRenderedSeverity,
531
+ objectId: contextIdentifier === null || contextIdentifier === void 0 ? void 0 : contextIdentifier.objectId,
532
+ distortedDuration: distortedDuration,
533
+ pageLoadType: pageLoadType,
534
+ pageType: pageType,
535
+ timings: timings,
536
+ extensionKeys: extensionKeys,
537
+ ufoInteractionId: getInteractionId().current
538
+ };
539
+ dispatchAnalyticsEvent({
540
+ action: ACTION.PROSEMIRROR_RENDERED,
541
+ actionSubject: ACTION_SUBJECT.EDITOR,
542
+ attributes: attributes,
543
+ eventType: EVENT_TYPE.OPERATIONAL
544
+ });
545
+ } else {
546
+ var _attributes = {
495
547
  duration: duration,
496
548
  startTime: startTime,
497
549
  nodes: nodes,
@@ -503,9 +555,14 @@ export function ReactEditorView(props) {
503
555
  severity: proseMirrorRenderedSeverity,
504
556
  objectId: contextIdentifier === null || contextIdentifier === void 0 ? void 0 : contextIdentifier.objectId,
505
557
  distortedDuration: distortedDuration
506
- },
507
- eventType: EVENT_TYPE.OPERATIONAL
508
- });
558
+ };
559
+ dispatchAnalyticsEvent({
560
+ action: ACTION.PROSEMIRROR_RENDERED,
561
+ actionSubject: ACTION_SUBJECT.EDITOR,
562
+ attributes: _attributes,
563
+ eventType: EVENT_TYPE.OPERATIONAL
564
+ });
565
+ }
509
566
  }
510
567
  });
511
568
  pluginInjectionAPI.current.onEditorViewUpdated({
@@ -769,20 +826,29 @@ export function ReactEditorView(props) {
769
826
  if (!isSSR() || !expValEquals('platform_editor_ssr_renderer', 'isEnabled', true)) {
770
827
  return null;
771
828
  }
772
- var plugins = createPluginsList(props.preset,
773
- // Don't pass props.editorProps directly, because editoProps in the dependency will lead to
774
- // multiple repaints, because props.editorPros is not stable object.
775
- {
776
- allowBlockType: allowBlockType
777
- }, pluginInjectionAPI.current);
778
- var schema = createSchema(processPluginsList(plugins));
779
- var doc = buildDoc(schema);
829
+ var doCreatePluginList = function doCreatePluginList() {
830
+ return createPluginsList(props.preset,
831
+ // Don't pass props.editorProps directly, because editoProps in the dependency will lead to
832
+ // multiple repaints, because props.editorPros is not stable object.
833
+ {
834
+ allowBlockType: allowBlockType
835
+ }, pluginInjectionAPI.current);
836
+ };
837
+ var plugins = fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/createPluginsList"), doCreatePluginList, onSSRMeasure) : doCreatePluginList();
838
+ var doCreateSchema = function doCreateSchema() {
839
+ return createSchema(processPluginsList(plugins));
840
+ };
841
+ var schema = fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/createSchema"), doCreateSchema, onSSRMeasure) : doCreateSchema();
842
+ var doBuildDoc = function doBuildDoc() {
843
+ return buildDoc(schema);
844
+ };
845
+ var doc = fg('platform_editor_better_editor_ssr_spans') ? profileSSROperation("".concat(SSR_TRACE_SEGMENT_NAME, "/buildDoc"), doBuildDoc, onSSRMeasure) : doBuildDoc();
780
846
  return {
781
847
  plugins: plugins,
782
848
  schema: schema,
783
849
  doc: doc
784
850
  };
785
- }, [allowBlockType, buildDoc, props.preset]);
851
+ }, [allowBlockType, buildDoc, props.preset, onSSRMeasure]);
786
852
  var _props$editorProps3 = props.editorProps,
787
853
  assistiveLabel = _props$editorProps3.assistiveLabel,
788
854
  assistiveDescribedBy = _props$editorProps3.assistiveDescribedBy;
@@ -805,6 +871,7 @@ export function ReactEditorView(props) {
805
871
  id: EDIT_AREA_ID,
806
872
  "aria-describedby": assistiveDescribedBy,
807
873
  "data-editor-id": editorId.current,
874
+ onSSRMeasure: onSSRMeasure,
808
875
  onEditorStateChanged: function onEditorStateChanged(state) {
809
876
  ssrEditorStateRef.current = state;
810
877
  // Notify listeners about the initial SSR state
@@ -814,7 +881,7 @@ export function ReactEditorView(props) {
814
881
  });
815
882
  }
816
883
  });
817
- }, [ssrDeps, props.intl, props.portalProviderAPI, assistiveLabel, assistiveDescribedBy, isPageAppearance]);
884
+ }, [ssrDeps, props.intl, props.portalProviderAPI, assistiveLabel, isPageAppearance, assistiveDescribedBy, onSSRMeasure]);
818
885
  var editor = useMemo(function () {
819
886
  // SSR editor will be available only in SSR environment,
820
887
  // in a browser `ssrEditor` will be `null`, and we will render a normal one ProseMirror.
@@ -831,7 +898,11 @@ export function ReactEditorView(props) {
831
898
  // Render tracking is firing too many events in Jira so we are disabling them for now. See - https://product-fabric.atlassian.net/browse/ED-25616
832
899
  // Also firing too many events for the legacy content macro, so disabling for now. See - https://product-fabric.atlassian.net/browse/ED-26650
833
900
  var renderTrackingEnabled = !fg('platform_editor_disable_rerender_tracking_jira') && !featureFlags.lcmPreventRenderTracking;
834
- return /*#__PURE__*/React.createElement(ReactEditorViewContext.Provider, {
901
+ return /*#__PURE__*/React.createElement(SSRRenderMeasure, {
902
+ segmentName: SSR_TRACE_SEGMENT_NAME,
903
+ startTimestampRef: firstRenderStartTimestampRef,
904
+ onSSRMeasure: fg('platform_editor_better_editor_ssr_spans') ? onSSRMeasure : undefined
905
+ }, /*#__PURE__*/React.createElement(ReactEditorViewContext.Provider, {
835
906
  value: {
836
907
  editorRef: editorRef,
837
908
  editorView: viewRef.current,
@@ -852,7 +923,7 @@ export function ReactEditorView(props) {
852
923
  dispatchAnalyticsEvent: dispatchAnalyticsEvent,
853
924
  editorRef: editorRef,
854
925
  editorAPI: expVal('platform_editor_no_state_plugin_injection_api', 'isEnabled', false) ? pluginInjectionAPI.current.api() : editorAPI
855
- })) !== null && _props$render !== void 0 ? _props$render : editor : editor);
926
+ })) !== null && _props$render !== void 0 ? _props$render : editor : editor));
856
927
  }
857
928
 
858
929
  // Preserving exact type generated by TypeScript
@@ -9,6 +9,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
9
9
  import { jsx } from '@emotion/react';
10
10
  import { browser as browserLegacy, getBrowserInfo } from '@atlaskit/editor-common/browser';
11
11
  import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
12
+ import { SSRRenderMeasure } from '@atlaskit/editor-common/performance/ssr-measures';
12
13
  import { ContextPanelWidthProvider } from '@atlaskit/editor-common/ui';
13
14
  import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
14
15
  import { FULL_PAGE_EDITOR_TOOLBAR_HEIGHT } from '@atlaskit/editor-shared-styles';
@@ -20,6 +21,7 @@ import { FullPageContentArea } from './FullPageContentArea';
20
21
  import { FullPageToolbar } from './FullPageToolbar';
21
22
  import { FullPageToolbarNext } from './FullPageToolbarNext';
22
23
  import { fullPageEditorWrapper } from './StyledComponents';
24
+ var SSR_TRACE_SEGMENT_NAME = 'reactEditorView/fullPageAppearance';
23
25
  var useShowKeyline = function useShowKeyline(contentAreaRef) {
24
26
  var _useState = useState(false),
25
27
  _useState2 = _slicedToArray(_useState, 2),
@@ -61,6 +63,8 @@ var hasCustomComponents = function hasCustomComponents(components) {
61
63
  };
62
64
  export var FullPageEditor = function FullPageEditor(props) {
63
65
  var _scrollContentContain, _scrollContentContain2, _scrollContentContain3, _wrapperElementRef$cu;
66
+ // Should be always the first statement in the component
67
+ var firstRenderStartTimestampRef = useRef(performance.now());
64
68
  var wrapperElementRef = useMemo(function () {
65
69
  return props.innerRef;
66
70
  }, [props.innerRef]);
@@ -134,7 +138,11 @@ export var FullPageEditor = function FullPageEditor(props) {
134
138
  }
135
139
  var isToolbarAIFCEnabled = Boolean(editorAPI === null || editorAPI === void 0 ? void 0 : editorAPI.toolbar);
136
140
  var popupsBoundariesElement = props.popupsBoundariesElement || (scrollContentContainerRef === null || scrollContentContainerRef === void 0 || (_scrollContentContain = scrollContentContainerRef.current) === null || _scrollContentContain === void 0 ? void 0 : _scrollContentContain.containerArea) || undefined;
137
- return jsx(ContextPanelWidthProvider, null, jsx("div", {
141
+ return jsx(SSRRenderMeasure, {
142
+ segmentName: SSR_TRACE_SEGMENT_NAME,
143
+ startTimestampRef: firstRenderStartTimestampRef,
144
+ onSSRMeasure: fg('platform_editor_better_editor_ssr_spans') ? props.onSSRMeasure : undefined
145
+ }, jsx(ContextPanelWidthProvider, null, jsx("div", {
138
146
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766
139
147
  css: fullPageEditorWrapper
140
148
  // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
@@ -211,5 +219,5 @@ export var FullPageEditor = function FullPageEditor(props) {
211
219
  viewMode: state.editorViewMode,
212
220
  hasHadInteraction: hasHadInteraction,
213
221
  contentMode: props.contentMode
214
- })));
222
+ }))));
215
223
  };
@@ -1,3 +1,4 @@
1
+ // When experiment platform_editor_prosemirror_rendered_data is removed, review whether this helper is still needed.
1
2
  export function getNodesCount(node) {
2
3
  var count = {};
3
4
  node.nodesBetween(0, node.nodeSize - 2, function (node) {
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Counts node types and extension keys in a single traversal.
3
+ * We exclude the end-of-doc token by iterating to nodeSize - 2,
4
+ * which matches the typical ProseMirror pattern for traversing all child nodes.
5
+ */
6
+ export function getNodesCountWithExtensionKeys(node) {
7
+ var nodes = {};
8
+ var extensionKeys = {};
9
+ var extensionNodeNames = ['extension', 'bodiedExtension', 'inlineExtension', 'multiBodiedExtension'];
10
+ node.nodesBetween(0, node.nodeSize - 2, function (currentNode) {
11
+ var _currentNode$attrs;
12
+ nodes[currentNode.type.name] = (nodes[currentNode.type.name] || 0) + 1;
13
+ if (!extensionNodeNames.includes(currentNode.type.name)) {
14
+ return;
15
+ }
16
+ var extensionKey = (_currentNode$attrs = currentNode.attrs) === null || _currentNode$attrs === void 0 ? void 0 : _currentNode$attrs.extensionKey;
17
+ if (!extensionKey) {
18
+ return;
19
+ }
20
+ extensionKeys[extensionKey] = (extensionKeys[extensionKey] || 0) + 1;
21
+ });
22
+ return {
23
+ nodes: nodes,
24
+ extensionKeys: extensionKeys
25
+ };
26
+ }
@@ -1,2 +1,2 @@
1
1
  export var name = "@atlaskit/editor-core";
2
- export var version = "216.10.0";
2
+ export var version = "216.11.1";
@@ -29,6 +29,11 @@ export interface EditorViewProps extends WrappedComponentProps {
29
29
  transformer?: Transformer<string>;
30
30
  view: EditorView;
31
31
  }) => void;
32
+ onSSRMeasure?: (measure: {
33
+ endTimestamp: number;
34
+ segmentName: string;
35
+ startTimestamp: number;
36
+ }) => void;
32
37
  portalProviderAPI: PortalProviderAPI;
33
38
  preset: EditorPresetBuilder<string[], AllEditorPresetPluginTypes[]>;
34
39
  providerFactory: ProviderFactory;
@@ -37,6 +37,11 @@ export interface EditorAppearanceComponentProps<Plugins extends NextEditorPlugin
37
37
  minHeight?: number;
38
38
  onCancel?: (editorView: EditorView) => void;
39
39
  onSave?: (editorView: EditorView) => void;
40
+ onSSRMeasure?: (measure: {
41
+ endTimestamp: number;
42
+ segmentName: string;
43
+ startTimestamp: number;
44
+ }) => void;
40
45
  persistScrollGutter?: boolean;
41
46
  pluginHooks?: ReactHookFactory[];
42
47
  popupsBoundariesElement?: HTMLElement;
@@ -119,6 +119,32 @@ interface EditorBaseProps {
119
119
  onChange?: EditorOnChangeHandler;
120
120
  onDestroy?: () => void;
121
121
  onEditorReady?: (editorActions: EditorActions) => void;
122
+ /**
123
+ * Callback for measuring Server-Side Rendering (SSR) performance metrics.
124
+ * Invoked during SSR to track timing information for different segments of the rendering process.
125
+ *
126
+ * @param measure - Performance measurement data
127
+ * @param measure.startTimestamp - Absolute timestamp when the segment started (from `performance.now()`)
128
+ * @param measure.endTimestamp - Absolute timestamp when the segment completed (from `performance.now()`)
129
+ * @param measure.segmentName - Name identifier of the SSR segment being measured
130
+ *
131
+ * @remarks
132
+ * Both timestamps are absolute values from `performance.now()`, not relative to measurement start.
133
+ * Calculate duration as: `measure.endTimestamp - measure.startTimestamp`
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * onSSRMeasure={(measure) => {
138
+ * const duration = measure.endTimestamp - measure.startTimestamp;
139
+ * console.log(`${measure.segmentName}: ${duration}ms`);
140
+ * }}
141
+ * ```
142
+ */
143
+ onSSRMeasure?: (measure: {
144
+ endTimestamp: number;
145
+ segmentName: string;
146
+ startTimestamp: number;
147
+ }) => void;
122
148
  persistScrollGutter?: boolean;
123
149
  popupsBoundariesElement?: HTMLElement;
124
150
  popupsMountPoint?: HTMLElement;
@@ -0,0 +1,12 @@
1
+ import type { Node } from '@atlaskit/editor-prosemirror/model';
2
+ type NodesAndExtensionKeys = {
3
+ extensionKeys: Record<string, number>;
4
+ nodes: Record<string, number>;
5
+ };
6
+ /**
7
+ * Counts node types and extension keys in a single traversal.
8
+ * We exclude the end-of-doc token by iterating to nodeSize - 2,
9
+ * which matches the typical ProseMirror pattern for traversing all child nodes.
10
+ */
11
+ export declare function getNodesCountWithExtensionKeys(node: Node): NodesAndExtensionKeys;
12
+ export {};
@@ -29,6 +29,11 @@ export interface EditorViewProps extends WrappedComponentProps {
29
29
  transformer?: Transformer<string>;
30
30
  view: EditorView;
31
31
  }) => void;
32
+ onSSRMeasure?: (measure: {
33
+ endTimestamp: number;
34
+ segmentName: string;
35
+ startTimestamp: number;
36
+ }) => void;
32
37
  portalProviderAPI: PortalProviderAPI;
33
38
  preset: EditorPresetBuilder<string[], AllEditorPresetPluginTypes[]>;
34
39
  providerFactory: ProviderFactory;
@@ -37,6 +37,11 @@ export interface EditorAppearanceComponentProps<Plugins extends NextEditorPlugin
37
37
  minHeight?: number;
38
38
  onCancel?: (editorView: EditorView) => void;
39
39
  onSave?: (editorView: EditorView) => void;
40
+ onSSRMeasure?: (measure: {
41
+ endTimestamp: number;
42
+ segmentName: string;
43
+ startTimestamp: number;
44
+ }) => void;
40
45
  persistScrollGutter?: boolean;
41
46
  pluginHooks?: ReactHookFactory[];
42
47
  popupsBoundariesElement?: HTMLElement;
@@ -119,6 +119,32 @@ interface EditorBaseProps {
119
119
  onChange?: EditorOnChangeHandler;
120
120
  onDestroy?: () => void;
121
121
  onEditorReady?: (editorActions: EditorActions) => void;
122
+ /**
123
+ * Callback for measuring Server-Side Rendering (SSR) performance metrics.
124
+ * Invoked during SSR to track timing information for different segments of the rendering process.
125
+ *
126
+ * @param measure - Performance measurement data
127
+ * @param measure.startTimestamp - Absolute timestamp when the segment started (from `performance.now()`)
128
+ * @param measure.endTimestamp - Absolute timestamp when the segment completed (from `performance.now()`)
129
+ * @param measure.segmentName - Name identifier of the SSR segment being measured
130
+ *
131
+ * @remarks
132
+ * Both timestamps are absolute values from `performance.now()`, not relative to measurement start.
133
+ * Calculate duration as: `measure.endTimestamp - measure.startTimestamp`
134
+ *
135
+ * @example
136
+ * ```typescript
137
+ * onSSRMeasure={(measure) => {
138
+ * const duration = measure.endTimestamp - measure.startTimestamp;
139
+ * console.log(`${measure.segmentName}: ${duration}ms`);
140
+ * }}
141
+ * ```
142
+ */
143
+ onSSRMeasure?: (measure: {
144
+ endTimestamp: number;
145
+ segmentName: string;
146
+ startTimestamp: number;
147
+ }) => void;
122
148
  persistScrollGutter?: boolean;
123
149
  popupsBoundariesElement?: HTMLElement;
124
150
  popupsMountPoint?: HTMLElement;
@@ -0,0 +1,12 @@
1
+ import type { Node } from '@atlaskit/editor-prosemirror/model';
2
+ type NodesAndExtensionKeys = {
3
+ extensionKeys: Record<string, number>;
4
+ nodes: Record<string, number>;
5
+ };
6
+ /**
7
+ * Counts node types and extension keys in a single traversal.
8
+ * We exclude the end-of-doc token by iterating to nodeSize - 2,
9
+ * which matches the typical ProseMirror pattern for traversing all child nodes.
10
+ */
11
+ export declare function getNodesCountWithExtensionKeys(node: Node): NodesAndExtensionKeys;
12
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-core",
3
- "version": "216.10.1",
3
+ "version": "216.11.2",
4
4
  "description": "A package contains Atlassian editor core functionality",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -54,7 +54,7 @@
54
54
  "@atlaskit/editor-plugins": "^12.0.0",
55
55
  "@atlaskit/editor-prosemirror": "^7.3.0",
56
56
  "@atlaskit/editor-shared-styles": "^3.10.0",
57
- "@atlaskit/editor-ssr-renderer": "^2.1.0",
57
+ "@atlaskit/editor-ssr-renderer": "^2.2.0",
58
58
  "@atlaskit/editor-toolbar": "^0.19.0",
59
59
  "@atlaskit/editor-toolbar-model": "^0.3.0",
60
60
  "@atlaskit/emoji": "^69.10.0",
@@ -66,7 +66,7 @@
66
66
  "@atlaskit/platform-feature-flags-react": "^0.4.0",
67
67
  "@atlaskit/react-ufo": "^5.2.0",
68
68
  "@atlaskit/task-decision": "^19.2.0",
69
- "@atlaskit/tmp-editor-statsig": "^26.0.0",
69
+ "@atlaskit/tmp-editor-statsig": "^27.1.0",
70
70
  "@atlaskit/tokens": "^11.0.0",
71
71
  "@atlaskit/tooltip": "^20.14.0",
72
72
  "@atlaskit/width-detector": "^5.0.0",
@@ -331,6 +331,9 @@
331
331
  "platform_editor_a11y_find_replace_focus_ring": {
332
332
  "type": "boolean"
333
333
  },
334
+ "platform_editor_better_editor_ssr_spans": {
335
+ "type": "boolean"
336
+ },
334
337
  "platform_editor_context_context_types_migration": {
335
338
  "type": "boolean"
336
339
  },