dockview 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.
package/dist/dockview.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * dockview
3
- * @version 8.1.0
3
+ * @version 8.2.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -22,7 +22,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
22
22
  //#region ../dockview-core/dist/package/main.esm.mjs
23
23
  /**
24
24
  * dockview-core
25
- * @version 8.1.0
25
+ * @version 8.2.0
26
26
  * @link https://github.com/dockview/dockview
27
27
  * @license MIT
28
28
  */
@@ -10366,7 +10366,11 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
10366
10366
  this.panels.forEach((panel) => {
10367
10367
  this.rerender(panel);
10368
10368
  });
10369
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10369
+ if (this._activePanel) {
10370
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10371
+ const { width, height } = this.contentDimensions();
10372
+ this._activePanel.layout(width, height);
10373
+ }
10370
10374
  }
10371
10375
  get renderContainer() {
10372
10376
  var _this$_overwriteRende;
@@ -11313,7 +11317,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11313
11317
  this._group = value;
11314
11318
  this._onDidGroupChange.fire({});
11315
11319
  this.setupGroupEventListeners(oldGroup);
11316
- this._onDidLocationChange.fire({ location: this.group.api.location });
11320
+ this.fireLocationChange();
11317
11321
  }
11318
11322
  }
11319
11323
  get group() {
@@ -11389,6 +11393,23 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11389
11393
  exitMaximized() {
11390
11394
  this.group.api.exitMaximized();
11391
11395
  }
11396
+ /**
11397
+ * Report that this panel's location may have moved.
11398
+ *
11399
+ * A relocation touches the location more than once - the panel is
11400
+ * reparented into the destination group, then that group is tagged with
11401
+ * the location it ends up at - so the event is coalesced to the end of the
11402
+ * enclosing layout mutation and reports where the panel actually settled.
11403
+ * Firing each signal as it happened surfaced an intermediate `grid`
11404
+ * location the panel was never in, at a point where it was in neither
11405
+ * group's panel list. Outside a mutation this fires straight away.
11406
+ */
11407
+ fireLocationChange() {
11408
+ this.accessor.deferLocationChange(this, () => {
11409
+ if (this.isDisposed) return;
11410
+ this._onDidLocationChange.fire({ location: this.location });
11411
+ });
11412
+ }
11392
11413
  setupGroupEventListeners(previousGroup) {
11393
11414
  var _previousGroup$isActi;
11394
11415
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11397,9 +11418,9 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
11397
11418
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11398
11419
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11399
11420
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11400
- }), this.group.api.onDidLocationChange((event) => {
11421
+ }), this.group.api.onDidLocationChange(() => {
11401
11422
  if (this.group !== this.panel.group) return;
11402
- this._onDidLocationChange.fire(event);
11423
+ this.fireLocationChange();
11403
11424
  }), this.group.api.onDidActiveChange(() => {
11404
11425
  if (this.group !== this.panel.group) return;
11405
11426
  if (_trackGroupActive !== this.isGroupActive) {
@@ -13359,6 +13380,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13359
13380
  });
13360
13381
  }
13361
13382
  };
13383
+ /**
13384
+ * Placeholder handle marking "a reposition frame has been requested but the
13385
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13386
+ * practice — it is replaced within the same synchronous block — and harmless
13387
+ * if it were, since no frame carries this id.
13388
+ */
13389
+ const SCHEDULED = -1;
13362
13390
  function createFocusableElement() {
13363
13391
  const element = document.createElement("div");
13364
13392
  element.tabIndex = -1;
@@ -13371,12 +13399,13 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13371
13399
  this.accessor = accessor;
13372
13400
  this.map = {};
13373
13401
  this._disposed = false;
13402
+ this._generation = 0;
13374
13403
  this.positionCache = new PositionCache();
13375
- this.pendingUpdates = /* @__PURE__ */ new Set();
13376
13404
  this.addDisposables(Disposable.from(() => {
13377
13405
  for (const value of Object.values(this.map)) {
13378
13406
  value.disposable.dispose();
13379
13407
  value.destroy.dispose();
13408
+ this.cancelPendingUpdate(value);
13380
13409
  }
13381
13410
  this._disposed = true;
13382
13411
  }));
@@ -13403,11 +13432,24 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13403
13432
  this.positionCache.invalidate();
13404
13433
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13405
13434
  }
13435
+ /**
13436
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13437
+ * bound to the reference container that was current when it was scheduled,
13438
+ * so it must be discarded whenever that container stops being the one the
13439
+ * overlay should track.
13440
+ */
13441
+ cancelPendingUpdate(entry) {
13442
+ if (entry.pendingUpdate !== void 0) {
13443
+ cancelAnimationFrame(entry.pendingUpdate);
13444
+ entry.pendingUpdate = void 0;
13445
+ }
13446
+ }
13406
13447
  detatch(panel) {
13407
13448
  if (this.map[panel.api.id]) {
13408
- const { disposable, destroy } = this.map[panel.api.id];
13409
- disposable.dispose();
13410
- destroy.dispose();
13449
+ const entry = this.map[panel.api.id];
13450
+ entry.disposable.dispose();
13451
+ entry.destroy.dispose();
13452
+ this.cancelPendingUpdate(entry);
13411
13453
  delete this.map[panel.api.id];
13412
13454
  return true;
13413
13455
  }
@@ -13423,22 +13465,73 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13423
13465
  panel,
13424
13466
  disposable: Disposable.NONE,
13425
13467
  destroy: Disposable.NONE,
13426
- element
13468
+ element,
13469
+ retainPreviousGeometry: false,
13470
+ positioned: false,
13471
+ generation: ++this._generation
13427
13472
  };
13428
13473
  }
13429
- const focusContainer = this.map[panel.api.id].element;
13474
+ const mapEntry = this.map[panel.api.id];
13475
+ /**
13476
+ * A *change* of reference container supersedes the previous `attach`:
13477
+ * take a fresh generation so its `resize` closure can no longer run,
13478
+ * and drop the frame it queued against the old container. During
13479
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13480
+ * detached staging group measuring 0x0, so leaving its frame in flight
13481
+ * both wastes the update and delays the reposition against the real one.
13482
+ *
13483
+ * Re-attaching over the *same* container (re-open, active panel change)
13484
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13485
+ * (the auto-hide peek) schedules a frame carrying the sticky
13486
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13487
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13488
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13489
+ * nothing. A newly created entry already holds a fresh generation, so
13490
+ * the stale closures a `detatch` left behind are fenced off either way.
13491
+ */
13492
+ if (mapEntry.referenceContainer !== referenceContainer) {
13493
+ mapEntry.generation = ++this._generation;
13494
+ this.cancelPendingUpdate(mapEntry);
13495
+ mapEntry.referenceContainer = referenceContainer;
13496
+ /**
13497
+ * Arm the retain flag only here: it means "a replacement reference
13498
+ * container is awaiting layout", so the 0x0 early-return below
13499
+ * holds the last good box rather than blanking the panel mid-move.
13500
+ * Arming it on every re-attach instead would make a container that
13501
+ * genuinely collapses to zero keep its stale geometry forever.
13502
+ *
13503
+ * Gated on `positioned` because an overlay that has never been
13504
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13505
+ * defaults to 100%/100% — retaining "previous" geometry there would
13506
+ * pin the content over the whole container.
13507
+ */
13508
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13509
+ }
13510
+ const generation = mapEntry.generation;
13511
+ const focusContainer = mapEntry.element;
13430
13512
  const contentElement = panel.view.content.element;
13431
13513
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13432
13514
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13433
13515
  const resize = () => {
13434
13516
  const panelId = panel.api.id;
13435
- if (this.pendingUpdates.has(panelId)) return;
13436
- this.pendingUpdates.add(panelId);
13437
- requestAnimationFrame(() => {
13517
+ const scheduling = this.map[panelId];
13518
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13519
+ if (scheduling.pendingUpdate !== void 0) return;
13520
+ /**
13521
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13522
+ * shimmed to run inline by some hosts (and by several suites here),
13523
+ * in which case the callback below clears `pendingUpdate` while
13524
+ * this call is still on the stack — assigning the handle afterwards
13525
+ * would resurrect a slot that is already spent and block every
13526
+ * later reposition. The sentinel is replaced by the real handle
13527
+ * only if the frame has not already run.
13528
+ */
13529
+ scheduling.pendingUpdate = SCHEDULED;
13530
+ const handle = requestAnimationFrame(() => {
13438
13531
  var _entry$forceVisible;
13439
- this.pendingUpdates.delete(panelId);
13440
13532
  const entry = this.map[panelId];
13441
- if (this.isDisposed || !entry) return;
13533
+ if (entry) entry.pendingUpdate = void 0;
13534
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13442
13535
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13443
13536
  const clip = entry.clip;
13444
13537
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13447,6 +13540,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13447
13540
  const top = box.top - box2.top;
13448
13541
  const width = box.width;
13449
13542
  const height = box.height;
13543
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13544
+ if (!panel.api.isVisible && !forceVisible) {
13545
+ focusContainer.style.visibility = "hidden";
13546
+ focusContainer.style.pointerEvents = "none";
13547
+ }
13548
+ return;
13549
+ }
13550
+ entry.retainPreviousGeometry = false;
13551
+ entry.positioned = true;
13450
13552
  focusContainer.style.left = `${left}px`;
13451
13553
  focusContainer.style.top = `${top}px`;
13452
13554
  focusContainer.style.width = `${width}px`;
@@ -13475,11 +13577,30 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13475
13577
  } else focusContainer.style.clipPath = "";
13476
13578
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13477
13579
  });
13580
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13478
13581
  };
13479
13582
  const visibilityChanged = () => {
13480
13583
  if (panel.api.isVisible) {
13584
+ var _this$map$panel$api$i;
13481
13585
  this.positionCache.invalidate();
13482
13586
  resize();
13587
+ /**
13588
+ * Existing geometry is safe to show while its replacement lays
13589
+ * out. `retainPreviousGeometry` is only set when geometry has
13590
+ * actually been written (see `attach`), so this can never
13591
+ * un-hide an overlay whose left/top/width/height are unset —
13592
+ * `.dv-render-overlay` is 100%/100% by default and would
13593
+ * otherwise cover the whole dock.
13594
+ *
13595
+ * The retained geometry is the panel's position in the previous
13596
+ * layout, so restoring a layout that moves the panel does paint
13597
+ * it at stale coordinates until the reposition lands. That is
13598
+ * bounded to the next frame now that a superseded `attach` can
13599
+ * no longer swallow the reposition (see `attach`); showing
13600
+ * stale-but-real geometry for a frame is the deliberate
13601
+ * trade-off against blanking the panel for the whole rebuild.
13602
+ */
13603
+ 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 = "";
13483
13604
  focusContainer.style.pointerEvents = "";
13484
13605
  } else {
13485
13606
  focusContainer.style.visibility = "hidden";
@@ -13696,7 +13817,17 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
13696
13817
  window: externalWindow
13697
13818
  });
13698
13819
  return new Promise((resolve, reject) => {
13699
- externalWindow.addEventListener("unload", (e) => {});
13820
+ externalWindow.addEventListener("unload", () => {});
13821
+ /**
13822
+ * `load` is the only event that resolves this promise with a
13823
+ * container, so a window that goes away first would leave the
13824
+ * caller awaiting a promise that never settles - and
13825
+ * `addPopoutGroup` holds its layout transaction open until it does.
13826
+ * Settle with `null` on close, the same signal a blocked popup
13827
+ * gives and one the caller already handles. `resolve` after the
13828
+ * fact is a no-op, so a `load` that arrived first still wins.
13829
+ */
13830
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13700
13831
  externalWindow.addEventListener("load", () => {
13701
13832
  /**
13702
13833
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -15096,6 +15227,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15096
15227
  this._onWillDrop = new Emitter();
15097
15228
  this.onWillDrop = this._onWillDrop.event;
15098
15229
  this._mutationDepth = 0;
15230
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15099
15231
  this._origin = "user";
15100
15232
  this._originDepth = 0;
15101
15233
  this._onWillMutateLayout = new Emitter();
@@ -15193,7 +15325,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15193
15325
  toggleClass(this.element, "dv-debug", !!options.debug);
15194
15326
  this.updateTheme();
15195
15327
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15196
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15328
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15197
15329
  var _this$rootDropTargetC2, _this$floatingDropTar;
15198
15330
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15199
15331
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15267,7 +15399,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15267
15399
  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;
15268
15400
  }
15269
15401
  addPopoutGroup(itemToPopout, options) {
15270
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15402
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15271
15403
  }
15272
15404
  /** Enumerate the popout groups currently open in their own windows. */
15273
15405
  getPopouts() {
@@ -15364,15 +15496,32 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15364
15496
  }
15365
15497
  };
15366
15498
  let floatingBox;
15367
- 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(() => {
15368
- const panel = referenceGroup.model.removePanel(itemToPopout);
15369
- group.model.openPanel(panel);
15370
- });
15371
- else {
15499
+ /**
15500
+ * Popping out rehomes panels into `group`, so like the floating
15501
+ * case it owes the caller an `onDidMovePanel` per panel. The
15502
+ * events are deferred until the window is wired up so listeners
15503
+ * observe the panels' final `popout` location.
15504
+ */
15505
+ let movedPanels = [];
15506
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15507
+ const sourceGroup = itemToPopout.group;
15508
+ this.movingLock(() => {
15509
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15510
+ group.model.openPanel(panel);
15511
+ });
15512
+ movedPanels = [{
15513
+ panel: itemToPopout,
15514
+ from: sourceGroup
15515
+ }];
15516
+ } else {
15372
15517
  this.movingLock(() => moveGroupWithoutDestroying({
15373
15518
  from: referenceGroup,
15374
15519
  to: group
15375
15520
  }));
15521
+ movedPanels = group.panels.map((panel) => ({
15522
+ panel,
15523
+ from: referenceGroup
15524
+ }));
15376
15525
  switch (referenceLocation) {
15377
15526
  case "grid":
15378
15527
  referenceGroup.api.setVisible(false);
@@ -15481,6 +15630,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15481
15630
  group: value.popoutGroup,
15482
15631
  window: value.getWindow()
15483
15632
  });
15633
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15484
15634
  return true;
15485
15635
  }).catch((err) => {
15486
15636
  console.error("dockview: failed to create popout.", err);
@@ -15558,6 +15708,16 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15558
15708
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15559
15709
  const anchorPresent = members.includes(group);
15560
15710
  const anchorIsSoleMember = anchorPresent && members.length === 1;
15711
+ /**
15712
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
15713
+ * window owes the caller the same: every panel in it is rehomed, into
15714
+ * the reference group the popout left behind or into a fresh grid slot.
15715
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
15716
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
15717
+ * events are deferred to the end so listeners observe the panels at
15718
+ * their final group and location.
15719
+ */
15720
+ const movedPanels = [];
15561
15721
  if (genuineClose) for (const member of members) {
15562
15722
  if (member === group) continue;
15563
15723
  this.movingLock(() => {
@@ -15568,12 +15728,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15568
15728
  });
15569
15729
  this.redockGroupToMainGrid(member);
15570
15730
  });
15731
+ for (const panel of member.panels) movedPanels.push({
15732
+ panel,
15733
+ from: member
15734
+ });
15571
15735
  }
15572
15736
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
15737
+ const rehomed = [...group.panels];
15573
15738
  this.movingLock(() => moveGroupWithoutDestroying({
15574
15739
  from: group,
15575
15740
  to: referenceGroup
15576
15741
  }));
15742
+ for (const panel of rehomed) movedPanels.push({
15743
+ panel,
15744
+ from: group
15745
+ });
15577
15746
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15578
15747
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15579
15748
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15604,10 +15773,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15604
15773
  this.movingLock(() => {
15605
15774
  this.doAddGroup(group, [0]);
15606
15775
  });
15776
+ for (const panel of group.panels) movedPanels.push({
15777
+ panel,
15778
+ from: group
15779
+ });
15607
15780
  }
15608
15781
  this.doSetGroupAndPanelActive(group);
15609
15782
  }
15610
15783
  disposePopoutGridview();
15784
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15611
15785
  }
15612
15786
  addFloatingGroup(item, options) {
15613
15787
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15616,7 +15790,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15616
15790
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15617
15791
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15618
15792
  let group;
15793
+ /**
15794
+ * Floating relocates panels exactly like a drop onto the grid does, so
15795
+ * it owes the caller an `onDidMovePanel` per panel. The events are
15796
+ * deferred until the window is mounted below: only then do the panels
15797
+ * report their final `floating` location.
15798
+ */
15799
+ let movedPanels = [];
15619
15800
  if (item instanceof DockviewPanel) {
15801
+ const sourceGroup = item.group;
15620
15802
  group = this.createGroup();
15621
15803
  this._onDidAddGroup.fire(group);
15622
15804
  this.movingLock(() => this.removePanel(item, {
@@ -15625,6 +15807,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15625
15807
  skipSetActiveGroup: true
15626
15808
  }));
15627
15809
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
15810
+ movedPanels = [{
15811
+ panel: item,
15812
+ from: sourceGroup
15813
+ }];
15628
15814
  } else {
15629
15815
  var _this$_popoutWindowSe8;
15630
15816
  group = item;
@@ -15641,11 +15827,21 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15641
15827
  });
15642
15828
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15643
15829
  group = popoutReferenceGroup;
15644
- } else this.doRemoveGroup(item, {
15645
- skipDispose: true,
15646
- skipPopoutReturn: true,
15647
- skipPopoutAssociated: false
15648
- });
15830
+ movedPanels = group.panels.map((panel) => ({
15831
+ panel,
15832
+ from: item
15833
+ }));
15834
+ } else {
15835
+ this.doRemoveGroup(item, {
15836
+ skipDispose: true,
15837
+ skipPopoutReturn: true,
15838
+ skipPopoutAssociated: false
15839
+ });
15840
+ movedPanels = group.panels.map((panel) => ({
15841
+ panel,
15842
+ from: group
15843
+ }));
15844
+ }
15649
15845
  }
15650
15846
  function getAnchoredBox() {
15651
15847
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15678,6 +15874,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
15678
15874
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15679
15875
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15680
15876
  });
15877
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15681
15878
  }
15682
15879
  /**
15683
15880
  * Build an empty gridview configured to match the main grid's styling, for
@@ -16109,35 +16306,119 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16109
16306
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16110
16307
  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");
16111
16308
  const existingPanels = /* @__PURE__ */ new Map();
16112
- let tempGroup;
16309
+ const temporaryGroups = /* @__PURE__ */ new Map();
16310
+ const stagedPanels = [];
16311
+ const temporaryGroupDisposables = [];
16312
+ /**
16313
+ * The staging groups are detached from `_groups` and never enter the
16314
+ * grid, so nothing else will ever tear them down.
16315
+ *
16316
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16317
+ * throwing renderer must not abort the rest of the cleanup — that would
16318
+ * re-introduce the very leak this reclaims — nor replace the
16319
+ * deserialization error the caller is being given. Draining the list
16320
+ * also makes this safe to call more than once.
16321
+ */
16322
+ const disposeTemporaryGroups = () => {
16323
+ for (const disposable of temporaryGroupDisposables) try {
16324
+ disposable.dispose();
16325
+ } catch (err) {
16326
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16327
+ }
16328
+ temporaryGroupDisposables.length = 0;
16329
+ };
16113
16330
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16114
16331
  /**
16115
- * What are we doing here?
16116
- *
16117
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16118
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16332
+ * Visible, always-rendered panels need individual staging groups
16333
+ * to remain active. Other reused panels can share a staging group.
16334
+ * The staging groups are excluded from the layout clear below.
16119
16335
  */
16120
- tempGroup = this.createGroup();
16121
- this._groups.delete(tempGroup.api.id);
16122
16336
  const newPanels = Object.keys(data.panels);
16123
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16124
- this.movingLock(() => {
16125
- Array.from(existingPanels.values()).forEach((panel) => {
16126
- this.moveGroupOrPanel({
16127
- from: {
16128
- groupId: panel.api.group.api.id,
16129
- panelId: panel.api.id
16130
- },
16131
- to: {
16132
- group: tempGroup,
16133
- position: "center"
16134
- },
16135
- keepEmptyGroups: true
16337
+ let sharedTemporaryGroup;
16338
+ const createTemporaryGroup = () => {
16339
+ const temporaryGroup = this.createGroup();
16340
+ /**
16341
+ * Removing the group from `_groups` also drops the record's
16342
+ * `CompositeDisposable`, so capture it first and tear both it
16343
+ * and the group down in the `finally` below. Left undisposed a
16344
+ * staging group leaks its `ResizeObserver`, its
16345
+ * `onDidOptionsChange` subscription (retained by this
16346
+ * component's emitter) and the watermark its model mounts when
16347
+ * the group empties — once per reused panel, per `fromJSON`.
16348
+ */
16349
+ const record = this._groups.get(temporaryGroup.api.id);
16350
+ this._groups.delete(temporaryGroup.api.id);
16351
+ temporaryGroupDisposables.push(Disposable.from(() => {
16352
+ /**
16353
+ * Reclaim the group's own resources, never its panels.
16354
+ * Staging is driven by the ids in `data.panels` while
16355
+ * reclaiming is driven by the group `views` that
16356
+ * reference them, so a panel whose state is present but
16357
+ * unreferenced is still staged here — and `dispose()`
16358
+ * on a non-empty group reaches the consumer's
16359
+ * `IContentRenderer.dispose()`. Emptying the group
16360
+ * first leaves those panels exactly as they were before
16361
+ * this teardown existed, which also keeps this safe
16362
+ * against any future path that rebuilds a panel under
16363
+ * an id that is live in the new layout.
16364
+ */
16365
+ this.movingLock(() => {
16366
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16367
+ });
16368
+ record === null || record === void 0 || record.disposable.dispose();
16369
+ temporaryGroup.dispose();
16370
+ }));
16371
+ return temporaryGroup;
16372
+ };
16373
+ /**
16374
+ * Staging and the clear below run before the deserialization
16375
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16376
+ * renderer teardown during `clear`) would escape without reclaiming
16377
+ * the staging groups already created.
16378
+ *
16379
+ * The creation loop is inside the guard too, not just the moves and
16380
+ * the clear: `createGroup()` reaches consumer code of its own —
16381
+ * `initialize()` mounts the watermark through
16382
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16383
+ * otherwise strand the n-1 staging groups already built.
16384
+ */
16385
+ try {
16386
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16387
+ existingPanels.set(panel.api.id, panel);
16388
+ let temporaryGroup;
16389
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16390
+ else {
16391
+ var _sharedTemporaryGroup;
16392
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16393
+ temporaryGroup = sharedTemporaryGroup;
16394
+ }
16395
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16396
+ stagedPanels.push({
16397
+ panel,
16398
+ temporaryGroup
16399
+ });
16400
+ }
16401
+ this.movingLock(() => {
16402
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16403
+ this.moveGroupOrPanel({
16404
+ from: {
16405
+ groupId: panel.api.group.api.id,
16406
+ panelId: panel.api.id
16407
+ },
16408
+ to: {
16409
+ group: temporaryGroup,
16410
+ position: "center"
16411
+ },
16412
+ keepEmptyGroups: true
16413
+ });
16136
16414
  });
16137
16415
  });
16138
- });
16139
- }
16140
- this.clear();
16416
+ this.clear();
16417
+ } catch (err) {
16418
+ disposeTemporaryGroups();
16419
+ throw err;
16420
+ }
16421
+ } else this.clear();
16141
16422
  const { grid, panels, activeGroup } = data;
16142
16423
  try {
16143
16424
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16169,9 +16450,10 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16169
16450
  * due to a corruption of input data.
16170
16451
  */
16171
16452
  const existingPanel = existingPanels.get(child);
16172
- if (tempGroup && existingPanel) {
16453
+ const temporaryGroup = temporaryGroups.get(child);
16454
+ if (temporaryGroup && existingPanel) {
16173
16455
  this.movingLock(() => {
16174
- tempGroup.model.removePanel(existingPanel);
16456
+ temporaryGroup.model.removePanel(existingPanel);
16175
16457
  });
16176
16458
  createdPanels.push(existingPanel);
16177
16459
  existingPanel.updateFromStateModel(panels[child]);
@@ -16201,7 +16483,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16201
16483
  return createGroupFromSerializedState(node.data);
16202
16484
  } });
16203
16485
  this._layoutFromShell(width, height);
16204
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16486
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16205
16487
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16206
16488
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16207
16489
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16238,6 +16520,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16238
16520
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16239
16521
  */
16240
16522
  throw err;
16523
+ } finally {
16524
+ disposeTemporaryGroups();
16241
16525
  }
16242
16526
  this.debouncedUpdateAllPositions();
16243
16527
  this._onDidLayoutFromJSON.fire();
@@ -16260,7 +16544,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16260
16544
  members
16261
16545
  };
16262
16546
  }
16263
- deserializeEdgeGroups(edgeGroups, panels) {
16547
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16264
16548
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16265
16549
  if (!edgeService) return;
16266
16550
  for (const _position of [
@@ -16290,13 +16574,40 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16290
16574
  if (groupState) {
16291
16575
  const { views, activeView } = groupState;
16292
16576
  const createdPanels = [];
16293
- for (const panelId of views) if (panels[panelId]) {
16294
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16295
- createdPanels.push(panel);
16577
+ for (const panelId of views) {
16578
+ if (!panels[panelId]) continue;
16579
+ /**
16580
+ * Reclaim a staged panel rather than rebuilding it, the
16581
+ * same way the grid path does. Deserializing here instead
16582
+ * would honour `reuseExistingPanels` everywhere except edge
16583
+ * groups: the live panel keeps sitting in its staging group
16584
+ * while a second panel is built under the same id, so the
16585
+ * consumer's renderer for the original is orphaned — never
16586
+ * disposed, and its element left inside the id-keyed
16587
+ * overlay entry the replacement now shares.
16588
+ */
16589
+ const existingPanel = existingPanels.get(panelId);
16590
+ const temporaryGroup = temporaryGroups.get(panelId);
16591
+ if (temporaryGroup && existingPanel) {
16592
+ this.movingLock(() => {
16593
+ temporaryGroup.model.removePanel(existingPanel);
16594
+ });
16595
+ createdPanels.push(existingPanel);
16596
+ existingPanel.updateFromStateModel(panels[panelId]);
16597
+ } else {
16598
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16599
+ createdPanels.push(panel);
16600
+ }
16296
16601
  }
16297
16602
  for (const panel of createdPanels) {
16298
16603
  const isActive = activeView === panel.id;
16299
- edgeGroup.model.openPanel(panel, {
16604
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16605
+ edgeGroup.model.openPanel(panel, {
16606
+ skipSetActive: !isActive,
16607
+ skipSetGroupActive: true
16608
+ });
16609
+ });
16610
+ else edgeGroup.model.openPanel(panel, {
16300
16611
  skipSetActive: !isActive,
16301
16612
  skipSetGroupActive: true
16302
16613
  });
@@ -16720,22 +17031,100 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16720
17031
  * outermost mutation.
16721
17032
  */
16722
17033
  mutation(kind, func) {
16723
- const outer = this._mutationDepth === 0;
17034
+ const close = this.openMutation(kind);
17035
+ try {
17036
+ return func();
17037
+ } finally {
17038
+ close();
17039
+ }
17040
+ }
17041
+ /**
17042
+ * `mutation()` for an operation whose work continues after the synchronous
17043
+ * call returns. The transaction stays open until the returned promise
17044
+ * settles, so everything the operation does - the group it adds, the panels
17045
+ * it relocates - lands inside the bracket, matching the synchronous
17046
+ * move / float paths.
17047
+ */
17048
+ mutationAsync(kind, func) {
17049
+ var _this = this;
17050
+ return _asyncToGenerator(function* () {
17051
+ const close = _this.openMutation(kind);
17052
+ try {
17053
+ return yield func();
17054
+ } finally {
17055
+ close();
17056
+ }
17057
+ })();
17058
+ }
17059
+ /**
17060
+ * Open a transaction and return the function that closes it. Shared by the
17061
+ * synchronous and asynchronous brackets; the returned function must be
17062
+ * called exactly once.
17063
+ *
17064
+ * Both ends key off the depth counter reaching zero rather than off which
17065
+ * bracket opened first. For synchronous nesting the two are the same thing
17066
+ * - brackets close in the order they opened - but asynchronous transactions
17067
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17068
+ * once, or a restore staggering several), and there the first to open is
17069
+ * not the last to close. Reporting on the opener would fire `didMutate`
17070
+ * while the other transaction was still doing structural work, which is the
17071
+ * very thing the async bracket exists to prevent. Overlapping transactions
17072
+ * therefore report as one, tagged with the kind of the last to finish.
17073
+ */
17074
+ openMutation(kind) {
16724
17075
  const origin = this._origin;
16725
- if (outer) this._onWillMutateLayout.fire({
17076
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16726
17077
  kind,
16727
17078
  origin
16728
17079
  });
16729
17080
  this._mutationDepth++;
16730
- try {
16731
- return func();
16732
- } finally {
17081
+ return () => {
16733
17082
  this._mutationDepth--;
16734
- if (outer) this._onDidMutateLayout.fire({
16735
- kind,
16736
- origin
16737
- });
17083
+ if (this._mutationDepth === 0) {
17084
+ this.flushLocationChanges();
17085
+ this._onDidMutateLayout.fire({
17086
+ kind,
17087
+ origin
17088
+ });
17089
+ }
17090
+ };
17091
+ }
17092
+ /**
17093
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17094
+ * transaction, or fire it immediately when no transaction is in flight.
17095
+ *
17096
+ * Relocating a panel touches its location twice: once as it is reparented
17097
+ * into the destination group - which, when that group has just been created
17098
+ * for a floating or popout window, has not been told where it lives yet -
17099
+ * and once as the group is tagged with its final location. Reporting each
17100
+ * signal as it happens therefore leaks an intermediate `grid` location the
17101
+ * panel was never in, at a moment when it belongs to neither group's panel
17102
+ * list and so is missing from `api.panels`.
17103
+ *
17104
+ * Deferring collapses those signals into a single event carrying the
17105
+ * settled location. Note the signals are deliberately *not* compared
17106
+ * against the panel's previous location: a panel can move between two
17107
+ * floating windows without its location type changing, and this event is
17108
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17109
+ * z-index against its new host window.
17110
+ */
17111
+ deferLocationChange(key, fire) {
17112
+ if (this._mutationDepth === 0) {
17113
+ fire();
17114
+ return;
16738
17115
  }
17116
+ this._pendingLocationChanges.set(key, fire);
17117
+ }
17118
+ /**
17119
+ * Report every panel whose location moved during the transaction that has
17120
+ * just closed. Each callback reads the panel's location at this point, so
17121
+ * what it reports is where the panel ended up.
17122
+ */
17123
+ flushLocationChanges() {
17124
+ if (this._pendingLocationChanges.size === 0) return;
17125
+ const pending = Array.from(this._pendingLocationChanges.values());
17126
+ this._pendingLocationChanges.clear();
17127
+ for (const fire of pending) fire();
16739
17128
  }
16740
17129
  /**
16741
17130
  * The origin of the operation currently in progress (`'user'` by default).
@@ -16776,6 +17165,18 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16776
17165
  origin: this._origin
16777
17166
  });
16778
17167
  }
17168
+ /**
17169
+ * Announce that `panel` has finished relocating. Must be called once the
17170
+ * move has settled: `to` is read from the panel's current group, so an
17171
+ * early call would report the source group as the destination.
17172
+ */
17173
+ fireDidMovePanel(panel, from) {
17174
+ this._onDidMovePanel.fire({
17175
+ panel,
17176
+ from,
17177
+ to: panel.group
17178
+ });
17179
+ }
16779
17180
  moveGroupOrPanel(options) {
16780
17181
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
16781
17182
  }
@@ -16836,10 +17237,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16836
17237
  });
16837
17238
  });
16838
17239
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
16839
- this._onDidMovePanel.fire({
16840
- panel: removedPanel,
16841
- from: sourceGroup
16842
- });
17240
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16843
17241
  } else {
16844
17242
  /**
16845
17243
  * Dropping a panel to the extremities of a group which will place that panel
@@ -16858,10 +17256,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16858
17256
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
16859
17257
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
16860
17258
  this.gridview.moveView(sourceParentLocation, from, to);
16861
- this._onDidMovePanel.fire({
16862
- panel: this.getGroupPanel(sourceItemId),
16863
- from: sourceGroup
16864
- });
17259
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16865
17260
  return;
16866
17261
  }
16867
17262
  }
@@ -16892,10 +17287,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16892
17287
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
16893
17288
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
16894
17289
  this.doSetGroupAndPanelActive(newGroup);
16895
- this._onDidMovePanel.fire({
16896
- panel: this.getGroupPanel(sourceItemId),
16897
- from: sourceGroup
16898
- });
17290
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16899
17291
  return;
16900
17292
  }
16901
17293
  if (sourceGroup.api.location.type === "edge") {
@@ -16912,10 +17304,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16912
17304
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
16913
17305
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16914
17306
  this.doSetGroupAndPanelActive(newGroup);
16915
- this._onDidMovePanel.fire({
16916
- panel: removedPanel,
16917
- from: sourceGroup
16918
- });
17307
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16919
17308
  return;
16920
17309
  }
16921
17310
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -16927,10 +17316,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16927
17316
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
16928
17317
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
16929
17318
  this.doSetGroupAndPanelActive(targetGroup);
16930
- this._onDidMovePanel.fire({
16931
- panel: this.getGroupPanel(sourceItemId),
16932
- from: sourceGroup
16933
- });
17319
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16934
17320
  } else {
16935
17321
  /**
16936
17322
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -16945,10 +17331,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16945
17331
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
16946
17332
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16947
17333
  this.doSetGroupAndPanelActive(group);
16948
- this._onDidMovePanel.fire({
16949
- panel: removedPanel,
16950
- from: sourceGroup
16951
- });
17334
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16952
17335
  }
16953
17336
  }
16954
17337
  }
@@ -16983,10 +17366,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
16983
17366
  });
16984
17367
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
16985
17368
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
16986
- for (const panel of removedPanels) this._onDidMovePanel.fire({
16987
- panel,
16988
- from: sourceGroup
16989
- });
17369
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
16990
17370
  };
16991
17371
  let targetGroup;
16992
17372
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -17007,10 +17387,12 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17007
17387
  this.mutation("maximize", () => super.exitMaximizedGroup());
17008
17388
  }
17009
17389
  _doMoveGroup(options) {
17390
+ var _mergedPanels;
17010
17391
  const from = options.from.group;
17011
17392
  const to = options.to.group;
17012
17393
  const target = options.to.position;
17013
17394
  let source = from;
17395
+ let mergedPanels;
17014
17396
  if (target === "center") {
17015
17397
  const activePanel = from.activePanel;
17016
17398
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17028,6 +17410,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17028
17410
  skipSetGroupActive: true
17029
17411
  });
17030
17412
  });
17413
+ mergedPanels = panels;
17031
17414
  for (const snapshot of tabGroupSnapshots) {
17032
17415
  const newTabGroup = to.model.createTabGroup({
17033
17416
  label: snapshot.label,
@@ -17117,11 +17500,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
17117
17500
  this.setGroupLocationForRoot(source, destGridview);
17118
17501
  }
17119
17502
  }
17120
- source.panels.forEach((panel) => {
17121
- this._onDidMovePanel.fire({
17122
- panel,
17123
- from
17124
- });
17503
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17504
+ this.fireDidMovePanel(panel, from);
17125
17505
  });
17126
17506
  this.debouncedUpdateAllPositions();
17127
17507
  if (options.skipSetActive === false) {