dockview-core 8.0.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.0.0
3
+ * @version 8.2.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -8,7 +8,9 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
8
8
 
9
9
  //#region src/dnd/dataTransfer.ts
10
10
  var _LocalSelectionTransfer;
11
- var TransferObject = class {};
11
+ var TransferObject = class {
12
+ constructor() {}
13
+ };
12
14
  var PanelTransfer = class extends TransferObject {
13
15
  constructor(viewId, groupId, panelId, tabGroupId) {
14
16
  super();
@@ -201,7 +203,6 @@ var Emitter = class Emitter {
201
203
  this.options = options;
202
204
  this._listeners = [];
203
205
  this._disposed = false;
204
- this._pauseTokens = /* @__PURE__ */ new Set();
205
206
  }
206
207
  get event() {
207
208
  if (!this._event) {
@@ -222,14 +223,26 @@ var Emitter = class Emitter {
222
223
  }
223
224
  fire(e) {
224
225
  var _this$options2;
225
- if (this._pauseTokens.size > 0) return;
226
+ if (this._pauseTokens !== void 0 && this._pauseTokens.size > 0) return;
226
227
  if ((_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.replay) this._last = e;
227
- for (const listener of this._listeners.slice()) listener.callback(e);
228
+ const listeners = this._listeners;
229
+ const length = listeners.length;
230
+ if (length === 0) return;
231
+ if (length === 1) {
232
+ listeners[0].callback(e);
233
+ return;
234
+ }
235
+ for (const listener of listeners.slice()) listener.callback(e);
228
236
  }
229
237
  pause() {
238
+ var _this$_pauseTokens;
230
239
  const token = {};
240
+ (_this$_pauseTokens = this._pauseTokens) !== null && _this$_pauseTokens !== void 0 || (this._pauseTokens = /* @__PURE__ */ new Set());
231
241
  this._pauseTokens.add(token);
232
- return Disposable.from(() => this._pauseTokens.delete(token));
242
+ return Disposable.from(() => {
243
+ var _this$_pauseTokens2;
244
+ return (_this$_pauseTokens2 = this._pauseTokens) === null || _this$_pauseTokens2 === void 0 ? void 0 : _this$_pauseTokens2.delete(token);
245
+ });
233
246
  }
234
247
  dispose() {
235
248
  if (!this._disposed) {
@@ -532,8 +545,10 @@ function onDidWindowMoveEnd(window) {
532
545
  let previousScreenX = window.screenX;
533
546
  let previousScreenY = window.screenY;
534
547
  let timeout;
548
+ let rafHandle = 0;
549
+ let stopped = false;
535
550
  const checkMovement = () => {
536
- if (window.closed) return;
551
+ if (stopped || window.closed) return;
537
552
  const currentScreenX = window.screenX;
538
553
  const currentScreenY = window.screenY;
539
554
  if (currentScreenX !== previousScreenX || currentScreenY !== previousScreenY) {
@@ -544,9 +559,18 @@ function onDidWindowMoveEnd(window) {
544
559
  previousScreenX = currentScreenX;
545
560
  previousScreenY = currentScreenY;
546
561
  }
547
- requestAnimationFrame(checkMovement);
562
+ rafHandle = requestAnimationFrame(checkMovement);
548
563
  };
549
564
  checkMovement();
565
+ const disposeEmitter = emitter.dispose.bind(emitter);
566
+ emitter.dispose = () => {
567
+ if (!stopped) {
568
+ stopped = true;
569
+ cancelAnimationFrame(rafHandle);
570
+ clearTimeout(timeout);
571
+ }
572
+ disposeEmitter();
573
+ };
550
574
  return emitter;
551
575
  }
552
576
  function onDidWindowResizeEnd(element, cb) {
@@ -682,6 +706,11 @@ var ViewItem = class {
682
706
  set size(size) {
683
707
  this._size = size;
684
708
  }
709
+ setContainerGeometry(prop, value) {
710
+ if (this._appliedStyles[prop] === value) return;
711
+ this._appliedStyles[prop] = value;
712
+ this.container.style[prop] = value;
713
+ }
685
714
  get size() {
686
715
  return this._size;
687
716
  }
@@ -717,6 +746,7 @@ var ViewItem = class {
717
746
  this.view = view;
718
747
  this.disposable = disposable;
719
748
  this._cachedVisibleSize = void 0;
749
+ this._appliedStyles = {};
720
750
  if (typeof size === "number") {
721
751
  this._size = size;
722
752
  this._cachedVisibleSize = void 0;
@@ -765,6 +795,16 @@ let LayoutPriority = /* @__PURE__ */ function(LayoutPriority) {
765
795
  LayoutPriority["Normal"] = "normal";
766
796
  return LayoutPriority;
767
797
  }({});
798
+ function setSashPosition(sash, left, top) {
799
+ if (sash.appliedLeft !== left) {
800
+ sash.appliedLeft = left;
801
+ sash.container.style.left = left;
802
+ }
803
+ if (sash.appliedTop !== top) {
804
+ sash.appliedTop = top;
805
+ sash.container.style.top = top;
806
+ }
807
+ }
768
808
  let Sizing;
769
809
  (function(_Sizing) {
770
810
  _Sizing.Distribute = { type: "distribute" };
@@ -883,10 +923,6 @@ var Splitview = class {
883
923
  pushToEnd(upIndexes, i);
884
924
  pushToEnd(downIndexes, i);
885
925
  }
886
- const upItems = upIndexes.map((i) => this.viewItems[i]);
887
- const upSizes = upIndexes.map((i) => sizes[i]);
888
- const downItems = downIndexes.map((i) => this.viewItems[i]);
889
- const downSizes = downIndexes.map((i) => sizes[i]);
890
926
  const minDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].minimumSize - sizes[i], 0);
891
927
  const maxDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].maximumSize - sizes[i], 0);
892
928
  const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((_, i) => _ + sizes[i] - this.viewItems[i].minimumSize, 0);
@@ -910,19 +946,21 @@ var Splitview = class {
910
946
  const tentativeDelta = clamp(delta, minDelta, maxDelta);
911
947
  let actualDelta = 0;
912
948
  let deltaUp = tentativeDelta;
913
- for (let i = 0; i < upItems.length; i++) {
914
- const item = upItems[i];
915
- const size = clamp(upSizes[i] + deltaUp, item.minimumSize, item.maximumSize);
916
- const viewDelta = size - upSizes[i];
949
+ for (const upIndex of upIndexes) {
950
+ const item = this.viewItems[upIndex];
951
+ const priorSize = sizes[upIndex];
952
+ const size = clamp(priorSize + deltaUp, item.minimumSize, item.maximumSize);
953
+ const viewDelta = size - priorSize;
917
954
  actualDelta += viewDelta;
918
955
  deltaUp -= viewDelta;
919
956
  item.size = size;
920
957
  }
921
958
  let deltaDown = actualDelta;
922
- for (let i = 0; i < downItems.length; i++) {
923
- const item = downItems[i];
924
- const size = clamp(downSizes[i] - deltaDown, item.minimumSize, item.maximumSize);
925
- const viewDelta = size - downSizes[i];
959
+ for (const downIndex of downIndexes) {
960
+ const item = this.viewItems[downIndex];
961
+ const priorSize = sizes[downIndex];
962
+ const size = clamp(priorSize - deltaDown, item.minimumSize, item.maximumSize);
963
+ const viewDelta = size - priorSize;
926
964
  deltaDown += viewDelta;
927
965
  item.size = size;
928
966
  }
@@ -1170,13 +1208,22 @@ var Splitview = class {
1170
1208
  this.saveProportions();
1171
1209
  }
1172
1210
  distributeEmptySpace(lowPriorityIndex) {
1173
- const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1211
+ let contentSize = 0;
1212
+ for (const item of this.viewItems) contentSize += item.size;
1174
1213
  let emptyDelta = this.size - contentSize;
1214
+ if (emptyDelta === 0) return;
1175
1215
  const indexes = range(this.viewItems.length - 1, -1);
1176
- const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
1177
- const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
1178
- for (const index of highPriorityIndexes) pushToStart(indexes, index);
1179
- for (const index of lowPriorityIndexes) pushToEnd(indexes, index);
1216
+ let hasPriority = false;
1217
+ for (const item of this.viewItems) if (item.priority === "low" || item.priority === "high") {
1218
+ hasPriority = true;
1219
+ break;
1220
+ }
1221
+ if (hasPriority) {
1222
+ const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
1223
+ const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
1224
+ for (const index of highPriorityIndexes) pushToStart(indexes, index);
1225
+ for (const index of lowPriorityIndexes) pushToEnd(indexes, index);
1226
+ }
1180
1227
  if (typeof lowPriorityIndex === "number") pushToEnd(indexes, lowPriorityIndex);
1181
1228
  for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) {
1182
1229
  const item = this.viewItems[indexes[i]];
@@ -1199,49 +1246,45 @@ var Splitview = class {
1199
1246
  * For each view `i` the offet must be adjusted by `m * i/(n - 1)`.
1200
1247
  */
1201
1248
  layoutViews() {
1202
- this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1249
+ let contentSize = 0;
1250
+ let visibleViewCount = 0;
1251
+ for (const item of this.viewItems) {
1252
+ contentSize += item.size;
1253
+ if (item.visible) visibleViewCount++;
1254
+ }
1255
+ this._contentSize = contentSize;
1203
1256
  this.updateSashEnablement();
1204
1257
  if (this.viewItems.length === 0) return;
1205
- const visibleViewItems = this.viewItems.filter((i) => i.visible);
1206
- const sashCount = Math.max(0, visibleViewItems.length - 1);
1207
- const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewItems.length);
1258
+ const sashCount = Math.max(0, visibleViewCount - 1);
1259
+ const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewCount);
1208
1260
  let totalLeftOffset = 0;
1209
1261
  const viewLeftOffsets = [];
1210
1262
  const sashWidth = 4;
1211
- const runningVisiblePanelCount = this.viewItems.reduce((arr, viewItem, i) => {
1212
- const flag = viewItem.visible ? 1 : 0;
1213
- if (i === 0) arr.push(flag);
1214
- else arr.push(arr[i - 1] + flag);
1215
- return arr;
1216
- }, []);
1263
+ let runningVisiblePanelCount = 0;
1217
1264
  this.viewItems.forEach((view, i) => {
1218
1265
  totalLeftOffset += this.viewItems[i].size;
1219
1266
  viewLeftOffsets.push(totalLeftOffset);
1267
+ runningVisiblePanelCount += view.visible ? 1 : 0;
1220
1268
  const size = view.visible ? view.size - marginReducedSize : 0;
1221
- const visiblePanelsBeforeThisView = Math.max(0, runningVisiblePanelCount[i] - 1);
1269
+ const visiblePanelsBeforeThisView = Math.max(0, runningVisiblePanelCount - 1);
1222
1270
  const offset = i === 0 || visiblePanelsBeforeThisView === 0 ? 0 : viewLeftOffsets[i - 1] + visiblePanelsBeforeThisView / sashCount * marginReducedSize;
1223
1271
  if (i < this.viewItems.length - 1) {
1224
1272
  const newSize = view.visible ? offset + size - sashWidth / 2 + this.margin / 2 : offset;
1225
- if (this._orientation === "HORIZONTAL") {
1226
- this.sashes[i].container.style.left = `${newSize}px`;
1227
- this.sashes[i].container.style.top = `0px`;
1228
- }
1229
- if (this._orientation === "VERTICAL") {
1230
- this.sashes[i].container.style.left = `0px`;
1231
- this.sashes[i].container.style.top = `${newSize}px`;
1232
- }
1273
+ const sash = this.sashes[i];
1274
+ if (this._orientation === "HORIZONTAL") setSashPosition(sash, `${newSize}px`, `0px`);
1275
+ if (this._orientation === "VERTICAL") setSashPosition(sash, `0px`, `${newSize}px`);
1233
1276
  }
1234
1277
  if (this._orientation === "HORIZONTAL") {
1235
- view.container.style.width = `${size}px`;
1236
- view.container.style.left = `${offset}px`;
1237
- view.container.style.top = "";
1238
- view.container.style.height = "";
1278
+ view.setContainerGeometry("width", `${size}px`);
1279
+ view.setContainerGeometry("left", `${offset}px`);
1280
+ view.setContainerGeometry("top", "");
1281
+ view.setContainerGeometry("height", "");
1239
1282
  }
1240
1283
  if (this._orientation === "VERTICAL") {
1241
- view.container.style.height = `${size}px`;
1242
- view.container.style.top = `${offset}px`;
1243
- view.container.style.width = "";
1244
- view.container.style.left = "";
1284
+ view.setContainerGeometry("height", `${size}px`);
1285
+ view.setContainerGeometry("top", `${offset}px`);
1286
+ view.setContainerGeometry("width", "");
1287
+ view.setContainerGeometry("left", "");
1245
1288
  }
1246
1289
  view.view.layout(view.size - marginReducedSize, this._orthogonalSize);
1247
1290
  });
@@ -1259,6 +1302,7 @@ var Splitview = class {
1259
1302
  }
1260
1303
  }
1261
1304
  updateSashEnablement() {
1305
+ if (this.sashes.length === 0) return;
1262
1306
  let previous = false;
1263
1307
  const collapsesDown = this.viewItems.map((i) => previous = i.size - i.minimumSize > 0 || previous);
1264
1308
  previous = false;
@@ -1565,10 +1609,20 @@ var BranchNode = class BranchNode extends CompositeDisposable {
1565
1609
  return this.orientation === "HORIZONTAL" ? this.orthogonalSize : this.size;
1566
1610
  }
1567
1611
  get minimumSize() {
1568
- return this.children.length === 0 ? 0 : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.minimumOrthogonalSize : 0));
1612
+ if (this._cachedMinimumSize === void 0) {
1613
+ let value = 0;
1614
+ for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.max(value, this.children[index].minimumOrthogonalSize);
1615
+ this._cachedMinimumSize = value;
1616
+ }
1617
+ return this._cachedMinimumSize;
1569
1618
  }
1570
1619
  get maximumSize() {
1571
- return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.maximumOrthogonalSize : Number.POSITIVE_INFINITY));
1620
+ if (this._cachedMaximumSize === void 0) {
1621
+ let value = Number.POSITIVE_INFINITY;
1622
+ for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.min(value, this.children[index].maximumOrthogonalSize);
1623
+ this._cachedMaximumSize = value;
1624
+ }
1625
+ return this._cachedMaximumSize;
1572
1626
  }
1573
1627
  get minimumOrthogonalSize() {
1574
1628
  return this.splitview.minimumSize;
@@ -1595,15 +1649,29 @@ var BranchNode = class BranchNode extends CompositeDisposable {
1595
1649
  return this.orientation === "HORIZONTAL" ? this.maximumSize : this.maximumOrthogonalSize;
1596
1650
  }
1597
1651
  get priority() {
1652
+ var _this$_cachedPriority;
1653
+ (_this$_cachedPriority = this._cachedPriority) !== null && _this$_cachedPriority !== void 0 || (this._cachedPriority = this.computePriority());
1654
+ return this._cachedPriority;
1655
+ }
1656
+ computePriority() {
1598
1657
  if (this.children.length === 0) return "normal";
1599
- const priorities = new Set(this.children.map((c) => {
1600
- var _c$priority;
1601
- return (_c$priority = c.priority) !== null && _c$priority !== void 0 ? _c$priority : "normal";
1602
- }));
1603
- if (priorities.has("high")) return "high";
1604
- else if (priorities.has("low")) return "low";
1658
+ let hasHigh = false;
1659
+ let hasLow = false;
1660
+ for (const child of this.children) {
1661
+ var _child$priority;
1662
+ const priority = (_child$priority = child.priority) !== null && _child$priority !== void 0 ? _child$priority : "normal";
1663
+ if (priority === "high") hasHigh = true;
1664
+ else if (priority === "low") hasLow = true;
1665
+ }
1666
+ if (hasHigh) return "high";
1667
+ else if (hasLow) return "low";
1605
1668
  return "normal";
1606
1669
  }
1670
+ invalidateCachedSizes() {
1671
+ this._cachedMinimumSize = void 0;
1672
+ this._cachedMaximumSize = void 0;
1673
+ this._cachedPriority = void 0;
1674
+ }
1607
1675
  get disabled() {
1608
1676
  return this.splitview.disabled;
1609
1677
  }
@@ -1679,6 +1747,7 @@ var BranchNode = class BranchNode extends CompositeDisposable {
1679
1747
  if (this.splitview.isViewVisible(index) === visible) return;
1680
1748
  const wereAllChildrenHidden = this.splitview.contentSize === 0;
1681
1749
  this.splitview.setViewVisible(index, visible);
1750
+ this.invalidateCachedSizes();
1682
1751
  const areAllChildrenHidden = this.splitview.contentSize === 0;
1683
1752
  if (visible && wereAllChildrenHidden || !visible && areAllChildrenHidden) this._onDidVisibilityChange.fire({ visible });
1684
1753
  }
@@ -1727,12 +1796,14 @@ var BranchNode = class BranchNode extends CompositeDisposable {
1727
1796
  return child;
1728
1797
  }
1729
1798
  setupChildrenEvents() {
1799
+ this.invalidateCachedSizes();
1730
1800
  this._childrenDisposable.dispose();
1731
1801
  this._childrenDisposable = new CompositeDisposable(Event.any(...this.children.map((c) => c.onDidChange))((e) => {
1732
1802
  /**
1733
1803
  * indicate a change has occured to allows any re-rendering but don't bubble
1734
1804
  * event because that was specific to this branch
1735
1805
  */
1806
+ this.invalidateCachedSizes();
1736
1807
  this._onDidChange.fire({ size: e.orthogonalSize });
1737
1808
  }), ...this.children.map((c, i) => {
1738
1809
  if (c instanceof BranchNode) return c.onDidVisibilityChange(({ visible }) => {
@@ -3409,7 +3480,7 @@ var DockviewApi = class {
3409
3480
  * Add a group and return the created object.
3410
3481
  */
3411
3482
  addGroup(options) {
3412
- return this.component.addGroup(options);
3483
+ return this.component.withOrigin("api", () => this.component.addGroup(options));
3413
3484
  }
3414
3485
  /**
3415
3486
  * Close all groups and panels.
@@ -3547,13 +3618,13 @@ var DockviewApi = class {
3547
3618
  this.activatePrevious(options);
3548
3619
  }
3549
3620
  maximizeGroup(panel) {
3550
- this.component.maximizeGroup(panel.group);
3621
+ this.component.withOrigin("api", () => this.component.maximizeGroup(panel.group));
3551
3622
  }
3552
3623
  hasMaximizedGroup() {
3553
3624
  return this.component.hasMaximizedGroup();
3554
3625
  }
3555
3626
  exitMaximizedGroup() {
3556
- this.component.exitMaximizedGroup();
3627
+ this.component.withOrigin("api", () => this.component.exitMaximizedGroup());
3557
3628
  }
3558
3629
  get onDidMaximizedGroupChange() {
3559
3630
  return this.component.onDidMaximizedGroupChange;
@@ -5508,9 +5579,6 @@ var ContentContainer = class extends CompositeDisposable {
5508
5579
  const doRender = ((_options$asActive = options === null || options === void 0 ? void 0 : options.asActive) !== null && _options$asActive !== void 0 ? _options$asActive : true) || this.panel && this.group.isPanelActive(this.panel);
5509
5580
  if (((_this$panel = this.panel) === null || _this$panel === void 0 ? void 0 : _this$panel.view.content.element.parentElement) === this._element) {
5510
5581
  var _this$panel$view$cont, _this$panel$view$cont2;
5511
- /**
5512
- * If the currently attached panel is mounted directly to the content then remove it
5513
- */
5514
5582
  this.panel.view.content.element.remove();
5515
5583
  (_this$panel$view$cont = (_this$panel$view$cont2 = this.panel.view.content).onHide) === null || _this$panel$view$cont === void 0 || _this$panel$view$cont.call(_this$panel$view$cont2);
5516
5584
  }
@@ -6251,7 +6319,7 @@ var Scrollbar = class Scrollbar extends CompositeDisposable {
6251
6319
  this.element.appendChild(this._scrollbar);
6252
6320
  this.addDisposables(addDisposableListener(this.element, "wheel", (event) => {
6253
6321
  this._scrollOffset += event.deltaY * Scrollbar.MouseWheelSpeed;
6254
- this.calculateScrollbarStyles();
6322
+ this.scheduleStyleUpdate();
6255
6323
  }), addDisposableListener(this._scrollbar, "pointerdown", (event) => {
6256
6324
  var _this$_scrollbar$owne;
6257
6325
  event.preventDefault();
@@ -6263,7 +6331,7 @@ var Scrollbar = class Scrollbar extends CompositeDisposable {
6263
6331
  const delta = this._orientation === "horizontal" ? event.clientX - originalClient : event.clientY - originalClient;
6264
6332
  const p = (this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight) / (this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight);
6265
6333
  this._scrollOffset = originalScrollOffset + delta / p;
6266
- this.calculateScrollbarStyles();
6334
+ this.scheduleStyleUpdate();
6267
6335
  };
6268
6336
  const onEnd = () => {
6269
6337
  toggleClass(this.element, "dv-scrollable-scrolling", false);
@@ -6275,10 +6343,10 @@ var Scrollbar = class Scrollbar extends CompositeDisposable {
6275
6343
  doc.addEventListener("pointerup", onEnd);
6276
6344
  doc.addEventListener("pointercancel", onEnd);
6277
6345
  }), addDisposableListener(this.element, "scroll", () => {
6278
- this.calculateScrollbarStyles();
6346
+ this.scheduleStyleUpdate();
6279
6347
  }), addDisposableListener(this.scrollableElement, "scroll", () => {
6280
6348
  this._scrollOffset = this._orientation === "horizontal" ? this.scrollableElement.scrollLeft : this.scrollableElement.scrollTop;
6281
- this.calculateScrollbarStyles();
6349
+ this.scheduleStyleUpdate();
6282
6350
  }), watchElementResize(this.element, () => {
6283
6351
  toggleClass(this.element, "dv-scrollable-resizing", true);
6284
6352
  if (this._animationTimer) clearTimeout(this._animationTimer);
@@ -6286,9 +6354,29 @@ var Scrollbar = class Scrollbar extends CompositeDisposable {
6286
6354
  clearTimeout(this._animationTimer);
6287
6355
  toggleClass(this.element, "dv-scrollable-resizing", false);
6288
6356
  }, 500);
6289
- this.calculateScrollbarStyles();
6357
+ this.scheduleStyleUpdate();
6358
+ }), Disposable.from(() => {
6359
+ if (this._pendingStyleFrame !== void 0) {
6360
+ cancelAnimationFrame(this._pendingStyleFrame);
6361
+ this._pendingStyleFrame = void 0;
6362
+ }
6290
6363
  }));
6291
6364
  }
6365
+ /**
6366
+ * Coalesce the scrollbar restyle to one pass per animation frame. The
6367
+ * wheel / scroll / pointermove handlers can each fire many times per frame,
6368
+ * and `calculateScrollbarStyles` both reads layout (`clientWidth`/
6369
+ * `scrollWidth`) and writes styles + `scrollLeft`/`scrollTop`; running it
6370
+ * synchronously per event caused repeated read→write→read reflow. Batching
6371
+ * to a frame keeps a burst of events to a single measure+write.
6372
+ */
6373
+ scheduleStyleUpdate() {
6374
+ if (this._pendingStyleFrame !== void 0) return;
6375
+ this._pendingStyleFrame = requestAnimationFrame(() => {
6376
+ this._pendingStyleFrame = void 0;
6377
+ this.calculateScrollbarStyles();
6378
+ });
6379
+ }
6292
6380
  calculateScrollbarStyles() {
6293
6381
  const clientSize = this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight;
6294
6382
  const scrollSize = this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight;
@@ -8749,8 +8837,8 @@ var Tabs = class extends CompositeDisposable {
8749
8837
  }, true), addDisposableListener(this._tabsList, "dragend", () => {
8750
8838
  this._reorder.resetDragAnimation();
8751
8839
  }), addDisposableListener(this._tabsList, "drop", (event) => {
8752
- var _this$accessor$option, _this$group$model$dro;
8753
- if (!this._animState || this._animState.currentInsertionIndex === null) return;
8840
+ var _this$_animState3, _this$accessor$option, _this$group$model$dro;
8841
+ if (((_this$_animState3 = this._animState) === null || _this$_animState3 === void 0 ? void 0 : _this$_animState3.currentInsertionIndex) == null) return;
8754
8842
  if (((_this$accessor$option = this.accessor.options.theme) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.tabAnimation) !== "smooth" && !this._animState.sourceTabGroupId) return;
8755
8843
  event.stopPropagation();
8756
8844
  event.preventDefault();
@@ -8955,7 +9043,7 @@ var Tabs = class extends CompositeDisposable {
8955
9043
  };
8956
9044
  this._pendingCollapse = true;
8957
9045
  requestAnimationFrame(() => {
8958
- var _this$_animState3, _this$_animState3$cur;
9046
+ var _this$_animState4, _this$_animState4$cur;
8959
9047
  this._pendingCollapse = false;
8960
9048
  if (!this._animState) return;
8961
9049
  if (!this._tabsList.classList.contains("dv-tabs-container--wrap")) {
@@ -8963,7 +9051,7 @@ var Tabs = class extends CompositeDisposable {
8963
9051
  toggleClass(tab.element, "dv-tab--dragging", true);
8964
9052
  tab.element.offsetHeight;
8965
9053
  }
8966
- (_this$_animState3$cur = (_this$_animState3 = this._animState).currentInsertionIndex) !== null && _this$_animState3$cur !== void 0 || (_this$_animState3.currentInsertionIndex = sourceIndex);
9054
+ (_this$_animState4$cur = (_this$_animState4 = this._animState).currentInsertionIndex) !== null && _this$_animState4$cur !== void 0 || (_this$_animState4.currentInsertionIndex = sourceIndex);
8967
9055
  this.applyDragOverTransforms(true);
8968
9056
  tab.element.style.removeProperty("transition");
8969
9057
  });
@@ -9052,8 +9140,8 @@ var Tabs = class extends CompositeDisposable {
9052
9140
  }
9053
9141
  }
9054
9142
  delete(id) {
9055
- var _this$_animState4;
9056
- if (((_this$_animState4 = this._animState) === null || _this$_animState4 === void 0 ? void 0 : _this$_animState4.sourceTabId) === id) {
9143
+ var _this$_animState5;
9144
+ if (((_this$_animState5 = this._animState) === null || _this$_animState5 === void 0 ? void 0 : _this$_animState5.sourceTabId) === id) {
9057
9145
  this.resetTabTransforms();
9058
9146
  this._animState = null;
9059
9147
  }
@@ -9170,7 +9258,7 @@ var Tabs = class extends CompositeDisposable {
9170
9258
  const groupPanelIds = new Set(tabGroup.panelIds);
9171
9259
  this._pendingCollapse = true;
9172
9260
  requestAnimationFrame(() => {
9173
- var _this$_animState5, _this$_animState5$cur;
9261
+ var _this$_animState6, _this$_animState6$cur;
9174
9262
  this._pendingCollapse = false;
9175
9263
  if (!this._animState) return;
9176
9264
  for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) {
@@ -9185,7 +9273,7 @@ var Tabs = class extends CompositeDisposable {
9185
9273
  this._tabsList.offsetHeight;
9186
9274
  const underline = this._tabGroupManager.groupUnderlines.get(tabGroup.id);
9187
9275
  if (underline) underline.style.display = "none";
9188
- (_this$_animState5$cur = (_this$_animState5 = this._animState).currentInsertionIndex) !== null && _this$_animState5$cur !== void 0 || (_this$_animState5.currentInsertionIndex = firstIdx);
9276
+ (_this$_animState6$cur = (_this$_animState6 = this._animState).currentInsertionIndex) !== null && _this$_animState6$cur !== void 0 || (_this$_animState6.currentInsertionIndex = firstIdx);
9189
9277
  this.applyDragOverTransforms(true);
9190
9278
  for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) t.value.element.style.removeProperty("transition");
9191
9279
  if (chipEntry) chipEntry.chip.element.style.removeProperty("transition");
@@ -9920,6 +10008,7 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
9920
10008
  set headerPosition(value) {
9921
10009
  var _this$_activePanel;
9922
10010
  this._headerPosition = value;
10011
+ this.invalidateHeaderSize();
9923
10012
  removeClasses(this.container, "dv-groupview-header-top", "dv-groupview-header-bottom", "dv-groupview-header-left", "dv-groupview-header-right");
9924
10013
  addClasses(this.container, `dv-groupview-header-${value}`);
9925
10014
  const direction = value === "top" || value === "bottom" ? "horizontal" : "vertical";
@@ -10061,7 +10150,14 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10061
10150
  this.header.hidden = !!options.hideHeader;
10062
10151
  this.locked = (_options$locked = options.locked) !== null && _options$locked !== void 0 ? _options$locked : false;
10063
10152
  this.headerPosition = (_options$headerPositi = options.headerPosition) !== null && _options$headerPositi !== void 0 ? _options$headerPositi : accessor.defaultHeaderPosition;
10064
- this.addDisposables(this._onDidPanelTitleChange.event(() => this.updateAccessibleLabel()), this._onTabDragStart, this._onGroupDragStart, this._onWillShowOverlay, this.tabsContainer.onTabDragStart((event) => {
10153
+ this.addDisposables(watchElementResize(this.tabsContainer.element, () => {
10154
+ if (this.isDisposed) return;
10155
+ const size = this.measureHeaderSize();
10156
+ if (size !== this._cachedHeaderSize) {
10157
+ this._cachedHeaderSize = size;
10158
+ this.layout(this._width, this._height);
10159
+ }
10160
+ }), this._onDidPanelTitleChange.event(() => this.updateAccessibleLabel()), this._onTabDragStart, this._onGroupDragStart, this._onWillShowOverlay, this.tabsContainer.onTabDragStart((event) => {
10065
10161
  this._onTabDragStart.fire(event);
10066
10162
  }), this.tabsContainer.onGroupDragStart((event) => {
10067
10163
  this._onGroupDragStart.fire(event);
@@ -10426,7 +10522,11 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10426
10522
  this.panels.forEach((panel) => {
10427
10523
  this.rerender(panel);
10428
10524
  });
10429
- 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
+ }
10430
10530
  }
10431
10531
  get renderContainer() {
10432
10532
  var _this$_overwriteRende;
@@ -10518,16 +10618,11 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10518
10618
  (_this$_activePanel3 = this._activePanel) === null || _this$_activePanel3 === void 0 || _this$_activePanel3.focus();
10519
10619
  }
10520
10620
  openPanel(panel, options = {}) {
10521
- /**
10522
- * set the panel group
10523
- * add the panel
10524
- * check if group active
10525
- * check if panel active
10526
- */
10527
10621
  if (typeof options.index !== "number" || options.index > this.panels.length) options.index = this.panels.length;
10528
10622
  const skipSetActive = !!options.skipSetActive;
10529
10623
  panel.updateParentGroup(this.groupPanel, { skipSetActive: options.skipSetActive });
10530
10624
  this.doAddPanel(panel, options.index, { skipSetActive });
10625
+ this.invalidateHeaderSize();
10531
10626
  if (this._activePanel === panel) {
10532
10627
  this.contentContainer.renderPanel(panel, { asActive: true });
10533
10628
  return;
@@ -10590,6 +10685,7 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10590
10685
  * changing) down to the content + active panel.
10591
10686
  */
10592
10687
  relayout() {
10688
+ this.invalidateHeaderSize();
10593
10689
  this.layout(this._width, this._height);
10594
10690
  }
10595
10691
  /**
@@ -10599,17 +10695,32 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10599
10695
  * width); a hidden header has `display:none` so its `offset*` is 0 and
10600
10696
  * nothing is subtracted.
10601
10697
  */
10602
- contentDimensions() {
10698
+ /**
10699
+ * Measure the header's extent along its occupied axis. This is the single
10700
+ * forced-reflow read; it now runs only on a cache miss (first layout after
10701
+ * a header change) and inside the header's ResizeObserver — not on every
10702
+ * layout frame.
10703
+ */
10704
+ measureHeaderSize() {
10603
10705
  const headerEl = this.tabsContainer.element;
10706
+ return this.headerPosition === "top" || this.headerPosition === "bottom" ? headerEl.offsetHeight : headerEl.offsetWidth;
10707
+ }
10708
+ invalidateHeaderSize() {
10709
+ this._cachedHeaderSize = void 0;
10710
+ }
10711
+ contentDimensions() {
10712
+ var _this$_cachedHeaderSi;
10604
10713
  const horizontal = this.headerPosition === "top" || this.headerPosition === "bottom";
10714
+ const headerSize = (_this$_cachedHeaderSi = this._cachedHeaderSize) !== null && _this$_cachedHeaderSi !== void 0 ? _this$_cachedHeaderSi : this._cachedHeaderSize = this.measureHeaderSize();
10605
10715
  return {
10606
- width: horizontal ? this._width : Math.max(0, this._width - headerEl.offsetWidth),
10607
- height: horizontal ? Math.max(0, this._height - headerEl.offsetHeight) : this._height
10716
+ width: horizontal ? this._width : Math.max(0, this._width - headerSize),
10717
+ height: horizontal ? Math.max(0, this._height - headerSize) : this._height
10608
10718
  };
10609
10719
  }
10610
10720
  _removePanel(panel, options) {
10611
10721
  const isActivePanel = this._activePanel === panel;
10612
10722
  this.doRemovePanel(panel);
10723
+ this.invalidateHeaderSize();
10613
10724
  if (isActivePanel && this.panels.length > 0) {
10614
10725
  const nextPanel = this.mostRecentlyUsed[0];
10615
10726
  this.openPanel(nextPanel, {
@@ -10935,6 +11046,7 @@ var GridviewPanel = class extends BasePanelView {
10935
11046
  if (typeof event.minimumHeight === "number" || typeof event.minimumHeight === "function") this._minimumHeight = event.minimumHeight;
10936
11047
  if (typeof event.maximumWidth === "number" || typeof event.maximumWidth === "function") this._maximumWidth = event.maximumWidth;
10937
11048
  if (typeof event.maximumHeight === "number" || typeof event.maximumHeight === "function") this._maximumHeight = event.maximumHeight;
11049
+ this._onDidChange.fire(void 0);
10938
11050
  }), this.api.onDidSizeChange((event) => {
10939
11051
  this._onDidChange.fire({
10940
11052
  height: event.height,
@@ -11054,20 +11166,22 @@ var DockviewGroupPanelApiImpl = class extends GridviewPanelApiImpl {
11054
11166
  return this._group.model.headerPosition;
11055
11167
  }
11056
11168
  moveTo(options) {
11057
- var _options$group, _options$position, _options$skipSetActiv, _options$position2;
11058
11169
  if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
11059
- const group = (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this.accessor.addGroup({
11060
- direction: positionToDirection((_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "right"),
11061
- skipSetActive: (_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false
11062
- });
11063
- this.accessor.moveGroupOrPanel({
11064
- from: { groupId: this._group.id },
11065
- to: {
11066
- group,
11067
- position: options.group ? (_options$position2 = options.position) !== null && _options$position2 !== void 0 ? _options$position2 : "center" : "center",
11068
- index: options.index
11069
- },
11070
- skipSetActive: options.skipSetActive
11170
+ this.accessor.withOrigin("api", () => {
11171
+ var _options$group, _options$position, _options$skipSetActiv, _options$position2;
11172
+ const group = (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this.accessor.addGroup({
11173
+ direction: positionToDirection((_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "right"),
11174
+ skipSetActive: (_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false
11175
+ });
11176
+ this.accessor.moveGroupOrPanel({
11177
+ from: { groupId: this._group.id },
11178
+ to: {
11179
+ group,
11180
+ position: options.group ? (_options$position2 = options.position) !== null && _options$position2 !== void 0 ? _options$position2 : "center" : "center",
11181
+ index: options.index
11182
+ },
11183
+ skipSetActive: options.skipSetActive
11184
+ });
11071
11185
  });
11072
11186
  }
11073
11187
  maximize() {
@@ -11377,7 +11491,7 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11377
11491
  this._group = value;
11378
11492
  this._onDidGroupChange.fire({});
11379
11493
  this.setupGroupEventListeners(oldGroup);
11380
- this._onDidLocationChange.fire({ location: this.group.api.location });
11494
+ this.fireLocationChange();
11381
11495
  }
11382
11496
  }
11383
11497
  get group() {
@@ -11416,18 +11530,20 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11416
11530
  this.accessor.withOrigin("api", () => super.setActive());
11417
11531
  }
11418
11532
  moveTo(options) {
11419
- var _options$group, _options$position;
11420
- this.accessor.moveGroupOrPanel({
11421
- from: {
11422
- groupId: this._group.id,
11423
- panelId: this.panel.id
11424
- },
11425
- to: {
11426
- group: (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this._group,
11427
- position: options.group ? (_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "center" : "center",
11428
- index: options.index
11429
- },
11430
- skipSetActive: options.skipSetActive
11533
+ this.accessor.withOrigin("api", () => {
11534
+ var _options$group, _options$position;
11535
+ return this.accessor.moveGroupOrPanel({
11536
+ from: {
11537
+ groupId: this._group.id,
11538
+ panelId: this.panel.id
11539
+ },
11540
+ to: {
11541
+ group: (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this._group,
11542
+ position: options.group ? (_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "center" : "center",
11543
+ index: options.index
11544
+ },
11545
+ skipSetActive: options.skipSetActive
11546
+ });
11431
11547
  });
11432
11548
  }
11433
11549
  setTitle(title) {
@@ -11451,6 +11567,23 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11451
11567
  exitMaximized() {
11452
11568
  this.group.api.exitMaximized();
11453
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
+ }
11454
11587
  setupGroupEventListeners(previousGroup) {
11455
11588
  var _previousGroup$isActi;
11456
11589
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11459,9 +11592,9 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11459
11592
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11460
11593
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11461
11594
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11462
- }), this.group.api.onDidLocationChange((event) => {
11595
+ }), this.group.api.onDidLocationChange(() => {
11463
11596
  if (this.group !== this.panel.group) return;
11464
- this._onDidLocationChange.fire(event);
11597
+ this.fireLocationChange();
11465
11598
  }), this.group.api.onDidActiveChange(() => {
11466
11599
  if (this.group !== this.panel.group) return;
11467
11600
  if (_trackGroupActive !== this.isGroupActive) {
@@ -12537,12 +12670,6 @@ const OPTION_MODULE_RULES = [
12537
12670
  moduleName: "ContextMenu",
12538
12671
  when: (o) => o.getTabGroupChipContextMenuItems != null
12539
12672
  },
12540
- {
12541
- optionKey: "createContextMenuItemComponent",
12542
- reason: "createContextMenuItemComponent",
12543
- moduleName: "ContextMenu",
12544
- when: (o) => o.createContextMenuItemComponent != null
12545
- },
12546
12673
  {
12547
12674
  optionKey: "dndCompass",
12548
12675
  reason: "dndCompass",
@@ -13271,7 +13398,18 @@ var LiveRegionService = class extends CompositeDisposable {
13271
13398
  prev = next;
13272
13399
  const panel = group.activePanel;
13273
13400
  if (!panel) return;
13274
- const kind = next === "floating" ? "float" : next === "popout" ? "popout" : "dock";
13401
+ let kind;
13402
+ switch (next) {
13403
+ case "floating":
13404
+ kind = "float";
13405
+ break;
13406
+ case "popout":
13407
+ kind = "popout";
13408
+ break;
13409
+ default:
13410
+ kind = "dock";
13411
+ break;
13412
+ }
13275
13413
  this._announce(panel, kind);
13276
13414
  });
13277
13415
  this._locationSubs.set(group.id, sub);
@@ -13323,29 +13461,14 @@ const LiveRegionModule = defineModule({
13323
13461
 
13324
13462
  //#endregion
13325
13463
  //#region src/dockview/advancedDnDService.ts
13326
- /** Cursor offset of the group drag ghost, matched to the long-shipped default. */
13464
+ /** Cursor offset of the group drag ghost. */
13327
13465
  const GROUP_DRAG_GHOST_OFFSET_X = 30;
13328
13466
  const GROUP_DRAG_GHOST_OFFSET_Y = -10;
13329
13467
  /**
13330
- * The narrow surface the {@link AdvancedDnDService} needs from the host
13331
- * (the `DockviewComponent`).
13332
- *
13333
- * The `onWill*` emitters stay on the component so the public event shape is
13334
- * unchanged whether or not this module is registered; the service is only the
13335
- * dispatch point those fires are routed through. Engine policy (e.g. the
13336
- * `disableDnd` guard) stays on the component, ahead of the dispatch.
13337
- */
13338
- /**
13339
13468
  * Owns the dispatch of the advanced drag-and-drop hooks: `onWillDragPanel`,
13340
- * `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`.
13341
- *
13342
- * At this stage the service is a thin dispatcher that forwards to the host's
13343
- * emitters; the customisation surface it gates (custom drop overlays, custom
13344
- * group drag ghosts, hook veto/transform behaviour) is currently inlined in
13345
- * core and is extracted into this service in later phases. Routing the
13346
- * dispatch through a module slot is what lets that customisation layer move
13347
- * into a separately-distributed package in a future major version without
13348
- * changing the public event surface.
13469
+ * `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`, forwarding each to
13470
+ * the host's emitters so the public event shape is unchanged whether or not
13471
+ * this module is registered.
13349
13472
  *
13350
13473
  * The service holds no drag state of its own. The gesture is driven by the
13351
13474
  * DnD backends, and the per-group subscriptions live on the component's group
@@ -13471,7 +13594,7 @@ const AllModules = [
13471
13594
  //#region src/overlay/overlayRenderContainer.ts
13472
13595
  var PositionCache = class {
13473
13596
  constructor() {
13474
- this.cache = /* @__PURE__ */ new Map();
13597
+ this.cache = /* @__PURE__ */ new WeakMap();
13475
13598
  this.currentFrameId = 0;
13476
13599
  this.rafId = null;
13477
13600
  }
@@ -13497,6 +13620,13 @@ var PositionCache = class {
13497
13620
  });
13498
13621
  }
13499
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;
13500
13630
  function createFocusableElement() {
13501
13631
  const element = document.createElement("div");
13502
13632
  element.tabIndex = -1;
@@ -13509,12 +13639,13 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13509
13639
  this.accessor = accessor;
13510
13640
  this.map = {};
13511
13641
  this._disposed = false;
13642
+ this._generation = 0;
13512
13643
  this.positionCache = new PositionCache();
13513
- this.pendingUpdates = /* @__PURE__ */ new Set();
13514
13644
  this.addDisposables(Disposable.from(() => {
13515
13645
  for (const value of Object.values(this.map)) {
13516
13646
  value.disposable.dispose();
13517
13647
  value.destroy.dispose();
13648
+ this.cancelPendingUpdate(value);
13518
13649
  }
13519
13650
  this._disposed = true;
13520
13651
  }));
@@ -13541,11 +13672,24 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13541
13672
  this.positionCache.invalidate();
13542
13673
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13543
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
+ }
13544
13687
  detatch(panel) {
13545
13688
  if (this.map[panel.api.id]) {
13546
- const { disposable, destroy } = this.map[panel.api.id];
13547
- disposable.dispose();
13548
- destroy.dispose();
13689
+ const entry = this.map[panel.api.id];
13690
+ entry.disposable.dispose();
13691
+ entry.destroy.dispose();
13692
+ this.cancelPendingUpdate(entry);
13549
13693
  delete this.map[panel.api.id];
13550
13694
  return true;
13551
13695
  }
@@ -13561,22 +13705,73 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13561
13705
  panel,
13562
13706
  disposable: Disposable.NONE,
13563
13707
  destroy: Disposable.NONE,
13564
- element
13708
+ element,
13709
+ retainPreviousGeometry: false,
13710
+ positioned: false,
13711
+ generation: ++this._generation
13565
13712
  };
13566
13713
  }
13567
- 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;
13568
13752
  const contentElement = panel.view.content.element;
13569
13753
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13570
13754
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13571
13755
  const resize = () => {
13572
13756
  const panelId = panel.api.id;
13573
- if (this.pendingUpdates.has(panelId)) return;
13574
- this.pendingUpdates.add(panelId);
13575
- 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(() => {
13576
13771
  var _entry$forceVisible;
13577
- this.pendingUpdates.delete(panelId);
13578
13772
  const entry = this.map[panelId];
13579
- 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;
13580
13775
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13581
13776
  const clip = entry.clip;
13582
13777
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13585,6 +13780,15 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13585
13780
  const top = box.top - box2.top;
13586
13781
  const width = box.width;
13587
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;
13588
13792
  focusContainer.style.left = `${left}px`;
13589
13793
  focusContainer.style.top = `${top}px`;
13590
13794
  focusContainer.style.width = `${width}px`;
@@ -13613,11 +13817,30 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13613
13817
  } else focusContainer.style.clipPath = "";
13614
13818
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13615
13819
  });
13820
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13616
13821
  };
13617
13822
  const visibilityChanged = () => {
13618
13823
  if (panel.api.isVisible) {
13824
+ var _this$map$panel$api$i;
13619
13825
  this.positionCache.invalidate();
13620
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 = "";
13621
13844
  focusContainer.style.pointerEvents = "";
13622
13845
  } else {
13623
13846
  focusContainer.style.visibility = "hidden";
@@ -13840,7 +14063,17 @@ var PopoutWindow = class extends CompositeDisposable {
13840
14063
  window: externalWindow
13841
14064
  });
13842
14065
  return new Promise((resolve, reject) => {
13843
- 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)));
13844
14077
  externalWindow.addEventListener("load", () => {
13845
14078
  /**
13846
14079
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -15140,7 +15373,22 @@ var DockviewComponent = class extends BaseGrid {
15140
15373
  const rect = candidate.element.getBoundingClientRect();
15141
15374
  const dx = rect.left + rect.width / 2 - fromX;
15142
15375
  const dy = rect.top + rect.height / 2 - fromY;
15143
- if (!(direction === "left" ? dx < 0 && Math.abs(dx) >= Math.abs(dy) : direction === "right" ? dx > 0 && Math.abs(dx) >= Math.abs(dy) : direction === "up" ? dy < 0 && Math.abs(dy) >= Math.abs(dx) : dy > 0 && Math.abs(dy) >= Math.abs(dx))) continue;
15376
+ let inDirection;
15377
+ switch (direction) {
15378
+ case "left":
15379
+ inDirection = dx < 0 && Math.abs(dx) >= Math.abs(dy);
15380
+ break;
15381
+ case "right":
15382
+ inDirection = dx > 0 && Math.abs(dx) >= Math.abs(dy);
15383
+ break;
15384
+ case "up":
15385
+ inDirection = dy < 0 && Math.abs(dy) >= Math.abs(dx);
15386
+ break;
15387
+ default:
15388
+ inDirection = dy > 0 && Math.abs(dy) >= Math.abs(dx);
15389
+ break;
15390
+ }
15391
+ if (!inDirection) continue;
15144
15392
  const distance = dx * dx + dy * dy;
15145
15393
  if (distance < bestDistance) {
15146
15394
  bestDistance = distance;
@@ -15243,6 +15491,7 @@ var DockviewComponent = class extends BaseGrid {
15243
15491
  this._onWillDrop = new Emitter();
15244
15492
  this.onWillDrop = this._onWillDrop.event;
15245
15493
  this._mutationDepth = 0;
15494
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15246
15495
  this._origin = "user";
15247
15496
  this._originDepth = 0;
15248
15497
  this._onWillMutateLayout = new Emitter();
@@ -15340,7 +15589,7 @@ var DockviewComponent = class extends BaseGrid {
15340
15589
  toggleClass(this.element, "dv-debug", !!options.debug);
15341
15590
  this.updateTheme();
15342
15591
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15343
- 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", () => {
15344
15593
  var _this$rootDropTargetC2, _this$floatingDropTar;
15345
15594
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15346
15595
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15414,7 +15663,7 @@ var DockviewComponent = class extends BaseGrid {
15414
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;
15415
15664
  }
15416
15665
  addPopoutGroup(itemToPopout, options) {
15417
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15666
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15418
15667
  }
15419
15668
  /** Enumerate the popout groups currently open in their own windows. */
15420
15669
  getPopouts() {
@@ -15511,15 +15760,32 @@ var DockviewComponent = class extends BaseGrid {
15511
15760
  }
15512
15761
  };
15513
15762
  let floatingBox;
15514
- 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(() => {
15515
- const panel = referenceGroup.model.removePanel(itemToPopout);
15516
- group.model.openPanel(panel);
15517
- });
15518
- 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 {
15519
15781
  this.movingLock(() => moveGroupWithoutDestroying({
15520
15782
  from: referenceGroup,
15521
15783
  to: group
15522
15784
  }));
15785
+ movedPanels = group.panels.map((panel) => ({
15786
+ panel,
15787
+ from: referenceGroup
15788
+ }));
15523
15789
  switch (referenceLocation) {
15524
15790
  case "grid":
15525
15791
  referenceGroup.api.setVisible(false);
@@ -15628,6 +15894,7 @@ var DockviewComponent = class extends BaseGrid {
15628
15894
  group: value.popoutGroup,
15629
15895
  window: value.getWindow()
15630
15896
  });
15897
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15631
15898
  return true;
15632
15899
  }).catch((err) => {
15633
15900
  console.error("dockview: failed to create popout.", err);
@@ -15705,6 +15972,16 @@ var DockviewComponent = class extends BaseGrid {
15705
15972
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15706
15973
  const anchorPresent = members.includes(group);
15707
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 = [];
15708
15985
  if (genuineClose) for (const member of members) {
15709
15986
  if (member === group) continue;
15710
15987
  this.movingLock(() => {
@@ -15715,12 +15992,21 @@ var DockviewComponent = class extends BaseGrid {
15715
15992
  });
15716
15993
  this.redockGroupToMainGrid(member);
15717
15994
  });
15995
+ for (const panel of member.panels) movedPanels.push({
15996
+ panel,
15997
+ from: member
15998
+ });
15718
15999
  }
15719
16000
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16001
+ const rehomed = [...group.panels];
15720
16002
  this.movingLock(() => moveGroupWithoutDestroying({
15721
16003
  from: group,
15722
16004
  to: referenceGroup
15723
16005
  }));
16006
+ for (const panel of rehomed) movedPanels.push({
16007
+ panel,
16008
+ from: group
16009
+ });
15724
16010
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15725
16011
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15726
16012
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15751,10 +16037,15 @@ var DockviewComponent = class extends BaseGrid {
15751
16037
  this.movingLock(() => {
15752
16038
  this.doAddGroup(group, [0]);
15753
16039
  });
16040
+ for (const panel of group.panels) movedPanels.push({
16041
+ panel,
16042
+ from: group
16043
+ });
15754
16044
  }
15755
16045
  this.doSetGroupAndPanelActive(group);
15756
16046
  }
15757
16047
  disposePopoutGridview();
16048
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15758
16049
  }
15759
16050
  addFloatingGroup(item, options) {
15760
16051
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15763,7 +16054,15 @@ var DockviewComponent = class extends BaseGrid {
15763
16054
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15764
16055
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15765
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 = [];
15766
16064
  if (item instanceof DockviewPanel) {
16065
+ const sourceGroup = item.group;
15767
16066
  group = this.createGroup();
15768
16067
  this._onDidAddGroup.fire(group);
15769
16068
  this.movingLock(() => this.removePanel(item, {
@@ -15772,6 +16071,10 @@ var DockviewComponent = class extends BaseGrid {
15772
16071
  skipSetActiveGroup: true
15773
16072
  }));
15774
16073
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16074
+ movedPanels = [{
16075
+ panel: item,
16076
+ from: sourceGroup
16077
+ }];
15775
16078
  } else {
15776
16079
  var _this$_popoutWindowSe8;
15777
16080
  group = item;
@@ -15788,11 +16091,21 @@ var DockviewComponent = class extends BaseGrid {
15788
16091
  });
15789
16092
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15790
16093
  group = popoutReferenceGroup;
15791
- } else this.doRemoveGroup(item, {
15792
- skipDispose: true,
15793
- skipPopoutReturn: true,
15794
- skipPopoutAssociated: false
15795
- });
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
+ }
15796
16109
  }
15797
16110
  function getAnchoredBox() {
15798
16111
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15825,6 +16138,7 @@ var DockviewComponent = class extends BaseGrid {
15825
16138
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15826
16139
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15827
16140
  });
16141
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15828
16142
  }
15829
16143
  /**
15830
16144
  * Build an empty gridview configured to match the main grid's styling, for
@@ -15988,11 +16302,15 @@ var DockviewComponent = class extends BaseGrid {
15988
16302
  /**
15989
16303
  * Reveal (create-or-fill) the edge group at `position` and move the dragged
15990
16304
  * item described by `data` into it. A newly created edge group is created
15991
- * collapsed and flagged `autoReveal` so it tears down to zero footprint when
15992
- * later emptied. If an edge group already exists there it is reused: the
15993
- * panel is added to its tabs and its collapsed/toggled state is left as-is
15994
- * (never re-created; `addEdgeGroup` throws on a duplicate position). No-op if
15995
- * the EdgeGroup module is absent.
16305
+ * collapsed, flagged `autoReveal` so it tears down to zero footprint when
16306
+ * later emptied, and takes its auto-hide state from `options.autoHide`. If an
16307
+ * edge group already exists there it is reused: the panel is added to its
16308
+ * tabs and its collapsed/toggled *and auto-hide* state are left as-is (never
16309
+ * re-created; `addEdgeGroup` throws on a duplicate position). This keeps a
16310
+ * drag-reveal from silently converting a static edge group into an
16311
+ * auto-hiding one; to change an existing group's auto-hide, call
16312
+ * `api.getEdgeGroup(position)?.setAutoHide(...)` directly. No-op if the
16313
+ * EdgeGroup module is absent.
15996
16314
  *
15997
16315
  * This is the primitive behind the dock-to-edge groups: the two-band
15998
16316
  * drag-reveal affordance routes its outer-band drops here.
@@ -16011,7 +16329,7 @@ var DockviewComponent = class extends BaseGrid {
16011
16329
  collapsed: true
16012
16330
  });
16013
16331
  group = service.get(position);
16014
- } else if ((options === null || options === void 0 ? void 0 : options.autoHide) !== void 0) this.setEdgeGroupAutoHide(group, options.autoHide);
16332
+ }
16015
16333
  if (!group) return;
16016
16334
  this.moveGroupOrPanel({
16017
16335
  from: {
@@ -16252,35 +16570,119 @@ var DockviewComponent = class extends BaseGrid {
16252
16570
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16253
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");
16254
16572
  const existingPanels = /* @__PURE__ */ new Map();
16255
- 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
+ };
16256
16594
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16257
16595
  /**
16258
- * What are we doing here?
16259
- *
16260
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16261
- * 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.
16262
16599
  */
16263
- tempGroup = this.createGroup();
16264
- this._groups.delete(tempGroup.api.id);
16265
16600
  const newPanels = Object.keys(data.panels);
16266
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16267
- this.movingLock(() => {
16268
- Array.from(existingPanels.values()).forEach((panel) => {
16269
- this.moveGroupOrPanel({
16270
- from: {
16271
- groupId: panel.api.group.api.id,
16272
- panelId: panel.api.id
16273
- },
16274
- to: {
16275
- group: tempGroup,
16276
- position: "center"
16277
- },
16278
- 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
+ });
16279
16678
  });
16280
16679
  });
16281
- });
16282
- }
16283
- this.clear();
16680
+ this.clear();
16681
+ } catch (err) {
16682
+ disposeTemporaryGroups();
16683
+ throw err;
16684
+ }
16685
+ } else this.clear();
16284
16686
  const { grid, panels, activeGroup } = data;
16285
16687
  try {
16286
16688
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16312,9 +16714,10 @@ var DockviewComponent = class extends BaseGrid {
16312
16714
  * due to a corruption of input data.
16313
16715
  */
16314
16716
  const existingPanel = existingPanels.get(child);
16315
- if (tempGroup && existingPanel) {
16717
+ const temporaryGroup = temporaryGroups.get(child);
16718
+ if (temporaryGroup && existingPanel) {
16316
16719
  this.movingLock(() => {
16317
- tempGroup.model.removePanel(existingPanel);
16720
+ temporaryGroup.model.removePanel(existingPanel);
16318
16721
  });
16319
16722
  createdPanels.push(existingPanel);
16320
16723
  existingPanel.updateFromStateModel(panels[child]);
@@ -16344,7 +16747,7 @@ var DockviewComponent = class extends BaseGrid {
16344
16747
  return createGroupFromSerializedState(node.data);
16345
16748
  } });
16346
16749
  this._layoutFromShell(width, height);
16347
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16750
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16348
16751
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16349
16752
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16350
16753
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16381,6 +16784,8 @@ var DockviewComponent = class extends BaseGrid {
16381
16784
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16382
16785
  */
16383
16786
  throw err;
16787
+ } finally {
16788
+ disposeTemporaryGroups();
16384
16789
  }
16385
16790
  this.debouncedUpdateAllPositions();
16386
16791
  this._onDidLayoutFromJSON.fire();
@@ -16403,7 +16808,7 @@ var DockviewComponent = class extends BaseGrid {
16403
16808
  members
16404
16809
  };
16405
16810
  }
16406
- deserializeEdgeGroups(edgeGroups, panels) {
16811
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16407
16812
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16408
16813
  if (!edgeService) return;
16409
16814
  for (const _position of [
@@ -16433,13 +16838,40 @@ var DockviewComponent = class extends BaseGrid {
16433
16838
  if (groupState) {
16434
16839
  const { views, activeView } = groupState;
16435
16840
  const createdPanels = [];
16436
- for (const panelId of views) if (panels[panelId]) {
16437
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16438
- 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
+ }
16439
16865
  }
16440
16866
  for (const panel of createdPanels) {
16441
16867
  const isActive = activeView === panel.id;
16442
- 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, {
16443
16875
  skipSetActive: !isActive,
16444
16876
  skipSetGroupActive: true
16445
16877
  });
@@ -16542,6 +16974,7 @@ var DockviewComponent = class extends BaseGrid {
16542
16974
  height: options.initialHeight
16543
16975
  };
16544
16976
  let index;
16977
+ const shouldSkipSetActive = (group) => !!options.inactive && group.model.size > 0;
16545
16978
  if (options.position) if (isPanelOptionsWithPanel(options.position)) {
16546
16979
  const referencePanel = typeof options.position.referencePanel === "string" ? this.getGroupPanel(options.position.referencePanel) : options.position.referencePanel;
16547
16980
  index = options.position.index;
@@ -16564,7 +16997,7 @@ var DockviewComponent = class extends BaseGrid {
16564
16997
  const group = this.orthogonalize(directionToPosition(options.position.direction));
16565
16998
  const panel = this.createPanel(options, group);
16566
16999
  group.model.openPanel(panel, {
16567
- skipSetActive: options.inactive,
17000
+ skipSetActive: shouldSkipSetActive(group),
16568
17001
  skipSetGroupActive: options.inactive,
16569
17002
  index
16570
17003
  });
@@ -16591,14 +17024,14 @@ var DockviewComponent = class extends BaseGrid {
16591
17024
  }));
16592
17025
  panel = this.createPanel(options, group);
16593
17026
  group.model.openPanel(panel, {
16594
- skipSetActive: options.inactive,
17027
+ skipSetActive: shouldSkipSetActive(group),
16595
17028
  skipSetGroupActive: options.inactive,
16596
17029
  index
16597
17030
  });
16598
17031
  } else if (referenceGroup.api.location.type === "floating" || referenceGroup.api.location.type === "edge" || target === "center") {
16599
17032
  panel = this.createPanel(options, referenceGroup);
16600
17033
  referenceGroup.model.openPanel(panel, {
16601
- skipSetActive: options.inactive,
17034
+ skipSetActive: shouldSkipSetActive(referenceGroup),
16602
17035
  skipSetGroupActive: options.inactive,
16603
17036
  index
16604
17037
  });
@@ -16613,7 +17046,7 @@ var DockviewComponent = class extends BaseGrid {
16613
17046
  const group = this.createGroupAtLocation(relativeLocation, this.orientationAtLocation(relativeLocation) === "VERTICAL" ? initial === null || initial === void 0 ? void 0 : initial.height : initial === null || initial === void 0 ? void 0 : initial.width);
16614
17047
  panel = this.createPanel(options, group);
16615
17048
  group.model.openPanel(panel, {
16616
- skipSetActive: options.inactive,
17049
+ skipSetActive: shouldSkipSetActive(group),
16617
17050
  skipSetGroupActive: options.inactive,
16618
17051
  index
16619
17052
  });
@@ -16630,7 +17063,7 @@ var DockviewComponent = class extends BaseGrid {
16630
17063
  }));
16631
17064
  panel = this.createPanel(options, group);
16632
17065
  group.model.openPanel(panel, {
16633
- skipSetActive: options.inactive,
17066
+ skipSetActive: shouldSkipSetActive(group),
16634
17067
  skipSetGroupActive: options.inactive,
16635
17068
  index
16636
17069
  });
@@ -16638,7 +17071,7 @@ var DockviewComponent = class extends BaseGrid {
16638
17071
  const group = this.createGroupAtLocation([0], this.gridview.orientation === "VERTICAL" ? initial === null || initial === void 0 ? void 0 : initial.height : initial === null || initial === void 0 ? void 0 : initial.width);
16639
17072
  panel = this.createPanel(options, group);
16640
17073
  group.model.openPanel(panel, {
16641
- skipSetActive: options.inactive,
17074
+ skipSetActive: shouldSkipSetActive(group),
16642
17075
  skipSetGroupActive: options.inactive,
16643
17076
  index
16644
17077
  });
@@ -16862,22 +17295,100 @@ var DockviewComponent = class extends BaseGrid {
16862
17295
  * outermost mutation.
16863
17296
  */
16864
17297
  mutation(kind, func) {
16865
- 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) {
16866
17339
  const origin = this._origin;
16867
- if (outer) this._onWillMutateLayout.fire({
17340
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16868
17341
  kind,
16869
17342
  origin
16870
17343
  });
16871
17344
  this._mutationDepth++;
16872
- try {
16873
- return func();
16874
- } finally {
17345
+ return () => {
16875
17346
  this._mutationDepth--;
16876
- if (outer) this._onDidMutateLayout.fire({
16877
- kind,
16878
- origin
16879
- });
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;
16880
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();
16881
17392
  }
16882
17393
  /**
16883
17394
  * The origin of the operation currently in progress (`'user'` by default).
@@ -16918,6 +17429,18 @@ var DockviewComponent = class extends BaseGrid {
16918
17429
  origin: this._origin
16919
17430
  });
16920
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
+ }
16921
17444
  moveGroupOrPanel(options) {
16922
17445
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
16923
17446
  }
@@ -16978,10 +17501,7 @@ var DockviewComponent = class extends BaseGrid {
16978
17501
  });
16979
17502
  });
16980
17503
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
16981
- this._onDidMovePanel.fire({
16982
- panel: removedPanel,
16983
- from: sourceGroup
16984
- });
17504
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16985
17505
  } else {
16986
17506
  /**
16987
17507
  * Dropping a panel to the extremities of a group which will place that panel
@@ -17000,10 +17520,7 @@ var DockviewComponent = class extends BaseGrid {
17000
17520
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
17001
17521
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
17002
17522
  this.gridview.moveView(sourceParentLocation, from, to);
17003
- this._onDidMovePanel.fire({
17004
- panel: this.getGroupPanel(sourceItemId),
17005
- from: sourceGroup
17006
- });
17523
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17007
17524
  return;
17008
17525
  }
17009
17526
  }
@@ -17034,10 +17551,7 @@ var DockviewComponent = class extends BaseGrid {
17034
17551
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17035
17552
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
17036
17553
  this.doSetGroupAndPanelActive(newGroup);
17037
- this._onDidMovePanel.fire({
17038
- panel: this.getGroupPanel(sourceItemId),
17039
- from: sourceGroup
17040
- });
17554
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17041
17555
  return;
17042
17556
  }
17043
17557
  if (sourceGroup.api.location.type === "edge") {
@@ -17054,10 +17568,7 @@ var DockviewComponent = class extends BaseGrid {
17054
17568
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17055
17569
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17056
17570
  this.doSetGroupAndPanelActive(newGroup);
17057
- this._onDidMovePanel.fire({
17058
- panel: removedPanel,
17059
- from: sourceGroup
17060
- });
17571
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17061
17572
  return;
17062
17573
  }
17063
17574
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -17069,10 +17580,7 @@ var DockviewComponent = class extends BaseGrid {
17069
17580
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17070
17581
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
17071
17582
  this.doSetGroupAndPanelActive(targetGroup);
17072
- this._onDidMovePanel.fire({
17073
- panel: this.getGroupPanel(sourceItemId),
17074
- from: sourceGroup
17075
- });
17583
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17076
17584
  } else {
17077
17585
  /**
17078
17586
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -17087,10 +17595,7 @@ var DockviewComponent = class extends BaseGrid {
17087
17595
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17088
17596
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17089
17597
  this.doSetGroupAndPanelActive(group);
17090
- this._onDidMovePanel.fire({
17091
- panel: removedPanel,
17092
- from: sourceGroup
17093
- });
17598
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17094
17599
  }
17095
17600
  }
17096
17601
  }
@@ -17125,10 +17630,7 @@ var DockviewComponent = class extends BaseGrid {
17125
17630
  });
17126
17631
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17127
17632
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17128
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17129
- panel,
17130
- from: sourceGroup
17131
- });
17633
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17132
17634
  };
17133
17635
  let targetGroup;
17134
17636
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -17149,10 +17651,12 @@ var DockviewComponent = class extends BaseGrid {
17149
17651
  this.mutation("maximize", () => super.exitMaximizedGroup());
17150
17652
  }
17151
17653
  _doMoveGroup(options) {
17654
+ var _mergedPanels;
17152
17655
  const from = options.from.group;
17153
17656
  const to = options.to.group;
17154
17657
  const target = options.to.position;
17155
17658
  let source = from;
17659
+ let mergedPanels;
17156
17660
  if (target === "center") {
17157
17661
  const activePanel = from.activePanel;
17158
17662
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17170,6 +17674,7 @@ var DockviewComponent = class extends BaseGrid {
17170
17674
  skipSetGroupActive: true
17171
17675
  });
17172
17676
  });
17677
+ mergedPanels = panels;
17173
17678
  for (const snapshot of tabGroupSnapshots) {
17174
17679
  const newTabGroup = to.model.createTabGroup({
17175
17680
  label: snapshot.label,
@@ -17259,11 +17764,8 @@ var DockviewComponent = class extends BaseGrid {
17259
17764
  this.setGroupLocationForRoot(source, destGridview);
17260
17765
  }
17261
17766
  }
17262
- source.panels.forEach((panel) => {
17263
- this._onDidMovePanel.fire({
17264
- panel,
17265
- from
17266
- });
17767
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17768
+ this.fireDidMovePanel(panel, from);
17267
17769
  });
17268
17770
  this.debouncedUpdateAllPositions();
17269
17771
  if (options.skipSetActive === false) {