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
  */
@@ -10535,7 +10535,11 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
10535
10535
  this.panels.forEach((panel) => {
10536
10536
  this.rerender(panel);
10537
10537
  });
10538
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10538
+ if (this._activePanel) {
10539
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10540
+ const { width, height } = this.contentDimensions();
10541
+ this._activePanel.layout(width, height);
10542
+ }
10539
10543
  }
10540
10544
  get renderContainer() {
10541
10545
  var _this$_overwriteRende;
@@ -11500,7 +11504,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11500
11504
  this._group = value;
11501
11505
  this._onDidGroupChange.fire({});
11502
11506
  this.setupGroupEventListeners(oldGroup);
11503
- this._onDidLocationChange.fire({ location: this.group.api.location });
11507
+ this.fireLocationChange();
11504
11508
  }
11505
11509
  }
11506
11510
  get group() {
@@ -11576,6 +11580,23 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11576
11580
  exitMaximized() {
11577
11581
  this.group.api.exitMaximized();
11578
11582
  }
11583
+ /**
11584
+ * Report that this panel's location may have moved.
11585
+ *
11586
+ * A relocation touches the location more than once - the panel is
11587
+ * reparented into the destination group, then that group is tagged with
11588
+ * the location it ends up at - so the event is coalesced to the end of the
11589
+ * enclosing layout mutation and reports where the panel actually settled.
11590
+ * Firing each signal as it happened surfaced an intermediate `grid`
11591
+ * location the panel was never in, at a point where it was in neither
11592
+ * group's panel list. Outside a mutation this fires straight away.
11593
+ */
11594
+ fireLocationChange() {
11595
+ this.accessor.deferLocationChange(this, () => {
11596
+ if (this.isDisposed) return;
11597
+ this._onDidLocationChange.fire({ location: this.location });
11598
+ });
11599
+ }
11579
11600
  setupGroupEventListeners(previousGroup) {
11580
11601
  var _previousGroup$isActi;
11581
11602
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11584,9 +11605,9 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11584
11605
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11585
11606
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11586
11607
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11587
- }), this.group.api.onDidLocationChange((event) => {
11608
+ }), this.group.api.onDidLocationChange(() => {
11588
11609
  if (this.group !== this.panel.group) return;
11589
- this._onDidLocationChange.fire(event);
11610
+ this.fireLocationChange();
11590
11611
  }), this.group.api.onDidActiveChange(() => {
11591
11612
  if (this.group !== this.panel.group) return;
11592
11613
  if (_trackGroupActive !== this.isGroupActive) {
@@ -13612,6 +13633,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13612
13633
  });
13613
13634
  }
13614
13635
  };
13636
+ /**
13637
+ * Placeholder handle marking "a reposition frame has been requested but the
13638
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13639
+ * practice — it is replaced within the same synchronous block — and harmless
13640
+ * if it were, since no frame carries this id.
13641
+ */
13642
+ const SCHEDULED = -1;
13615
13643
  function createFocusableElement() {
13616
13644
  const element = document.createElement("div");
13617
13645
  element.tabIndex = -1;
@@ -13624,12 +13652,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13624
13652
  this.accessor = accessor;
13625
13653
  this.map = {};
13626
13654
  this._disposed = false;
13655
+ this._generation = 0;
13627
13656
  this.positionCache = new PositionCache();
13628
- this.pendingUpdates = /* @__PURE__ */ new Set();
13629
13657
  this.addDisposables(Disposable.from(() => {
13630
13658
  for (const value of Object.values(this.map)) {
13631
13659
  value.disposable.dispose();
13632
13660
  value.destroy.dispose();
13661
+ this.cancelPendingUpdate(value);
13633
13662
  }
13634
13663
  this._disposed = true;
13635
13664
  }));
@@ -13656,11 +13685,24 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13656
13685
  this.positionCache.invalidate();
13657
13686
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13658
13687
  }
13688
+ /**
13689
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13690
+ * bound to the reference container that was current when it was scheduled,
13691
+ * so it must be discarded whenever that container stops being the one the
13692
+ * overlay should track.
13693
+ */
13694
+ cancelPendingUpdate(entry) {
13695
+ if (entry.pendingUpdate !== void 0) {
13696
+ cancelAnimationFrame(entry.pendingUpdate);
13697
+ entry.pendingUpdate = void 0;
13698
+ }
13699
+ }
13659
13700
  detatch(panel) {
13660
13701
  if (this.map[panel.api.id]) {
13661
- const { disposable, destroy } = this.map[panel.api.id];
13662
- disposable.dispose();
13663
- destroy.dispose();
13702
+ const entry = this.map[panel.api.id];
13703
+ entry.disposable.dispose();
13704
+ entry.destroy.dispose();
13705
+ this.cancelPendingUpdate(entry);
13664
13706
  delete this.map[panel.api.id];
13665
13707
  return true;
13666
13708
  }
@@ -13676,22 +13718,73 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13676
13718
  panel,
13677
13719
  disposable: Disposable.NONE,
13678
13720
  destroy: Disposable.NONE,
13679
- element
13721
+ element,
13722
+ retainPreviousGeometry: false,
13723
+ positioned: false,
13724
+ generation: ++this._generation
13680
13725
  };
13681
13726
  }
13682
- const focusContainer = this.map[panel.api.id].element;
13727
+ const mapEntry = this.map[panel.api.id];
13728
+ /**
13729
+ * A *change* of reference container supersedes the previous `attach`:
13730
+ * take a fresh generation so its `resize` closure can no longer run,
13731
+ * and drop the frame it queued against the old container. During
13732
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13733
+ * detached staging group measuring 0x0, so leaving its frame in flight
13734
+ * both wastes the update and delays the reposition against the real one.
13735
+ *
13736
+ * Re-attaching over the *same* container (re-open, active panel change)
13737
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13738
+ * (the auto-hide peek) schedules a frame carrying the sticky
13739
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13740
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13741
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13742
+ * nothing. A newly created entry already holds a fresh generation, so
13743
+ * the stale closures a `detatch` left behind are fenced off either way.
13744
+ */
13745
+ if (mapEntry.referenceContainer !== referenceContainer) {
13746
+ mapEntry.generation = ++this._generation;
13747
+ this.cancelPendingUpdate(mapEntry);
13748
+ mapEntry.referenceContainer = referenceContainer;
13749
+ /**
13750
+ * Arm the retain flag only here: it means "a replacement reference
13751
+ * container is awaiting layout", so the 0x0 early-return below
13752
+ * holds the last good box rather than blanking the panel mid-move.
13753
+ * Arming it on every re-attach instead would make a container that
13754
+ * genuinely collapses to zero keep its stale geometry forever.
13755
+ *
13756
+ * Gated on `positioned` because an overlay that has never been
13757
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13758
+ * defaults to 100%/100% — retaining "previous" geometry there would
13759
+ * pin the content over the whole container.
13760
+ */
13761
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13762
+ }
13763
+ const generation = mapEntry.generation;
13764
+ const focusContainer = mapEntry.element;
13683
13765
  const contentElement = panel.view.content.element;
13684
13766
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13685
13767
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13686
13768
  const resize = () => {
13687
13769
  const panelId = panel.api.id;
13688
- if (this.pendingUpdates.has(panelId)) return;
13689
- this.pendingUpdates.add(panelId);
13690
- requestAnimationFrame(() => {
13770
+ const scheduling = this.map[panelId];
13771
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13772
+ if (scheduling.pendingUpdate !== void 0) return;
13773
+ /**
13774
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13775
+ * shimmed to run inline by some hosts (and by several suites here),
13776
+ * in which case the callback below clears `pendingUpdate` while
13777
+ * this call is still on the stack — assigning the handle afterwards
13778
+ * would resurrect a slot that is already spent and block every
13779
+ * later reposition. The sentinel is replaced by the real handle
13780
+ * only if the frame has not already run.
13781
+ */
13782
+ scheduling.pendingUpdate = SCHEDULED;
13783
+ const handle = requestAnimationFrame(() => {
13691
13784
  var _entry$forceVisible;
13692
- this.pendingUpdates.delete(panelId);
13693
13785
  const entry = this.map[panelId];
13694
- if (this.isDisposed || !entry) return;
13786
+ if (entry) entry.pendingUpdate = void 0;
13787
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13695
13788
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13696
13789
  const clip = entry.clip;
13697
13790
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13700,6 +13793,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13700
13793
  const top = box.top - box2.top;
13701
13794
  const width = box.width;
13702
13795
  const height = box.height;
13796
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13797
+ if (!panel.api.isVisible && !forceVisible) {
13798
+ focusContainer.style.visibility = "hidden";
13799
+ focusContainer.style.pointerEvents = "none";
13800
+ }
13801
+ return;
13802
+ }
13803
+ entry.retainPreviousGeometry = false;
13804
+ entry.positioned = true;
13703
13805
  focusContainer.style.left = `${left}px`;
13704
13806
  focusContainer.style.top = `${top}px`;
13705
13807
  focusContainer.style.width = `${width}px`;
@@ -13728,11 +13830,30 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13728
13830
  } else focusContainer.style.clipPath = "";
13729
13831
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13730
13832
  });
13833
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13731
13834
  };
13732
13835
  const visibilityChanged = () => {
13733
13836
  if (panel.api.isVisible) {
13837
+ var _this$map$panel$api$i;
13734
13838
  this.positionCache.invalidate();
13735
13839
  resize();
13840
+ /**
13841
+ * Existing geometry is safe to show while its replacement lays
13842
+ * out. `retainPreviousGeometry` is only set when geometry has
13843
+ * actually been written (see `attach`), so this can never
13844
+ * un-hide an overlay whose left/top/width/height are unset —
13845
+ * `.dv-render-overlay` is 100%/100% by default and would
13846
+ * otherwise cover the whole dock.
13847
+ *
13848
+ * The retained geometry is the panel's position in the previous
13849
+ * layout, so restoring a layout that moves the panel does paint
13850
+ * it at stale coordinates until the reposition lands. That is
13851
+ * bounded to the next frame now that a superseded `attach` can
13852
+ * no longer swallow the reposition (see `attach`); showing
13853
+ * stale-but-real geometry for a frame is the deliberate
13854
+ * trade-off against blanking the panel for the whole rebuild.
13855
+ */
13856
+ 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 = "";
13736
13857
  focusContainer.style.pointerEvents = "";
13737
13858
  } else {
13738
13859
  focusContainer.style.visibility = "hidden";
@@ -13955,7 +14076,17 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13955
14076
  window: externalWindow
13956
14077
  });
13957
14078
  return new Promise((resolve, reject) => {
13958
- externalWindow.addEventListener("unload", (e) => {});
14079
+ externalWindow.addEventListener("unload", () => {});
14080
+ /**
14081
+ * `load` is the only event that resolves this promise with a
14082
+ * container, so a window that goes away first would leave the
14083
+ * caller awaiting a promise that never settles - and
14084
+ * `addPopoutGroup` holds its layout transaction open until it does.
14085
+ * Settle with `null` on close, the same signal a blocked popup
14086
+ * gives and one the caller already handles. `resolve` after the
14087
+ * fact is a no-op, so a `load` that arrived first still wins.
14088
+ */
14089
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13959
14090
  externalWindow.addEventListener("load", () => {
13960
14091
  /**
13961
14092
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -15373,6 +15504,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15373
15504
  this._onWillDrop = new Emitter();
15374
15505
  this.onWillDrop = this._onWillDrop.event;
15375
15506
  this._mutationDepth = 0;
15507
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15376
15508
  this._origin = "user";
15377
15509
  this._originDepth = 0;
15378
15510
  this._onWillMutateLayout = new Emitter();
@@ -15470,7 +15602,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15470
15602
  toggleClass(this.element, "dv-debug", !!options.debug);
15471
15603
  this.updateTheme();
15472
15604
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15473
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15605
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15474
15606
  var _this$rootDropTargetC2, _this$floatingDropTar;
15475
15607
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15476
15608
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15544,7 +15676,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15544
15676
  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;
15545
15677
  }
15546
15678
  addPopoutGroup(itemToPopout, options) {
15547
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15679
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15548
15680
  }
15549
15681
  /** Enumerate the popout groups currently open in their own windows. */
15550
15682
  getPopouts() {
@@ -15641,15 +15773,32 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15641
15773
  }
15642
15774
  };
15643
15775
  let floatingBox;
15644
- 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(() => {
15645
- const panel = referenceGroup.model.removePanel(itemToPopout);
15646
- group.model.openPanel(panel);
15647
- });
15648
- else {
15776
+ /**
15777
+ * Popping out rehomes panels into `group`, so like the floating
15778
+ * case it owes the caller an `onDidMovePanel` per panel. The
15779
+ * events are deferred until the window is wired up so listeners
15780
+ * observe the panels' final `popout` location.
15781
+ */
15782
+ let movedPanels = [];
15783
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15784
+ const sourceGroup = itemToPopout.group;
15785
+ this.movingLock(() => {
15786
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15787
+ group.model.openPanel(panel);
15788
+ });
15789
+ movedPanels = [{
15790
+ panel: itemToPopout,
15791
+ from: sourceGroup
15792
+ }];
15793
+ } else {
15649
15794
  this.movingLock(() => moveGroupWithoutDestroying({
15650
15795
  from: referenceGroup,
15651
15796
  to: group
15652
15797
  }));
15798
+ movedPanels = group.panels.map((panel) => ({
15799
+ panel,
15800
+ from: referenceGroup
15801
+ }));
15653
15802
  switch (referenceLocation) {
15654
15803
  case "grid":
15655
15804
  referenceGroup.api.setVisible(false);
@@ -15758,6 +15907,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15758
15907
  group: value.popoutGroup,
15759
15908
  window: value.getWindow()
15760
15909
  });
15910
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15761
15911
  return true;
15762
15912
  }).catch((err) => {
15763
15913
  console.error("dockview: failed to create popout.", err);
@@ -15835,6 +15985,16 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15835
15985
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15836
15986
  const anchorPresent = members.includes(group);
15837
15987
  const anchorIsSoleMember = anchorPresent && members.length === 1;
15988
+ /**
15989
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
15990
+ * window owes the caller the same: every panel in it is rehomed, into
15991
+ * the reference group the popout left behind or into a fresh grid slot.
15992
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
15993
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
15994
+ * events are deferred to the end so listeners observe the panels at
15995
+ * their final group and location.
15996
+ */
15997
+ const movedPanels = [];
15838
15998
  if (genuineClose) for (const member of members) {
15839
15999
  if (member === group) continue;
15840
16000
  this.movingLock(() => {
@@ -15845,12 +16005,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15845
16005
  });
15846
16006
  this.redockGroupToMainGrid(member);
15847
16007
  });
16008
+ for (const panel of member.panels) movedPanels.push({
16009
+ panel,
16010
+ from: member
16011
+ });
15848
16012
  }
15849
16013
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16014
+ const rehomed = [...group.panels];
15850
16015
  this.movingLock(() => moveGroupWithoutDestroying({
15851
16016
  from: group,
15852
16017
  to: referenceGroup
15853
16018
  }));
16019
+ for (const panel of rehomed) movedPanels.push({
16020
+ panel,
16021
+ from: group
16022
+ });
15854
16023
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15855
16024
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15856
16025
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15881,10 +16050,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15881
16050
  this.movingLock(() => {
15882
16051
  this.doAddGroup(group, [0]);
15883
16052
  });
16053
+ for (const panel of group.panels) movedPanels.push({
16054
+ panel,
16055
+ from: group
16056
+ });
15884
16057
  }
15885
16058
  this.doSetGroupAndPanelActive(group);
15886
16059
  }
15887
16060
  disposePopoutGridview();
16061
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15888
16062
  }
15889
16063
  addFloatingGroup(item, options) {
15890
16064
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15893,7 +16067,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15893
16067
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15894
16068
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15895
16069
  let group;
16070
+ /**
16071
+ * Floating relocates panels exactly like a drop onto the grid does, so
16072
+ * it owes the caller an `onDidMovePanel` per panel. The events are
16073
+ * deferred until the window is mounted below: only then do the panels
16074
+ * report their final `floating` location.
16075
+ */
16076
+ let movedPanels = [];
15896
16077
  if (item instanceof DockviewPanel) {
16078
+ const sourceGroup = item.group;
15897
16079
  group = this.createGroup();
15898
16080
  this._onDidAddGroup.fire(group);
15899
16081
  this.movingLock(() => this.removePanel(item, {
@@ -15902,6 +16084,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15902
16084
  skipSetActiveGroup: true
15903
16085
  }));
15904
16086
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16087
+ movedPanels = [{
16088
+ panel: item,
16089
+ from: sourceGroup
16090
+ }];
15905
16091
  } else {
15906
16092
  var _this$_popoutWindowSe8;
15907
16093
  group = item;
@@ -15918,11 +16104,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15918
16104
  });
15919
16105
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15920
16106
  group = popoutReferenceGroup;
15921
- } else this.doRemoveGroup(item, {
15922
- skipDispose: true,
15923
- skipPopoutReturn: true,
15924
- skipPopoutAssociated: false
15925
- });
16107
+ movedPanels = group.panels.map((panel) => ({
16108
+ panel,
16109
+ from: item
16110
+ }));
16111
+ } else {
16112
+ this.doRemoveGroup(item, {
16113
+ skipDispose: true,
16114
+ skipPopoutReturn: true,
16115
+ skipPopoutAssociated: false
16116
+ });
16117
+ movedPanels = group.panels.map((panel) => ({
16118
+ panel,
16119
+ from: group
16120
+ }));
16121
+ }
15926
16122
  }
15927
16123
  function getAnchoredBox() {
15928
16124
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15955,6 +16151,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15955
16151
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15956
16152
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15957
16153
  });
16154
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15958
16155
  }
15959
16156
  /**
15960
16157
  * Build an empty gridview configured to match the main grid's styling, for
@@ -16386,35 +16583,119 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16386
16583
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16387
16584
  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");
16388
16585
  const existingPanels = /* @__PURE__ */ new Map();
16389
- let tempGroup;
16586
+ const temporaryGroups = /* @__PURE__ */ new Map();
16587
+ const stagedPanels = [];
16588
+ const temporaryGroupDisposables = [];
16589
+ /**
16590
+ * The staging groups are detached from `_groups` and never enter the
16591
+ * grid, so nothing else will ever tear them down.
16592
+ *
16593
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16594
+ * throwing renderer must not abort the rest of the cleanup — that would
16595
+ * re-introduce the very leak this reclaims — nor replace the
16596
+ * deserialization error the caller is being given. Draining the list
16597
+ * also makes this safe to call more than once.
16598
+ */
16599
+ const disposeTemporaryGroups = () => {
16600
+ for (const disposable of temporaryGroupDisposables) try {
16601
+ disposable.dispose();
16602
+ } catch (err) {
16603
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16604
+ }
16605
+ temporaryGroupDisposables.length = 0;
16606
+ };
16390
16607
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16391
16608
  /**
16392
- * What are we doing here?
16393
- *
16394
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16395
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16609
+ * Visible, always-rendered panels need individual staging groups
16610
+ * to remain active. Other reused panels can share a staging group.
16611
+ * The staging groups are excluded from the layout clear below.
16396
16612
  */
16397
- tempGroup = this.createGroup();
16398
- this._groups.delete(tempGroup.api.id);
16399
16613
  const newPanels = Object.keys(data.panels);
16400
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16401
- this.movingLock(() => {
16402
- Array.from(existingPanels.values()).forEach((panel) => {
16403
- this.moveGroupOrPanel({
16404
- from: {
16405
- groupId: panel.api.group.api.id,
16406
- panelId: panel.api.id
16407
- },
16408
- to: {
16409
- group: tempGroup,
16410
- position: "center"
16411
- },
16412
- keepEmptyGroups: true
16614
+ let sharedTemporaryGroup;
16615
+ const createTemporaryGroup = () => {
16616
+ const temporaryGroup = this.createGroup();
16617
+ /**
16618
+ * Removing the group from `_groups` also drops the record's
16619
+ * `CompositeDisposable`, so capture it first and tear both it
16620
+ * and the group down in the `finally` below. Left undisposed a
16621
+ * staging group leaks its `ResizeObserver`, its
16622
+ * `onDidOptionsChange` subscription (retained by this
16623
+ * component's emitter) and the watermark its model mounts when
16624
+ * the group empties — once per reused panel, per `fromJSON`.
16625
+ */
16626
+ const record = this._groups.get(temporaryGroup.api.id);
16627
+ this._groups.delete(temporaryGroup.api.id);
16628
+ temporaryGroupDisposables.push(Disposable.from(() => {
16629
+ /**
16630
+ * Reclaim the group's own resources, never its panels.
16631
+ * Staging is driven by the ids in `data.panels` while
16632
+ * reclaiming is driven by the group `views` that
16633
+ * reference them, so a panel whose state is present but
16634
+ * unreferenced is still staged here — and `dispose()`
16635
+ * on a non-empty group reaches the consumer's
16636
+ * `IContentRenderer.dispose()`. Emptying the group
16637
+ * first leaves those panels exactly as they were before
16638
+ * this teardown existed, which also keeps this safe
16639
+ * against any future path that rebuilds a panel under
16640
+ * an id that is live in the new layout.
16641
+ */
16642
+ this.movingLock(() => {
16643
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16644
+ });
16645
+ record === null || record === void 0 || record.disposable.dispose();
16646
+ temporaryGroup.dispose();
16647
+ }));
16648
+ return temporaryGroup;
16649
+ };
16650
+ /**
16651
+ * Staging and the clear below run before the deserialization
16652
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16653
+ * renderer teardown during `clear`) would escape without reclaiming
16654
+ * the staging groups already created.
16655
+ *
16656
+ * The creation loop is inside the guard too, not just the moves and
16657
+ * the clear: `createGroup()` reaches consumer code of its own —
16658
+ * `initialize()` mounts the watermark through
16659
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16660
+ * otherwise strand the n-1 staging groups already built.
16661
+ */
16662
+ try {
16663
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16664
+ existingPanels.set(panel.api.id, panel);
16665
+ let temporaryGroup;
16666
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16667
+ else {
16668
+ var _sharedTemporaryGroup;
16669
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16670
+ temporaryGroup = sharedTemporaryGroup;
16671
+ }
16672
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16673
+ stagedPanels.push({
16674
+ panel,
16675
+ temporaryGroup
16676
+ });
16677
+ }
16678
+ this.movingLock(() => {
16679
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16680
+ this.moveGroupOrPanel({
16681
+ from: {
16682
+ groupId: panel.api.group.api.id,
16683
+ panelId: panel.api.id
16684
+ },
16685
+ to: {
16686
+ group: temporaryGroup,
16687
+ position: "center"
16688
+ },
16689
+ keepEmptyGroups: true
16690
+ });
16413
16691
  });
16414
16692
  });
16415
- });
16416
- }
16417
- this.clear();
16693
+ this.clear();
16694
+ } catch (err) {
16695
+ disposeTemporaryGroups();
16696
+ throw err;
16697
+ }
16698
+ } else this.clear();
16418
16699
  const { grid, panels, activeGroup } = data;
16419
16700
  try {
16420
16701
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16446,9 +16727,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16446
16727
  * due to a corruption of input data.
16447
16728
  */
16448
16729
  const existingPanel = existingPanels.get(child);
16449
- if (tempGroup && existingPanel) {
16730
+ const temporaryGroup = temporaryGroups.get(child);
16731
+ if (temporaryGroup && existingPanel) {
16450
16732
  this.movingLock(() => {
16451
- tempGroup.model.removePanel(existingPanel);
16733
+ temporaryGroup.model.removePanel(existingPanel);
16452
16734
  });
16453
16735
  createdPanels.push(existingPanel);
16454
16736
  existingPanel.updateFromStateModel(panels[child]);
@@ -16478,7 +16760,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16478
16760
  return createGroupFromSerializedState(node.data);
16479
16761
  } });
16480
16762
  this._layoutFromShell(width, height);
16481
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16763
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16482
16764
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16483
16765
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16484
16766
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16515,6 +16797,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16515
16797
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16516
16798
  */
16517
16799
  throw err;
16800
+ } finally {
16801
+ disposeTemporaryGroups();
16518
16802
  }
16519
16803
  this.debouncedUpdateAllPositions();
16520
16804
  this._onDidLayoutFromJSON.fire();
@@ -16537,7 +16821,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16537
16821
  members
16538
16822
  };
16539
16823
  }
16540
- deserializeEdgeGroups(edgeGroups, panels) {
16824
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16541
16825
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16542
16826
  if (!edgeService) return;
16543
16827
  for (const _position of [
@@ -16567,13 +16851,40 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16567
16851
  if (groupState) {
16568
16852
  const { views, activeView } = groupState;
16569
16853
  const createdPanels = [];
16570
- for (const panelId of views) if (panels[panelId]) {
16571
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16572
- createdPanels.push(panel);
16854
+ for (const panelId of views) {
16855
+ if (!panels[panelId]) continue;
16856
+ /**
16857
+ * Reclaim a staged panel rather than rebuilding it, the
16858
+ * same way the grid path does. Deserializing here instead
16859
+ * would honour `reuseExistingPanels` everywhere except edge
16860
+ * groups: the live panel keeps sitting in its staging group
16861
+ * while a second panel is built under the same id, so the
16862
+ * consumer's renderer for the original is orphaned — never
16863
+ * disposed, and its element left inside the id-keyed
16864
+ * overlay entry the replacement now shares.
16865
+ */
16866
+ const existingPanel = existingPanels.get(panelId);
16867
+ const temporaryGroup = temporaryGroups.get(panelId);
16868
+ if (temporaryGroup && existingPanel) {
16869
+ this.movingLock(() => {
16870
+ temporaryGroup.model.removePanel(existingPanel);
16871
+ });
16872
+ createdPanels.push(existingPanel);
16873
+ existingPanel.updateFromStateModel(panels[panelId]);
16874
+ } else {
16875
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16876
+ createdPanels.push(panel);
16877
+ }
16573
16878
  }
16574
16879
  for (const panel of createdPanels) {
16575
16880
  const isActive = activeView === panel.id;
16576
- edgeGroup.model.openPanel(panel, {
16881
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16882
+ edgeGroup.model.openPanel(panel, {
16883
+ skipSetActive: !isActive,
16884
+ skipSetGroupActive: true
16885
+ });
16886
+ });
16887
+ else edgeGroup.model.openPanel(panel, {
16577
16888
  skipSetActive: !isActive,
16578
16889
  skipSetGroupActive: true
16579
16890
  });
@@ -16997,22 +17308,100 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16997
17308
  * outermost mutation.
16998
17309
  */
16999
17310
  mutation(kind, func) {
17000
- const outer = this._mutationDepth === 0;
17311
+ const close = this.openMutation(kind);
17312
+ try {
17313
+ return func();
17314
+ } finally {
17315
+ close();
17316
+ }
17317
+ }
17318
+ /**
17319
+ * `mutation()` for an operation whose work continues after the synchronous
17320
+ * call returns. The transaction stays open until the returned promise
17321
+ * settles, so everything the operation does - the group it adds, the panels
17322
+ * it relocates - lands inside the bracket, matching the synchronous
17323
+ * move / float paths.
17324
+ */
17325
+ mutationAsync(kind, func) {
17326
+ var _this = this;
17327
+ return _asyncToGenerator(function* () {
17328
+ const close = _this.openMutation(kind);
17329
+ try {
17330
+ return yield func();
17331
+ } finally {
17332
+ close();
17333
+ }
17334
+ })();
17335
+ }
17336
+ /**
17337
+ * Open a transaction and return the function that closes it. Shared by the
17338
+ * synchronous and asynchronous brackets; the returned function must be
17339
+ * called exactly once.
17340
+ *
17341
+ * Both ends key off the depth counter reaching zero rather than off which
17342
+ * bracket opened first. For synchronous nesting the two are the same thing
17343
+ * - brackets close in the order they opened - but asynchronous transactions
17344
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17345
+ * once, or a restore staggering several), and there the first to open is
17346
+ * not the last to close. Reporting on the opener would fire `didMutate`
17347
+ * while the other transaction was still doing structural work, which is the
17348
+ * very thing the async bracket exists to prevent. Overlapping transactions
17349
+ * therefore report as one, tagged with the kind of the last to finish.
17350
+ */
17351
+ openMutation(kind) {
17001
17352
  const origin = this._origin;
17002
- if (outer) this._onWillMutateLayout.fire({
17353
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
17003
17354
  kind,
17004
17355
  origin
17005
17356
  });
17006
17357
  this._mutationDepth++;
17007
- try {
17008
- return func();
17009
- } finally {
17358
+ return () => {
17010
17359
  this._mutationDepth--;
17011
- if (outer) this._onDidMutateLayout.fire({
17012
- kind,
17013
- origin
17014
- });
17360
+ if (this._mutationDepth === 0) {
17361
+ this.flushLocationChanges();
17362
+ this._onDidMutateLayout.fire({
17363
+ kind,
17364
+ origin
17365
+ });
17366
+ }
17367
+ };
17368
+ }
17369
+ /**
17370
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17371
+ * transaction, or fire it immediately when no transaction is in flight.
17372
+ *
17373
+ * Relocating a panel touches its location twice: once as it is reparented
17374
+ * into the destination group - which, when that group has just been created
17375
+ * for a floating or popout window, has not been told where it lives yet -
17376
+ * and once as the group is tagged with its final location. Reporting each
17377
+ * signal as it happens therefore leaks an intermediate `grid` location the
17378
+ * panel was never in, at a moment when it belongs to neither group's panel
17379
+ * list and so is missing from `api.panels`.
17380
+ *
17381
+ * Deferring collapses those signals into a single event carrying the
17382
+ * settled location. Note the signals are deliberately *not* compared
17383
+ * against the panel's previous location: a panel can move between two
17384
+ * floating windows without its location type changing, and this event is
17385
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17386
+ * z-index against its new host window.
17387
+ */
17388
+ deferLocationChange(key, fire) {
17389
+ if (this._mutationDepth === 0) {
17390
+ fire();
17391
+ return;
17015
17392
  }
17393
+ this._pendingLocationChanges.set(key, fire);
17394
+ }
17395
+ /**
17396
+ * Report every panel whose location moved during the transaction that has
17397
+ * just closed. Each callback reads the panel's location at this point, so
17398
+ * what it reports is where the panel ended up.
17399
+ */
17400
+ flushLocationChanges() {
17401
+ if (this._pendingLocationChanges.size === 0) return;
17402
+ const pending = Array.from(this._pendingLocationChanges.values());
17403
+ this._pendingLocationChanges.clear();
17404
+ for (const fire of pending) fire();
17016
17405
  }
17017
17406
  /**
17018
17407
  * The origin of the operation currently in progress (`'user'` by default).
@@ -17053,6 +17442,18 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17053
17442
  origin: this._origin
17054
17443
  });
17055
17444
  }
17445
+ /**
17446
+ * Announce that `panel` has finished relocating. Must be called once the
17447
+ * move has settled: `to` is read from the panel's current group, so an
17448
+ * early call would report the source group as the destination.
17449
+ */
17450
+ fireDidMovePanel(panel, from) {
17451
+ this._onDidMovePanel.fire({
17452
+ panel,
17453
+ from,
17454
+ to: panel.group
17455
+ });
17456
+ }
17056
17457
  moveGroupOrPanel(options) {
17057
17458
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
17058
17459
  }
@@ -17113,10 +17514,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17113
17514
  });
17114
17515
  });
17115
17516
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
17116
- this._onDidMovePanel.fire({
17117
- panel: removedPanel,
17118
- from: sourceGroup
17119
- });
17517
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17120
17518
  } else {
17121
17519
  /**
17122
17520
  * Dropping a panel to the extremities of a group which will place that panel
@@ -17135,10 +17533,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17135
17533
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
17136
17534
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
17137
17535
  this.gridview.moveView(sourceParentLocation, from, to);
17138
- this._onDidMovePanel.fire({
17139
- panel: this.getGroupPanel(sourceItemId),
17140
- from: sourceGroup
17141
- });
17536
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17142
17537
  return;
17143
17538
  }
17144
17539
  }
@@ -17169,10 +17564,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17169
17564
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17170
17565
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
17171
17566
  this.doSetGroupAndPanelActive(newGroup);
17172
- this._onDidMovePanel.fire({
17173
- panel: this.getGroupPanel(sourceItemId),
17174
- from: sourceGroup
17175
- });
17567
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17176
17568
  return;
17177
17569
  }
17178
17570
  if (sourceGroup.api.location.type === "edge") {
@@ -17189,10 +17581,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17189
17581
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17190
17582
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17191
17583
  this.doSetGroupAndPanelActive(newGroup);
17192
- this._onDidMovePanel.fire({
17193
- panel: removedPanel,
17194
- from: sourceGroup
17195
- });
17584
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17196
17585
  return;
17197
17586
  }
17198
17587
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -17204,10 +17593,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17204
17593
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17205
17594
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
17206
17595
  this.doSetGroupAndPanelActive(targetGroup);
17207
- this._onDidMovePanel.fire({
17208
- panel: this.getGroupPanel(sourceItemId),
17209
- from: sourceGroup
17210
- });
17596
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17211
17597
  } else {
17212
17598
  /**
17213
17599
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -17222,10 +17608,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17222
17608
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17223
17609
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17224
17610
  this.doSetGroupAndPanelActive(group);
17225
- this._onDidMovePanel.fire({
17226
- panel: removedPanel,
17227
- from: sourceGroup
17228
- });
17611
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17229
17612
  }
17230
17613
  }
17231
17614
  }
@@ -17260,10 +17643,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17260
17643
  });
17261
17644
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17262
17645
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17263
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17264
- panel,
17265
- from: sourceGroup
17266
- });
17646
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17267
17647
  };
17268
17648
  let targetGroup;
17269
17649
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -17284,10 +17664,12 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17284
17664
  this.mutation("maximize", () => super.exitMaximizedGroup());
17285
17665
  }
17286
17666
  _doMoveGroup(options) {
17667
+ var _mergedPanels;
17287
17668
  const from = options.from.group;
17288
17669
  const to = options.to.group;
17289
17670
  const target = options.to.position;
17290
17671
  let source = from;
17672
+ let mergedPanels;
17291
17673
  if (target === "center") {
17292
17674
  const activePanel = from.activePanel;
17293
17675
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17305,6 +17687,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17305
17687
  skipSetGroupActive: true
17306
17688
  });
17307
17689
  });
17690
+ mergedPanels = panels;
17308
17691
  for (const snapshot of tabGroupSnapshots) {
17309
17692
  const newTabGroup = to.model.createTabGroup({
17310
17693
  label: snapshot.label,
@@ -17394,11 +17777,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17394
17777
  this.setGroupLocationForRoot(source, destGridview);
17395
17778
  }
17396
17779
  }
17397
- source.panels.forEach((panel) => {
17398
- this._onDidMovePanel.fire({
17399
- panel,
17400
- from
17401
- });
17780
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17781
+ this.fireDidMovePanel(panel, from);
17402
17782
  });
17403
17783
  this.debouncedUpdateAllPositions();
17404
17784
  if (options.skipSetActive === false) {