dockview-core 4.6.2 → 4.7.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 (58) hide show
  1. package/dist/cjs/constants.d.ts +1 -0
  2. package/dist/cjs/constants.js +2 -1
  3. package/dist/cjs/dnd/abstractDragHandler.d.ts +3 -1
  4. package/dist/cjs/dnd/abstractDragHandler.js +6 -2
  5. package/dist/cjs/dnd/droptarget.js +46 -17
  6. package/dist/cjs/dnd/groupDragHandler.d.ts +1 -1
  7. package/dist/cjs/dnd/groupDragHandler.js +2 -2
  8. package/dist/cjs/dockview/components/tab/tab.d.ts +1 -0
  9. package/dist/cjs/dockview/components/tab/tab.js +6 -5
  10. package/dist/cjs/dockview/components/titlebar/voidContainer.d.ts +1 -0
  11. package/dist/cjs/dockview/components/titlebar/voidContainer.js +3 -2
  12. package/dist/cjs/dockview/dockviewComponent.d.ts +6 -0
  13. package/dist/cjs/dockview/dockviewComponent.js +115 -86
  14. package/dist/cjs/gridview/gridview.d.ts +1 -0
  15. package/dist/cjs/gridview/gridview.js +37 -0
  16. package/dist/cjs/overlay/overlayRenderContainer.d.ts +3 -0
  17. package/dist/cjs/overlay/overlayRenderContainer.js +82 -8
  18. package/dist/dockview-core.amd.js +235 -67
  19. package/dist/dockview-core.amd.js.map +1 -1
  20. package/dist/dockview-core.amd.min.js +2 -2
  21. package/dist/dockview-core.amd.min.js.map +1 -1
  22. package/dist/dockview-core.amd.min.noStyle.js +2 -2
  23. package/dist/dockview-core.amd.min.noStyle.js.map +1 -1
  24. package/dist/dockview-core.amd.noStyle.js +234 -66
  25. package/dist/dockview-core.amd.noStyle.js.map +1 -1
  26. package/dist/dockview-core.cjs.js +235 -67
  27. package/dist/dockview-core.cjs.js.map +1 -1
  28. package/dist/dockview-core.esm.js +235 -67
  29. package/dist/dockview-core.esm.js.map +1 -1
  30. package/dist/dockview-core.esm.min.js +2 -2
  31. package/dist/dockview-core.esm.min.js.map +1 -1
  32. package/dist/dockview-core.js +235 -67
  33. package/dist/dockview-core.js.map +1 -1
  34. package/dist/dockview-core.min.js +2 -2
  35. package/dist/dockview-core.min.js.map +1 -1
  36. package/dist/dockview-core.min.noStyle.js +2 -2
  37. package/dist/dockview-core.min.noStyle.js.map +1 -1
  38. package/dist/dockview-core.noStyle.js +234 -66
  39. package/dist/dockview-core.noStyle.js.map +1 -1
  40. package/dist/esm/constants.d.ts +1 -0
  41. package/dist/esm/constants.js +1 -0
  42. package/dist/esm/dnd/abstractDragHandler.d.ts +3 -1
  43. package/dist/esm/dnd/abstractDragHandler.js +6 -2
  44. package/dist/esm/dnd/droptarget.js +46 -17
  45. package/dist/esm/dnd/groupDragHandler.d.ts +1 -1
  46. package/dist/esm/dnd/groupDragHandler.js +2 -2
  47. package/dist/esm/dockview/components/tab/tab.d.ts +1 -0
  48. package/dist/esm/dockview/components/tab/tab.js +6 -5
  49. package/dist/esm/dockview/components/titlebar/voidContainer.d.ts +1 -0
  50. package/dist/esm/dockview/components/titlebar/voidContainer.js +3 -2
  51. package/dist/esm/dockview/dockviewComponent.d.ts +6 -0
  52. package/dist/esm/dockview/dockviewComponent.js +65 -30
  53. package/dist/esm/gridview/gridview.d.ts +1 -0
  54. package/dist/esm/gridview/gridview.js +36 -0
  55. package/dist/esm/overlay/overlayRenderContainer.d.ts +3 -0
  56. package/dist/esm/overlay/overlayRenderContainer.js +69 -8
  57. package/dist/styles/dockview.css +37 -5
  58. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-core
3
- * @version 4.6.2
3
+ * @version 4.7.1
4
4
  * @link https://github.com/mathuo/dockview
5
5
  * @license MIT
6
6
  */
@@ -2139,6 +2139,19 @@
2139
2139
  }
2140
2140
  throw new Error('invalid node');
2141
2141
  }
2142
+ function cloneNode(node, size, orthogonalSize) {
2143
+ if (node instanceof BranchNode) {
2144
+ const result = new BranchNode(node.orientation, node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
2145
+ for (let i = node.children.length - 1; i >= 0; i--) {
2146
+ const child = node.children[i];
2147
+ result.addChild(cloneNode(child, child.size, child.orthogonalSize), child.size, 0, true);
2148
+ }
2149
+ return result;
2150
+ }
2151
+ else {
2152
+ return new LeafNode(node.view, node.orientation, orthogonalSize);
2153
+ }
2154
+ }
2142
2155
  function flipNode(node, size, orthogonalSize) {
2143
2156
  if (node instanceof BranchNode) {
2144
2157
  const result = new BranchNode(orthogonal(node.orientation), node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
@@ -2489,6 +2502,29 @@
2489
2502
  this._onDidChange.fire(e);
2490
2503
  });
2491
2504
  }
2505
+ normalize() {
2506
+ if (!this._root) {
2507
+ return;
2508
+ }
2509
+ if (this._root.children.length !== 1) {
2510
+ return;
2511
+ }
2512
+ const oldRoot = this.root;
2513
+ // can remove one level of redundant branching if there is only a single child
2514
+ const childReference = oldRoot.children[0];
2515
+ if (childReference instanceof LeafNode) {
2516
+ return;
2517
+ }
2518
+ oldRoot.element.remove();
2519
+ const child = oldRoot.removeChild(0); // Remove child to prevent double disposal
2520
+ oldRoot.dispose(); // Dispose old root (won't dispose removed child)
2521
+ child.dispose(); // Dispose the removed child
2522
+ this._root = cloneNode(childReference, childReference.size, childReference.orthogonalSize);
2523
+ this.element.appendChild(this._root.element);
2524
+ this.disposable.value = this._root.onDidChange((e) => {
2525
+ this._onDidChange.fire(e);
2526
+ });
2527
+ }
2492
2528
  /**
2493
2529
  * If the root is orientated as a VERTICAL node then nest the existing root within a new HORIZIONTAL root node
2494
2530
  * If the root is orientated as a HORIZONTAL node then nest the existing root within a new VERITCAL root node
@@ -3776,9 +3812,10 @@
3776
3812
  }
3777
3813
 
3778
3814
  class DragHandler extends CompositeDisposable {
3779
- constructor(el) {
3815
+ constructor(el, disabled) {
3780
3816
  super();
3781
3817
  this.el = el;
3818
+ this.disabled = disabled;
3782
3819
  this.dataDisposable = new MutableDisposable();
3783
3820
  this.pointerEventsDisposable = new MutableDisposable();
3784
3821
  this._onDragStart = new Emitter();
@@ -3786,12 +3823,15 @@
3786
3823
  this.addDisposables(this._onDragStart, this.dataDisposable, this.pointerEventsDisposable);
3787
3824
  this.configure();
3788
3825
  }
3826
+ setDisabled(disabled) {
3827
+ this.disabled = disabled;
3828
+ }
3789
3829
  isCancelled(_event) {
3790
3830
  return false;
3791
3831
  }
3792
3832
  configure() {
3793
3833
  this.addDisposables(this._onDragStart, addDisposableListener(this.el, 'dragstart', (event) => {
3794
- if (event.defaultPrevented || this.isCancelled(event)) {
3834
+ if (event.defaultPrevented || this.isCancelled(event) || this.disabled) {
3795
3835
  event.preventDefault();
3796
3836
  return;
3797
3837
  }
@@ -3880,6 +3920,48 @@
3880
3920
  }
3881
3921
  }
3882
3922
 
3923
+ function setGPUOptimizedBounds(element, bounds) {
3924
+ const { top, left, width, height } = bounds;
3925
+ const topPx = `${Math.round(top)}px`;
3926
+ const leftPx = `${Math.round(left)}px`;
3927
+ const widthPx = `${Math.round(width)}px`;
3928
+ const heightPx = `${Math.round(height)}px`;
3929
+ // Use traditional positioning but maintain GPU layer
3930
+ element.style.top = topPx;
3931
+ element.style.left = leftPx;
3932
+ element.style.width = widthPx;
3933
+ element.style.height = heightPx;
3934
+ element.style.visibility = 'visible';
3935
+ // Ensure GPU layer is maintained
3936
+ if (!element.style.transform || element.style.transform === '') {
3937
+ element.style.transform = 'translate3d(0, 0, 0)';
3938
+ }
3939
+ }
3940
+ function setGPUOptimizedBoundsFromStrings(element, bounds) {
3941
+ const { top, left, width, height } = bounds;
3942
+ // Use traditional positioning but maintain GPU layer
3943
+ element.style.top = top;
3944
+ element.style.left = left;
3945
+ element.style.width = width;
3946
+ element.style.height = height;
3947
+ element.style.visibility = 'visible';
3948
+ // Ensure GPU layer is maintained
3949
+ if (!element.style.transform || element.style.transform === '') {
3950
+ element.style.transform = 'translate3d(0, 0, 0)';
3951
+ }
3952
+ }
3953
+ function checkBoundsChanged(element, bounds) {
3954
+ const { top, left, width, height } = bounds;
3955
+ const topPx = `${Math.round(top)}px`;
3956
+ const leftPx = `${Math.round(left)}px`;
3957
+ const widthPx = `${Math.round(width)}px`;
3958
+ const heightPx = `${Math.round(height)}px`;
3959
+ // Check if position or size changed (back to traditional method)
3960
+ return element.style.top !== topPx ||
3961
+ element.style.left !== leftPx ||
3962
+ element.style.width !== widthPx ||
3963
+ element.style.height !== heightPx;
3964
+ }
3883
3965
  class WillShowOverlayEvent extends DockviewEvent {
3884
3966
  get nativeEvent() {
3885
3967
  return this.options.nativeEvent;
@@ -4161,21 +4243,11 @@
4161
4243
  box.left = rootLeft + width - 4;
4162
4244
  box.width = 4;
4163
4245
  }
4164
- const topPx = `${Math.round(box.top)}px`;
4165
- const leftPx = `${Math.round(box.left)}px`;
4166
- const widthPx = `${Math.round(box.width)}px`;
4167
- const heightPx = `${Math.round(box.height)}px`;
4168
- if (overlay.style.top === topPx &&
4169
- overlay.style.left === leftPx &&
4170
- overlay.style.width === widthPx &&
4171
- overlay.style.height === heightPx) {
4246
+ // Use GPU-optimized bounds checking and setting
4247
+ if (!checkBoundsChanged(overlay, box)) {
4172
4248
  return;
4173
4249
  }
4174
- overlay.style.top = topPx;
4175
- overlay.style.left = leftPx;
4176
- overlay.style.width = widthPx;
4177
- overlay.style.height = heightPx;
4178
- overlay.style.visibility = 'visible';
4250
+ setGPUOptimizedBounds(overlay, box);
4179
4251
  overlay.className = `dv-drop-target-anchor${this.options.className ? ` ${this.options.className}` : ''}`;
4180
4252
  toggleClass(overlay, 'dv-drop-target-left', isLeft);
4181
4253
  toggleClass(overlay, 'dv-drop-target-right', isRight);
@@ -4227,10 +4299,7 @@
4227
4299
  box.top = `${100 * (1 - size)}%`;
4228
4300
  box.height = `${100 * size}%`;
4229
4301
  }
4230
- this.overlayElement.style.top = box.top;
4231
- this.overlayElement.style.left = box.left;
4232
- this.overlayElement.style.width = box.width;
4233
- this.overlayElement.style.height = box.height;
4302
+ setGPUOptimizedBoundsFromStrings(this.overlayElement, box);
4234
4303
  toggleClass(this.overlayElement, 'dv-drop-target-small-vertical', isSmallY);
4235
4304
  toggleClass(this.overlayElement, 'dv-drop-target-small-horizontal', isSmallX);
4236
4305
  toggleClass(this.overlayElement, 'dv-drop-target-left', isLeft);
@@ -4962,8 +5031,8 @@
4962
5031
  }
4963
5032
 
4964
5033
  class TabDragHandler extends DragHandler {
4965
- constructor(element, accessor, group, panel) {
4966
- super(element);
5034
+ constructor(element, accessor, group, panel, disabled) {
5035
+ super(element, disabled);
4967
5036
  this.accessor = accessor;
4968
5037
  this.group = group;
4969
5038
  this.panel = panel;
@@ -4999,7 +5068,7 @@
4999
5068
  this._element.tabIndex = 0;
5000
5069
  this._element.draggable = !this.accessor.options.disableDnd;
5001
5070
  toggleClass(this.element, 'dv-inactive-tab', true);
5002
- const dragHandler = new TabDragHandler(this._element, this.accessor, this.group, this.panel);
5071
+ this.dragHandler = new TabDragHandler(this._element, this.accessor, this.group, this.panel, !!this.accessor.options.disableDnd);
5003
5072
  this.dropTarget = new Droptarget(this._element, {
5004
5073
  acceptedTargetZones: ['left', 'right'],
5005
5074
  overlayModel: { activationSize: { value: 50, type: 'percentage' } },
@@ -5016,7 +5085,7 @@
5016
5085
  getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
5017
5086
  });
5018
5087
  this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
5019
- this.addDisposables(this._onPointDown, this._onDropped, this._onDragStart, dragHandler.onDragStart((event) => {
5088
+ this.addDisposables(this._onPointDown, this._onDropped, this._onDragStart, this.dragHandler.onDragStart((event) => {
5020
5089
  if (event.dataTransfer) {
5021
5090
  const style = getComputedStyle(this.element);
5022
5091
  const newNode = this.element.cloneNode(true);
@@ -5028,7 +5097,7 @@
5028
5097
  });
5029
5098
  }
5030
5099
  this._onDragStart.fire(event);
5031
- }), dragHandler, addDisposableListener(this._element, 'pointerdown', (event) => {
5100
+ }), this.dragHandler, addDisposableListener(this._element, 'pointerdown', (event) => {
5032
5101
  this._onPointDown.fire(event);
5033
5102
  }), this.dropTarget.onDrop((event) => {
5034
5103
  this._onDropped.fire(event);
@@ -5047,6 +5116,7 @@
5047
5116
  }
5048
5117
  updateDragAndDropState() {
5049
5118
  this._element.draggable = !this.accessor.options.disableDnd;
5119
+ this.dragHandler.setDisabled(!!this.accessor.options.disableDnd);
5050
5120
  }
5051
5121
  dispose() {
5052
5122
  super.dispose();
@@ -5088,8 +5158,8 @@
5088
5158
  }
5089
5159
 
5090
5160
  class GroupDragHandler extends DragHandler {
5091
- constructor(element, accessor, group) {
5092
- super(element);
5161
+ constructor(element, accessor, group, disabled) {
5162
+ super(element, disabled);
5093
5163
  this.accessor = accessor;
5094
5164
  this.group = group;
5095
5165
  this.panelTransfer = LocalSelectionTransfer.getInstance();
@@ -5158,7 +5228,7 @@
5158
5228
  this.addDisposables(this._onDrop, this._onDragStart, addDisposableListener(this._element, 'pointerdown', () => {
5159
5229
  this.accessor.doSetGroupActive(this.group);
5160
5230
  }));
5161
- const handler = new GroupDragHandler(this._element, accessor, group);
5231
+ this.handler = new GroupDragHandler(this._element, accessor, group, !!this.accessor.options.disableDnd);
5162
5232
  this.dropTarget = new Droptarget(this._element, {
5163
5233
  acceptedTargetZones: ['center'],
5164
5234
  canDisplayOverlay: (event, position) => {
@@ -5171,7 +5241,7 @@
5171
5241
  getOverrideTarget: () => { var _a; return (_a = group.model.dropTargetContainer) === null || _a === void 0 ? void 0 : _a.model; },
5172
5242
  });
5173
5243
  this.onWillShowOverlay = this.dropTarget.onWillShowOverlay;
5174
- this.addDisposables(handler, handler.onDragStart((event) => {
5244
+ this.addDisposables(this.handler, this.handler.onDragStart((event) => {
5175
5245
  this._onDragStart.fire(event);
5176
5246
  }), this.dropTarget.onDrop((event) => {
5177
5247
  this._onDrop.fire(event);
@@ -5180,6 +5250,7 @@
5180
5250
  updateDragAndDropState() {
5181
5251
  this._element.draggable = !this.accessor.options.disableDnd;
5182
5252
  toggleClass(this._element, 'dv-draggable', !this.accessor.options.disableDnd);
5253
+ this.handler.setDisabled(!!this.accessor.options.disableDnd);
5183
5254
  }
5184
5255
  }
5185
5256
 
@@ -7673,7 +7744,36 @@
7673
7744
 
7674
7745
  const DEFAULT_FLOATING_GROUP_OVERFLOW_SIZE = 100;
7675
7746
  const DEFAULT_FLOATING_GROUP_POSITION = { left: 100, top: 100, width: 300, height: 300 };
7747
+ const DESERIALIZATION_POPOUT_DELAY_MS = 100;
7676
7748
 
7749
+ class PositionCache {
7750
+ constructor() {
7751
+ this.cache = new Map();
7752
+ this.currentFrameId = 0;
7753
+ this.rafId = null;
7754
+ }
7755
+ getPosition(element) {
7756
+ const cached = this.cache.get(element);
7757
+ if (cached && cached.frameId === this.currentFrameId) {
7758
+ return cached.rect;
7759
+ }
7760
+ this.scheduleFrameUpdate();
7761
+ const rect = getDomNodePagePosition(element);
7762
+ this.cache.set(element, { rect, frameId: this.currentFrameId });
7763
+ return rect;
7764
+ }
7765
+ invalidate() {
7766
+ this.currentFrameId++;
7767
+ }
7768
+ scheduleFrameUpdate() {
7769
+ if (this.rafId)
7770
+ return;
7771
+ this.rafId = requestAnimationFrame(() => {
7772
+ this.currentFrameId++;
7773
+ this.rafId = null;
7774
+ });
7775
+ }
7776
+ }
7677
7777
  function createFocusableElement() {
7678
7778
  const element = document.createElement('div');
7679
7779
  element.tabIndex = -1;
@@ -7686,6 +7786,8 @@
7686
7786
  this.accessor = accessor;
7687
7787
  this.map = {};
7688
7788
  this._disposed = false;
7789
+ this.positionCache = new PositionCache();
7790
+ this.pendingUpdates = new Set();
7689
7791
  this.addDisposables(exports.DockviewDisposable.from(() => {
7690
7792
  for (const value of Object.values(this.map)) {
7691
7793
  value.disposable.dispose();
@@ -7694,6 +7796,19 @@
7694
7796
  this._disposed = true;
7695
7797
  }));
7696
7798
  }
7799
+ updateAllPositions() {
7800
+ if (this._disposed) {
7801
+ return;
7802
+ }
7803
+ // Invalidate position cache to force recalculation
7804
+ this.positionCache.invalidate();
7805
+ // Call resize function directly for all visible panels
7806
+ for (const entry of Object.values(this.map)) {
7807
+ if (entry.panel.api.isVisible && entry.resize) {
7808
+ entry.resize();
7809
+ }
7810
+ }
7811
+ }
7697
7812
  detatch(panel) {
7698
7813
  if (this.map[panel.api.id]) {
7699
7814
  const { disposable, destroy } = this.map[panel.api.id];
@@ -7724,17 +7839,33 @@
7724
7839
  this.element.appendChild(focusContainer);
7725
7840
  }
7726
7841
  const resize = () => {
7727
- // TODO propagate position to avoid getDomNodePagePosition calls, possible performance bottleneck?
7728
- const box = getDomNodePagePosition(referenceContainer.element);
7729
- const box2 = getDomNodePagePosition(this.element);
7730
- focusContainer.style.left = `${box.left - box2.left}px`;
7731
- focusContainer.style.top = `${box.top - box2.top}px`;
7732
- focusContainer.style.width = `${box.width}px`;
7733
- focusContainer.style.height = `${box.height}px`;
7734
- toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location.type === 'floating');
7842
+ const panelId = panel.api.id;
7843
+ if (this.pendingUpdates.has(panelId)) {
7844
+ return; // Update already scheduled
7845
+ }
7846
+ this.pendingUpdates.add(panelId);
7847
+ requestAnimationFrame(() => {
7848
+ this.pendingUpdates.delete(panelId);
7849
+ if (this.isDisposed || !this.map[panelId]) {
7850
+ return;
7851
+ }
7852
+ const box = this.positionCache.getPosition(referenceContainer.element);
7853
+ const box2 = this.positionCache.getPosition(this.element);
7854
+ // Use traditional positioning for overlay containers
7855
+ const left = box.left - box2.left;
7856
+ const top = box.top - box2.top;
7857
+ const width = box.width;
7858
+ const height = box.height;
7859
+ focusContainer.style.left = `${left}px`;
7860
+ focusContainer.style.top = `${top}px`;
7861
+ focusContainer.style.width = `${width}px`;
7862
+ focusContainer.style.height = `${height}px`;
7863
+ toggleClass(focusContainer, 'dv-render-overlay-float', panel.group.api.location.type === 'floating');
7864
+ });
7735
7865
  };
7736
7866
  const visibilityChanged = () => {
7737
7867
  if (panel.api.isVisible) {
7868
+ this.positionCache.invalidate();
7738
7869
  resize();
7739
7870
  }
7740
7871
  focusContainer.style.display = panel.api.isVisible ? '' : 'none';
@@ -7829,6 +7960,8 @@
7829
7960
  this.map[panel.api.id].disposable.dispose();
7830
7961
  // and reset the disposable to the active reference-container
7831
7962
  this.map[panel.api.id].disposable = disposable;
7963
+ // store the resize function for direct access
7964
+ this.map[panel.api.id].resize = resize;
7832
7965
  return focusContainer;
7833
7966
  }
7834
7967
  }
@@ -8221,6 +8354,13 @@
8221
8354
  get floatingGroups() {
8222
8355
  return this._floatingGroups;
8223
8356
  }
8357
+ /**
8358
+ * Promise that resolves when all popout groups from the last fromJSON call are restored.
8359
+ * Useful for tests that need to wait for delayed popout creation.
8360
+ */
8361
+ get popoutRestorationPromise() {
8362
+ return this._popoutRestorationPromise;
8363
+ }
8224
8364
  constructor(container, options) {
8225
8365
  var _a, _b, _c;
8226
8366
  super(container, {
@@ -8269,6 +8409,7 @@
8269
8409
  this.onDidMaximizedGroupChange = this._onDidMaximizedGroupChange.event;
8270
8410
  this._floatingGroups = [];
8271
8411
  this._popoutGroups = [];
8412
+ this._popoutRestorationPromise = Promise.resolve();
8272
8413
  this._onDidRemoveGroup = new Emitter();
8273
8414
  this.onDidRemoveGroup = this._onDidRemoveGroup.event;
8274
8415
  this._onDidAddGroup = new Emitter();
@@ -8818,6 +8959,7 @@
8818
8959
  this.updateWatermark();
8819
8960
  }
8820
8961
  orthogonalize(position, options) {
8962
+ this.gridview.normalize();
8821
8963
  switch (position) {
8822
8964
  case 'top':
8823
8965
  case 'bottom':
@@ -9061,18 +9203,30 @@
9061
9203
  });
9062
9204
  }
9063
9205
  const serializedPopoutGroups = (_b = data.popoutGroups) !== null && _b !== void 0 ? _b : [];
9064
- for (const serializedPopoutGroup of serializedPopoutGroups) {
9206
+ // Create a promise that resolves when all popout groups are created
9207
+ const popoutPromises = [];
9208
+ // Queue popup group creation with delays to avoid browser blocking
9209
+ serializedPopoutGroups.forEach((serializedPopoutGroup, index) => {
9065
9210
  const { data, position, gridReferenceGroup, url } = serializedPopoutGroup;
9066
9211
  const group = createGroupFromSerializedState(data);
9067
- this.addPopoutGroup(group, {
9068
- position: position !== null && position !== void 0 ? position : undefined,
9069
- overridePopoutGroup: gridReferenceGroup ? group : undefined,
9070
- referenceGroup: gridReferenceGroup
9071
- ? this.getPanel(gridReferenceGroup)
9072
- : undefined,
9073
- popoutUrl: url,
9212
+ // Add a small delay for each popup after the first to avoid browser popup blocking
9213
+ const popoutPromise = new Promise((resolve) => {
9214
+ setTimeout(() => {
9215
+ this.addPopoutGroup(group, {
9216
+ position: position !== null && position !== void 0 ? position : undefined,
9217
+ overridePopoutGroup: gridReferenceGroup ? group : undefined,
9218
+ referenceGroup: gridReferenceGroup
9219
+ ? this.getPanel(gridReferenceGroup)
9220
+ : undefined,
9221
+ popoutUrl: url,
9222
+ });
9223
+ resolve();
9224
+ }, index * DESERIALIZATION_POPOUT_DELAY_MS); // 100ms delay between each popup
9074
9225
  });
9075
- }
9226
+ popoutPromises.push(popoutPromise);
9227
+ });
9228
+ // Store the promise for tests to wait on
9229
+ this._popoutRestorationPromise = Promise.all(popoutPromises).then(() => void 0);
9076
9230
  for (const floatingGroup of this._floatingGroups) {
9077
9231
  floatingGroup.overlay.setBounds();
9078
9232
  }
@@ -9119,6 +9273,10 @@
9119
9273
  throw err;
9120
9274
  }
9121
9275
  this.updateWatermark();
9276
+ // Force position updates for always visible panels after DOM layout is complete
9277
+ requestAnimationFrame(() => {
9278
+ this.overlayRenderContainer.updateAllPositions();
9279
+ });
9122
9280
  this._onDidLayoutFromJSON.fire();
9123
9281
  }
9124
9282
  clear() {
@@ -9506,11 +9664,13 @@
9506
9664
  // remove the group and do not set a new group as active
9507
9665
  this.doRemoveGroup(sourceGroup, { skipActive: true });
9508
9666
  }
9667
+ // Check if destination group is empty - if so, force render the component
9668
+ const isDestinationGroupEmpty = destinationGroup.model.size === 0;
9509
9669
  this.movingLock(() => {
9510
9670
  var _a;
9511
9671
  return destinationGroup.model.openPanel(removedPanel, {
9512
9672
  index: destinationIndex,
9513
- skipSetActive: (_a = options.skipSetActive) !== null && _a !== void 0 ? _a : false,
9673
+ skipSetActive: ((_a = options.skipSetActive) !== null && _a !== void 0 ? _a : false) && !isDestinationGroupEmpty,
9514
9674
  skipSetGroupActive: true,
9515
9675
  });
9516
9676
  });
@@ -9619,7 +9779,6 @@
9619
9779
  const target = options.to.position;
9620
9780
  if (target === 'center') {
9621
9781
  const activePanel = from.activePanel;
9622
- const targetActivePanel = to.activePanel;
9623
9782
  const panels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, {
9624
9783
  skipSetActive: true,
9625
9784
  })));
@@ -9629,22 +9788,21 @@
9629
9788
  this.movingLock(() => {
9630
9789
  for (const panel of panels) {
9631
9790
  to.model.openPanel(panel, {
9632
- skipSetActive: true, // Always skip setting panels active during move
9791
+ skipSetActive: panel !== activePanel,
9633
9792
  skipSetGroupActive: true,
9634
9793
  });
9635
9794
  }
9636
9795
  });
9637
- if (!options.skipSetActive) {
9638
- // Make the moved panel (from the source group) active
9639
- if (activePanel) {
9640
- this.doSetGroupAndPanelActive(to);
9641
- }
9796
+ // Ensure group becomes active after move
9797
+ if (options.skipSetActive !== true) {
9798
+ // For center moves (merges), we need to ensure the target group is active
9799
+ // unless explicitly told not to (skipSetActive: true)
9800
+ this.doSetGroupAndPanelActive(to);
9642
9801
  }
9643
- else if (targetActivePanel) {
9644
- // Ensure the target group's original active panel remains active
9645
- to.model.openPanel(targetActivePanel, {
9646
- skipSetGroupActive: true
9647
- });
9802
+ else if (!this.activePanel) {
9803
+ // Even with skipSetActive: true, ensure there's an active panel if none exists
9804
+ // This maintains basic functionality while respecting skipSetActive
9805
+ this.doSetGroupAndPanelActive(to);
9648
9806
  }
9649
9807
  }
9650
9808
  else {
@@ -9674,20 +9832,26 @@
9674
9832
  if (selectedPopoutGroup.referenceGroup) {
9675
9833
  const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup);
9676
9834
  if (referenceGroup && !referenceGroup.api.isVisible) {
9677
- this.doRemoveGroup(referenceGroup, { skipActive: true });
9835
+ this.doRemoveGroup(referenceGroup, {
9836
+ skipActive: true,
9837
+ });
9678
9838
  }
9679
9839
  }
9680
9840
  // Manually dispose the window without triggering restoration
9681
9841
  selectedPopoutGroup.window.dispose();
9682
9842
  // Update group's location and containers for target
9683
9843
  if (to.api.location.type === 'grid') {
9684
- from.model.renderContainer = this.overlayRenderContainer;
9685
- from.model.dropTargetContainer = this.rootDropTargetContainer;
9844
+ from.model.renderContainer =
9845
+ this.overlayRenderContainer;
9846
+ from.model.dropTargetContainer =
9847
+ this.rootDropTargetContainer;
9686
9848
  from.model.location = { type: 'grid' };
9687
9849
  }
9688
9850
  else if (to.api.location.type === 'floating') {
9689
- from.model.renderContainer = this.overlayRenderContainer;
9690
- from.model.dropTargetContainer = this.rootDropTargetContainer;
9851
+ from.model.renderContainer =
9852
+ this.overlayRenderContainer;
9853
+ from.model.dropTargetContainer =
9854
+ this.rootDropTargetContainer;
9691
9855
  from.model.location = { type: 'floating' };
9692
9856
  }
9693
9857
  break;
@@ -9755,8 +9919,12 @@
9755
9919
  from.panels.forEach((panel) => {
9756
9920
  this._onDidMovePanel.fire({ panel, from });
9757
9921
  });
9758
- if (!options.skipSetActive) {
9759
- this.doSetGroupAndPanelActive(from);
9922
+ // Ensure group becomes active after move
9923
+ if (options.skipSetActive === false) {
9924
+ // Only activate when explicitly requested (skipSetActive: false)
9925
+ // Use 'to' group for non-center moves since 'from' may have been destroyed
9926
+ const targetGroup = to !== null && to !== void 0 ? to : from;
9927
+ this.doSetGroupAndPanelActive(targetGroup);
9760
9928
  }
9761
9929
  }
9762
9930
  doSetGroupActive(group) {