@atlaskit/editor-plugin-block-controls 12.1.1 → 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.
Files changed (35) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/cjs/editor-commands/move-node.js +2 -1
  3. package/dist/cjs/pm-plugins/decorations-drag-handle.js +19 -1
  4. package/dist/cjs/pm-plugins/decorations-drop-target.js +2 -1
  5. package/dist/cjs/pm-plugins/decorations-quick-insert-button.js +19 -1
  6. package/dist/cjs/pm-plugins/main.js +77 -19
  7. package/dist/cjs/ui/consts.js +3 -1
  8. package/dist/cjs/ui/drag-handle.js +9 -0
  9. package/dist/cjs/ui/global-styles.js +13 -1
  10. package/dist/cjs/ui/quick-insert-button.js +8 -0
  11. package/dist/es2019/editor-commands/move-node.js +2 -1
  12. package/dist/es2019/pm-plugins/decorations-drag-handle.js +14 -0
  13. package/dist/es2019/pm-plugins/decorations-drop-target.js +2 -1
  14. package/dist/es2019/pm-plugins/decorations-quick-insert-button.js +13 -0
  15. package/dist/es2019/pm-plugins/main.js +79 -21
  16. package/dist/es2019/ui/consts.js +2 -0
  17. package/dist/es2019/ui/drag-handle.js +10 -1
  18. package/dist/es2019/ui/global-styles.js +17 -2
  19. package/dist/es2019/ui/quick-insert-button.js +9 -1
  20. package/dist/esm/editor-commands/move-node.js +2 -1
  21. package/dist/esm/pm-plugins/decorations-drag-handle.js +18 -0
  22. package/dist/esm/pm-plugins/decorations-drop-target.js +2 -1
  23. package/dist/esm/pm-plugins/decorations-quick-insert-button.js +18 -0
  24. package/dist/esm/pm-plugins/main.js +79 -21
  25. package/dist/esm/ui/consts.js +2 -0
  26. package/dist/esm/ui/drag-handle.js +10 -1
  27. package/dist/esm/ui/global-styles.js +14 -2
  28. package/dist/esm/ui/quick-insert-button.js +9 -1
  29. package/dist/types/pm-plugins/decorations-drag-handle.d.ts +7 -0
  30. package/dist/types/pm-plugins/decorations-quick-insert-button.d.ts +7 -0
  31. package/dist/types/ui/consts.d.ts +2 -0
  32. package/dist/types-ts4.5/pm-plugins/decorations-drag-handle.d.ts +7 -0
  33. package/dist/types-ts4.5/pm-plugins/decorations-quick-insert-button.d.ts +7 -0
  34. package/dist/types-ts4.5/ui/consts.d.ts +2 -0
  35. package/package.json +2 -2
@@ -19,10 +19,10 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
19
19
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
20
20
  import { getAnchorAttrName } from '../ui/utils/dom-attr-name';
21
21
  import { findNodeDecs, nodeDecorations } from './decorations-anchor';
22
- import { dragHandleDecoration, emptyParagraphNodeDecorations, findHandleDec } from './decorations-drag-handle';
22
+ import { createActiveDragHandleNodeDecoration, dragHandleDecoration, emptyParagraphNodeDecorations, findActiveDragHandleNodeDec, findHandleDec } from './decorations-drag-handle';
23
23
  import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-target';
24
24
  import { getActiveDropTargetDecorations } from './decorations-drop-target-active';
25
- import { findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
25
+ import { createActiveQuickInsertNodeDecoration, findActiveQuickInsertNodeDec, findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
26
26
  import { handleMouseDown } from './handle-mouse-down';
27
27
  import { handleMouseOver } from './handle-mouse-over';
28
28
  import { boundKeydownHandler } from './keymap';
@@ -435,34 +435,45 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
435
435
  }
436
436
  }
437
437
  if (shouldRemoveHandle) {
438
- var _activeNode5, _activeNode6;
438
+ var _activeNode5, _activeNode6, _activeNode7;
439
439
  const oldHandle = findHandleDec(decorations, (_activeNode5 = activeNode) === null || _activeNode5 === void 0 ? void 0 : _activeNode5.pos, (_activeNode6 = activeNode) === null || _activeNode6 === void 0 ? void 0 : _activeNode6.pos);
440
440
  decorations = decorations.remove(oldHandle);
441
+ // When removing the handle, also remove the anchor-marker node decorations
442
+ // (data-active-drag-handle / data-active-quick-insert) so the DOM attributes
443
+ // don't linger on nodes that are no longer active.
444
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && ((_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.pos) !== undefined) {
445
+ const oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, activeNode.pos, activeNode.pos);
446
+ decorations = decorations.remove(oldActiveNodeDec);
447
+ if (activeNode.rootPos !== undefined) {
448
+ const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, activeNode.rootPos, activeNode.rootPos);
449
+ decorations = decorations.remove(oldActiveQuickInsertDec);
450
+ }
451
+ }
441
452
  // platform_editor_controls note: enables quick insert
442
453
  if (flags.toolbarFlagsEnabled && quickInsertButtonEnabled) {
443
- var _activeNode7, _activeNode8;
444
- const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
454
+ var _activeNode8, _activeNode9;
455
+ const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos);
445
456
  decorations = decorations.remove(oldQuickInsertButton);
446
457
  for (const factory of nodeDecorationRegistry) {
447
- var _activeNode9, _activeNode0;
448
- const old = decorations.find((_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, spec => spec.type === factory.type);
458
+ var _activeNode0, _activeNode1;
459
+ const old = decorations.find((_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, spec => spec.type === factory.type);
449
460
  decorations = decorations.remove(old);
450
461
  }
451
462
  if (rightSideControlsEnabled && isViewMode && fg('confluence_remix_button_right_side_block_fg')) {
452
463
  for (const factory of nodeDecorationRegistry) {
453
464
  if (factory.showInViewMode) {
454
- var _activeNode1, _activeNode10;
455
- const old = decorations.find((_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, spec => spec.type === factory.type);
465
+ var _activeNode10, _activeNode11;
466
+ const old = decorations.find((_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.rootPos, spec => spec.type === factory.type);
456
467
  decorations = decorations.remove(old);
457
468
  }
458
469
  }
459
470
  }
460
471
  }
461
472
  } else if (api) {
462
- var _latestActiveNode5, _latestActiveNode1;
473
+ var _latestActiveNode5, _latestActiveNode10;
463
474
  if (shouldRecreateHandle && (!rightSideControlsEnabled || !isViewMode)) {
464
- var _activeNode11, _activeNode12, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
465
- const oldHandle = findHandleDec(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.pos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos);
475
+ var _activeNode12, _activeNode13, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
476
+ const oldHandle = findHandleDec(decorations, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.pos);
466
477
  decorations = decorations.remove(oldHandle);
467
478
  const handleDec = dragHandleDecoration({
468
479
  api,
@@ -476,12 +487,44 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
476
487
  editorState: newState
477
488
  });
478
489
  decorations = decorations.add(newState.doc, [handleDec]);
490
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true)) {
491
+ // Recreate the drag handle node decoration when the edit-mode node itself changed
492
+ // or its content was modified. Other triggers (editorSizeChanged, handleNeedsRedraw)
493
+ // don't move the decoration — DecorationSet.map() already remaps it correctly.
494
+ const needsNodeDecUpdate = activeNodeChanged || isActiveNodeModified;
495
+ if (needsNodeDecUpdate) {
496
+ var _newState$doc$nodeAt;
497
+ const nodeSize = (_newState$doc$nodeAt = newState.doc.nodeAt(latestActiveNode.pos)) === null || _newState$doc$nodeAt === void 0 ? void 0 : _newState$doc$nodeAt.nodeSize;
498
+ if (nodeSize !== undefined) {
499
+ var _activeNode14, _activeNode15;
500
+ const oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.pos, (_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.pos);
501
+ decorations = decorations.remove(oldActiveNodeDec);
502
+ decorations = decorations.add(newState.doc, [createActiveDragHandleNodeDecoration(latestActiveNode.pos, nodeSize)]);
503
+ }
504
+ }
505
+
506
+ // The quick-insert decoration lives on the root node (rootPos), which can change
507
+ // independently of the edit-mode node — e.g. when only editorSizeChanged fires but
508
+ // rootActiveNodeChanged is also true. So we use a separate, broader guard that
509
+ // includes rootActiveNodeChanged to avoid leaving the attribute on a stale root node.
510
+ const needsQuickInsertDecUpdate = activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged;
511
+ if (needsQuickInsertDecUpdate && latestActiveNode.rootPos !== undefined) {
512
+ var _newState$doc$nodeAt2;
513
+ const rootNodeSize = (_newState$doc$nodeAt2 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt2 === void 0 ? void 0 : _newState$doc$nodeAt2.nodeSize;
514
+ if (rootNodeSize !== undefined) {
515
+ var _activeNode16, _activeNode17;
516
+ const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, (_activeNode17 = activeNode) === null || _activeNode17 === void 0 ? void 0 : _activeNode17.rootPos);
517
+ decorations = decorations.remove(oldActiveQuickInsertDec);
518
+ decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
519
+ }
520
+ }
521
+ }
479
522
  }
480
523
  if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
481
524
  // platform_editor_controls note: enables quick insert
482
525
  flags.toolbarFlagsEnabled && quickInsertButtonEnabled && (!rightSideControlsEnabled || !isViewMode)) {
483
- var _activeNode13, _activeNode14, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
484
- const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
526
+ var _activeNode18, _activeNode19, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0, _latestActiveNode1;
527
+ const oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode18 = activeNode) === null || _activeNode18 === void 0 ? void 0 : _activeNode18.rootPos, (_activeNode19 = activeNode) === null || _activeNode19 === void 0 ? void 0 : _activeNode19.rootPos);
485
528
  decorations = decorations.remove(oldQuickInsertButton);
486
529
  const quickInsertButton = quickInsertButtonDecoration({
487
530
  api,
@@ -496,9 +539,24 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
496
539
  editorState: newState
497
540
  });
498
541
  decorations = decorations.add(newState.doc, [quickInsertButton]);
542
+
543
+ // Update quick insert node decoration when the quick insert button is recreated but
544
+ // the drag handle was NOT recreated (shouldRecreateHandle was false). When
545
+ // shouldRecreateHandle is true, the block above already handles this.
546
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !shouldRecreateHandle && ((_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos) !== undefined) {
547
+ var _activeNode20, _newState$doc$nodeAt3;
548
+ if (((_activeNode20 = activeNode) === null || _activeNode20 === void 0 ? void 0 : _activeNode20.rootPos) !== undefined) {
549
+ const oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, activeNode.rootPos, activeNode.rootPos);
550
+ decorations = decorations.remove(oldActiveQuickInsertDec);
551
+ }
552
+ const rootNodeSize = (_newState$doc$nodeAt3 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt3 === void 0 ? void 0 : _newState$doc$nodeAt3.nodeSize;
553
+ if (rootNodeSize !== undefined) {
554
+ decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
555
+ }
556
+ }
499
557
  if (rightSideControlsEnabled) {
500
558
  for (const factory of nodeDecorationRegistry) {
501
- var _activeNode15, _activeNode16;
559
+ var _activeNode21, _activeNode22;
502
560
  if (!latestActiveNode || latestActiveNode.rootPos === undefined) {
503
561
  continue;
504
562
  }
@@ -511,7 +569,7 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
511
569
  rootAnchorName: latestActiveNode.rootAnchorName,
512
570
  rootNodeType: latestActiveNode.rootNodeType
513
571
  };
514
- const old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, spec => spec.type === factory.type);
572
+ const old = decorations.find((_activeNode21 = activeNode) === null || _activeNode21 === void 0 ? void 0 : _activeNode21.rootPos, (_activeNode22 = activeNode) === null || _activeNode22 === void 0 ? void 0 : _activeNode22.rootPos, spec => spec.type === factory.type);
515
573
  decorations = decorations.remove(old);
516
574
 
517
575
  // determines whether to show the decorations, see malleableUiPlugin.tsx
@@ -530,7 +588,7 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
530
588
  }
531
589
 
532
590
  // In view mode (edit/live pages), show right-side controls on block hover (without drag handle or quick insert)
533
- const rootPos = (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos;
591
+ const rootPos = (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.rootPos;
534
592
  // rootPos is computed using the same logic as the floating insert menu, so it always points to a top-level (doc child) block when defined.
535
593
  const isDocLevel = rootPos !== undefined && !isNaN(rootPos);
536
594
  if (isViewMode && isDocLevel && flags.toolbarFlagsEnabled && rightSideControlsEnabled) {
@@ -620,15 +678,15 @@ export const apply = (api, formatMessage, tr, currentState, newState, flags, nod
620
678
  let newActiveNode;
621
679
  // platform_editor_controls note: enables quick insert
622
680
  if (flags.toolbarFlagsEnabled) {
623
- var _latestActiveNode10, _latestActiveNode11;
681
+ var _latestActiveNode11, _latestActiveNode12;
624
682
  // remove isEmptyDoc check and let decorations render and determine their own visibility
625
683
  // In view mode with right-side controls we render node decorations (right-edge button), not the
626
684
  // handle - so findHandleDec is always empty. Don't clear activeNode in that case.
627
- const hasHandleOrViewModeControls = findHandleDec(decorations, (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.pos, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos).length > 0 || isViewMode && rightSideControlsEnabled;
685
+ const hasHandleOrViewModeControls = findHandleDec(decorations, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos).length > 0 || isViewMode && rightSideControlsEnabled;
628
686
  newActiveNode = meta !== null && meta !== void 0 && meta.editorBlurred || !(meta !== null && meta !== void 0 && meta.activeNode) && !hasHandleOrViewModeControls ? null : latestActiveNode;
629
687
  } else {
630
- var _latestActiveNode12, _latestActiveNode13;
631
- newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos, (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.pos).length === 0 ? null : latestActiveNode;
688
+ var _latestActiveNode13, _latestActiveNode14;
689
+ newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.pos, (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.pos).length === 0 ? null : latestActiveNode;
632
690
  }
633
691
  let isMenuOpenNew = isMenuOpen;
634
692
  if (editorExperiment('platform_editor_block_menu', true)) {
@@ -3,6 +3,8 @@ import { breakoutResizableNodes as breakoutResizableNodesNew } from '@atlaskit/e
3
3
  import { akEditorUnitZIndex, akRichMediaResizeZIndex } from '@atlaskit/editor-shared-styles';
4
4
  import { fg } from '@atlaskit/platform-feature-flags';
5
5
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
6
+ export const ACTIVE_DRAG_HANDLE_ATTR = 'data-active-drag-handle';
7
+ export const ACTIVE_QUICK_INSERT_ATTR = 'data-active-quick-insert';
6
8
  export const DRAG_HANDLE_HEIGHT = 24;
7
9
  export const DRAG_HANDLE_BORDER_RADIUS = 4;
8
10
  export const DRAG_HANDLE_ZINDEX = akRichMediaResizeZIndex + akEditorUnitZIndex; //place above legacy resizer
@@ -35,7 +35,7 @@ import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, ge
35
35
  import { expandAndUpdateSelection } from '../pm-plugins/utils/expand-and-update-selection';
36
36
  import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
37
37
  import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
38
- import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
38
+ import { ACTIVE_DRAG_HANDLE_ATTR, DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
39
39
  import { DragHandleNestedIcon } from './drag-handle-nested-icon';
40
40
  import { dragPreview } from './drag-preview';
41
41
  import { refreshAnchorName } from './utils/anchor-name';
@@ -807,6 +807,15 @@ export const DragHandle = ({
807
807
  anchorName
808
808
  }) : anchorName;
809
809
  const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${safeAnchorName}"]`);
810
+
811
+ // Defence-in-depth guard: since the node decoration sets data-active-drag-handle on the
812
+ // active node, we check for it directly. This is a cheap DOM attribute read (no reflow,
813
+ // no style recalculation) and hides the control if the decoration hasn't been applied yet.
814
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !(dom !== null && dom !== void 0 && dom.hasAttribute(ACTIVE_DRAG_HANDLE_ATTR))) {
815
+ return {
816
+ display: 'none'
817
+ };
818
+ }
810
819
  const hasResizer = nodeType === 'table' || nodeType === 'mediaSingle';
811
820
  const isExtension = nodeType === 'extension' || nodeType === 'bodiedExtension' || nodeType === 'multiBodiedExtension' && fg('confluence_frontend_native_tabs_extension');
812
821
  const isBlockCard = nodeType === 'blockCard' && !!blockCardWidth;
@@ -11,7 +11,7 @@ import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
11
11
  import { akEditorBreakoutPadding, akEditorCalculatedWideLayoutWidth, akEditorCalculatedWideLayoutWidthSmallViewport, akEditorFullPageNarrowBreakout, akEditorGutterPaddingDynamic, akEditorGutterPaddingReduced } from '@atlaskit/editor-shared-styles';
12
12
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
13
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
14
- import { DRAG_HANDLE_MAX_WIDTH_PLUS_GAP } from './consts';
14
+ import { ACTIVE_DRAG_HANDLE_ATTR, ACTIVE_QUICK_INSERT_ATTR, DRAG_HANDLE_MAX_WIDTH_PLUS_GAP } from './consts';
15
15
  import { NODE_ANCHOR_ATTR_NAME } from './utils/dom-attr-name';
16
16
 
17
17
  /**
@@ -449,6 +449,21 @@ const dragHandlerAnchorStyles = css({
449
449
  }
450
450
  }
451
451
  });
452
+ const activeControlsSelector = `[${ACTIVE_DRAG_HANDLE_ATTR}], [${ACTIVE_QUICK_INSERT_ATTR}]`;
453
+
454
+ // Applies anchor-name via node decoration attributes rather than adjacency CSS selectors.
455
+ // This is more reliable than dragHandlerAnchorStyles which depends on DOM structure.
456
+ // Only nodes decorated with data-active-drag-handle / data-active-quick-insert get anchor-name.
457
+ const staticControlsAnchorStyles = css({
458
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors
459
+ '.ProseMirror': {
460
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors, @atlaskit/ui-styling-standard/no-unsafe-selectors, @atlaskit/ui-styling-standard/no-unsafe-values
461
+ [activeControlsSelector]: {
462
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/ui-styling-standard/no-unsafe-values
463
+ anchorName: `var(${ANCHOR_VARIABLE_NAME}, attr(data-node-anchor type(<custom-ident>)))`
464
+ }
465
+ }
466
+ });
452
467
 
453
468
  // Styles applied to nodes with anchors when dragging
454
469
  const dragAnchorStyles = css({
@@ -499,6 +514,6 @@ export const GlobalStylesWrapper = ({
499
514
  exposure: true
500
515
  }) ? expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? extendHoverZoneReducedNext : extendHoverZoneReduced : undefined,
501
516
  // platform_editor_controls note: this allows drag handles to render on empty lines
502
- toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone, shouldRenderAnchors && (isDragging ? dragAnchorStyles : dragHandlerAnchorStyles)]
517
+ toolbarFlagsEnabled ? undefined : withInlineNodeStyle, withDeleteLinesStyleFix, withMediaSingleStyleFix, legacyBreakoutWideLayoutStyle, headingWithIndentationInLayoutStyleFix, editorExperiment('advanced_layouts', true) ? blockCardWithoutLayout : undefined, withDividerInPanelStyleFix, withFormatInLayoutStyleFix, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withRelativePosStyleNext : withRelativePosStyle, topLevelNodeMarginStyles, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) ? withAnchorNameZindexStyleNext : withAnchorNameZindexStyle, expValEquals('platform_editor_native_anchor_with_dnd', 'isEnabled', true) && expValEquals('advanced_layouts', 'isEnabled', true) ? layoutColumnExtendedHoverZone : layoutColumnWithoutHoverZone, shouldRenderAnchors && (isDragging ? dragAnchorStyles : dragHandlerAnchorStyles), expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) ? staticControlsAnchorStyles : undefined]
503
518
  });
504
519
  };
@@ -24,7 +24,7 @@ import Tooltip from '@atlaskit/tooltip';
24
24
  import { getNodeTypeWithLevel } from '../pm-plugins/decorations-common';
25
25
  import { getControlBottomCSSValue, getControlHeightCSSValue, getNodeHeight, getTopPosition, shouldBeSticky } from '../pm-plugins/utils/drag-handle-positions';
26
26
  import { getLeftPositionForRootElement } from '../pm-plugins/utils/widget-positions';
27
- import { QUICK_INSERT_DIMENSIONS, QUICK_INSERT_HEIGHT, QUICK_INSERT_LEFT_OFFSET, QUICK_INSERT_WIDTH, rootElementGap, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
27
+ import { ACTIVE_QUICK_INSERT_ATTR, QUICK_INSERT_DIMENSIONS, QUICK_INSERT_HEIGHT, QUICK_INSERT_LEFT_OFFSET, QUICK_INSERT_WIDTH, rootElementGap, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
28
28
  import { refreshAnchorName } from './utils/anchor-name';
29
29
  import { isInTextSelection, isNestedNodeSelected, isNonEditableBlock, isSelectionInNode } from './utils/document-checks';
30
30
  import { getAnchorAttrName } from './utils/dom-attr-name';
@@ -179,6 +179,14 @@ export const TypeAheadControl = ({
179
179
  anchorName: rootAnchorName
180
180
  });
181
181
  const dom = view.dom.querySelector(`[${getAnchorAttrName()}="${safeAnchorName}"]`);
182
+
183
+ // Defence-in-depth guard: the node decoration sets data-active-quick-insert on the
184
+ // active root node. Check for it directly — cheap DOM attribute read, no reflow.
185
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !(dom !== null && dom !== void 0 && dom.hasAttribute(ACTIVE_QUICK_INSERT_ATTR))) {
186
+ return {
187
+ display: 'none'
188
+ };
189
+ }
182
190
  const hasResizer = rootNodeType === 'table' || rootNodeType === 'mediaSingle';
183
191
  const isExtension = rootNodeType === 'extension' || rootNodeType === 'bodiedExtension' || rootNodeType === 'multiBodiedExtension' && fg('confluence_frontend_native_tabs_extension');
184
192
  const isBlockCard = rootNodeType === 'blockCard';
@@ -7,7 +7,8 @@ import { blockControlsMessages } from '@atlaskit/editor-common/messages';
7
7
  import { expandSelectionBounds, GapCursorSelection } from '@atlaskit/editor-common/selection';
8
8
  import { transformSliceNestedExpandToExpand } from '@atlaskit/editor-common/transforms';
9
9
  import { DIRECTION } from '@atlaskit/editor-common/types';
10
- import { getBaseNodeTypeName, isEmptyParagraph } from '@atlaskit/editor-common/utils';
10
+ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
11
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
11
12
  import { Fragment, Slice } from '@atlaskit/editor-prosemirror/model';
12
13
  import { NodeSelection, Selection } from '@atlaskit/editor-prosemirror/state';
13
14
  import { Mapping, StepMap } from '@atlaskit/editor-prosemirror/transform';
@@ -8,9 +8,27 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
8
8
  import { fg } from '@atlaskit/platform-feature-flags';
9
9
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
10
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
+ import { ACTIVE_DRAG_HANDLE_ATTR } from '../ui/consts';
11
12
  import { DragHandle, DragHandleWithVisibility } from '../ui/drag-handle';
12
13
  import { TYPE_HANDLE_DEC, TYPE_NODE_DEC, unmountDecorations } from './decorations-common';
13
14
  import { getActiveBlockMarks, getMatchingBlockMarks } from './utils/marks';
15
+ var TYPE_ACTIVE_HANDLE_DEC = 'active-drag-handle-node';
16
+
17
+ /**
18
+ * Creates a Decoration.node that marks the active node with `data-active-drag-handle="true"`.
19
+ * The CSS in staticControlsAnchorStyles then applies `anchor-name` to this attribute,
20
+ * which is more reliable than the adjacency-selector approach in dragHandlerAnchorStyles.
21
+ */
22
+ export var createActiveDragHandleNodeDecoration = function createActiveDragHandleNodeDecoration(pos, nodeSize) {
23
+ return Decoration.node(pos, pos + nodeSize, _defineProperty({}, ACTIVE_DRAG_HANDLE_ATTR, 'true'), {
24
+ type: TYPE_ACTIVE_HANDLE_DEC
25
+ });
26
+ };
27
+ export var findActiveDragHandleNodeDec = function findActiveDragHandleNodeDec(decorations, from, to) {
28
+ return decorations.find(from, to, function (spec) {
29
+ return spec.type === TYPE_ACTIVE_HANDLE_DEC;
30
+ });
31
+ };
14
32
  export var emptyParagraphNodeDecorations = function emptyParagraphNodeDecorations() {
15
33
  var anchorName = "--node-anchor-paragraph-0";
16
34
  var style = "anchor-name: ".concat(anchorName, "; margin-top: 0px;");
@@ -7,7 +7,8 @@ import memoizeOne from 'memoize-one';
7
7
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
8
8
  import uuid from 'uuid';
9
9
  import { expandSelectionBounds } from '@atlaskit/editor-common/selection';
10
- import { getBaseNodeTypeName, isEmptyParagraph } from '@atlaskit/editor-common/utils';
10
+ import { isEmptyParagraph } from '@atlaskit/editor-common/utils';
11
+ import { getBaseNodeTypeName } from '@atlaskit/editor-common/utils/node-type-utils';
11
12
  import { Decoration } from '@atlaskit/editor-prosemirror/view';
12
13
  import { fg } from '@atlaskit/platform-feature-flags';
13
14
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
@@ -1,3 +1,4 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
1
2
  import { createElement } from 'react';
2
3
  // eslint-disable-next-line @atlaskit/platform/prefer-crypto-random-uuid -- Use crypto.randomUUID instead
3
4
  import uuid from 'uuid';
@@ -5,14 +6,31 @@ import { Decoration } from '@atlaskit/editor-prosemirror/view';
5
6
  import { fg } from '@atlaskit/platform-feature-flags';
6
7
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
7
8
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
9
+ import { ACTIVE_QUICK_INSERT_ATTR } from '../ui/consts';
8
10
  import { QuickInsertWithVisibility } from '../ui/quick-insert-button';
9
11
  import { getActiveBlockMarks, getMatchingBlockMarks } from './utils/marks';
10
12
  var TYPE_QUICK_INSERT = 'INSERT_BUTTON';
13
+ var TYPE_ACTIVE_QUICK_INSERT_NODE = 'active-quick-insert-node';
11
14
  export var findQuickInsertInsertButtonDecoration = function findQuickInsertInsertButtonDecoration(decorations, from, to) {
12
15
  return decorations.find(from, to, function (spec) {
13
16
  return spec.type === TYPE_QUICK_INSERT;
14
17
  });
15
18
  };
19
+ /**
20
+ * Creates a Decoration.node that marks the active node with `data-active-quick-insert="true"`.
21
+ * The CSS in staticControlsAnchorStyles applies `anchor-name` to this attribute directly,
22
+ * replacing the unreliable adjacency selector `[block-ctrl-quick-insert-button] + *`.
23
+ */
24
+ export var createActiveQuickInsertNodeDecoration = function createActiveQuickInsertNodeDecoration(pos, nodeSize) {
25
+ return Decoration.node(pos, pos + nodeSize, _defineProperty({}, ACTIVE_QUICK_INSERT_ATTR, 'true'), {
26
+ type: TYPE_ACTIVE_QUICK_INSERT_NODE
27
+ });
28
+ };
29
+ export var findActiveQuickInsertNodeDec = function findActiveQuickInsertNodeDec(decorations, from, to) {
30
+ return decorations.find(from, to, function (spec) {
31
+ return spec.type === TYPE_ACTIVE_QUICK_INSERT_NODE;
32
+ });
33
+ };
16
34
  export var quickInsertButtonDecoration = function quickInsertButtonDecoration(_ref) {
17
35
  var api = _ref.api,
18
36
  formatMessage = _ref.formatMessage,
@@ -25,10 +25,10 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
25
25
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
26
26
  import { getAnchorAttrName } from '../ui/utils/dom-attr-name';
27
27
  import { findNodeDecs, nodeDecorations } from './decorations-anchor';
28
- import { dragHandleDecoration, emptyParagraphNodeDecorations, findHandleDec } from './decorations-drag-handle';
28
+ import { createActiveDragHandleNodeDecoration, dragHandleDecoration, emptyParagraphNodeDecorations, findActiveDragHandleNodeDec, findHandleDec } from './decorations-drag-handle';
29
29
  import { dropTargetDecorations, findDropTargetDecs } from './decorations-drop-target';
30
30
  import { getActiveDropTargetDecorations } from './decorations-drop-target-active';
31
- import { findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
31
+ import { createActiveQuickInsertNodeDecoration, findActiveQuickInsertNodeDec, findQuickInsertInsertButtonDecoration, quickInsertButtonDecoration } from './decorations-quick-insert-button';
32
32
  import { handleMouseDown } from './handle-mouse-down';
33
33
  import { handleMouseOver } from './handle-mouse-over';
34
34
  import { boundKeydownHandler } from './keymap';
@@ -438,21 +438,32 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
438
438
  }
439
439
  }
440
440
  if (shouldRemoveHandle) {
441
- var _activeNode5, _activeNode6;
441
+ var _activeNode5, _activeNode6, _activeNode7;
442
442
  var oldHandle = findHandleDec(decorations, (_activeNode5 = activeNode) === null || _activeNode5 === void 0 ? void 0 : _activeNode5.pos, (_activeNode6 = activeNode) === null || _activeNode6 === void 0 ? void 0 : _activeNode6.pos);
443
443
  decorations = decorations.remove(oldHandle);
444
+ // When removing the handle, also remove the anchor-marker node decorations
445
+ // (data-active-drag-handle / data-active-quick-insert) so the DOM attributes
446
+ // don't linger on nodes that are no longer active.
447
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && ((_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.pos) !== undefined) {
448
+ var oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, activeNode.pos, activeNode.pos);
449
+ decorations = decorations.remove(oldActiveNodeDec);
450
+ if (activeNode.rootPos !== undefined) {
451
+ var oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, activeNode.rootPos, activeNode.rootPos);
452
+ decorations = decorations.remove(oldActiveQuickInsertDec);
453
+ }
454
+ }
444
455
  // platform_editor_controls note: enables quick insert
445
456
  if (flags.toolbarFlagsEnabled && quickInsertButtonEnabled) {
446
- var _activeNode7, _activeNode8;
447
- var oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode7 = activeNode) === null || _activeNode7 === void 0 ? void 0 : _activeNode7.rootPos, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos);
457
+ var _activeNode8, _activeNode9;
458
+ var oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode8 = activeNode) === null || _activeNode8 === void 0 ? void 0 : _activeNode8.rootPos, (_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos);
448
459
  decorations = decorations.remove(oldQuickInsertButton);
449
460
  var _iterator = _createForOfIteratorHelper(nodeDecorationRegistry),
450
461
  _step;
451
462
  try {
452
463
  var _loop2 = function _loop2() {
453
- var _activeNode1, _activeNode10;
464
+ var _activeNode10, _activeNode11;
454
465
  var factory = _step.value;
455
- var old = decorations.find((_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, (_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, function (spec) {
466
+ var old = decorations.find((_activeNode10 = activeNode) === null || _activeNode10 === void 0 ? void 0 : _activeNode10.rootPos, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.rootPos, function (spec) {
456
467
  return spec.type === factory.type;
457
468
  });
458
469
  decorations = decorations.remove(old);
@@ -472,8 +483,8 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
472
483
  var _loop = function _loop() {
473
484
  var factory = _step2.value;
474
485
  if (factory.showInViewMode) {
475
- var _activeNode9, _activeNode0;
476
- var old = decorations.find((_activeNode9 = activeNode) === null || _activeNode9 === void 0 ? void 0 : _activeNode9.rootPos, (_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, function (spec) {
486
+ var _activeNode0, _activeNode1;
487
+ var old = decorations.find((_activeNode0 = activeNode) === null || _activeNode0 === void 0 ? void 0 : _activeNode0.rootPos, (_activeNode1 = activeNode) === null || _activeNode1 === void 0 ? void 0 : _activeNode1.rootPos, function (spec) {
477
488
  return spec.type === factory.type;
478
489
  });
479
490
  decorations = decorations.remove(old);
@@ -490,10 +501,10 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
490
501
  }
491
502
  }
492
503
  } else if (api) {
493
- var _latestActiveNode5, _latestActiveNode1;
504
+ var _latestActiveNode5, _latestActiveNode10;
494
505
  if (shouldRecreateHandle && (!rightSideControlsEnabled || !isViewMode)) {
495
- var _activeNode11, _activeNode12, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
496
- var _oldHandle = findHandleDec(decorations, (_activeNode11 = activeNode) === null || _activeNode11 === void 0 ? void 0 : _activeNode11.pos, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos);
506
+ var _activeNode12, _activeNode13, _latestActiveNode, _latestActiveNode2, _latestActiveNode3, _latestActiveNode4;
507
+ var _oldHandle = findHandleDec(decorations, (_activeNode12 = activeNode) === null || _activeNode12 === void 0 ? void 0 : _activeNode12.pos, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.pos);
497
508
  decorations = decorations.remove(_oldHandle);
498
509
  var handleDec = dragHandleDecoration({
499
510
  api: api,
@@ -507,12 +518,44 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
507
518
  editorState: newState
508
519
  });
509
520
  decorations = decorations.add(newState.doc, [handleDec]);
521
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true)) {
522
+ // Recreate the drag handle node decoration when the edit-mode node itself changed
523
+ // or its content was modified. Other triggers (editorSizeChanged, handleNeedsRedraw)
524
+ // don't move the decoration — DecorationSet.map() already remaps it correctly.
525
+ var needsNodeDecUpdate = activeNodeChanged || isActiveNodeModified;
526
+ if (needsNodeDecUpdate) {
527
+ var _newState$doc$nodeAt;
528
+ var nodeSize = (_newState$doc$nodeAt = newState.doc.nodeAt(latestActiveNode.pos)) === null || _newState$doc$nodeAt === void 0 ? void 0 : _newState$doc$nodeAt.nodeSize;
529
+ if (nodeSize !== undefined) {
530
+ var _activeNode14, _activeNode15;
531
+ var _oldActiveNodeDec = findActiveDragHandleNodeDec(decorations, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.pos, (_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.pos);
532
+ decorations = decorations.remove(_oldActiveNodeDec);
533
+ decorations = decorations.add(newState.doc, [createActiveDragHandleNodeDecoration(latestActiveNode.pos, nodeSize)]);
534
+ }
535
+ }
536
+
537
+ // The quick-insert decoration lives on the root node (rootPos), which can change
538
+ // independently of the edit-mode node — e.g. when only editorSizeChanged fires but
539
+ // rootActiveNodeChanged is also true. So we use a separate, broader guard that
540
+ // includes rootActiveNodeChanged to avoid leaving the attribute on a stale root node.
541
+ var needsQuickInsertDecUpdate = activeNodeChanged || isActiveNodeModified || rootActiveNodeChanged;
542
+ if (needsQuickInsertDecUpdate && latestActiveNode.rootPos !== undefined) {
543
+ var _newState$doc$nodeAt2;
544
+ var rootNodeSize = (_newState$doc$nodeAt2 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt2 === void 0 ? void 0 : _newState$doc$nodeAt2.nodeSize;
545
+ if (rootNodeSize !== undefined) {
546
+ var _activeNode16, _activeNode17;
547
+ var _oldActiveQuickInsertDec = findActiveQuickInsertNodeDec(decorations, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, (_activeNode17 = activeNode) === null || _activeNode17 === void 0 ? void 0 : _activeNode17.rootPos);
548
+ decorations = decorations.remove(_oldActiveQuickInsertDec);
549
+ decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, rootNodeSize)]);
550
+ }
551
+ }
552
+ }
510
553
  }
511
554
  if (shouldRecreateQuickInsertButton && ((_latestActiveNode5 = latestActiveNode) === null || _latestActiveNode5 === void 0 ? void 0 : _latestActiveNode5.rootPos) !== undefined &&
512
555
  // platform_editor_controls note: enables quick insert
513
556
  flags.toolbarFlagsEnabled && quickInsertButtonEnabled && (!rightSideControlsEnabled || !isViewMode)) {
514
- var _activeNode13, _activeNode14, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0;
515
- var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode13 = activeNode) === null || _activeNode13 === void 0 ? void 0 : _activeNode13.rootPos, (_activeNode14 = activeNode) === null || _activeNode14 === void 0 ? void 0 : _activeNode14.rootPos);
557
+ var _activeNode18, _activeNode19, _latestActiveNode6, _latestActiveNode7, _latestActiveNode8, _latestActiveNode9, _latestActiveNode0, _latestActiveNode1;
558
+ var _oldQuickInsertButton = findQuickInsertInsertButtonDecoration(decorations, (_activeNode18 = activeNode) === null || _activeNode18 === void 0 ? void 0 : _activeNode18.rootPos, (_activeNode19 = activeNode) === null || _activeNode19 === void 0 ? void 0 : _activeNode19.rootPos);
516
559
  decorations = decorations.remove(_oldQuickInsertButton);
517
560
  var quickInsertButton = quickInsertButtonDecoration({
518
561
  api: api,
@@ -527,12 +570,27 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
527
570
  editorState: newState
528
571
  });
529
572
  decorations = decorations.add(newState.doc, [quickInsertButton]);
573
+
574
+ // Update quick insert node decoration when the quick insert button is recreated but
575
+ // the drag handle was NOT recreated (shouldRecreateHandle was false). When
576
+ // shouldRecreateHandle is true, the block above already handles this.
577
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !shouldRecreateHandle && ((_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos) !== undefined) {
578
+ var _activeNode20, _newState$doc$nodeAt3;
579
+ if (((_activeNode20 = activeNode) === null || _activeNode20 === void 0 ? void 0 : _activeNode20.rootPos) !== undefined) {
580
+ var _oldActiveQuickInsertDec2 = findActiveQuickInsertNodeDec(decorations, activeNode.rootPos, activeNode.rootPos);
581
+ decorations = decorations.remove(_oldActiveQuickInsertDec2);
582
+ }
583
+ var _rootNodeSize = (_newState$doc$nodeAt3 = newState.doc.nodeAt(latestActiveNode.rootPos)) === null || _newState$doc$nodeAt3 === void 0 ? void 0 : _newState$doc$nodeAt3.nodeSize;
584
+ if (_rootNodeSize !== undefined) {
585
+ decorations = decorations.add(newState.doc, [createActiveQuickInsertNodeDecoration(latestActiveNode.rootPos, _rootNodeSize)]);
586
+ }
587
+ }
530
588
  if (rightSideControlsEnabled) {
531
589
  var _iterator3 = _createForOfIteratorHelper(nodeDecorationRegistry),
532
590
  _step3;
533
591
  try {
534
592
  var _loop3 = function _loop3() {
535
- var _activeNode15, _activeNode16;
593
+ var _activeNode21, _activeNode22;
536
594
  var factory = _step3.value;
537
595
  if (!latestActiveNode || latestActiveNode.rootPos === undefined) {
538
596
  return 0; // continue
@@ -546,7 +604,7 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
546
604
  rootAnchorName: latestActiveNode.rootAnchorName,
547
605
  rootNodeType: latestActiveNode.rootNodeType
548
606
  };
549
- var old = decorations.find((_activeNode15 = activeNode) === null || _activeNode15 === void 0 ? void 0 : _activeNode15.rootPos, (_activeNode16 = activeNode) === null || _activeNode16 === void 0 ? void 0 : _activeNode16.rootPos, function (spec) {
607
+ var old = decorations.find((_activeNode21 = activeNode) === null || _activeNode21 === void 0 ? void 0 : _activeNode21.rootPos, (_activeNode22 = activeNode) === null || _activeNode22 === void 0 ? void 0 : _activeNode22.rootPos, function (spec) {
550
608
  return spec.type === factory.type;
551
609
  });
552
610
  decorations = decorations.remove(old);
@@ -591,7 +649,7 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
591
649
  }
592
650
 
593
651
  // In view mode (edit/live pages), show right-side controls on block hover (without drag handle or quick insert)
594
- var rootPos = (_latestActiveNode1 = latestActiveNode) === null || _latestActiveNode1 === void 0 ? void 0 : _latestActiveNode1.rootPos;
652
+ var rootPos = (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.rootPos;
595
653
  // rootPos is computed using the same logic as the floating insert menu, so it always points to a top-level (doc child) block when defined.
596
654
  var isDocLevel = rootPos !== undefined && !isNaN(rootPos);
597
655
  if (isViewMode && isDocLevel && flags.toolbarFlagsEnabled && rightSideControlsEnabled) {
@@ -712,15 +770,15 @@ var _apply = function apply(api, formatMessage, tr, currentState, newState, flag
712
770
  var newActiveNode;
713
771
  // platform_editor_controls note: enables quick insert
714
772
  if (flags.toolbarFlagsEnabled) {
715
- var _latestActiveNode10, _latestActiveNode11;
773
+ var _latestActiveNode11, _latestActiveNode12;
716
774
  // remove isEmptyDoc check and let decorations render and determine their own visibility
717
775
  // In view mode with right-side controls we render node decorations (right-edge button), not the
718
776
  // handle - so findHandleDec is always empty. Don't clear activeNode in that case.
719
- var hasHandleOrViewModeControls = findHandleDec(decorations, (_latestActiveNode10 = latestActiveNode) === null || _latestActiveNode10 === void 0 ? void 0 : _latestActiveNode10.pos, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos).length > 0 || isViewMode && rightSideControlsEnabled;
777
+ var hasHandleOrViewModeControls = findHandleDec(decorations, (_latestActiveNode11 = latestActiveNode) === null || _latestActiveNode11 === void 0 ? void 0 : _latestActiveNode11.pos, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos).length > 0 || isViewMode && rightSideControlsEnabled;
720
778
  newActiveNode = meta !== null && meta !== void 0 && meta.editorBlurred || !(meta !== null && meta !== void 0 && meta.activeNode) && !hasHandleOrViewModeControls ? null : latestActiveNode;
721
779
  } else {
722
- var _latestActiveNode12, _latestActiveNode13;
723
- newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode12 = latestActiveNode) === null || _latestActiveNode12 === void 0 ? void 0 : _latestActiveNode12.pos, (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.pos).length === 0 ? null : latestActiveNode;
780
+ var _latestActiveNode13, _latestActiveNode14;
781
+ newActiveNode = isEmptyDoc || !(meta !== null && meta !== void 0 && meta.activeNode) && findHandleDec(decorations, (_latestActiveNode13 = latestActiveNode) === null || _latestActiveNode13 === void 0 ? void 0 : _latestActiveNode13.pos, (_latestActiveNode14 = latestActiveNode) === null || _latestActiveNode14 === void 0 ? void 0 : _latestActiveNode14.pos).length === 0 ? null : latestActiveNode;
724
782
  }
725
783
  var isMenuOpenNew = isMenuOpen;
726
784
  if (editorExperiment('platform_editor_block_menu', true)) {
@@ -5,6 +5,8 @@ import { breakoutResizableNodes as breakoutResizableNodesNew } from '@atlaskit/e
5
5
  import { akEditorUnitZIndex, akRichMediaResizeZIndex } from '@atlaskit/editor-shared-styles';
6
6
  import { fg } from '@atlaskit/platform-feature-flags';
7
7
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
8
+ export var ACTIVE_DRAG_HANDLE_ATTR = 'data-active-drag-handle';
9
+ export var ACTIVE_QUICK_INSERT_ATTR = 'data-active-quick-insert';
8
10
  export var DRAG_HANDLE_HEIGHT = 24;
9
11
  export var DRAG_HANDLE_BORDER_RADIUS = 4;
10
12
  export var DRAG_HANDLE_ZINDEX = akRichMediaResizeZIndex + akEditorUnitZIndex; //place above legacy resizer
@@ -40,7 +40,7 @@ import { getControlBottomCSSValue, getControlHeightCSSValue, getLeftPosition, ge
40
40
  import { expandAndUpdateSelection } from '../pm-plugins/utils/expand-and-update-selection';
41
41
  import { isHandleCorrelatedToSelection, selectNode } from '../pm-plugins/utils/getSelection';
42
42
  import { alignAnchorHeadInDirectionOfPos, expandSelectionHeadToNodeAtPos } from '../pm-plugins/utils/selection';
43
- import { DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
43
+ import { ACTIVE_DRAG_HANDLE_ATTR, DRAG_HANDLE_BORDER_RADIUS, DRAG_HANDLE_HEIGHT, DRAG_HANDLE_MAX_SHIFT_CLICK_DEPTH, DRAG_HANDLE_ZINDEX, dragHandleGap, nodeMargins, spacingBetweenNodesForPreview, STICKY_CONTROLS_TOP_MARGIN, STICKY_CONTROLS_TOP_MARGIN_FOR_STICKY_HEADER, topPositionAdjustment } from './consts';
44
44
  import { DragHandleNestedIcon } from './drag-handle-nested-icon';
45
45
  import { dragPreview } from './drag-preview';
46
46
  import { refreshAnchorName } from './utils/anchor-name';
@@ -823,6 +823,15 @@ export var DragHandle = function DragHandle(_ref) {
823
823
  anchorName: anchorName
824
824
  }) : anchorName;
825
825
  var dom = view.dom.querySelector("[".concat(getAnchorAttrName(), "=\"").concat(safeAnchorName, "\"]"));
826
+
827
+ // Defence-in-depth guard: since the node decoration sets data-active-drag-handle on the
828
+ // active node, we check for it directly. This is a cheap DOM attribute read (no reflow,
829
+ // no style recalculation) and hides the control if the decoration hasn't been applied yet.
830
+ if (expValEquals('platform_editor_controls_reliable_anchor', 'isEnabled', true) && !(dom !== null && dom !== void 0 && dom.hasAttribute(ACTIVE_DRAG_HANDLE_ATTR))) {
831
+ return {
832
+ display: 'none'
833
+ };
834
+ }
826
835
  var hasResizer = nodeType === 'table' || nodeType === 'mediaSingle';
827
836
  var isExtension = nodeType === 'extension' || nodeType === 'bodiedExtension' || nodeType === 'multiBodiedExtension' && fg('confluence_frontend_native_tabs_extension');
828
837
  var isBlockCard = nodeType === 'blockCard' && !!blockCardWidth;