dockview-core 8.1.0 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview-core
3
- * @version 8.1.0
3
+ * @version 8.2.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -10527,7 +10527,11 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
10527
10527
  this.panels.forEach((panel) => {
10528
10528
  this.rerender(panel);
10529
10529
  });
10530
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10530
+ if (this._activePanel) {
10531
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10532
+ const { width, height } = this.contentDimensions();
10533
+ this._activePanel.layout(width, height);
10534
+ }
10531
10535
  }
10532
10536
  get renderContainer() {
10533
10537
  var _this$_overwriteRende;
@@ -11492,7 +11496,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11492
11496
  this._group = value;
11493
11497
  this._onDidGroupChange.fire({});
11494
11498
  this.setupGroupEventListeners(oldGroup);
11495
- this._onDidLocationChange.fire({ location: this.group.api.location });
11499
+ this.fireLocationChange();
11496
11500
  }
11497
11501
  }
11498
11502
  get group() {
@@ -11568,6 +11572,23 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11568
11572
  exitMaximized() {
11569
11573
  this.group.api.exitMaximized();
11570
11574
  }
11575
+ /**
11576
+ * Report that this panel's location may have moved.
11577
+ *
11578
+ * A relocation touches the location more than once - the panel is
11579
+ * reparented into the destination group, then that group is tagged with
11580
+ * the location it ends up at - so the event is coalesced to the end of the
11581
+ * enclosing layout mutation and reports where the panel actually settled.
11582
+ * Firing each signal as it happened surfaced an intermediate `grid`
11583
+ * location the panel was never in, at a point where it was in neither
11584
+ * group's panel list. Outside a mutation this fires straight away.
11585
+ */
11586
+ fireLocationChange() {
11587
+ this.accessor.deferLocationChange(this, () => {
11588
+ if (this.isDisposed) return;
11589
+ this._onDidLocationChange.fire({ location: this.location });
11590
+ });
11591
+ }
11571
11592
  setupGroupEventListeners(previousGroup) {
11572
11593
  var _previousGroup$isActi;
11573
11594
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11576,9 +11597,9 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11576
11597
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11577
11598
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11578
11599
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11579
- }), this.group.api.onDidLocationChange((event) => {
11600
+ }), this.group.api.onDidLocationChange(() => {
11580
11601
  if (this.group !== this.panel.group) return;
11581
- this._onDidLocationChange.fire(event);
11602
+ this.fireLocationChange();
11582
11603
  }), this.group.api.onDidActiveChange(() => {
11583
11604
  if (this.group !== this.panel.group) return;
11584
11605
  if (_trackGroupActive !== this.isGroupActive) {
@@ -13604,6 +13625,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13604
13625
  });
13605
13626
  }
13606
13627
  };
13628
+ /**
13629
+ * Placeholder handle marking "a reposition frame has been requested but the
13630
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13631
+ * practice — it is replaced within the same synchronous block — and harmless
13632
+ * if it were, since no frame carries this id.
13633
+ */
13634
+ const SCHEDULED = -1;
13607
13635
  function createFocusableElement() {
13608
13636
  const element = document.createElement("div");
13609
13637
  element.tabIndex = -1;
@@ -13616,12 +13644,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13616
13644
  this.accessor = accessor;
13617
13645
  this.map = {};
13618
13646
  this._disposed = false;
13647
+ this._generation = 0;
13619
13648
  this.positionCache = new PositionCache();
13620
- this.pendingUpdates = /* @__PURE__ */ new Set();
13621
13649
  this.addDisposables(Disposable.from(() => {
13622
13650
  for (const value of Object.values(this.map)) {
13623
13651
  value.disposable.dispose();
13624
13652
  value.destroy.dispose();
13653
+ this.cancelPendingUpdate(value);
13625
13654
  }
13626
13655
  this._disposed = true;
13627
13656
  }));
@@ -13648,11 +13677,24 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13648
13677
  this.positionCache.invalidate();
13649
13678
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13650
13679
  }
13680
+ /**
13681
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13682
+ * bound to the reference container that was current when it was scheduled,
13683
+ * so it must be discarded whenever that container stops being the one the
13684
+ * overlay should track.
13685
+ */
13686
+ cancelPendingUpdate(entry) {
13687
+ if (entry.pendingUpdate !== void 0) {
13688
+ cancelAnimationFrame(entry.pendingUpdate);
13689
+ entry.pendingUpdate = void 0;
13690
+ }
13691
+ }
13651
13692
  detatch(panel) {
13652
13693
  if (this.map[panel.api.id]) {
13653
- const { disposable, destroy } = this.map[panel.api.id];
13654
- disposable.dispose();
13655
- destroy.dispose();
13694
+ const entry = this.map[panel.api.id];
13695
+ entry.disposable.dispose();
13696
+ entry.destroy.dispose();
13697
+ this.cancelPendingUpdate(entry);
13656
13698
  delete this.map[panel.api.id];
13657
13699
  return true;
13658
13700
  }
@@ -13668,22 +13710,73 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13668
13710
  panel,
13669
13711
  disposable: Disposable.NONE,
13670
13712
  destroy: Disposable.NONE,
13671
- element
13713
+ element,
13714
+ retainPreviousGeometry: false,
13715
+ positioned: false,
13716
+ generation: ++this._generation
13672
13717
  };
13673
13718
  }
13674
- const focusContainer = this.map[panel.api.id].element;
13719
+ const mapEntry = this.map[panel.api.id];
13720
+ /**
13721
+ * A *change* of reference container supersedes the previous `attach`:
13722
+ * take a fresh generation so its `resize` closure can no longer run,
13723
+ * and drop the frame it queued against the old container. During
13724
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13725
+ * detached staging group measuring 0x0, so leaving its frame in flight
13726
+ * both wastes the update and delays the reposition against the real one.
13727
+ *
13728
+ * Re-attaching over the *same* container (re-open, active panel change)
13729
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13730
+ * (the auto-hide peek) schedules a frame carrying the sticky
13731
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13732
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13733
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13734
+ * nothing. A newly created entry already holds a fresh generation, so
13735
+ * the stale closures a `detatch` left behind are fenced off either way.
13736
+ */
13737
+ if (mapEntry.referenceContainer !== referenceContainer) {
13738
+ mapEntry.generation = ++this._generation;
13739
+ this.cancelPendingUpdate(mapEntry);
13740
+ mapEntry.referenceContainer = referenceContainer;
13741
+ /**
13742
+ * Arm the retain flag only here: it means "a replacement reference
13743
+ * container is awaiting layout", so the 0x0 early-return below
13744
+ * holds the last good box rather than blanking the panel mid-move.
13745
+ * Arming it on every re-attach instead would make a container that
13746
+ * genuinely collapses to zero keep its stale geometry forever.
13747
+ *
13748
+ * Gated on `positioned` because an overlay that has never been
13749
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13750
+ * defaults to 100%/100% — retaining "previous" geometry there would
13751
+ * pin the content over the whole container.
13752
+ */
13753
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13754
+ }
13755
+ const generation = mapEntry.generation;
13756
+ const focusContainer = mapEntry.element;
13675
13757
  const contentElement = panel.view.content.element;
13676
13758
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13677
13759
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13678
13760
  const resize = () => {
13679
13761
  const panelId = panel.api.id;
13680
- if (this.pendingUpdates.has(panelId)) return;
13681
- this.pendingUpdates.add(panelId);
13682
- requestAnimationFrame(() => {
13762
+ const scheduling = this.map[panelId];
13763
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13764
+ if (scheduling.pendingUpdate !== void 0) return;
13765
+ /**
13766
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13767
+ * shimmed to run inline by some hosts (and by several suites here),
13768
+ * in which case the callback below clears `pendingUpdate` while
13769
+ * this call is still on the stack — assigning the handle afterwards
13770
+ * would resurrect a slot that is already spent and block every
13771
+ * later reposition. The sentinel is replaced by the real handle
13772
+ * only if the frame has not already run.
13773
+ */
13774
+ scheduling.pendingUpdate = SCHEDULED;
13775
+ const handle = requestAnimationFrame(() => {
13683
13776
  var _entry$forceVisible;
13684
- this.pendingUpdates.delete(panelId);
13685
13777
  const entry = this.map[panelId];
13686
- if (this.isDisposed || !entry) return;
13778
+ if (entry) entry.pendingUpdate = void 0;
13779
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13687
13780
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13688
13781
  const clip = entry.clip;
13689
13782
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13692,6 +13785,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13692
13785
  const top = box.top - box2.top;
13693
13786
  const width = box.width;
13694
13787
  const height = box.height;
13788
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13789
+ if (!panel.api.isVisible && !forceVisible) {
13790
+ focusContainer.style.visibility = "hidden";
13791
+ focusContainer.style.pointerEvents = "none";
13792
+ }
13793
+ return;
13794
+ }
13795
+ entry.retainPreviousGeometry = false;
13796
+ entry.positioned = true;
13695
13797
  focusContainer.style.left = `${left}px`;
13696
13798
  focusContainer.style.top = `${top}px`;
13697
13799
  focusContainer.style.width = `${width}px`;
@@ -13720,11 +13822,30 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13720
13822
  } else focusContainer.style.clipPath = "";
13721
13823
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13722
13824
  });
13825
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13723
13826
  };
13724
13827
  const visibilityChanged = () => {
13725
13828
  if (panel.api.isVisible) {
13829
+ var _this$map$panel$api$i;
13726
13830
  this.positionCache.invalidate();
13727
13831
  resize();
13832
+ /**
13833
+ * Existing geometry is safe to show while its replacement lays
13834
+ * out. `retainPreviousGeometry` is only set when geometry has
13835
+ * actually been written (see `attach`), so this can never
13836
+ * un-hide an overlay whose left/top/width/height are unset —
13837
+ * `.dv-render-overlay` is 100%/100% by default and would
13838
+ * otherwise cover the whole dock.
13839
+ *
13840
+ * The retained geometry is the panel's position in the previous
13841
+ * layout, so restoring a layout that moves the panel does paint
13842
+ * it at stale coordinates until the reposition lands. That is
13843
+ * bounded to the next frame now that a superseded `attach` can
13844
+ * no longer swallow the reposition (see `attach`); showing
13845
+ * stale-but-real geometry for a frame is the deliberate
13846
+ * trade-off against blanking the panel for the whole rebuild.
13847
+ */
13848
+ if ((_this$map$panel$api$i = this.map[panel.api.id]) === null || _this$map$panel$api$i === void 0 ? void 0 : _this$map$panel$api$i.retainPreviousGeometry) focusContainer.style.visibility = "";
13728
13849
  focusContainer.style.pointerEvents = "";
13729
13850
  } else {
13730
13851
  focusContainer.style.visibility = "hidden";
@@ -13947,7 +14068,17 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13947
14068
  window: externalWindow
13948
14069
  });
13949
14070
  return new Promise((resolve, reject) => {
13950
- externalWindow.addEventListener("unload", (e) => {});
14071
+ externalWindow.addEventListener("unload", () => {});
14072
+ /**
14073
+ * `load` is the only event that resolves this promise with a
14074
+ * container, so a window that goes away first would leave the
14075
+ * caller awaiting a promise that never settles - and
14076
+ * `addPopoutGroup` holds its layout transaction open until it does.
14077
+ * Settle with `null` on close, the same signal a blocked popup
14078
+ * gives and one the caller already handles. `resolve` after the
14079
+ * fact is a no-op, so a `load` that arrived first still wins.
14080
+ */
14081
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13951
14082
  externalWindow.addEventListener("load", () => {
13952
14083
  /**
13953
14084
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -15365,6 +15496,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15365
15496
  this._onWillDrop = new Emitter();
15366
15497
  this.onWillDrop = this._onWillDrop.event;
15367
15498
  this._mutationDepth = 0;
15499
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15368
15500
  this._origin = "user";
15369
15501
  this._originDepth = 0;
15370
15502
  this._onWillMutateLayout = new Emitter();
@@ -15462,7 +15594,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15462
15594
  toggleClass(this.element, "dv-debug", !!options.debug);
15463
15595
  this.updateTheme();
15464
15596
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15465
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15597
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15466
15598
  var _this$rootDropTargetC2, _this$floatingDropTar;
15467
15599
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15468
15600
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15536,7 +15668,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15536
15668
  return (_this$_popoutWindowSe3 = (_this$_popoutWindowSe4 = this._popoutWindowService) === null || _this$_popoutWindowSe4 === void 0 || (_this$_popoutWindowSe4 = _this$_popoutWindowSe4.findByGroup(group)) === null || _this$_popoutWindowSe4 === void 0 ? void 0 : _this$_popoutWindowSe4.popupService) !== null && _this$_popoutWindowSe3 !== void 0 ? _this$_popoutWindowSe3 : this.popupService;
15537
15669
  }
15538
15670
  addPopoutGroup(itemToPopout, options) {
15539
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15671
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15540
15672
  }
15541
15673
  /** Enumerate the popout groups currently open in their own windows. */
15542
15674
  getPopouts() {
@@ -15633,15 +15765,32 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15633
15765
  }
15634
15766
  };
15635
15767
  let floatingBox;
15636
- if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) this.movingLock(() => {
15637
- const panel = referenceGroup.model.removePanel(itemToPopout);
15638
- group.model.openPanel(panel);
15639
- });
15640
- else {
15768
+ /**
15769
+ * Popping out rehomes panels into `group`, so like the floating
15770
+ * case it owes the caller an `onDidMovePanel` per panel. The
15771
+ * events are deferred until the window is wired up so listeners
15772
+ * observe the panels' final `popout` location.
15773
+ */
15774
+ let movedPanels = [];
15775
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15776
+ const sourceGroup = itemToPopout.group;
15777
+ this.movingLock(() => {
15778
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15779
+ group.model.openPanel(panel);
15780
+ });
15781
+ movedPanels = [{
15782
+ panel: itemToPopout,
15783
+ from: sourceGroup
15784
+ }];
15785
+ } else {
15641
15786
  this.movingLock(() => moveGroupWithoutDestroying({
15642
15787
  from: referenceGroup,
15643
15788
  to: group
15644
15789
  }));
15790
+ movedPanels = group.panels.map((panel) => ({
15791
+ panel,
15792
+ from: referenceGroup
15793
+ }));
15645
15794
  switch (referenceLocation) {
15646
15795
  case "grid":
15647
15796
  referenceGroup.api.setVisible(false);
@@ -15750,6 +15899,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15750
15899
  group: value.popoutGroup,
15751
15900
  window: value.getWindow()
15752
15901
  });
15902
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15753
15903
  return true;
15754
15904
  }).catch((err) => {
15755
15905
  console.error("dockview: failed to create popout.", err);
@@ -15827,6 +15977,16 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15827
15977
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15828
15978
  const anchorPresent = members.includes(group);
15829
15979
  const anchorIsSoleMember = anchorPresent && members.length === 1;
15980
+ /**
15981
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
15982
+ * window owes the caller the same: every panel in it is rehomed, into
15983
+ * the reference group the popout left behind or into a fresh grid slot.
15984
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
15985
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
15986
+ * events are deferred to the end so listeners observe the panels at
15987
+ * their final group and location.
15988
+ */
15989
+ const movedPanels = [];
15830
15990
  if (genuineClose) for (const member of members) {
15831
15991
  if (member === group) continue;
15832
15992
  this.movingLock(() => {
@@ -15837,12 +15997,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15837
15997
  });
15838
15998
  this.redockGroupToMainGrid(member);
15839
15999
  });
16000
+ for (const panel of member.panels) movedPanels.push({
16001
+ panel,
16002
+ from: member
16003
+ });
15840
16004
  }
15841
16005
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16006
+ const rehomed = [...group.panels];
15842
16007
  this.movingLock(() => moveGroupWithoutDestroying({
15843
16008
  from: group,
15844
16009
  to: referenceGroup
15845
16010
  }));
16011
+ for (const panel of rehomed) movedPanels.push({
16012
+ panel,
16013
+ from: group
16014
+ });
15846
16015
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15847
16016
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15848
16017
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15873,10 +16042,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15873
16042
  this.movingLock(() => {
15874
16043
  this.doAddGroup(group, [0]);
15875
16044
  });
16045
+ for (const panel of group.panels) movedPanels.push({
16046
+ panel,
16047
+ from: group
16048
+ });
15876
16049
  }
15877
16050
  this.doSetGroupAndPanelActive(group);
15878
16051
  }
15879
16052
  disposePopoutGridview();
16053
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15880
16054
  }
15881
16055
  addFloatingGroup(item, options) {
15882
16056
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15885,7 +16059,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15885
16059
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15886
16060
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15887
16061
  let group;
16062
+ /**
16063
+ * Floating relocates panels exactly like a drop onto the grid does, so
16064
+ * it owes the caller an `onDidMovePanel` per panel. The events are
16065
+ * deferred until the window is mounted below: only then do the panels
16066
+ * report their final `floating` location.
16067
+ */
16068
+ let movedPanels = [];
15888
16069
  if (item instanceof DockviewPanel) {
16070
+ const sourceGroup = item.group;
15889
16071
  group = this.createGroup();
15890
16072
  this._onDidAddGroup.fire(group);
15891
16073
  this.movingLock(() => this.removePanel(item, {
@@ -15894,6 +16076,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15894
16076
  skipSetActiveGroup: true
15895
16077
  }));
15896
16078
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16079
+ movedPanels = [{
16080
+ panel: item,
16081
+ from: sourceGroup
16082
+ }];
15897
16083
  } else {
15898
16084
  var _this$_popoutWindowSe8;
15899
16085
  group = item;
@@ -15910,11 +16096,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15910
16096
  });
15911
16097
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15912
16098
  group = popoutReferenceGroup;
15913
- } else this.doRemoveGroup(item, {
15914
- skipDispose: true,
15915
- skipPopoutReturn: true,
15916
- skipPopoutAssociated: false
15917
- });
16099
+ movedPanels = group.panels.map((panel) => ({
16100
+ panel,
16101
+ from: item
16102
+ }));
16103
+ } else {
16104
+ this.doRemoveGroup(item, {
16105
+ skipDispose: true,
16106
+ skipPopoutReturn: true,
16107
+ skipPopoutAssociated: false
16108
+ });
16109
+ movedPanels = group.panels.map((panel) => ({
16110
+ panel,
16111
+ from: group
16112
+ }));
16113
+ }
15918
16114
  }
15919
16115
  function getAnchoredBox() {
15920
16116
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15947,6 +16143,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15947
16143
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15948
16144
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15949
16145
  });
16146
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15950
16147
  }
15951
16148
  /**
15952
16149
  * Build an empty gridview configured to match the main grid's styling, for
@@ -16378,35 +16575,119 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16378
16575
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16379
16576
  if (!data.grid || ((_data$grid$root = data.grid.root) === null || _data$grid$root === void 0 ? void 0 : _data$grid$root.type) !== "branch" || !Array.isArray(data.grid.root.data)) throw new Error("dockview: root must be of type branch");
16380
16577
  const existingPanels = /* @__PURE__ */ new Map();
16381
- let tempGroup;
16578
+ const temporaryGroups = /* @__PURE__ */ new Map();
16579
+ const stagedPanels = [];
16580
+ const temporaryGroupDisposables = [];
16581
+ /**
16582
+ * The staging groups are detached from `_groups` and never enter the
16583
+ * grid, so nothing else will ever tear them down.
16584
+ *
16585
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16586
+ * throwing renderer must not abort the rest of the cleanup — that would
16587
+ * re-introduce the very leak this reclaims — nor replace the
16588
+ * deserialization error the caller is being given. Draining the list
16589
+ * also makes this safe to call more than once.
16590
+ */
16591
+ const disposeTemporaryGroups = () => {
16592
+ for (const disposable of temporaryGroupDisposables) try {
16593
+ disposable.dispose();
16594
+ } catch (err) {
16595
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16596
+ }
16597
+ temporaryGroupDisposables.length = 0;
16598
+ };
16382
16599
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16383
16600
  /**
16384
- * What are we doing here?
16385
- *
16386
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16387
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16601
+ * Visible, always-rendered panels need individual staging groups
16602
+ * to remain active. Other reused panels can share a staging group.
16603
+ * The staging groups are excluded from the layout clear below.
16388
16604
  */
16389
- tempGroup = this.createGroup();
16390
- this._groups.delete(tempGroup.api.id);
16391
16605
  const newPanels = Object.keys(data.panels);
16392
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16393
- this.movingLock(() => {
16394
- Array.from(existingPanels.values()).forEach((panel) => {
16395
- this.moveGroupOrPanel({
16396
- from: {
16397
- groupId: panel.api.group.api.id,
16398
- panelId: panel.api.id
16399
- },
16400
- to: {
16401
- group: tempGroup,
16402
- position: "center"
16403
- },
16404
- keepEmptyGroups: true
16606
+ let sharedTemporaryGroup;
16607
+ const createTemporaryGroup = () => {
16608
+ const temporaryGroup = this.createGroup();
16609
+ /**
16610
+ * Removing the group from `_groups` also drops the record's
16611
+ * `CompositeDisposable`, so capture it first and tear both it
16612
+ * and the group down in the `finally` below. Left undisposed a
16613
+ * staging group leaks its `ResizeObserver`, its
16614
+ * `onDidOptionsChange` subscription (retained by this
16615
+ * component's emitter) and the watermark its model mounts when
16616
+ * the group empties — once per reused panel, per `fromJSON`.
16617
+ */
16618
+ const record = this._groups.get(temporaryGroup.api.id);
16619
+ this._groups.delete(temporaryGroup.api.id);
16620
+ temporaryGroupDisposables.push(Disposable.from(() => {
16621
+ /**
16622
+ * Reclaim the group's own resources, never its panels.
16623
+ * Staging is driven by the ids in `data.panels` while
16624
+ * reclaiming is driven by the group `views` that
16625
+ * reference them, so a panel whose state is present but
16626
+ * unreferenced is still staged here — and `dispose()`
16627
+ * on a non-empty group reaches the consumer's
16628
+ * `IContentRenderer.dispose()`. Emptying the group
16629
+ * first leaves those panels exactly as they were before
16630
+ * this teardown existed, which also keeps this safe
16631
+ * against any future path that rebuilds a panel under
16632
+ * an id that is live in the new layout.
16633
+ */
16634
+ this.movingLock(() => {
16635
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16636
+ });
16637
+ record === null || record === void 0 || record.disposable.dispose();
16638
+ temporaryGroup.dispose();
16639
+ }));
16640
+ return temporaryGroup;
16641
+ };
16642
+ /**
16643
+ * Staging and the clear below run before the deserialization
16644
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16645
+ * renderer teardown during `clear`) would escape without reclaiming
16646
+ * the staging groups already created.
16647
+ *
16648
+ * The creation loop is inside the guard too, not just the moves and
16649
+ * the clear: `createGroup()` reaches consumer code of its own —
16650
+ * `initialize()` mounts the watermark through
16651
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16652
+ * otherwise strand the n-1 staging groups already built.
16653
+ */
16654
+ try {
16655
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16656
+ existingPanels.set(panel.api.id, panel);
16657
+ let temporaryGroup;
16658
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16659
+ else {
16660
+ var _sharedTemporaryGroup;
16661
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16662
+ temporaryGroup = sharedTemporaryGroup;
16663
+ }
16664
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16665
+ stagedPanels.push({
16666
+ panel,
16667
+ temporaryGroup
16668
+ });
16669
+ }
16670
+ this.movingLock(() => {
16671
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16672
+ this.moveGroupOrPanel({
16673
+ from: {
16674
+ groupId: panel.api.group.api.id,
16675
+ panelId: panel.api.id
16676
+ },
16677
+ to: {
16678
+ group: temporaryGroup,
16679
+ position: "center"
16680
+ },
16681
+ keepEmptyGroups: true
16682
+ });
16405
16683
  });
16406
16684
  });
16407
- });
16408
- }
16409
- this.clear();
16685
+ this.clear();
16686
+ } catch (err) {
16687
+ disposeTemporaryGroups();
16688
+ throw err;
16689
+ }
16690
+ } else this.clear();
16410
16691
  const { grid, panels, activeGroup } = data;
16411
16692
  try {
16412
16693
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16438,9 +16719,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16438
16719
  * due to a corruption of input data.
16439
16720
  */
16440
16721
  const existingPanel = existingPanels.get(child);
16441
- if (tempGroup && existingPanel) {
16722
+ const temporaryGroup = temporaryGroups.get(child);
16723
+ if (temporaryGroup && existingPanel) {
16442
16724
  this.movingLock(() => {
16443
- tempGroup.model.removePanel(existingPanel);
16725
+ temporaryGroup.model.removePanel(existingPanel);
16444
16726
  });
16445
16727
  createdPanels.push(existingPanel);
16446
16728
  existingPanel.updateFromStateModel(panels[child]);
@@ -16470,7 +16752,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16470
16752
  return createGroupFromSerializedState(node.data);
16471
16753
  } });
16472
16754
  this._layoutFromShell(width, height);
16473
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16755
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16474
16756
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16475
16757
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16476
16758
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16507,6 +16789,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16507
16789
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16508
16790
  */
16509
16791
  throw err;
16792
+ } finally {
16793
+ disposeTemporaryGroups();
16510
16794
  }
16511
16795
  this.debouncedUpdateAllPositions();
16512
16796
  this._onDidLayoutFromJSON.fire();
@@ -16529,7 +16813,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16529
16813
  members
16530
16814
  };
16531
16815
  }
16532
- deserializeEdgeGroups(edgeGroups, panels) {
16816
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16533
16817
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16534
16818
  if (!edgeService) return;
16535
16819
  for (const _position of [
@@ -16559,13 +16843,40 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16559
16843
  if (groupState) {
16560
16844
  const { views, activeView } = groupState;
16561
16845
  const createdPanels = [];
16562
- for (const panelId of views) if (panels[panelId]) {
16563
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16564
- createdPanels.push(panel);
16846
+ for (const panelId of views) {
16847
+ if (!panels[panelId]) continue;
16848
+ /**
16849
+ * Reclaim a staged panel rather than rebuilding it, the
16850
+ * same way the grid path does. Deserializing here instead
16851
+ * would honour `reuseExistingPanels` everywhere except edge
16852
+ * groups: the live panel keeps sitting in its staging group
16853
+ * while a second panel is built under the same id, so the
16854
+ * consumer's renderer for the original is orphaned — never
16855
+ * disposed, and its element left inside the id-keyed
16856
+ * overlay entry the replacement now shares.
16857
+ */
16858
+ const existingPanel = existingPanels.get(panelId);
16859
+ const temporaryGroup = temporaryGroups.get(panelId);
16860
+ if (temporaryGroup && existingPanel) {
16861
+ this.movingLock(() => {
16862
+ temporaryGroup.model.removePanel(existingPanel);
16863
+ });
16864
+ createdPanels.push(existingPanel);
16865
+ existingPanel.updateFromStateModel(panels[panelId]);
16866
+ } else {
16867
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16868
+ createdPanels.push(panel);
16869
+ }
16565
16870
  }
16566
16871
  for (const panel of createdPanels) {
16567
16872
  const isActive = activeView === panel.id;
16568
- edgeGroup.model.openPanel(panel, {
16873
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16874
+ edgeGroup.model.openPanel(panel, {
16875
+ skipSetActive: !isActive,
16876
+ skipSetGroupActive: true
16877
+ });
16878
+ });
16879
+ else edgeGroup.model.openPanel(panel, {
16569
16880
  skipSetActive: !isActive,
16570
16881
  skipSetGroupActive: true
16571
16882
  });
@@ -16989,22 +17300,100 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16989
17300
  * outermost mutation.
16990
17301
  */
16991
17302
  mutation(kind, func) {
16992
- const outer = this._mutationDepth === 0;
17303
+ const close = this.openMutation(kind);
17304
+ try {
17305
+ return func();
17306
+ } finally {
17307
+ close();
17308
+ }
17309
+ }
17310
+ /**
17311
+ * `mutation()` for an operation whose work continues after the synchronous
17312
+ * call returns. The transaction stays open until the returned promise
17313
+ * settles, so everything the operation does - the group it adds, the panels
17314
+ * it relocates - lands inside the bracket, matching the synchronous
17315
+ * move / float paths.
17316
+ */
17317
+ mutationAsync(kind, func) {
17318
+ var _this = this;
17319
+ return _asyncToGenerator(function* () {
17320
+ const close = _this.openMutation(kind);
17321
+ try {
17322
+ return yield func();
17323
+ } finally {
17324
+ close();
17325
+ }
17326
+ })();
17327
+ }
17328
+ /**
17329
+ * Open a transaction and return the function that closes it. Shared by the
17330
+ * synchronous and asynchronous brackets; the returned function must be
17331
+ * called exactly once.
17332
+ *
17333
+ * Both ends key off the depth counter reaching zero rather than off which
17334
+ * bracket opened first. For synchronous nesting the two are the same thing
17335
+ * - brackets close in the order they opened - but asynchronous transactions
17336
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17337
+ * once, or a restore staggering several), and there the first to open is
17338
+ * not the last to close. Reporting on the opener would fire `didMutate`
17339
+ * while the other transaction was still doing structural work, which is the
17340
+ * very thing the async bracket exists to prevent. Overlapping transactions
17341
+ * therefore report as one, tagged with the kind of the last to finish.
17342
+ */
17343
+ openMutation(kind) {
16993
17344
  const origin = this._origin;
16994
- if (outer) this._onWillMutateLayout.fire({
17345
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16995
17346
  kind,
16996
17347
  origin
16997
17348
  });
16998
17349
  this._mutationDepth++;
16999
- try {
17000
- return func();
17001
- } finally {
17350
+ return () => {
17002
17351
  this._mutationDepth--;
17003
- if (outer) this._onDidMutateLayout.fire({
17004
- kind,
17005
- origin
17006
- });
17352
+ if (this._mutationDepth === 0) {
17353
+ this.flushLocationChanges();
17354
+ this._onDidMutateLayout.fire({
17355
+ kind,
17356
+ origin
17357
+ });
17358
+ }
17359
+ };
17360
+ }
17361
+ /**
17362
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17363
+ * transaction, or fire it immediately when no transaction is in flight.
17364
+ *
17365
+ * Relocating a panel touches its location twice: once as it is reparented
17366
+ * into the destination group - which, when that group has just been created
17367
+ * for a floating or popout window, has not been told where it lives yet -
17368
+ * and once as the group is tagged with its final location. Reporting each
17369
+ * signal as it happens therefore leaks an intermediate `grid` location the
17370
+ * panel was never in, at a moment when it belongs to neither group's panel
17371
+ * list and so is missing from `api.panels`.
17372
+ *
17373
+ * Deferring collapses those signals into a single event carrying the
17374
+ * settled location. Note the signals are deliberately *not* compared
17375
+ * against the panel's previous location: a panel can move between two
17376
+ * floating windows without its location type changing, and this event is
17377
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17378
+ * z-index against its new host window.
17379
+ */
17380
+ deferLocationChange(key, fire) {
17381
+ if (this._mutationDepth === 0) {
17382
+ fire();
17383
+ return;
17007
17384
  }
17385
+ this._pendingLocationChanges.set(key, fire);
17386
+ }
17387
+ /**
17388
+ * Report every panel whose location moved during the transaction that has
17389
+ * just closed. Each callback reads the panel's location at this point, so
17390
+ * what it reports is where the panel ended up.
17391
+ */
17392
+ flushLocationChanges() {
17393
+ if (this._pendingLocationChanges.size === 0) return;
17394
+ const pending = Array.from(this._pendingLocationChanges.values());
17395
+ this._pendingLocationChanges.clear();
17396
+ for (const fire of pending) fire();
17008
17397
  }
17009
17398
  /**
17010
17399
  * The origin of the operation currently in progress (`'user'` by default).
@@ -17045,6 +17434,18 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17045
17434
  origin: this._origin
17046
17435
  });
17047
17436
  }
17437
+ /**
17438
+ * Announce that `panel` has finished relocating. Must be called once the
17439
+ * move has settled: `to` is read from the panel's current group, so an
17440
+ * early call would report the source group as the destination.
17441
+ */
17442
+ fireDidMovePanel(panel, from) {
17443
+ this._onDidMovePanel.fire({
17444
+ panel,
17445
+ from,
17446
+ to: panel.group
17447
+ });
17448
+ }
17048
17449
  moveGroupOrPanel(options) {
17049
17450
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
17050
17451
  }
@@ -17105,10 +17506,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17105
17506
  });
17106
17507
  });
17107
17508
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
17108
- this._onDidMovePanel.fire({
17109
- panel: removedPanel,
17110
- from: sourceGroup
17111
- });
17509
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17112
17510
  } else {
17113
17511
  /**
17114
17512
  * Dropping a panel to the extremities of a group which will place that panel
@@ -17127,10 +17525,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17127
17525
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
17128
17526
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
17129
17527
  this.gridview.moveView(sourceParentLocation, from, to);
17130
- this._onDidMovePanel.fire({
17131
- panel: this.getGroupPanel(sourceItemId),
17132
- from: sourceGroup
17133
- });
17528
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17134
17529
  return;
17135
17530
  }
17136
17531
  }
@@ -17161,10 +17556,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17161
17556
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17162
17557
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
17163
17558
  this.doSetGroupAndPanelActive(newGroup);
17164
- this._onDidMovePanel.fire({
17165
- panel: this.getGroupPanel(sourceItemId),
17166
- from: sourceGroup
17167
- });
17559
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17168
17560
  return;
17169
17561
  }
17170
17562
  if (sourceGroup.api.location.type === "edge") {
@@ -17181,10 +17573,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17181
17573
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17182
17574
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17183
17575
  this.doSetGroupAndPanelActive(newGroup);
17184
- this._onDidMovePanel.fire({
17185
- panel: removedPanel,
17186
- from: sourceGroup
17187
- });
17576
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17188
17577
  return;
17189
17578
  }
17190
17579
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -17196,10 +17585,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17196
17585
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17197
17586
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
17198
17587
  this.doSetGroupAndPanelActive(targetGroup);
17199
- this._onDidMovePanel.fire({
17200
- panel: this.getGroupPanel(sourceItemId),
17201
- from: sourceGroup
17202
- });
17588
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17203
17589
  } else {
17204
17590
  /**
17205
17591
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -17214,10 +17600,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17214
17600
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17215
17601
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17216
17602
  this.doSetGroupAndPanelActive(group);
17217
- this._onDidMovePanel.fire({
17218
- panel: removedPanel,
17219
- from: sourceGroup
17220
- });
17603
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17221
17604
  }
17222
17605
  }
17223
17606
  }
@@ -17252,10 +17635,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17252
17635
  });
17253
17636
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17254
17637
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17255
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17256
- panel,
17257
- from: sourceGroup
17258
- });
17638
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17259
17639
  };
17260
17640
  let targetGroup;
17261
17641
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -17276,10 +17656,12 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17276
17656
  this.mutation("maximize", () => super.exitMaximizedGroup());
17277
17657
  }
17278
17658
  _doMoveGroup(options) {
17659
+ var _mergedPanels;
17279
17660
  const from = options.from.group;
17280
17661
  const to = options.to.group;
17281
17662
  const target = options.to.position;
17282
17663
  let source = from;
17664
+ let mergedPanels;
17283
17665
  if (target === "center") {
17284
17666
  const activePanel = from.activePanel;
17285
17667
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17297,6 +17679,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17297
17679
  skipSetGroupActive: true
17298
17680
  });
17299
17681
  });
17682
+ mergedPanels = panels;
17300
17683
  for (const snapshot of tabGroupSnapshots) {
17301
17684
  const newTabGroup = to.model.createTabGroup({
17302
17685
  label: snapshot.label,
@@ -17386,11 +17769,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17386
17769
  this.setGroupLocationForRoot(source, destGridview);
17387
17770
  }
17388
17771
  }
17389
- source.panels.forEach((panel) => {
17390
- this._onDidMovePanel.fire({
17391
- panel,
17392
- from
17393
- });
17772
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17773
+ this.fireDidMovePanel(panel, from);
17394
17774
  });
17395
17775
  this.debouncedUpdateAllPositions();
17396
17776
  if (options.skipSetActive === false) {