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
  */
@@ -10522,7 +10522,11 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10522
10522
  this.panels.forEach((panel) => {
10523
10523
  this.rerender(panel);
10524
10524
  });
10525
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10525
+ if (this._activePanel) {
10526
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10527
+ const { width, height } = this.contentDimensions();
10528
+ this._activePanel.layout(width, height);
10529
+ }
10526
10530
  }
10527
10531
  get renderContainer() {
10528
10532
  var _this$_overwriteRende;
@@ -11487,7 +11491,7 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11487
11491
  this._group = value;
11488
11492
  this._onDidGroupChange.fire({});
11489
11493
  this.setupGroupEventListeners(oldGroup);
11490
- this._onDidLocationChange.fire({ location: this.group.api.location });
11494
+ this.fireLocationChange();
11491
11495
  }
11492
11496
  }
11493
11497
  get group() {
@@ -11563,6 +11567,23 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11563
11567
  exitMaximized() {
11564
11568
  this.group.api.exitMaximized();
11565
11569
  }
11570
+ /**
11571
+ * Report that this panel's location may have moved.
11572
+ *
11573
+ * A relocation touches the location more than once - the panel is
11574
+ * reparented into the destination group, then that group is tagged with
11575
+ * the location it ends up at - so the event is coalesced to the end of the
11576
+ * enclosing layout mutation and reports where the panel actually settled.
11577
+ * Firing each signal as it happened surfaced an intermediate `grid`
11578
+ * location the panel was never in, at a point where it was in neither
11579
+ * group's panel list. Outside a mutation this fires straight away.
11580
+ */
11581
+ fireLocationChange() {
11582
+ this.accessor.deferLocationChange(this, () => {
11583
+ if (this.isDisposed) return;
11584
+ this._onDidLocationChange.fire({ location: this.location });
11585
+ });
11586
+ }
11566
11587
  setupGroupEventListeners(previousGroup) {
11567
11588
  var _previousGroup$isActi;
11568
11589
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11571,9 +11592,9 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11571
11592
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11572
11593
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11573
11594
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11574
- }), this.group.api.onDidLocationChange((event) => {
11595
+ }), this.group.api.onDidLocationChange(() => {
11575
11596
  if (this.group !== this.panel.group) return;
11576
- this._onDidLocationChange.fire(event);
11597
+ this.fireLocationChange();
11577
11598
  }), this.group.api.onDidActiveChange(() => {
11578
11599
  if (this.group !== this.panel.group) return;
11579
11600
  if (_trackGroupActive !== this.isGroupActive) {
@@ -13599,6 +13620,13 @@ var PositionCache = class {
13599
13620
  });
13600
13621
  }
13601
13622
  };
13623
+ /**
13624
+ * Placeholder handle marking "a reposition frame has been requested but the
13625
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13626
+ * practice — it is replaced within the same synchronous block — and harmless
13627
+ * if it were, since no frame carries this id.
13628
+ */
13629
+ const SCHEDULED = -1;
13602
13630
  function createFocusableElement() {
13603
13631
  const element = document.createElement("div");
13604
13632
  element.tabIndex = -1;
@@ -13611,12 +13639,13 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13611
13639
  this.accessor = accessor;
13612
13640
  this.map = {};
13613
13641
  this._disposed = false;
13642
+ this._generation = 0;
13614
13643
  this.positionCache = new PositionCache();
13615
- this.pendingUpdates = /* @__PURE__ */ new Set();
13616
13644
  this.addDisposables(Disposable.from(() => {
13617
13645
  for (const value of Object.values(this.map)) {
13618
13646
  value.disposable.dispose();
13619
13647
  value.destroy.dispose();
13648
+ this.cancelPendingUpdate(value);
13620
13649
  }
13621
13650
  this._disposed = true;
13622
13651
  }));
@@ -13643,11 +13672,24 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13643
13672
  this.positionCache.invalidate();
13644
13673
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13645
13674
  }
13675
+ /**
13676
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13677
+ * bound to the reference container that was current when it was scheduled,
13678
+ * so it must be discarded whenever that container stops being the one the
13679
+ * overlay should track.
13680
+ */
13681
+ cancelPendingUpdate(entry) {
13682
+ if (entry.pendingUpdate !== void 0) {
13683
+ cancelAnimationFrame(entry.pendingUpdate);
13684
+ entry.pendingUpdate = void 0;
13685
+ }
13686
+ }
13646
13687
  detatch(panel) {
13647
13688
  if (this.map[panel.api.id]) {
13648
- const { disposable, destroy } = this.map[panel.api.id];
13649
- disposable.dispose();
13650
- destroy.dispose();
13689
+ const entry = this.map[panel.api.id];
13690
+ entry.disposable.dispose();
13691
+ entry.destroy.dispose();
13692
+ this.cancelPendingUpdate(entry);
13651
13693
  delete this.map[panel.api.id];
13652
13694
  return true;
13653
13695
  }
@@ -13663,22 +13705,73 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13663
13705
  panel,
13664
13706
  disposable: Disposable.NONE,
13665
13707
  destroy: Disposable.NONE,
13666
- element
13708
+ element,
13709
+ retainPreviousGeometry: false,
13710
+ positioned: false,
13711
+ generation: ++this._generation
13667
13712
  };
13668
13713
  }
13669
- const focusContainer = this.map[panel.api.id].element;
13714
+ const mapEntry = this.map[panel.api.id];
13715
+ /**
13716
+ * A *change* of reference container supersedes the previous `attach`:
13717
+ * take a fresh generation so its `resize` closure can no longer run,
13718
+ * and drop the frame it queued against the old container. During
13719
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13720
+ * detached staging group measuring 0x0, so leaving its frame in flight
13721
+ * both wastes the update and delays the reposition against the real one.
13722
+ *
13723
+ * Re-attaching over the *same* container (re-open, active panel change)
13724
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13725
+ * (the auto-hide peek) schedules a frame carrying the sticky
13726
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13727
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13728
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13729
+ * nothing. A newly created entry already holds a fresh generation, so
13730
+ * the stale closures a `detatch` left behind are fenced off either way.
13731
+ */
13732
+ if (mapEntry.referenceContainer !== referenceContainer) {
13733
+ mapEntry.generation = ++this._generation;
13734
+ this.cancelPendingUpdate(mapEntry);
13735
+ mapEntry.referenceContainer = referenceContainer;
13736
+ /**
13737
+ * Arm the retain flag only here: it means "a replacement reference
13738
+ * container is awaiting layout", so the 0x0 early-return below
13739
+ * holds the last good box rather than blanking the panel mid-move.
13740
+ * Arming it on every re-attach instead would make a container that
13741
+ * genuinely collapses to zero keep its stale geometry forever.
13742
+ *
13743
+ * Gated on `positioned` because an overlay that has never been
13744
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13745
+ * defaults to 100%/100% — retaining "previous" geometry there would
13746
+ * pin the content over the whole container.
13747
+ */
13748
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13749
+ }
13750
+ const generation = mapEntry.generation;
13751
+ const focusContainer = mapEntry.element;
13670
13752
  const contentElement = panel.view.content.element;
13671
13753
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13672
13754
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13673
13755
  const resize = () => {
13674
13756
  const panelId = panel.api.id;
13675
- if (this.pendingUpdates.has(panelId)) return;
13676
- this.pendingUpdates.add(panelId);
13677
- requestAnimationFrame(() => {
13757
+ const scheduling = this.map[panelId];
13758
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13759
+ if (scheduling.pendingUpdate !== void 0) return;
13760
+ /**
13761
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13762
+ * shimmed to run inline by some hosts (and by several suites here),
13763
+ * in which case the callback below clears `pendingUpdate` while
13764
+ * this call is still on the stack — assigning the handle afterwards
13765
+ * would resurrect a slot that is already spent and block every
13766
+ * later reposition. The sentinel is replaced by the real handle
13767
+ * only if the frame has not already run.
13768
+ */
13769
+ scheduling.pendingUpdate = SCHEDULED;
13770
+ const handle = requestAnimationFrame(() => {
13678
13771
  var _entry$forceVisible;
13679
- this.pendingUpdates.delete(panelId);
13680
13772
  const entry = this.map[panelId];
13681
- if (this.isDisposed || !entry) return;
13773
+ if (entry) entry.pendingUpdate = void 0;
13774
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13682
13775
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13683
13776
  const clip = entry.clip;
13684
13777
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13687,6 +13780,15 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13687
13780
  const top = box.top - box2.top;
13688
13781
  const width = box.width;
13689
13782
  const height = box.height;
13783
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13784
+ if (!panel.api.isVisible && !forceVisible) {
13785
+ focusContainer.style.visibility = "hidden";
13786
+ focusContainer.style.pointerEvents = "none";
13787
+ }
13788
+ return;
13789
+ }
13790
+ entry.retainPreviousGeometry = false;
13791
+ entry.positioned = true;
13690
13792
  focusContainer.style.left = `${left}px`;
13691
13793
  focusContainer.style.top = `${top}px`;
13692
13794
  focusContainer.style.width = `${width}px`;
@@ -13715,11 +13817,30 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13715
13817
  } else focusContainer.style.clipPath = "";
13716
13818
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13717
13819
  });
13820
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13718
13821
  };
13719
13822
  const visibilityChanged = () => {
13720
13823
  if (panel.api.isVisible) {
13824
+ var _this$map$panel$api$i;
13721
13825
  this.positionCache.invalidate();
13722
13826
  resize();
13827
+ /**
13828
+ * Existing geometry is safe to show while its replacement lays
13829
+ * out. `retainPreviousGeometry` is only set when geometry has
13830
+ * actually been written (see `attach`), so this can never
13831
+ * un-hide an overlay whose left/top/width/height are unset —
13832
+ * `.dv-render-overlay` is 100%/100% by default and would
13833
+ * otherwise cover the whole dock.
13834
+ *
13835
+ * The retained geometry is the panel's position in the previous
13836
+ * layout, so restoring a layout that moves the panel does paint
13837
+ * it at stale coordinates until the reposition lands. That is
13838
+ * bounded to the next frame now that a superseded `attach` can
13839
+ * no longer swallow the reposition (see `attach`); showing
13840
+ * stale-but-real geometry for a frame is the deliberate
13841
+ * trade-off against blanking the panel for the whole rebuild.
13842
+ */
13843
+ 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 = "";
13723
13844
  focusContainer.style.pointerEvents = "";
13724
13845
  } else {
13725
13846
  focusContainer.style.visibility = "hidden";
@@ -13942,7 +14063,17 @@ var PopoutWindow = class extends CompositeDisposable {
13942
14063
  window: externalWindow
13943
14064
  });
13944
14065
  return new Promise((resolve, reject) => {
13945
- externalWindow.addEventListener("unload", (e) => {});
14066
+ externalWindow.addEventListener("unload", () => {});
14067
+ /**
14068
+ * `load` is the only event that resolves this promise with a
14069
+ * container, so a window that goes away first would leave the
14070
+ * caller awaiting a promise that never settles - and
14071
+ * `addPopoutGroup` holds its layout transaction open until it does.
14072
+ * Settle with `null` on close, the same signal a blocked popup
14073
+ * gives and one the caller already handles. `resolve` after the
14074
+ * fact is a no-op, so a `load` that arrived first still wins.
14075
+ */
14076
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13946
14077
  externalWindow.addEventListener("load", () => {
13947
14078
  /**
13948
14079
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -15360,6 +15491,7 @@ var DockviewComponent = class extends BaseGrid {
15360
15491
  this._onWillDrop = new Emitter();
15361
15492
  this.onWillDrop = this._onWillDrop.event;
15362
15493
  this._mutationDepth = 0;
15494
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15363
15495
  this._origin = "user";
15364
15496
  this._originDepth = 0;
15365
15497
  this._onWillMutateLayout = new Emitter();
@@ -15457,7 +15589,7 @@ var DockviewComponent = class extends BaseGrid {
15457
15589
  toggleClass(this.element, "dv-debug", !!options.debug);
15458
15590
  this.updateTheme();
15459
15591
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15460
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15592
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15461
15593
  var _this$rootDropTargetC2, _this$floatingDropTar;
15462
15594
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15463
15595
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15531,7 +15663,7 @@ var DockviewComponent = class extends BaseGrid {
15531
15663
  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;
15532
15664
  }
15533
15665
  addPopoutGroup(itemToPopout, options) {
15534
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15666
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15535
15667
  }
15536
15668
  /** Enumerate the popout groups currently open in their own windows. */
15537
15669
  getPopouts() {
@@ -15628,15 +15760,32 @@ var DockviewComponent = class extends BaseGrid {
15628
15760
  }
15629
15761
  };
15630
15762
  let floatingBox;
15631
- 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(() => {
15632
- const panel = referenceGroup.model.removePanel(itemToPopout);
15633
- group.model.openPanel(panel);
15634
- });
15635
- else {
15763
+ /**
15764
+ * Popping out rehomes panels into `group`, so like the floating
15765
+ * case it owes the caller an `onDidMovePanel` per panel. The
15766
+ * events are deferred until the window is wired up so listeners
15767
+ * observe the panels' final `popout` location.
15768
+ */
15769
+ let movedPanels = [];
15770
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15771
+ const sourceGroup = itemToPopout.group;
15772
+ this.movingLock(() => {
15773
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15774
+ group.model.openPanel(panel);
15775
+ });
15776
+ movedPanels = [{
15777
+ panel: itemToPopout,
15778
+ from: sourceGroup
15779
+ }];
15780
+ } else {
15636
15781
  this.movingLock(() => moveGroupWithoutDestroying({
15637
15782
  from: referenceGroup,
15638
15783
  to: group
15639
15784
  }));
15785
+ movedPanels = group.panels.map((panel) => ({
15786
+ panel,
15787
+ from: referenceGroup
15788
+ }));
15640
15789
  switch (referenceLocation) {
15641
15790
  case "grid":
15642
15791
  referenceGroup.api.setVisible(false);
@@ -15745,6 +15894,7 @@ var DockviewComponent = class extends BaseGrid {
15745
15894
  group: value.popoutGroup,
15746
15895
  window: value.getWindow()
15747
15896
  });
15897
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15748
15898
  return true;
15749
15899
  }).catch((err) => {
15750
15900
  console.error("dockview: failed to create popout.", err);
@@ -15822,6 +15972,16 @@ var DockviewComponent = class extends BaseGrid {
15822
15972
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15823
15973
  const anchorPresent = members.includes(group);
15824
15974
  const anchorIsSoleMember = anchorPresent && members.length === 1;
15975
+ /**
15976
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
15977
+ * window owes the caller the same: every panel in it is rehomed, into
15978
+ * the reference group the popout left behind or into a fresh grid slot.
15979
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
15980
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
15981
+ * events are deferred to the end so listeners observe the panels at
15982
+ * their final group and location.
15983
+ */
15984
+ const movedPanels = [];
15825
15985
  if (genuineClose) for (const member of members) {
15826
15986
  if (member === group) continue;
15827
15987
  this.movingLock(() => {
@@ -15832,12 +15992,21 @@ var DockviewComponent = class extends BaseGrid {
15832
15992
  });
15833
15993
  this.redockGroupToMainGrid(member);
15834
15994
  });
15995
+ for (const panel of member.panels) movedPanels.push({
15996
+ panel,
15997
+ from: member
15998
+ });
15835
15999
  }
15836
16000
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16001
+ const rehomed = [...group.panels];
15837
16002
  this.movingLock(() => moveGroupWithoutDestroying({
15838
16003
  from: group,
15839
16004
  to: referenceGroup
15840
16005
  }));
16006
+ for (const panel of rehomed) movedPanels.push({
16007
+ panel,
16008
+ from: group
16009
+ });
15841
16010
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15842
16011
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15843
16012
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15868,10 +16037,15 @@ var DockviewComponent = class extends BaseGrid {
15868
16037
  this.movingLock(() => {
15869
16038
  this.doAddGroup(group, [0]);
15870
16039
  });
16040
+ for (const panel of group.panels) movedPanels.push({
16041
+ panel,
16042
+ from: group
16043
+ });
15871
16044
  }
15872
16045
  this.doSetGroupAndPanelActive(group);
15873
16046
  }
15874
16047
  disposePopoutGridview();
16048
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15875
16049
  }
15876
16050
  addFloatingGroup(item, options) {
15877
16051
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15880,7 +16054,15 @@ var DockviewComponent = class extends BaseGrid {
15880
16054
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15881
16055
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15882
16056
  let group;
16057
+ /**
16058
+ * Floating relocates panels exactly like a drop onto the grid does, so
16059
+ * it owes the caller an `onDidMovePanel` per panel. The events are
16060
+ * deferred until the window is mounted below: only then do the panels
16061
+ * report their final `floating` location.
16062
+ */
16063
+ let movedPanels = [];
15883
16064
  if (item instanceof DockviewPanel) {
16065
+ const sourceGroup = item.group;
15884
16066
  group = this.createGroup();
15885
16067
  this._onDidAddGroup.fire(group);
15886
16068
  this.movingLock(() => this.removePanel(item, {
@@ -15889,6 +16071,10 @@ var DockviewComponent = class extends BaseGrid {
15889
16071
  skipSetActiveGroup: true
15890
16072
  }));
15891
16073
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16074
+ movedPanels = [{
16075
+ panel: item,
16076
+ from: sourceGroup
16077
+ }];
15892
16078
  } else {
15893
16079
  var _this$_popoutWindowSe8;
15894
16080
  group = item;
@@ -15905,11 +16091,21 @@ var DockviewComponent = class extends BaseGrid {
15905
16091
  });
15906
16092
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15907
16093
  group = popoutReferenceGroup;
15908
- } else this.doRemoveGroup(item, {
15909
- skipDispose: true,
15910
- skipPopoutReturn: true,
15911
- skipPopoutAssociated: false
15912
- });
16094
+ movedPanels = group.panels.map((panel) => ({
16095
+ panel,
16096
+ from: item
16097
+ }));
16098
+ } else {
16099
+ this.doRemoveGroup(item, {
16100
+ skipDispose: true,
16101
+ skipPopoutReturn: true,
16102
+ skipPopoutAssociated: false
16103
+ });
16104
+ movedPanels = group.panels.map((panel) => ({
16105
+ panel,
16106
+ from: group
16107
+ }));
16108
+ }
15913
16109
  }
15914
16110
  function getAnchoredBox() {
15915
16111
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15942,6 +16138,7 @@ var DockviewComponent = class extends BaseGrid {
15942
16138
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15943
16139
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15944
16140
  });
16141
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15945
16142
  }
15946
16143
  /**
15947
16144
  * Build an empty gridview configured to match the main grid's styling, for
@@ -16373,35 +16570,119 @@ var DockviewComponent = class extends BaseGrid {
16373
16570
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16374
16571
  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");
16375
16572
  const existingPanels = /* @__PURE__ */ new Map();
16376
- let tempGroup;
16573
+ const temporaryGroups = /* @__PURE__ */ new Map();
16574
+ const stagedPanels = [];
16575
+ const temporaryGroupDisposables = [];
16576
+ /**
16577
+ * The staging groups are detached from `_groups` and never enter the
16578
+ * grid, so nothing else will ever tear them down.
16579
+ *
16580
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16581
+ * throwing renderer must not abort the rest of the cleanup — that would
16582
+ * re-introduce the very leak this reclaims — nor replace the
16583
+ * deserialization error the caller is being given. Draining the list
16584
+ * also makes this safe to call more than once.
16585
+ */
16586
+ const disposeTemporaryGroups = () => {
16587
+ for (const disposable of temporaryGroupDisposables) try {
16588
+ disposable.dispose();
16589
+ } catch (err) {
16590
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16591
+ }
16592
+ temporaryGroupDisposables.length = 0;
16593
+ };
16377
16594
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16378
16595
  /**
16379
- * What are we doing here?
16380
- *
16381
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16382
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16596
+ * Visible, always-rendered panels need individual staging groups
16597
+ * to remain active. Other reused panels can share a staging group.
16598
+ * The staging groups are excluded from the layout clear below.
16383
16599
  */
16384
- tempGroup = this.createGroup();
16385
- this._groups.delete(tempGroup.api.id);
16386
16600
  const newPanels = Object.keys(data.panels);
16387
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16388
- this.movingLock(() => {
16389
- Array.from(existingPanels.values()).forEach((panel) => {
16390
- this.moveGroupOrPanel({
16391
- from: {
16392
- groupId: panel.api.group.api.id,
16393
- panelId: panel.api.id
16394
- },
16395
- to: {
16396
- group: tempGroup,
16397
- position: "center"
16398
- },
16399
- keepEmptyGroups: true
16601
+ let sharedTemporaryGroup;
16602
+ const createTemporaryGroup = () => {
16603
+ const temporaryGroup = this.createGroup();
16604
+ /**
16605
+ * Removing the group from `_groups` also drops the record's
16606
+ * `CompositeDisposable`, so capture it first and tear both it
16607
+ * and the group down in the `finally` below. Left undisposed a
16608
+ * staging group leaks its `ResizeObserver`, its
16609
+ * `onDidOptionsChange` subscription (retained by this
16610
+ * component's emitter) and the watermark its model mounts when
16611
+ * the group empties — once per reused panel, per `fromJSON`.
16612
+ */
16613
+ const record = this._groups.get(temporaryGroup.api.id);
16614
+ this._groups.delete(temporaryGroup.api.id);
16615
+ temporaryGroupDisposables.push(Disposable.from(() => {
16616
+ /**
16617
+ * Reclaim the group's own resources, never its panels.
16618
+ * Staging is driven by the ids in `data.panels` while
16619
+ * reclaiming is driven by the group `views` that
16620
+ * reference them, so a panel whose state is present but
16621
+ * unreferenced is still staged here — and `dispose()`
16622
+ * on a non-empty group reaches the consumer's
16623
+ * `IContentRenderer.dispose()`. Emptying the group
16624
+ * first leaves those panels exactly as they were before
16625
+ * this teardown existed, which also keeps this safe
16626
+ * against any future path that rebuilds a panel under
16627
+ * an id that is live in the new layout.
16628
+ */
16629
+ this.movingLock(() => {
16630
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16631
+ });
16632
+ record === null || record === void 0 || record.disposable.dispose();
16633
+ temporaryGroup.dispose();
16634
+ }));
16635
+ return temporaryGroup;
16636
+ };
16637
+ /**
16638
+ * Staging and the clear below run before the deserialization
16639
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16640
+ * renderer teardown during `clear`) would escape without reclaiming
16641
+ * the staging groups already created.
16642
+ *
16643
+ * The creation loop is inside the guard too, not just the moves and
16644
+ * the clear: `createGroup()` reaches consumer code of its own —
16645
+ * `initialize()` mounts the watermark through
16646
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16647
+ * otherwise strand the n-1 staging groups already built.
16648
+ */
16649
+ try {
16650
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16651
+ existingPanels.set(panel.api.id, panel);
16652
+ let temporaryGroup;
16653
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16654
+ else {
16655
+ var _sharedTemporaryGroup;
16656
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16657
+ temporaryGroup = sharedTemporaryGroup;
16658
+ }
16659
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16660
+ stagedPanels.push({
16661
+ panel,
16662
+ temporaryGroup
16663
+ });
16664
+ }
16665
+ this.movingLock(() => {
16666
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16667
+ this.moveGroupOrPanel({
16668
+ from: {
16669
+ groupId: panel.api.group.api.id,
16670
+ panelId: panel.api.id
16671
+ },
16672
+ to: {
16673
+ group: temporaryGroup,
16674
+ position: "center"
16675
+ },
16676
+ keepEmptyGroups: true
16677
+ });
16400
16678
  });
16401
16679
  });
16402
- });
16403
- }
16404
- this.clear();
16680
+ this.clear();
16681
+ } catch (err) {
16682
+ disposeTemporaryGroups();
16683
+ throw err;
16684
+ }
16685
+ } else this.clear();
16405
16686
  const { grid, panels, activeGroup } = data;
16406
16687
  try {
16407
16688
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16433,9 +16714,10 @@ var DockviewComponent = class extends BaseGrid {
16433
16714
  * due to a corruption of input data.
16434
16715
  */
16435
16716
  const existingPanel = existingPanels.get(child);
16436
- if (tempGroup && existingPanel) {
16717
+ const temporaryGroup = temporaryGroups.get(child);
16718
+ if (temporaryGroup && existingPanel) {
16437
16719
  this.movingLock(() => {
16438
- tempGroup.model.removePanel(existingPanel);
16720
+ temporaryGroup.model.removePanel(existingPanel);
16439
16721
  });
16440
16722
  createdPanels.push(existingPanel);
16441
16723
  existingPanel.updateFromStateModel(panels[child]);
@@ -16465,7 +16747,7 @@ var DockviewComponent = class extends BaseGrid {
16465
16747
  return createGroupFromSerializedState(node.data);
16466
16748
  } });
16467
16749
  this._layoutFromShell(width, height);
16468
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16750
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16469
16751
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16470
16752
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16471
16753
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16502,6 +16784,8 @@ var DockviewComponent = class extends BaseGrid {
16502
16784
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16503
16785
  */
16504
16786
  throw err;
16787
+ } finally {
16788
+ disposeTemporaryGroups();
16505
16789
  }
16506
16790
  this.debouncedUpdateAllPositions();
16507
16791
  this._onDidLayoutFromJSON.fire();
@@ -16524,7 +16808,7 @@ var DockviewComponent = class extends BaseGrid {
16524
16808
  members
16525
16809
  };
16526
16810
  }
16527
- deserializeEdgeGroups(edgeGroups, panels) {
16811
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16528
16812
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16529
16813
  if (!edgeService) return;
16530
16814
  for (const _position of [
@@ -16554,13 +16838,40 @@ var DockviewComponent = class extends BaseGrid {
16554
16838
  if (groupState) {
16555
16839
  const { views, activeView } = groupState;
16556
16840
  const createdPanels = [];
16557
- for (const panelId of views) if (panels[panelId]) {
16558
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16559
- createdPanels.push(panel);
16841
+ for (const panelId of views) {
16842
+ if (!panels[panelId]) continue;
16843
+ /**
16844
+ * Reclaim a staged panel rather than rebuilding it, the
16845
+ * same way the grid path does. Deserializing here instead
16846
+ * would honour `reuseExistingPanels` everywhere except edge
16847
+ * groups: the live panel keeps sitting in its staging group
16848
+ * while a second panel is built under the same id, so the
16849
+ * consumer's renderer for the original is orphaned — never
16850
+ * disposed, and its element left inside the id-keyed
16851
+ * overlay entry the replacement now shares.
16852
+ */
16853
+ const existingPanel = existingPanels.get(panelId);
16854
+ const temporaryGroup = temporaryGroups.get(panelId);
16855
+ if (temporaryGroup && existingPanel) {
16856
+ this.movingLock(() => {
16857
+ temporaryGroup.model.removePanel(existingPanel);
16858
+ });
16859
+ createdPanels.push(existingPanel);
16860
+ existingPanel.updateFromStateModel(panels[panelId]);
16861
+ } else {
16862
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16863
+ createdPanels.push(panel);
16864
+ }
16560
16865
  }
16561
16866
  for (const panel of createdPanels) {
16562
16867
  const isActive = activeView === panel.id;
16563
- edgeGroup.model.openPanel(panel, {
16868
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16869
+ edgeGroup.model.openPanel(panel, {
16870
+ skipSetActive: !isActive,
16871
+ skipSetGroupActive: true
16872
+ });
16873
+ });
16874
+ else edgeGroup.model.openPanel(panel, {
16564
16875
  skipSetActive: !isActive,
16565
16876
  skipSetGroupActive: true
16566
16877
  });
@@ -16984,22 +17295,100 @@ var DockviewComponent = class extends BaseGrid {
16984
17295
  * outermost mutation.
16985
17296
  */
16986
17297
  mutation(kind, func) {
16987
- const outer = this._mutationDepth === 0;
17298
+ const close = this.openMutation(kind);
17299
+ try {
17300
+ return func();
17301
+ } finally {
17302
+ close();
17303
+ }
17304
+ }
17305
+ /**
17306
+ * `mutation()` for an operation whose work continues after the synchronous
17307
+ * call returns. The transaction stays open until the returned promise
17308
+ * settles, so everything the operation does - the group it adds, the panels
17309
+ * it relocates - lands inside the bracket, matching the synchronous
17310
+ * move / float paths.
17311
+ */
17312
+ mutationAsync(kind, func) {
17313
+ var _this = this;
17314
+ return _asyncToGenerator(function* () {
17315
+ const close = _this.openMutation(kind);
17316
+ try {
17317
+ return yield func();
17318
+ } finally {
17319
+ close();
17320
+ }
17321
+ })();
17322
+ }
17323
+ /**
17324
+ * Open a transaction and return the function that closes it. Shared by the
17325
+ * synchronous and asynchronous brackets; the returned function must be
17326
+ * called exactly once.
17327
+ *
17328
+ * Both ends key off the depth counter reaching zero rather than off which
17329
+ * bracket opened first. For synchronous nesting the two are the same thing
17330
+ * - brackets close in the order they opened - but asynchronous transactions
17331
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17332
+ * once, or a restore staggering several), and there the first to open is
17333
+ * not the last to close. Reporting on the opener would fire `didMutate`
17334
+ * while the other transaction was still doing structural work, which is the
17335
+ * very thing the async bracket exists to prevent. Overlapping transactions
17336
+ * therefore report as one, tagged with the kind of the last to finish.
17337
+ */
17338
+ openMutation(kind) {
16988
17339
  const origin = this._origin;
16989
- if (outer) this._onWillMutateLayout.fire({
17340
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16990
17341
  kind,
16991
17342
  origin
16992
17343
  });
16993
17344
  this._mutationDepth++;
16994
- try {
16995
- return func();
16996
- } finally {
17345
+ return () => {
16997
17346
  this._mutationDepth--;
16998
- if (outer) this._onDidMutateLayout.fire({
16999
- kind,
17000
- origin
17001
- });
17347
+ if (this._mutationDepth === 0) {
17348
+ this.flushLocationChanges();
17349
+ this._onDidMutateLayout.fire({
17350
+ kind,
17351
+ origin
17352
+ });
17353
+ }
17354
+ };
17355
+ }
17356
+ /**
17357
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17358
+ * transaction, or fire it immediately when no transaction is in flight.
17359
+ *
17360
+ * Relocating a panel touches its location twice: once as it is reparented
17361
+ * into the destination group - which, when that group has just been created
17362
+ * for a floating or popout window, has not been told where it lives yet -
17363
+ * and once as the group is tagged with its final location. Reporting each
17364
+ * signal as it happens therefore leaks an intermediate `grid` location the
17365
+ * panel was never in, at a moment when it belongs to neither group's panel
17366
+ * list and so is missing from `api.panels`.
17367
+ *
17368
+ * Deferring collapses those signals into a single event carrying the
17369
+ * settled location. Note the signals are deliberately *not* compared
17370
+ * against the panel's previous location: a panel can move between two
17371
+ * floating windows without its location type changing, and this event is
17372
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17373
+ * z-index against its new host window.
17374
+ */
17375
+ deferLocationChange(key, fire) {
17376
+ if (this._mutationDepth === 0) {
17377
+ fire();
17378
+ return;
17002
17379
  }
17380
+ this._pendingLocationChanges.set(key, fire);
17381
+ }
17382
+ /**
17383
+ * Report every panel whose location moved during the transaction that has
17384
+ * just closed. Each callback reads the panel's location at this point, so
17385
+ * what it reports is where the panel ended up.
17386
+ */
17387
+ flushLocationChanges() {
17388
+ if (this._pendingLocationChanges.size === 0) return;
17389
+ const pending = Array.from(this._pendingLocationChanges.values());
17390
+ this._pendingLocationChanges.clear();
17391
+ for (const fire of pending) fire();
17003
17392
  }
17004
17393
  /**
17005
17394
  * The origin of the operation currently in progress (`'user'` by default).
@@ -17040,6 +17429,18 @@ var DockviewComponent = class extends BaseGrid {
17040
17429
  origin: this._origin
17041
17430
  });
17042
17431
  }
17432
+ /**
17433
+ * Announce that `panel` has finished relocating. Must be called once the
17434
+ * move has settled: `to` is read from the panel's current group, so an
17435
+ * early call would report the source group as the destination.
17436
+ */
17437
+ fireDidMovePanel(panel, from) {
17438
+ this._onDidMovePanel.fire({
17439
+ panel,
17440
+ from,
17441
+ to: panel.group
17442
+ });
17443
+ }
17043
17444
  moveGroupOrPanel(options) {
17044
17445
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
17045
17446
  }
@@ -17100,10 +17501,7 @@ var DockviewComponent = class extends BaseGrid {
17100
17501
  });
17101
17502
  });
17102
17503
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
17103
- this._onDidMovePanel.fire({
17104
- panel: removedPanel,
17105
- from: sourceGroup
17106
- });
17504
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17107
17505
  } else {
17108
17506
  /**
17109
17507
  * Dropping a panel to the extremities of a group which will place that panel
@@ -17122,10 +17520,7 @@ var DockviewComponent = class extends BaseGrid {
17122
17520
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
17123
17521
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
17124
17522
  this.gridview.moveView(sourceParentLocation, from, to);
17125
- this._onDidMovePanel.fire({
17126
- panel: this.getGroupPanel(sourceItemId),
17127
- from: sourceGroup
17128
- });
17523
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17129
17524
  return;
17130
17525
  }
17131
17526
  }
@@ -17156,10 +17551,7 @@ var DockviewComponent = class extends BaseGrid {
17156
17551
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17157
17552
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
17158
17553
  this.doSetGroupAndPanelActive(newGroup);
17159
- this._onDidMovePanel.fire({
17160
- panel: this.getGroupPanel(sourceItemId),
17161
- from: sourceGroup
17162
- });
17554
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17163
17555
  return;
17164
17556
  }
17165
17557
  if (sourceGroup.api.location.type === "edge") {
@@ -17176,10 +17568,7 @@ var DockviewComponent = class extends BaseGrid {
17176
17568
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17177
17569
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17178
17570
  this.doSetGroupAndPanelActive(newGroup);
17179
- this._onDidMovePanel.fire({
17180
- panel: removedPanel,
17181
- from: sourceGroup
17182
- });
17571
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17183
17572
  return;
17184
17573
  }
17185
17574
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -17191,10 +17580,7 @@ var DockviewComponent = class extends BaseGrid {
17191
17580
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17192
17581
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
17193
17582
  this.doSetGroupAndPanelActive(targetGroup);
17194
- this._onDidMovePanel.fire({
17195
- panel: this.getGroupPanel(sourceItemId),
17196
- from: sourceGroup
17197
- });
17583
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17198
17584
  } else {
17199
17585
  /**
17200
17586
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -17209,10 +17595,7 @@ var DockviewComponent = class extends BaseGrid {
17209
17595
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17210
17596
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17211
17597
  this.doSetGroupAndPanelActive(group);
17212
- this._onDidMovePanel.fire({
17213
- panel: removedPanel,
17214
- from: sourceGroup
17215
- });
17598
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17216
17599
  }
17217
17600
  }
17218
17601
  }
@@ -17247,10 +17630,7 @@ var DockviewComponent = class extends BaseGrid {
17247
17630
  });
17248
17631
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17249
17632
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17250
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17251
- panel,
17252
- from: sourceGroup
17253
- });
17633
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17254
17634
  };
17255
17635
  let targetGroup;
17256
17636
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -17271,10 +17651,12 @@ var DockviewComponent = class extends BaseGrid {
17271
17651
  this.mutation("maximize", () => super.exitMaximizedGroup());
17272
17652
  }
17273
17653
  _doMoveGroup(options) {
17654
+ var _mergedPanels;
17274
17655
  const from = options.from.group;
17275
17656
  const to = options.to.group;
17276
17657
  const target = options.to.position;
17277
17658
  let source = from;
17659
+ let mergedPanels;
17278
17660
  if (target === "center") {
17279
17661
  const activePanel = from.activePanel;
17280
17662
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17292,6 +17674,7 @@ var DockviewComponent = class extends BaseGrid {
17292
17674
  skipSetGroupActive: true
17293
17675
  });
17294
17676
  });
17677
+ mergedPanels = panels;
17295
17678
  for (const snapshot of tabGroupSnapshots) {
17296
17679
  const newTabGroup = to.model.createTabGroup({
17297
17680
  label: snapshot.label,
@@ -17381,11 +17764,8 @@ var DockviewComponent = class extends BaseGrid {
17381
17764
  this.setGroupLocationForRoot(source, destGridview);
17382
17765
  }
17383
17766
  }
17384
- source.panels.forEach((panel) => {
17385
- this._onDidMovePanel.fire({
17386
- panel,
17387
- from
17388
- });
17767
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17768
+ this.fireDidMovePanel(panel, from);
17389
17769
  });
17390
17770
  this.debouncedUpdateAllPositions();
17391
17771
  if (options.skipSetActive === false) {