dockview-react 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-react
3
- * @version 8.0.0
3
+ * @version 8.2.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -51,12 +51,14 @@ react_dom = __toESM(react_dom);
51
51
  //#region ../dockview-core/dist/package/main.esm.mjs
52
52
  /**
53
53
  * dockview-core
54
- * @version 8.0.0
54
+ * @version 8.2.0
55
55
  * @link https://github.com/dockview/dockview
56
56
  * @license MIT
57
57
  */
58
58
  var _LocalSelectionTransfer;
59
- var TransferObject = class {};
59
+ var TransferObject = class {
60
+ constructor() {}
61
+ };
60
62
  var PanelTransfer = class extends TransferObject {
61
63
  constructor(viewId, groupId, panelId, tabGroupId) {
62
64
  super();
@@ -243,7 +245,6 @@ react_dom = __toESM(react_dom);
243
245
  this.options = options;
244
246
  this._listeners = [];
245
247
  this._disposed = false;
246
- this._pauseTokens = /* @__PURE__ */ new Set();
247
248
  }
248
249
  get event() {
249
250
  if (!this._event) {
@@ -264,14 +265,26 @@ react_dom = __toESM(react_dom);
264
265
  }
265
266
  fire(e) {
266
267
  var _this$options2;
267
- if (this._pauseTokens.size > 0) return;
268
+ if (this._pauseTokens !== void 0 && this._pauseTokens.size > 0) return;
268
269
  if ((_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.replay) this._last = e;
269
- for (const listener of this._listeners.slice()) listener.callback(e);
270
+ const listeners = this._listeners;
271
+ const length = listeners.length;
272
+ if (length === 0) return;
273
+ if (length === 1) {
274
+ listeners[0].callback(e);
275
+ return;
276
+ }
277
+ for (const listener of listeners.slice()) listener.callback(e);
270
278
  }
271
279
  pause() {
280
+ var _this$_pauseTokens;
272
281
  const token = {};
282
+ (_this$_pauseTokens = this._pauseTokens) !== null && _this$_pauseTokens !== void 0 || (this._pauseTokens = /* @__PURE__ */ new Set());
273
283
  this._pauseTokens.add(token);
274
- return Disposable.from(() => this._pauseTokens.delete(token));
284
+ return Disposable.from(() => {
285
+ var _this$_pauseTokens2;
286
+ return (_this$_pauseTokens2 = this._pauseTokens) === null || _this$_pauseTokens2 === void 0 ? void 0 : _this$_pauseTokens2.delete(token);
287
+ });
275
288
  }
276
289
  dispose() {
277
290
  if (!this._disposed) {
@@ -571,8 +584,10 @@ react_dom = __toESM(react_dom);
571
584
  let previousScreenX = window.screenX;
572
585
  let previousScreenY = window.screenY;
573
586
  let timeout;
587
+ let rafHandle = 0;
588
+ let stopped = false;
574
589
  const checkMovement = () => {
575
- if (window.closed) return;
590
+ if (stopped || window.closed) return;
576
591
  const currentScreenX = window.screenX;
577
592
  const currentScreenY = window.screenY;
578
593
  if (currentScreenX !== previousScreenX || currentScreenY !== previousScreenY) {
@@ -583,9 +598,18 @@ react_dom = __toESM(react_dom);
583
598
  previousScreenX = currentScreenX;
584
599
  previousScreenY = currentScreenY;
585
600
  }
586
- requestAnimationFrame(checkMovement);
601
+ rafHandle = requestAnimationFrame(checkMovement);
587
602
  };
588
603
  checkMovement();
604
+ const disposeEmitter = emitter.dispose.bind(emitter);
605
+ emitter.dispose = () => {
606
+ if (!stopped) {
607
+ stopped = true;
608
+ cancelAnimationFrame(rafHandle);
609
+ clearTimeout(timeout);
610
+ }
611
+ disposeEmitter();
612
+ };
589
613
  return emitter;
590
614
  }
591
615
  function onDidWindowResizeEnd(element, cb) {
@@ -712,6 +736,11 @@ react_dom = __toESM(react_dom);
712
736
  set size(size) {
713
737
  this._size = size;
714
738
  }
739
+ setContainerGeometry(prop, value) {
740
+ if (this._appliedStyles[prop] === value) return;
741
+ this._appliedStyles[prop] = value;
742
+ this.container.style[prop] = value;
743
+ }
715
744
  get size() {
716
745
  return this._size;
717
746
  }
@@ -747,6 +776,7 @@ react_dom = __toESM(react_dom);
747
776
  this.view = view;
748
777
  this.disposable = disposable;
749
778
  this._cachedVisibleSize = void 0;
779
+ this._appliedStyles = {};
750
780
  if (typeof size === "number") {
751
781
  this._size = size;
752
782
  this._cachedVisibleSize = void 0;
@@ -792,6 +822,16 @@ react_dom = __toESM(react_dom);
792
822
  LayoutPriority["Normal"] = "normal";
793
823
  return LayoutPriority;
794
824
  }({});
825
+ function setSashPosition(sash, left, top) {
826
+ if (sash.appliedLeft !== left) {
827
+ sash.appliedLeft = left;
828
+ sash.container.style.left = left;
829
+ }
830
+ if (sash.appliedTop !== top) {
831
+ sash.appliedTop = top;
832
+ sash.container.style.top = top;
833
+ }
834
+ }
795
835
  let Sizing;
796
836
  (function(_Sizing) {
797
837
  _Sizing.Distribute = { type: "distribute" };
@@ -910,10 +950,6 @@ react_dom = __toESM(react_dom);
910
950
  pushToEnd(upIndexes, i);
911
951
  pushToEnd(downIndexes, i);
912
952
  }
913
- const upItems = upIndexes.map((i) => this.viewItems[i]);
914
- const upSizes = upIndexes.map((i) => sizes[i]);
915
- const downItems = downIndexes.map((i) => this.viewItems[i]);
916
- const downSizes = downIndexes.map((i) => sizes[i]);
917
953
  const minDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].minimumSize - sizes[i], 0);
918
954
  const maxDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].maximumSize - sizes[i], 0);
919
955
  const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((_, i) => _ + sizes[i] - this.viewItems[i].minimumSize, 0);
@@ -937,19 +973,21 @@ react_dom = __toESM(react_dom);
937
973
  const tentativeDelta = clamp(delta, minDelta, maxDelta);
938
974
  let actualDelta = 0;
939
975
  let deltaUp = tentativeDelta;
940
- for (let i = 0; i < upItems.length; i++) {
941
- const item = upItems[i];
942
- const size = clamp(upSizes[i] + deltaUp, item.minimumSize, item.maximumSize);
943
- const viewDelta = size - upSizes[i];
976
+ for (const upIndex of upIndexes) {
977
+ const item = this.viewItems[upIndex];
978
+ const priorSize = sizes[upIndex];
979
+ const size = clamp(priorSize + deltaUp, item.minimumSize, item.maximumSize);
980
+ const viewDelta = size - priorSize;
944
981
  actualDelta += viewDelta;
945
982
  deltaUp -= viewDelta;
946
983
  item.size = size;
947
984
  }
948
985
  let deltaDown = actualDelta;
949
- for (let i = 0; i < downItems.length; i++) {
950
- const item = downItems[i];
951
- const size = clamp(downSizes[i] - deltaDown, item.minimumSize, item.maximumSize);
952
- const viewDelta = size - downSizes[i];
986
+ for (const downIndex of downIndexes) {
987
+ const item = this.viewItems[downIndex];
988
+ const priorSize = sizes[downIndex];
989
+ const size = clamp(priorSize - deltaDown, item.minimumSize, item.maximumSize);
990
+ const viewDelta = size - priorSize;
953
991
  deltaDown += viewDelta;
954
992
  item.size = size;
955
993
  }
@@ -1197,13 +1235,22 @@ react_dom = __toESM(react_dom);
1197
1235
  this.saveProportions();
1198
1236
  }
1199
1237
  distributeEmptySpace(lowPriorityIndex) {
1200
- const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1238
+ let contentSize = 0;
1239
+ for (const item of this.viewItems) contentSize += item.size;
1201
1240
  let emptyDelta = this.size - contentSize;
1241
+ if (emptyDelta === 0) return;
1202
1242
  const indexes = range(this.viewItems.length - 1, -1);
1203
- const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
1204
- const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
1205
- for (const index of highPriorityIndexes) pushToStart(indexes, index);
1206
- for (const index of lowPriorityIndexes) pushToEnd(indexes, index);
1243
+ let hasPriority = false;
1244
+ for (const item of this.viewItems) if (item.priority === "low" || item.priority === "high") {
1245
+ hasPriority = true;
1246
+ break;
1247
+ }
1248
+ if (hasPriority) {
1249
+ const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
1250
+ const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
1251
+ for (const index of highPriorityIndexes) pushToStart(indexes, index);
1252
+ for (const index of lowPriorityIndexes) pushToEnd(indexes, index);
1253
+ }
1207
1254
  if (typeof lowPriorityIndex === "number") pushToEnd(indexes, lowPriorityIndex);
1208
1255
  for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) {
1209
1256
  const item = this.viewItems[indexes[i]];
@@ -1226,49 +1273,45 @@ react_dom = __toESM(react_dom);
1226
1273
  * For each view `i` the offet must be adjusted by `m * i/(n - 1)`.
1227
1274
  */
1228
1275
  layoutViews() {
1229
- this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
1276
+ let contentSize = 0;
1277
+ let visibleViewCount = 0;
1278
+ for (const item of this.viewItems) {
1279
+ contentSize += item.size;
1280
+ if (item.visible) visibleViewCount++;
1281
+ }
1282
+ this._contentSize = contentSize;
1230
1283
  this.updateSashEnablement();
1231
1284
  if (this.viewItems.length === 0) return;
1232
- const visibleViewItems = this.viewItems.filter((i) => i.visible);
1233
- const sashCount = Math.max(0, visibleViewItems.length - 1);
1234
- const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewItems.length);
1285
+ const sashCount = Math.max(0, visibleViewCount - 1);
1286
+ const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewCount);
1235
1287
  let totalLeftOffset = 0;
1236
1288
  const viewLeftOffsets = [];
1237
1289
  const sashWidth = 4;
1238
- const runningVisiblePanelCount = this.viewItems.reduce((arr, viewItem, i) => {
1239
- const flag = viewItem.visible ? 1 : 0;
1240
- if (i === 0) arr.push(flag);
1241
- else arr.push(arr[i - 1] + flag);
1242
- return arr;
1243
- }, []);
1290
+ let runningVisiblePanelCount = 0;
1244
1291
  this.viewItems.forEach((view, i) => {
1245
1292
  totalLeftOffset += this.viewItems[i].size;
1246
1293
  viewLeftOffsets.push(totalLeftOffset);
1294
+ runningVisiblePanelCount += view.visible ? 1 : 0;
1247
1295
  const size = view.visible ? view.size - marginReducedSize : 0;
1248
- const visiblePanelsBeforeThisView = Math.max(0, runningVisiblePanelCount[i] - 1);
1296
+ const visiblePanelsBeforeThisView = Math.max(0, runningVisiblePanelCount - 1);
1249
1297
  const offset = i === 0 || visiblePanelsBeforeThisView === 0 ? 0 : viewLeftOffsets[i - 1] + visiblePanelsBeforeThisView / sashCount * marginReducedSize;
1250
1298
  if (i < this.viewItems.length - 1) {
1251
1299
  const newSize = view.visible ? offset + size - sashWidth / 2 + this.margin / 2 : offset;
1252
- if (this._orientation === "HORIZONTAL") {
1253
- this.sashes[i].container.style.left = `${newSize}px`;
1254
- this.sashes[i].container.style.top = `0px`;
1255
- }
1256
- if (this._orientation === "VERTICAL") {
1257
- this.sashes[i].container.style.left = `0px`;
1258
- this.sashes[i].container.style.top = `${newSize}px`;
1259
- }
1300
+ const sash = this.sashes[i];
1301
+ if (this._orientation === "HORIZONTAL") setSashPosition(sash, `${newSize}px`, `0px`);
1302
+ if (this._orientation === "VERTICAL") setSashPosition(sash, `0px`, `${newSize}px`);
1260
1303
  }
1261
1304
  if (this._orientation === "HORIZONTAL") {
1262
- view.container.style.width = `${size}px`;
1263
- view.container.style.left = `${offset}px`;
1264
- view.container.style.top = "";
1265
- view.container.style.height = "";
1305
+ view.setContainerGeometry("width", `${size}px`);
1306
+ view.setContainerGeometry("left", `${offset}px`);
1307
+ view.setContainerGeometry("top", "");
1308
+ view.setContainerGeometry("height", "");
1266
1309
  }
1267
1310
  if (this._orientation === "VERTICAL") {
1268
- view.container.style.height = `${size}px`;
1269
- view.container.style.top = `${offset}px`;
1270
- view.container.style.width = "";
1271
- view.container.style.left = "";
1311
+ view.setContainerGeometry("height", `${size}px`);
1312
+ view.setContainerGeometry("top", `${offset}px`);
1313
+ view.setContainerGeometry("width", "");
1314
+ view.setContainerGeometry("left", "");
1272
1315
  }
1273
1316
  view.view.layout(view.size - marginReducedSize, this._orthogonalSize);
1274
1317
  });
@@ -1286,6 +1329,7 @@ react_dom = __toESM(react_dom);
1286
1329
  }
1287
1330
  }
1288
1331
  updateSashEnablement() {
1332
+ if (this.sashes.length === 0) return;
1289
1333
  let previous = false;
1290
1334
  const collapsesDown = this.viewItems.map((i) => previous = i.size - i.minimumSize > 0 || previous);
1291
1335
  previous = false;
@@ -1580,10 +1624,20 @@ react_dom = __toESM(react_dom);
1580
1624
  return this.orientation === "HORIZONTAL" ? this.orthogonalSize : this.size;
1581
1625
  }
1582
1626
  get minimumSize() {
1583
- return this.children.length === 0 ? 0 : Math.max(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.minimumOrthogonalSize : 0));
1627
+ if (this._cachedMinimumSize === void 0) {
1628
+ let value = 0;
1629
+ for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.max(value, this.children[index].minimumOrthogonalSize);
1630
+ this._cachedMinimumSize = value;
1631
+ }
1632
+ return this._cachedMinimumSize;
1584
1633
  }
1585
1634
  get maximumSize() {
1586
- return Math.min(...this.children.map((c, index) => this.splitview.isViewVisible(index) ? c.maximumOrthogonalSize : Number.POSITIVE_INFINITY));
1635
+ if (this._cachedMaximumSize === void 0) {
1636
+ let value = Number.POSITIVE_INFINITY;
1637
+ for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.min(value, this.children[index].maximumOrthogonalSize);
1638
+ this._cachedMaximumSize = value;
1639
+ }
1640
+ return this._cachedMaximumSize;
1587
1641
  }
1588
1642
  get minimumOrthogonalSize() {
1589
1643
  return this.splitview.minimumSize;
@@ -1610,15 +1664,29 @@ react_dom = __toESM(react_dom);
1610
1664
  return this.orientation === "HORIZONTAL" ? this.maximumSize : this.maximumOrthogonalSize;
1611
1665
  }
1612
1666
  get priority() {
1667
+ var _this$_cachedPriority;
1668
+ (_this$_cachedPriority = this._cachedPriority) !== null && _this$_cachedPriority !== void 0 || (this._cachedPriority = this.computePriority());
1669
+ return this._cachedPriority;
1670
+ }
1671
+ computePriority() {
1613
1672
  if (this.children.length === 0) return "normal";
1614
- const priorities = new Set(this.children.map((c) => {
1615
- var _c$priority;
1616
- return (_c$priority = c.priority) !== null && _c$priority !== void 0 ? _c$priority : "normal";
1617
- }));
1618
- if (priorities.has("high")) return "high";
1619
- else if (priorities.has("low")) return "low";
1673
+ let hasHigh = false;
1674
+ let hasLow = false;
1675
+ for (const child of this.children) {
1676
+ var _child$priority;
1677
+ const priority = (_child$priority = child.priority) !== null && _child$priority !== void 0 ? _child$priority : "normal";
1678
+ if (priority === "high") hasHigh = true;
1679
+ else if (priority === "low") hasLow = true;
1680
+ }
1681
+ if (hasHigh) return "high";
1682
+ else if (hasLow) return "low";
1620
1683
  return "normal";
1621
1684
  }
1685
+ invalidateCachedSizes() {
1686
+ this._cachedMinimumSize = void 0;
1687
+ this._cachedMaximumSize = void 0;
1688
+ this._cachedPriority = void 0;
1689
+ }
1622
1690
  get disabled() {
1623
1691
  return this.splitview.disabled;
1624
1692
  }
@@ -1694,6 +1762,7 @@ react_dom = __toESM(react_dom);
1694
1762
  if (this.splitview.isViewVisible(index) === visible) return;
1695
1763
  const wereAllChildrenHidden = this.splitview.contentSize === 0;
1696
1764
  this.splitview.setViewVisible(index, visible);
1765
+ this.invalidateCachedSizes();
1697
1766
  const areAllChildrenHidden = this.splitview.contentSize === 0;
1698
1767
  if (visible && wereAllChildrenHidden || !visible && areAllChildrenHidden) this._onDidVisibilityChange.fire({ visible });
1699
1768
  }
@@ -1742,12 +1811,14 @@ react_dom = __toESM(react_dom);
1742
1811
  return child;
1743
1812
  }
1744
1813
  setupChildrenEvents() {
1814
+ this.invalidateCachedSizes();
1745
1815
  this._childrenDisposable.dispose();
1746
1816
  this._childrenDisposable = new CompositeDisposable(Event.any(...this.children.map((c) => c.onDidChange))((e) => {
1747
1817
  /**
1748
1818
  * indicate a change has occured to allows any re-rendering but don't bubble
1749
1819
  * event because that was specific to this branch
1750
1820
  */
1821
+ this.invalidateCachedSizes();
1751
1822
  this._onDidChange.fire({ size: e.orthogonalSize });
1752
1823
  }), ...this.children.map((c, i) => {
1753
1824
  if (c instanceof BranchNode) return c.onDidVisibilityChange(({ visible }) => {
@@ -3391,7 +3462,7 @@ react_dom = __toESM(react_dom);
3391
3462
  * Add a group and return the created object.
3392
3463
  */
3393
3464
  addGroup(options) {
3394
- return this.component.addGroup(options);
3465
+ return this.component.withOrigin("api", () => this.component.addGroup(options));
3395
3466
  }
3396
3467
  /**
3397
3468
  * Close all groups and panels.
@@ -3529,13 +3600,13 @@ react_dom = __toESM(react_dom);
3529
3600
  this.activatePrevious(options);
3530
3601
  }
3531
3602
  maximizeGroup(panel) {
3532
- this.component.maximizeGroup(panel.group);
3603
+ this.component.withOrigin("api", () => this.component.maximizeGroup(panel.group));
3533
3604
  }
3534
3605
  hasMaximizedGroup() {
3535
3606
  return this.component.hasMaximizedGroup();
3536
3607
  }
3537
3608
  exitMaximizedGroup() {
3538
- this.component.exitMaximizedGroup();
3609
+ this.component.withOrigin("api", () => this.component.exitMaximizedGroup());
3539
3610
  }
3540
3611
  get onDidMaximizedGroupChange() {
3541
3612
  return this.component.onDidMaximizedGroupChange;
@@ -5439,9 +5510,6 @@ react_dom = __toESM(react_dom);
5439
5510
  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);
5440
5511
  if (((_this$panel = this.panel) === null || _this$panel === void 0 ? void 0 : _this$panel.view.content.element.parentElement) === this._element) {
5441
5512
  var _this$panel$view$cont, _this$panel$view$cont2;
5442
- /**
5443
- * If the currently attached panel is mounted directly to the content then remove it
5444
- */
5445
5513
  this.panel.view.content.element.remove();
5446
5514
  (_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);
5447
5515
  }
@@ -6157,7 +6225,7 @@ react_dom = __toESM(react_dom);
6157
6225
  this.element.appendChild(this._scrollbar);
6158
6226
  this.addDisposables(addDisposableListener(this.element, "wheel", (event) => {
6159
6227
  this._scrollOffset += event.deltaY * Scrollbar.MouseWheelSpeed;
6160
- this.calculateScrollbarStyles();
6228
+ this.scheduleStyleUpdate();
6161
6229
  }), addDisposableListener(this._scrollbar, "pointerdown", (event) => {
6162
6230
  var _this$_scrollbar$owne;
6163
6231
  event.preventDefault();
@@ -6169,7 +6237,7 @@ react_dom = __toESM(react_dom);
6169
6237
  const delta = this._orientation === "horizontal" ? event.clientX - originalClient : event.clientY - originalClient;
6170
6238
  const p = (this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight) / (this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight);
6171
6239
  this._scrollOffset = originalScrollOffset + delta / p;
6172
- this.calculateScrollbarStyles();
6240
+ this.scheduleStyleUpdate();
6173
6241
  };
6174
6242
  const onEnd = () => {
6175
6243
  toggleClass(this.element, "dv-scrollable-scrolling", false);
@@ -6181,10 +6249,10 @@ react_dom = __toESM(react_dom);
6181
6249
  doc.addEventListener("pointerup", onEnd);
6182
6250
  doc.addEventListener("pointercancel", onEnd);
6183
6251
  }), addDisposableListener(this.element, "scroll", () => {
6184
- this.calculateScrollbarStyles();
6252
+ this.scheduleStyleUpdate();
6185
6253
  }), addDisposableListener(this.scrollableElement, "scroll", () => {
6186
6254
  this._scrollOffset = this._orientation === "horizontal" ? this.scrollableElement.scrollLeft : this.scrollableElement.scrollTop;
6187
- this.calculateScrollbarStyles();
6255
+ this.scheduleStyleUpdate();
6188
6256
  }), watchElementResize(this.element, () => {
6189
6257
  toggleClass(this.element, "dv-scrollable-resizing", true);
6190
6258
  if (this._animationTimer) clearTimeout(this._animationTimer);
@@ -6192,9 +6260,29 @@ react_dom = __toESM(react_dom);
6192
6260
  clearTimeout(this._animationTimer);
6193
6261
  toggleClass(this.element, "dv-scrollable-resizing", false);
6194
6262
  }, 500);
6195
- this.calculateScrollbarStyles();
6263
+ this.scheduleStyleUpdate();
6264
+ }), Disposable.from(() => {
6265
+ if (this._pendingStyleFrame !== void 0) {
6266
+ cancelAnimationFrame(this._pendingStyleFrame);
6267
+ this._pendingStyleFrame = void 0;
6268
+ }
6196
6269
  }));
6197
6270
  }
6271
+ /**
6272
+ * Coalesce the scrollbar restyle to one pass per animation frame. The
6273
+ * wheel / scroll / pointermove handlers can each fire many times per frame,
6274
+ * and `calculateScrollbarStyles` both reads layout (`clientWidth`/
6275
+ * `scrollWidth`) and writes styles + `scrollLeft`/`scrollTop`; running it
6276
+ * synchronously per event caused repeated read→write→read reflow. Batching
6277
+ * to a frame keeps a burst of events to a single measure+write.
6278
+ */
6279
+ scheduleStyleUpdate() {
6280
+ if (this._pendingStyleFrame !== void 0) return;
6281
+ this._pendingStyleFrame = requestAnimationFrame(() => {
6282
+ this._pendingStyleFrame = void 0;
6283
+ this.calculateScrollbarStyles();
6284
+ });
6285
+ }
6198
6286
  calculateScrollbarStyles() {
6199
6287
  const clientSize = this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight;
6200
6288
  const scrollSize = this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight;
@@ -8634,8 +8722,8 @@ react_dom = __toESM(react_dom);
8634
8722
  }, true), addDisposableListener(this._tabsList, "dragend", () => {
8635
8723
  this._reorder.resetDragAnimation();
8636
8724
  }), addDisposableListener(this._tabsList, "drop", (event) => {
8637
- var _this$accessor$option, _this$group$model$dro;
8638
- if (!this._animState || this._animState.currentInsertionIndex === null) return;
8725
+ var _this$_animState3, _this$accessor$option, _this$group$model$dro;
8726
+ if (((_this$_animState3 = this._animState) === null || _this$_animState3 === void 0 ? void 0 : _this$_animState3.currentInsertionIndex) == null) return;
8639
8727
  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;
8640
8728
  event.stopPropagation();
8641
8729
  event.preventDefault();
@@ -8840,7 +8928,7 @@ react_dom = __toESM(react_dom);
8840
8928
  };
8841
8929
  this._pendingCollapse = true;
8842
8930
  requestAnimationFrame(() => {
8843
- var _this$_animState3, _this$_animState3$cur;
8931
+ var _this$_animState4, _this$_animState4$cur;
8844
8932
  this._pendingCollapse = false;
8845
8933
  if (!this._animState) return;
8846
8934
  if (!this._tabsList.classList.contains("dv-tabs-container--wrap")) {
@@ -8848,7 +8936,7 @@ react_dom = __toESM(react_dom);
8848
8936
  toggleClass(tab.element, "dv-tab--dragging", true);
8849
8937
  tab.element.offsetHeight;
8850
8938
  }
8851
- (_this$_animState3$cur = (_this$_animState3 = this._animState).currentInsertionIndex) !== null && _this$_animState3$cur !== void 0 || (_this$_animState3.currentInsertionIndex = sourceIndex);
8939
+ (_this$_animState4$cur = (_this$_animState4 = this._animState).currentInsertionIndex) !== null && _this$_animState4$cur !== void 0 || (_this$_animState4.currentInsertionIndex = sourceIndex);
8852
8940
  this.applyDragOverTransforms(true);
8853
8941
  tab.element.style.removeProperty("transition");
8854
8942
  });
@@ -8937,8 +9025,8 @@ react_dom = __toESM(react_dom);
8937
9025
  }
8938
9026
  }
8939
9027
  delete(id) {
8940
- var _this$_animState4;
8941
- if (((_this$_animState4 = this._animState) === null || _this$_animState4 === void 0 ? void 0 : _this$_animState4.sourceTabId) === id) {
9028
+ var _this$_animState5;
9029
+ if (((_this$_animState5 = this._animState) === null || _this$_animState5 === void 0 ? void 0 : _this$_animState5.sourceTabId) === id) {
8942
9030
  this.resetTabTransforms();
8943
9031
  this._animState = null;
8944
9032
  }
@@ -9055,7 +9143,7 @@ react_dom = __toESM(react_dom);
9055
9143
  const groupPanelIds = new Set(tabGroup.panelIds);
9056
9144
  this._pendingCollapse = true;
9057
9145
  requestAnimationFrame(() => {
9058
- var _this$_animState5, _this$_animState5$cur;
9146
+ var _this$_animState6, _this$_animState6$cur;
9059
9147
  this._pendingCollapse = false;
9060
9148
  if (!this._animState) return;
9061
9149
  for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) {
@@ -9070,7 +9158,7 @@ react_dom = __toESM(react_dom);
9070
9158
  this._tabsList.offsetHeight;
9071
9159
  const underline = this._tabGroupManager.groupUnderlines.get(tabGroup.id);
9072
9160
  if (underline) underline.style.display = "none";
9073
- (_this$_animState5$cur = (_this$_animState5 = this._animState).currentInsertionIndex) !== null && _this$_animState5$cur !== void 0 || (_this$_animState5.currentInsertionIndex = firstIdx);
9161
+ (_this$_animState6$cur = (_this$_animState6 = this._animState).currentInsertionIndex) !== null && _this$_animState6$cur !== void 0 || (_this$_animState6.currentInsertionIndex = firstIdx);
9074
9162
  this.applyDragOverTransforms(true);
9075
9163
  for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) t.value.element.style.removeProperty("transition");
9076
9164
  if (chipEntry) chipEntry.chip.element.style.removeProperty("transition");
@@ -9793,6 +9881,7 @@ react_dom = __toESM(react_dom);
9793
9881
  set headerPosition(value) {
9794
9882
  var _this$_activePanel;
9795
9883
  this._headerPosition = value;
9884
+ this.invalidateHeaderSize();
9796
9885
  removeClasses(this.container, "dv-groupview-header-top", "dv-groupview-header-bottom", "dv-groupview-header-left", "dv-groupview-header-right");
9797
9886
  addClasses(this.container, `dv-groupview-header-${value}`);
9798
9887
  const direction = value === "top" || value === "bottom" ? "horizontal" : "vertical";
@@ -9934,7 +10023,14 @@ react_dom = __toESM(react_dom);
9934
10023
  this.header.hidden = !!options.hideHeader;
9935
10024
  this.locked = (_options$locked = options.locked) !== null && _options$locked !== void 0 ? _options$locked : false;
9936
10025
  this.headerPosition = (_options$headerPositi = options.headerPosition) !== null && _options$headerPositi !== void 0 ? _options$headerPositi : accessor.defaultHeaderPosition;
9937
- this.addDisposables(this._onDidPanelTitleChange.event(() => this.updateAccessibleLabel()), this._onTabDragStart, this._onGroupDragStart, this._onWillShowOverlay, this.tabsContainer.onTabDragStart((event) => {
10026
+ this.addDisposables(watchElementResize(this.tabsContainer.element, () => {
10027
+ if (this.isDisposed) return;
10028
+ const size = this.measureHeaderSize();
10029
+ if (size !== this._cachedHeaderSize) {
10030
+ this._cachedHeaderSize = size;
10031
+ this.layout(this._width, this._height);
10032
+ }
10033
+ }), this._onDidPanelTitleChange.event(() => this.updateAccessibleLabel()), this._onTabDragStart, this._onGroupDragStart, this._onWillShowOverlay, this.tabsContainer.onTabDragStart((event) => {
9938
10034
  this._onTabDragStart.fire(event);
9939
10035
  }), this.tabsContainer.onGroupDragStart((event) => {
9940
10036
  this._onGroupDragStart.fire(event);
@@ -10299,7 +10395,11 @@ react_dom = __toESM(react_dom);
10299
10395
  this.panels.forEach((panel) => {
10300
10396
  this.rerender(panel);
10301
10397
  });
10302
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10398
+ if (this._activePanel) {
10399
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10400
+ const { width, height } = this.contentDimensions();
10401
+ this._activePanel.layout(width, height);
10402
+ }
10303
10403
  }
10304
10404
  get renderContainer() {
10305
10405
  var _this$_overwriteRende;
@@ -10391,16 +10491,11 @@ react_dom = __toESM(react_dom);
10391
10491
  (_this$_activePanel3 = this._activePanel) === null || _this$_activePanel3 === void 0 || _this$_activePanel3.focus();
10392
10492
  }
10393
10493
  openPanel(panel, options = {}) {
10394
- /**
10395
- * set the panel group
10396
- * add the panel
10397
- * check if group active
10398
- * check if panel active
10399
- */
10400
10494
  if (typeof options.index !== "number" || options.index > this.panels.length) options.index = this.panels.length;
10401
10495
  const skipSetActive = !!options.skipSetActive;
10402
10496
  panel.updateParentGroup(this.groupPanel, { skipSetActive: options.skipSetActive });
10403
10497
  this.doAddPanel(panel, options.index, { skipSetActive });
10498
+ this.invalidateHeaderSize();
10404
10499
  if (this._activePanel === panel) {
10405
10500
  this.contentContainer.renderPanel(panel, { asActive: true });
10406
10501
  return;
@@ -10463,6 +10558,7 @@ react_dom = __toESM(react_dom);
10463
10558
  * changing) down to the content + active panel.
10464
10559
  */
10465
10560
  relayout() {
10561
+ this.invalidateHeaderSize();
10466
10562
  this.layout(this._width, this._height);
10467
10563
  }
10468
10564
  /**
@@ -10472,17 +10568,32 @@ react_dom = __toESM(react_dom);
10472
10568
  * width); a hidden header has `display:none` so its `offset*` is 0 and
10473
10569
  * nothing is subtracted.
10474
10570
  */
10475
- contentDimensions() {
10571
+ /**
10572
+ * Measure the header's extent along its occupied axis. This is the single
10573
+ * forced-reflow read; it now runs only on a cache miss (first layout after
10574
+ * a header change) and inside the header's ResizeObserver — not on every
10575
+ * layout frame.
10576
+ */
10577
+ measureHeaderSize() {
10476
10578
  const headerEl = this.tabsContainer.element;
10579
+ return this.headerPosition === "top" || this.headerPosition === "bottom" ? headerEl.offsetHeight : headerEl.offsetWidth;
10580
+ }
10581
+ invalidateHeaderSize() {
10582
+ this._cachedHeaderSize = void 0;
10583
+ }
10584
+ contentDimensions() {
10585
+ var _this$_cachedHeaderSi;
10477
10586
  const horizontal = this.headerPosition === "top" || this.headerPosition === "bottom";
10587
+ const headerSize = (_this$_cachedHeaderSi = this._cachedHeaderSize) !== null && _this$_cachedHeaderSi !== void 0 ? _this$_cachedHeaderSi : this._cachedHeaderSize = this.measureHeaderSize();
10478
10588
  return {
10479
- width: horizontal ? this._width : Math.max(0, this._width - headerEl.offsetWidth),
10480
- height: horizontal ? Math.max(0, this._height - headerEl.offsetHeight) : this._height
10589
+ width: horizontal ? this._width : Math.max(0, this._width - headerSize),
10590
+ height: horizontal ? Math.max(0, this._height - headerSize) : this._height
10481
10591
  };
10482
10592
  }
10483
10593
  _removePanel(panel, options) {
10484
10594
  const isActivePanel = this._activePanel === panel;
10485
10595
  this.doRemovePanel(panel);
10596
+ this.invalidateHeaderSize();
10486
10597
  if (isActivePanel && this.panels.length > 0) {
10487
10598
  const nextPanel = this.mostRecentlyUsed[0];
10488
10599
  this.openPanel(nextPanel, {
@@ -10802,6 +10913,7 @@ react_dom = __toESM(react_dom);
10802
10913
  if (typeof event.minimumHeight === "number" || typeof event.minimumHeight === "function") this._minimumHeight = event.minimumHeight;
10803
10914
  if (typeof event.maximumWidth === "number" || typeof event.maximumWidth === "function") this._maximumWidth = event.maximumWidth;
10804
10915
  if (typeof event.maximumHeight === "number" || typeof event.maximumHeight === "function") this._maximumHeight = event.maximumHeight;
10916
+ this._onDidChange.fire(void 0);
10805
10917
  }), this.api.onDidSizeChange((event) => {
10806
10918
  this._onDidChange.fire({
10807
10919
  height: event.height,
@@ -10918,20 +11030,22 @@ react_dom = __toESM(react_dom);
10918
11030
  return this._group.model.headerPosition;
10919
11031
  }
10920
11032
  moveTo(options) {
10921
- var _options$group, _options$position, _options$skipSetActiv, _options$position2;
10922
11033
  if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
10923
- const group = (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this.accessor.addGroup({
10924
- direction: positionToDirection((_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "right"),
10925
- skipSetActive: (_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false
10926
- });
10927
- this.accessor.moveGroupOrPanel({
10928
- from: { groupId: this._group.id },
10929
- to: {
10930
- group,
10931
- position: options.group ? (_options$position2 = options.position) !== null && _options$position2 !== void 0 ? _options$position2 : "center" : "center",
10932
- index: options.index
10933
- },
10934
- skipSetActive: options.skipSetActive
11034
+ this.accessor.withOrigin("api", () => {
11035
+ var _options$group, _options$position, _options$skipSetActiv, _options$position2;
11036
+ const group = (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this.accessor.addGroup({
11037
+ direction: positionToDirection((_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "right"),
11038
+ skipSetActive: (_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false
11039
+ });
11040
+ this.accessor.moveGroupOrPanel({
11041
+ from: { groupId: this._group.id },
11042
+ to: {
11043
+ group,
11044
+ position: options.group ? (_options$position2 = options.position) !== null && _options$position2 !== void 0 ? _options$position2 : "center" : "center",
11045
+ index: options.index
11046
+ },
11047
+ skipSetActive: options.skipSetActive
11048
+ });
10935
11049
  });
10936
11050
  }
10937
11051
  maximize() {
@@ -11232,7 +11346,7 @@ react_dom = __toESM(react_dom);
11232
11346
  this._group = value;
11233
11347
  this._onDidGroupChange.fire({});
11234
11348
  this.setupGroupEventListeners(oldGroup);
11235
- this._onDidLocationChange.fire({ location: this.group.api.location });
11349
+ this.fireLocationChange();
11236
11350
  }
11237
11351
  }
11238
11352
  get group() {
@@ -11271,18 +11385,20 @@ react_dom = __toESM(react_dom);
11271
11385
  this.accessor.withOrigin("api", () => super.setActive());
11272
11386
  }
11273
11387
  moveTo(options) {
11274
- var _options$group, _options$position;
11275
- this.accessor.moveGroupOrPanel({
11276
- from: {
11277
- groupId: this._group.id,
11278
- panelId: this.panel.id
11279
- },
11280
- to: {
11281
- group: (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this._group,
11282
- position: options.group ? (_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "center" : "center",
11283
- index: options.index
11284
- },
11285
- skipSetActive: options.skipSetActive
11388
+ this.accessor.withOrigin("api", () => {
11389
+ var _options$group, _options$position;
11390
+ return this.accessor.moveGroupOrPanel({
11391
+ from: {
11392
+ groupId: this._group.id,
11393
+ panelId: this.panel.id
11394
+ },
11395
+ to: {
11396
+ group: (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this._group,
11397
+ position: options.group ? (_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "center" : "center",
11398
+ index: options.index
11399
+ },
11400
+ skipSetActive: options.skipSetActive
11401
+ });
11286
11402
  });
11287
11403
  }
11288
11404
  setTitle(title) {
@@ -11306,6 +11422,23 @@ react_dom = __toESM(react_dom);
11306
11422
  exitMaximized() {
11307
11423
  this.group.api.exitMaximized();
11308
11424
  }
11425
+ /**
11426
+ * Report that this panel's location may have moved.
11427
+ *
11428
+ * A relocation touches the location more than once - the panel is
11429
+ * reparented into the destination group, then that group is tagged with
11430
+ * the location it ends up at - so the event is coalesced to the end of the
11431
+ * enclosing layout mutation and reports where the panel actually settled.
11432
+ * Firing each signal as it happened surfaced an intermediate `grid`
11433
+ * location the panel was never in, at a point where it was in neither
11434
+ * group's panel list. Outside a mutation this fires straight away.
11435
+ */
11436
+ fireLocationChange() {
11437
+ this.accessor.deferLocationChange(this, () => {
11438
+ if (this.isDisposed) return;
11439
+ this._onDidLocationChange.fire({ location: this.location });
11440
+ });
11441
+ }
11309
11442
  setupGroupEventListeners(previousGroup) {
11310
11443
  var _previousGroup$isActi;
11311
11444
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11314,9 +11447,9 @@ react_dom = __toESM(react_dom);
11314
11447
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11315
11448
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11316
11449
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11317
- }), this.group.api.onDidLocationChange((event) => {
11450
+ }), this.group.api.onDidLocationChange(() => {
11318
11451
  if (this.group !== this.panel.group) return;
11319
- this._onDidLocationChange.fire(event);
11452
+ this.fireLocationChange();
11320
11453
  }), this.group.api.onDidActiveChange(() => {
11321
11454
  if (this.group !== this.panel.group) return;
11322
11455
  if (_trackGroupActive !== this.isGroupActive) {
@@ -12365,12 +12498,6 @@ react_dom = __toESM(react_dom);
12365
12498
  moduleName: "ContextMenu",
12366
12499
  when: (o) => o.getTabGroupChipContextMenuItems != null
12367
12500
  },
12368
- {
12369
- optionKey: "createContextMenuItemComponent",
12370
- reason: "createContextMenuItemComponent",
12371
- moduleName: "ContextMenu",
12372
- when: (o) => o.createContextMenuItemComponent != null
12373
- },
12374
12501
  {
12375
12502
  optionKey: "dndCompass",
12376
12503
  reason: "dndCompass",
@@ -13072,7 +13199,18 @@ react_dom = __toESM(react_dom);
13072
13199
  prev = next;
13073
13200
  const panel = group.activePanel;
13074
13201
  if (!panel) return;
13075
- const kind = next === "floating" ? "float" : next === "popout" ? "popout" : "dock";
13202
+ let kind;
13203
+ switch (next) {
13204
+ case "floating":
13205
+ kind = "float";
13206
+ break;
13207
+ case "popout":
13208
+ kind = "popout";
13209
+ break;
13210
+ default:
13211
+ kind = "dock";
13212
+ break;
13213
+ }
13076
13214
  this._announce(panel, kind);
13077
13215
  });
13078
13216
  this._locationSubs.set(group.id, sub);
@@ -13121,29 +13259,14 @@ react_dom = __toESM(react_dom);
13121
13259
  serviceKey: "liveRegionService",
13122
13260
  create: (host) => new LiveRegionService(host)
13123
13261
  });
13124
- /** Cursor offset of the group drag ghost, matched to the long-shipped default. */
13262
+ /** Cursor offset of the group drag ghost. */
13125
13263
  const GROUP_DRAG_GHOST_OFFSET_X = 30;
13126
13264
  const GROUP_DRAG_GHOST_OFFSET_Y = -10;
13127
13265
  /**
13128
- * The narrow surface the {@link AdvancedDnDService} needs from the host
13129
- * (the `DockviewComponent`).
13130
- *
13131
- * The `onWill*` emitters stay on the component so the public event shape is
13132
- * unchanged whether or not this module is registered; the service is only the
13133
- * dispatch point those fires are routed through. Engine policy (e.g. the
13134
- * `disableDnd` guard) stays on the component, ahead of the dispatch.
13135
- */
13136
- /**
13137
13266
  * Owns the dispatch of the advanced drag-and-drop hooks: `onWillDragPanel`,
13138
- * `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`.
13139
- *
13140
- * At this stage the service is a thin dispatcher that forwards to the host's
13141
- * emitters; the customisation surface it gates (custom drop overlays, custom
13142
- * group drag ghosts, hook veto/transform behaviour) is currently inlined in
13143
- * core and is extracted into this service in later phases. Routing the
13144
- * dispatch through a module slot is what lets that customisation layer move
13145
- * into a separately-distributed package in a future major version without
13146
- * changing the public event surface.
13267
+ * `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`, forwarding each to
13268
+ * the host's emitters so the public event shape is unchanged whether or not
13269
+ * this module is registered.
13147
13270
  *
13148
13271
  * The service holds no drag state of its own. The gesture is driven by the
13149
13272
  * DnD backends, and the per-group subscriptions live on the component's group
@@ -13260,7 +13383,7 @@ react_dom = __toESM(react_dom);
13260
13383
  ];
13261
13384
  var PositionCache = class {
13262
13385
  constructor() {
13263
- this.cache = /* @__PURE__ */ new Map();
13386
+ this.cache = /* @__PURE__ */ new WeakMap();
13264
13387
  this.currentFrameId = 0;
13265
13388
  this.rafId = null;
13266
13389
  }
@@ -13286,6 +13409,13 @@ react_dom = __toESM(react_dom);
13286
13409
  });
13287
13410
  }
13288
13411
  };
13412
+ /**
13413
+ * Placeholder handle marking "a reposition frame has been requested but the
13414
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13415
+ * practice — it is replaced within the same synchronous block — and harmless
13416
+ * if it were, since no frame carries this id.
13417
+ */
13418
+ const SCHEDULED = -1;
13289
13419
  function createFocusableElement() {
13290
13420
  const element = document.createElement("div");
13291
13421
  element.tabIndex = -1;
@@ -13298,12 +13428,13 @@ react_dom = __toESM(react_dom);
13298
13428
  this.accessor = accessor;
13299
13429
  this.map = {};
13300
13430
  this._disposed = false;
13431
+ this._generation = 0;
13301
13432
  this.positionCache = new PositionCache();
13302
- this.pendingUpdates = /* @__PURE__ */ new Set();
13303
13433
  this.addDisposables(Disposable.from(() => {
13304
13434
  for (const value of Object.values(this.map)) {
13305
13435
  value.disposable.dispose();
13306
13436
  value.destroy.dispose();
13437
+ this.cancelPendingUpdate(value);
13307
13438
  }
13308
13439
  this._disposed = true;
13309
13440
  }));
@@ -13330,11 +13461,24 @@ react_dom = __toESM(react_dom);
13330
13461
  this.positionCache.invalidate();
13331
13462
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13332
13463
  }
13464
+ /**
13465
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13466
+ * bound to the reference container that was current when it was scheduled,
13467
+ * so it must be discarded whenever that container stops being the one the
13468
+ * overlay should track.
13469
+ */
13470
+ cancelPendingUpdate(entry) {
13471
+ if (entry.pendingUpdate !== void 0) {
13472
+ cancelAnimationFrame(entry.pendingUpdate);
13473
+ entry.pendingUpdate = void 0;
13474
+ }
13475
+ }
13333
13476
  detatch(panel) {
13334
13477
  if (this.map[panel.api.id]) {
13335
- const { disposable, destroy } = this.map[panel.api.id];
13336
- disposable.dispose();
13337
- destroy.dispose();
13478
+ const entry = this.map[panel.api.id];
13479
+ entry.disposable.dispose();
13480
+ entry.destroy.dispose();
13481
+ this.cancelPendingUpdate(entry);
13338
13482
  delete this.map[panel.api.id];
13339
13483
  return true;
13340
13484
  }
@@ -13350,22 +13494,73 @@ react_dom = __toESM(react_dom);
13350
13494
  panel,
13351
13495
  disposable: Disposable.NONE,
13352
13496
  destroy: Disposable.NONE,
13353
- element
13497
+ element,
13498
+ retainPreviousGeometry: false,
13499
+ positioned: false,
13500
+ generation: ++this._generation
13354
13501
  };
13355
13502
  }
13356
- const focusContainer = this.map[panel.api.id].element;
13503
+ const mapEntry = this.map[panel.api.id];
13504
+ /**
13505
+ * A *change* of reference container supersedes the previous `attach`:
13506
+ * take a fresh generation so its `resize` closure can no longer run,
13507
+ * and drop the frame it queued against the old container. During
13508
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13509
+ * detached staging group measuring 0x0, so leaving its frame in flight
13510
+ * both wastes the update and delays the reposition against the real one.
13511
+ *
13512
+ * Re-attaching over the *same* container (re-open, active panel change)
13513
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13514
+ * (the auto-hide peek) schedules a frame carrying the sticky
13515
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13516
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13517
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13518
+ * nothing. A newly created entry already holds a fresh generation, so
13519
+ * the stale closures a `detatch` left behind are fenced off either way.
13520
+ */
13521
+ if (mapEntry.referenceContainer !== referenceContainer) {
13522
+ mapEntry.generation = ++this._generation;
13523
+ this.cancelPendingUpdate(mapEntry);
13524
+ mapEntry.referenceContainer = referenceContainer;
13525
+ /**
13526
+ * Arm the retain flag only here: it means "a replacement reference
13527
+ * container is awaiting layout", so the 0x0 early-return below
13528
+ * holds the last good box rather than blanking the panel mid-move.
13529
+ * Arming it on every re-attach instead would make a container that
13530
+ * genuinely collapses to zero keep its stale geometry forever.
13531
+ *
13532
+ * Gated on `positioned` because an overlay that has never been
13533
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13534
+ * defaults to 100%/100% — retaining "previous" geometry there would
13535
+ * pin the content over the whole container.
13536
+ */
13537
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13538
+ }
13539
+ const generation = mapEntry.generation;
13540
+ const focusContainer = mapEntry.element;
13357
13541
  const contentElement = panel.view.content.element;
13358
13542
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13359
13543
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13360
13544
  const resize = () => {
13361
13545
  const panelId = panel.api.id;
13362
- if (this.pendingUpdates.has(panelId)) return;
13363
- this.pendingUpdates.add(panelId);
13364
- requestAnimationFrame(() => {
13546
+ const scheduling = this.map[panelId];
13547
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13548
+ if (scheduling.pendingUpdate !== void 0) return;
13549
+ /**
13550
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13551
+ * shimmed to run inline by some hosts (and by several suites here),
13552
+ * in which case the callback below clears `pendingUpdate` while
13553
+ * this call is still on the stack — assigning the handle afterwards
13554
+ * would resurrect a slot that is already spent and block every
13555
+ * later reposition. The sentinel is replaced by the real handle
13556
+ * only if the frame has not already run.
13557
+ */
13558
+ scheduling.pendingUpdate = SCHEDULED;
13559
+ const handle = requestAnimationFrame(() => {
13365
13560
  var _entry$forceVisible;
13366
- this.pendingUpdates.delete(panelId);
13367
13561
  const entry = this.map[panelId];
13368
- if (this.isDisposed || !entry) return;
13562
+ if (entry) entry.pendingUpdate = void 0;
13563
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13369
13564
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13370
13565
  const clip = entry.clip;
13371
13566
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13374,6 +13569,15 @@ react_dom = __toESM(react_dom);
13374
13569
  const top = box.top - box2.top;
13375
13570
  const width = box.width;
13376
13571
  const height = box.height;
13572
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13573
+ if (!panel.api.isVisible && !forceVisible) {
13574
+ focusContainer.style.visibility = "hidden";
13575
+ focusContainer.style.pointerEvents = "none";
13576
+ }
13577
+ return;
13578
+ }
13579
+ entry.retainPreviousGeometry = false;
13580
+ entry.positioned = true;
13377
13581
  focusContainer.style.left = `${left}px`;
13378
13582
  focusContainer.style.top = `${top}px`;
13379
13583
  focusContainer.style.width = `${width}px`;
@@ -13402,11 +13606,30 @@ react_dom = __toESM(react_dom);
13402
13606
  } else focusContainer.style.clipPath = "";
13403
13607
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13404
13608
  });
13609
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13405
13610
  };
13406
13611
  const visibilityChanged = () => {
13407
13612
  if (panel.api.isVisible) {
13613
+ var _this$map$panel$api$i;
13408
13614
  this.positionCache.invalidate();
13409
13615
  resize();
13616
+ /**
13617
+ * Existing geometry is safe to show while its replacement lays
13618
+ * out. `retainPreviousGeometry` is only set when geometry has
13619
+ * actually been written (see `attach`), so this can never
13620
+ * un-hide an overlay whose left/top/width/height are unset —
13621
+ * `.dv-render-overlay` is 100%/100% by default and would
13622
+ * otherwise cover the whole dock.
13623
+ *
13624
+ * The retained geometry is the panel's position in the previous
13625
+ * layout, so restoring a layout that moves the panel does paint
13626
+ * it at stale coordinates until the reposition lands. That is
13627
+ * bounded to the next frame now that a superseded `attach` can
13628
+ * no longer swallow the reposition (see `attach`); showing
13629
+ * stale-but-real geometry for a frame is the deliberate
13630
+ * trade-off against blanking the panel for the whole rebuild.
13631
+ */
13632
+ 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 = "";
13410
13633
  focusContainer.style.pointerEvents = "";
13411
13634
  } else {
13412
13635
  focusContainer.style.visibility = "hidden";
@@ -13623,7 +13846,17 @@ react_dom = __toESM(react_dom);
13623
13846
  window: externalWindow
13624
13847
  });
13625
13848
  return new Promise((resolve, reject) => {
13626
- externalWindow.addEventListener("unload", (e) => {});
13849
+ externalWindow.addEventListener("unload", () => {});
13850
+ /**
13851
+ * `load` is the only event that resolves this promise with a
13852
+ * container, so a window that goes away first would leave the
13853
+ * caller awaiting a promise that never settles - and
13854
+ * `addPopoutGroup` holds its layout transaction open until it does.
13855
+ * Settle with `null` on close, the same signal a blocked popup
13856
+ * gives and one the caller already handles. `resolve` after the
13857
+ * fact is a no-op, so a `load` that arrived first still wins.
13858
+ */
13859
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13627
13860
  externalWindow.addEventListener("load", () => {
13628
13861
  /**
13629
13862
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -14905,7 +15138,22 @@ react_dom = __toESM(react_dom);
14905
15138
  const rect = candidate.element.getBoundingClientRect();
14906
15139
  const dx = rect.left + rect.width / 2 - fromX;
14907
15140
  const dy = rect.top + rect.height / 2 - fromY;
14908
- 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;
15141
+ let inDirection;
15142
+ switch (direction) {
15143
+ case "left":
15144
+ inDirection = dx < 0 && Math.abs(dx) >= Math.abs(dy);
15145
+ break;
15146
+ case "right":
15147
+ inDirection = dx > 0 && Math.abs(dx) >= Math.abs(dy);
15148
+ break;
15149
+ case "up":
15150
+ inDirection = dy < 0 && Math.abs(dy) >= Math.abs(dx);
15151
+ break;
15152
+ default:
15153
+ inDirection = dy > 0 && Math.abs(dy) >= Math.abs(dx);
15154
+ break;
15155
+ }
15156
+ if (!inDirection) continue;
14909
15157
  const distance = dx * dx + dy * dy;
14910
15158
  if (distance < bestDistance) {
14911
15159
  bestDistance = distance;
@@ -15008,6 +15256,7 @@ react_dom = __toESM(react_dom);
15008
15256
  this._onWillDrop = new Emitter();
15009
15257
  this.onWillDrop = this._onWillDrop.event;
15010
15258
  this._mutationDepth = 0;
15259
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15011
15260
  this._origin = "user";
15012
15261
  this._originDepth = 0;
15013
15262
  this._onWillMutateLayout = new Emitter();
@@ -15105,7 +15354,7 @@ react_dom = __toESM(react_dom);
15105
15354
  toggleClass(this.element, "dv-debug", !!options.debug);
15106
15355
  this.updateTheme();
15107
15356
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15108
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15357
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15109
15358
  var _this$rootDropTargetC2, _this$floatingDropTar;
15110
15359
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15111
15360
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15179,7 +15428,7 @@ react_dom = __toESM(react_dom);
15179
15428
  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;
15180
15429
  }
15181
15430
  addPopoutGroup(itemToPopout, options) {
15182
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15431
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15183
15432
  }
15184
15433
  /** Enumerate the popout groups currently open in their own windows. */
15185
15434
  getPopouts() {
@@ -15276,15 +15525,32 @@ react_dom = __toESM(react_dom);
15276
15525
  }
15277
15526
  };
15278
15527
  let floatingBox;
15279
- 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(() => {
15280
- const panel = referenceGroup.model.removePanel(itemToPopout);
15281
- group.model.openPanel(panel);
15282
- });
15283
- else {
15528
+ /**
15529
+ * Popping out rehomes panels into `group`, so like the floating
15530
+ * case it owes the caller an `onDidMovePanel` per panel. The
15531
+ * events are deferred until the window is wired up so listeners
15532
+ * observe the panels' final `popout` location.
15533
+ */
15534
+ let movedPanels = [];
15535
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15536
+ const sourceGroup = itemToPopout.group;
15537
+ this.movingLock(() => {
15538
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15539
+ group.model.openPanel(panel);
15540
+ });
15541
+ movedPanels = [{
15542
+ panel: itemToPopout,
15543
+ from: sourceGroup
15544
+ }];
15545
+ } else {
15284
15546
  this.movingLock(() => moveGroupWithoutDestroying({
15285
15547
  from: referenceGroup,
15286
15548
  to: group
15287
15549
  }));
15550
+ movedPanels = group.panels.map((panel) => ({
15551
+ panel,
15552
+ from: referenceGroup
15553
+ }));
15288
15554
  switch (referenceLocation) {
15289
15555
  case "grid":
15290
15556
  referenceGroup.api.setVisible(false);
@@ -15393,6 +15659,7 @@ react_dom = __toESM(react_dom);
15393
15659
  group: value.popoutGroup,
15394
15660
  window: value.getWindow()
15395
15661
  });
15662
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15396
15663
  return true;
15397
15664
  }).catch((err) => {
15398
15665
  console.error("dockview: failed to create popout.", err);
@@ -15470,6 +15737,16 @@ react_dom = __toESM(react_dom);
15470
15737
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15471
15738
  const anchorPresent = members.includes(group);
15472
15739
  const anchorIsSoleMember = anchorPresent && members.length === 1;
15740
+ /**
15741
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
15742
+ * window owes the caller the same: every panel in it is rehomed, into
15743
+ * the reference group the popout left behind or into a fresh grid slot.
15744
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
15745
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
15746
+ * events are deferred to the end so listeners observe the panels at
15747
+ * their final group and location.
15748
+ */
15749
+ const movedPanels = [];
15473
15750
  if (genuineClose) for (const member of members) {
15474
15751
  if (member === group) continue;
15475
15752
  this.movingLock(() => {
@@ -15480,12 +15757,21 @@ react_dom = __toESM(react_dom);
15480
15757
  });
15481
15758
  this.redockGroupToMainGrid(member);
15482
15759
  });
15760
+ for (const panel of member.panels) movedPanels.push({
15761
+ panel,
15762
+ from: member
15763
+ });
15483
15764
  }
15484
15765
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
15766
+ const rehomed = [...group.panels];
15485
15767
  this.movingLock(() => moveGroupWithoutDestroying({
15486
15768
  from: group,
15487
15769
  to: referenceGroup
15488
15770
  }));
15771
+ for (const panel of rehomed) movedPanels.push({
15772
+ panel,
15773
+ from: group
15774
+ });
15489
15775
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15490
15776
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15491
15777
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15516,10 +15802,15 @@ react_dom = __toESM(react_dom);
15516
15802
  this.movingLock(() => {
15517
15803
  this.doAddGroup(group, [0]);
15518
15804
  });
15805
+ for (const panel of group.panels) movedPanels.push({
15806
+ panel,
15807
+ from: group
15808
+ });
15519
15809
  }
15520
15810
  this.doSetGroupAndPanelActive(group);
15521
15811
  }
15522
15812
  disposePopoutGridview();
15813
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15523
15814
  }
15524
15815
  addFloatingGroup(item, options) {
15525
15816
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15528,7 +15819,15 @@ react_dom = __toESM(react_dom);
15528
15819
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15529
15820
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15530
15821
  let group;
15822
+ /**
15823
+ * Floating relocates panels exactly like a drop onto the grid does, so
15824
+ * it owes the caller an `onDidMovePanel` per panel. The events are
15825
+ * deferred until the window is mounted below: only then do the panels
15826
+ * report their final `floating` location.
15827
+ */
15828
+ let movedPanels = [];
15531
15829
  if (item instanceof DockviewPanel) {
15830
+ const sourceGroup = item.group;
15532
15831
  group = this.createGroup();
15533
15832
  this._onDidAddGroup.fire(group);
15534
15833
  this.movingLock(() => this.removePanel(item, {
@@ -15537,6 +15836,10 @@ react_dom = __toESM(react_dom);
15537
15836
  skipSetActiveGroup: true
15538
15837
  }));
15539
15838
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
15839
+ movedPanels = [{
15840
+ panel: item,
15841
+ from: sourceGroup
15842
+ }];
15540
15843
  } else {
15541
15844
  var _this$_popoutWindowSe8;
15542
15845
  group = item;
@@ -15553,11 +15856,21 @@ react_dom = __toESM(react_dom);
15553
15856
  });
15554
15857
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15555
15858
  group = popoutReferenceGroup;
15556
- } else this.doRemoveGroup(item, {
15557
- skipDispose: true,
15558
- skipPopoutReturn: true,
15559
- skipPopoutAssociated: false
15560
- });
15859
+ movedPanels = group.panels.map((panel) => ({
15860
+ panel,
15861
+ from: item
15862
+ }));
15863
+ } else {
15864
+ this.doRemoveGroup(item, {
15865
+ skipDispose: true,
15866
+ skipPopoutReturn: true,
15867
+ skipPopoutAssociated: false
15868
+ });
15869
+ movedPanels = group.panels.map((panel) => ({
15870
+ panel,
15871
+ from: group
15872
+ }));
15873
+ }
15561
15874
  }
15562
15875
  function getAnchoredBox() {
15563
15876
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15590,6 +15903,7 @@ react_dom = __toESM(react_dom);
15590
15903
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15591
15904
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15592
15905
  });
15906
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15593
15907
  }
15594
15908
  /**
15595
15909
  * Build an empty gridview configured to match the main grid's styling, for
@@ -15753,11 +16067,15 @@ react_dom = __toESM(react_dom);
15753
16067
  /**
15754
16068
  * Reveal (create-or-fill) the edge group at `position` and move the dragged
15755
16069
  * item described by `data` into it. A newly created edge group is created
15756
- * collapsed and flagged `autoReveal` so it tears down to zero footprint when
15757
- * later emptied. If an edge group already exists there it is reused: the
15758
- * panel is added to its tabs and its collapsed/toggled state is left as-is
15759
- * (never re-created; `addEdgeGroup` throws on a duplicate position). No-op if
15760
- * the EdgeGroup module is absent.
16070
+ * collapsed, flagged `autoReveal` so it tears down to zero footprint when
16071
+ * later emptied, and takes its auto-hide state from `options.autoHide`. If an
16072
+ * edge group already exists there it is reused: the panel is added to its
16073
+ * tabs and its collapsed/toggled *and auto-hide* state are left as-is (never
16074
+ * re-created; `addEdgeGroup` throws on a duplicate position). This keeps a
16075
+ * drag-reveal from silently converting a static edge group into an
16076
+ * auto-hiding one; to change an existing group's auto-hide, call
16077
+ * `api.getEdgeGroup(position)?.setAutoHide(...)` directly. No-op if the
16078
+ * EdgeGroup module is absent.
15761
16079
  *
15762
16080
  * This is the primitive behind the dock-to-edge groups: the two-band
15763
16081
  * drag-reveal affordance routes its outer-band drops here.
@@ -15776,7 +16094,7 @@ react_dom = __toESM(react_dom);
15776
16094
  collapsed: true
15777
16095
  });
15778
16096
  group = service.get(position);
15779
- } else if ((options === null || options === void 0 ? void 0 : options.autoHide) !== void 0) this.setEdgeGroupAutoHide(group, options.autoHide);
16097
+ }
15780
16098
  if (!group) return;
15781
16099
  this.moveGroupOrPanel({
15782
16100
  from: {
@@ -16017,35 +16335,119 @@ react_dom = __toESM(react_dom);
16017
16335
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16018
16336
  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");
16019
16337
  const existingPanels = /* @__PURE__ */ new Map();
16020
- let tempGroup;
16338
+ const temporaryGroups = /* @__PURE__ */ new Map();
16339
+ const stagedPanels = [];
16340
+ const temporaryGroupDisposables = [];
16341
+ /**
16342
+ * The staging groups are detached from `_groups` and never enter the
16343
+ * grid, so nothing else will ever tear them down.
16344
+ *
16345
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16346
+ * throwing renderer must not abort the rest of the cleanup — that would
16347
+ * re-introduce the very leak this reclaims — nor replace the
16348
+ * deserialization error the caller is being given. Draining the list
16349
+ * also makes this safe to call more than once.
16350
+ */
16351
+ const disposeTemporaryGroups = () => {
16352
+ for (const disposable of temporaryGroupDisposables) try {
16353
+ disposable.dispose();
16354
+ } catch (err) {
16355
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16356
+ }
16357
+ temporaryGroupDisposables.length = 0;
16358
+ };
16021
16359
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16022
16360
  /**
16023
- * What are we doing here?
16024
- *
16025
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16026
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16361
+ * Visible, always-rendered panels need individual staging groups
16362
+ * to remain active. Other reused panels can share a staging group.
16363
+ * The staging groups are excluded from the layout clear below.
16027
16364
  */
16028
- tempGroup = this.createGroup();
16029
- this._groups.delete(tempGroup.api.id);
16030
16365
  const newPanels = Object.keys(data.panels);
16031
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16032
- this.movingLock(() => {
16033
- Array.from(existingPanels.values()).forEach((panel) => {
16034
- this.moveGroupOrPanel({
16035
- from: {
16036
- groupId: panel.api.group.api.id,
16037
- panelId: panel.api.id
16038
- },
16039
- to: {
16040
- group: tempGroup,
16041
- position: "center"
16042
- },
16043
- keepEmptyGroups: true
16366
+ let sharedTemporaryGroup;
16367
+ const createTemporaryGroup = () => {
16368
+ const temporaryGroup = this.createGroup();
16369
+ /**
16370
+ * Removing the group from `_groups` also drops the record's
16371
+ * `CompositeDisposable`, so capture it first and tear both it
16372
+ * and the group down in the `finally` below. Left undisposed a
16373
+ * staging group leaks its `ResizeObserver`, its
16374
+ * `onDidOptionsChange` subscription (retained by this
16375
+ * component's emitter) and the watermark its model mounts when
16376
+ * the group empties — once per reused panel, per `fromJSON`.
16377
+ */
16378
+ const record = this._groups.get(temporaryGroup.api.id);
16379
+ this._groups.delete(temporaryGroup.api.id);
16380
+ temporaryGroupDisposables.push(Disposable.from(() => {
16381
+ /**
16382
+ * Reclaim the group's own resources, never its panels.
16383
+ * Staging is driven by the ids in `data.panels` while
16384
+ * reclaiming is driven by the group `views` that
16385
+ * reference them, so a panel whose state is present but
16386
+ * unreferenced is still staged here — and `dispose()`
16387
+ * on a non-empty group reaches the consumer's
16388
+ * `IContentRenderer.dispose()`. Emptying the group
16389
+ * first leaves those panels exactly as they were before
16390
+ * this teardown existed, which also keeps this safe
16391
+ * against any future path that rebuilds a panel under
16392
+ * an id that is live in the new layout.
16393
+ */
16394
+ this.movingLock(() => {
16395
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16396
+ });
16397
+ record === null || record === void 0 || record.disposable.dispose();
16398
+ temporaryGroup.dispose();
16399
+ }));
16400
+ return temporaryGroup;
16401
+ };
16402
+ /**
16403
+ * Staging and the clear below run before the deserialization
16404
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16405
+ * renderer teardown during `clear`) would escape without reclaiming
16406
+ * the staging groups already created.
16407
+ *
16408
+ * The creation loop is inside the guard too, not just the moves and
16409
+ * the clear: `createGroup()` reaches consumer code of its own —
16410
+ * `initialize()` mounts the watermark through
16411
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16412
+ * otherwise strand the n-1 staging groups already built.
16413
+ */
16414
+ try {
16415
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16416
+ existingPanels.set(panel.api.id, panel);
16417
+ let temporaryGroup;
16418
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16419
+ else {
16420
+ var _sharedTemporaryGroup;
16421
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16422
+ temporaryGroup = sharedTemporaryGroup;
16423
+ }
16424
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16425
+ stagedPanels.push({
16426
+ panel,
16427
+ temporaryGroup
16428
+ });
16429
+ }
16430
+ this.movingLock(() => {
16431
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16432
+ this.moveGroupOrPanel({
16433
+ from: {
16434
+ groupId: panel.api.group.api.id,
16435
+ panelId: panel.api.id
16436
+ },
16437
+ to: {
16438
+ group: temporaryGroup,
16439
+ position: "center"
16440
+ },
16441
+ keepEmptyGroups: true
16442
+ });
16044
16443
  });
16045
16444
  });
16046
- });
16047
- }
16048
- this.clear();
16445
+ this.clear();
16446
+ } catch (err) {
16447
+ disposeTemporaryGroups();
16448
+ throw err;
16449
+ }
16450
+ } else this.clear();
16049
16451
  const { grid, panels, activeGroup } = data;
16050
16452
  try {
16051
16453
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16077,9 +16479,10 @@ react_dom = __toESM(react_dom);
16077
16479
  * due to a corruption of input data.
16078
16480
  */
16079
16481
  const existingPanel = existingPanels.get(child);
16080
- if (tempGroup && existingPanel) {
16482
+ const temporaryGroup = temporaryGroups.get(child);
16483
+ if (temporaryGroup && existingPanel) {
16081
16484
  this.movingLock(() => {
16082
- tempGroup.model.removePanel(existingPanel);
16485
+ temporaryGroup.model.removePanel(existingPanel);
16083
16486
  });
16084
16487
  createdPanels.push(existingPanel);
16085
16488
  existingPanel.updateFromStateModel(panels[child]);
@@ -16109,7 +16512,7 @@ react_dom = __toESM(react_dom);
16109
16512
  return createGroupFromSerializedState(node.data);
16110
16513
  } });
16111
16514
  this._layoutFromShell(width, height);
16112
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16515
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16113
16516
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16114
16517
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16115
16518
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16146,6 +16549,8 @@ react_dom = __toESM(react_dom);
16146
16549
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16147
16550
  */
16148
16551
  throw err;
16552
+ } finally {
16553
+ disposeTemporaryGroups();
16149
16554
  }
16150
16555
  this.debouncedUpdateAllPositions();
16151
16556
  this._onDidLayoutFromJSON.fire();
@@ -16168,7 +16573,7 @@ react_dom = __toESM(react_dom);
16168
16573
  members
16169
16574
  };
16170
16575
  }
16171
- deserializeEdgeGroups(edgeGroups, panels) {
16576
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16172
16577
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16173
16578
  if (!edgeService) return;
16174
16579
  for (const _position of [
@@ -16198,13 +16603,40 @@ react_dom = __toESM(react_dom);
16198
16603
  if (groupState) {
16199
16604
  const { views, activeView } = groupState;
16200
16605
  const createdPanels = [];
16201
- for (const panelId of views) if (panels[panelId]) {
16202
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16203
- createdPanels.push(panel);
16606
+ for (const panelId of views) {
16607
+ if (!panels[panelId]) continue;
16608
+ /**
16609
+ * Reclaim a staged panel rather than rebuilding it, the
16610
+ * same way the grid path does. Deserializing here instead
16611
+ * would honour `reuseExistingPanels` everywhere except edge
16612
+ * groups: the live panel keeps sitting in its staging group
16613
+ * while a second panel is built under the same id, so the
16614
+ * consumer's renderer for the original is orphaned — never
16615
+ * disposed, and its element left inside the id-keyed
16616
+ * overlay entry the replacement now shares.
16617
+ */
16618
+ const existingPanel = existingPanels.get(panelId);
16619
+ const temporaryGroup = temporaryGroups.get(panelId);
16620
+ if (temporaryGroup && existingPanel) {
16621
+ this.movingLock(() => {
16622
+ temporaryGroup.model.removePanel(existingPanel);
16623
+ });
16624
+ createdPanels.push(existingPanel);
16625
+ existingPanel.updateFromStateModel(panels[panelId]);
16626
+ } else {
16627
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16628
+ createdPanels.push(panel);
16629
+ }
16204
16630
  }
16205
16631
  for (const panel of createdPanels) {
16206
16632
  const isActive = activeView === panel.id;
16207
- edgeGroup.model.openPanel(panel, {
16633
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16634
+ edgeGroup.model.openPanel(panel, {
16635
+ skipSetActive: !isActive,
16636
+ skipSetGroupActive: true
16637
+ });
16638
+ });
16639
+ else edgeGroup.model.openPanel(panel, {
16208
16640
  skipSetActive: !isActive,
16209
16641
  skipSetGroupActive: true
16210
16642
  });
@@ -16307,6 +16739,7 @@ react_dom = __toESM(react_dom);
16307
16739
  height: options.initialHeight
16308
16740
  };
16309
16741
  let index;
16742
+ const shouldSkipSetActive = (group) => !!options.inactive && group.model.size > 0;
16310
16743
  if (options.position) if (isPanelOptionsWithPanel(options.position)) {
16311
16744
  const referencePanel = typeof options.position.referencePanel === "string" ? this.getGroupPanel(options.position.referencePanel) : options.position.referencePanel;
16312
16745
  index = options.position.index;
@@ -16329,7 +16762,7 @@ react_dom = __toESM(react_dom);
16329
16762
  const group = this.orthogonalize(directionToPosition(options.position.direction));
16330
16763
  const panel = this.createPanel(options, group);
16331
16764
  group.model.openPanel(panel, {
16332
- skipSetActive: options.inactive,
16765
+ skipSetActive: shouldSkipSetActive(group),
16333
16766
  skipSetGroupActive: options.inactive,
16334
16767
  index
16335
16768
  });
@@ -16356,14 +16789,14 @@ react_dom = __toESM(react_dom);
16356
16789
  }));
16357
16790
  panel = this.createPanel(options, group);
16358
16791
  group.model.openPanel(panel, {
16359
- skipSetActive: options.inactive,
16792
+ skipSetActive: shouldSkipSetActive(group),
16360
16793
  skipSetGroupActive: options.inactive,
16361
16794
  index
16362
16795
  });
16363
16796
  } else if (referenceGroup.api.location.type === "floating" || referenceGroup.api.location.type === "edge" || target === "center") {
16364
16797
  panel = this.createPanel(options, referenceGroup);
16365
16798
  referenceGroup.model.openPanel(panel, {
16366
- skipSetActive: options.inactive,
16799
+ skipSetActive: shouldSkipSetActive(referenceGroup),
16367
16800
  skipSetGroupActive: options.inactive,
16368
16801
  index
16369
16802
  });
@@ -16378,7 +16811,7 @@ react_dom = __toESM(react_dom);
16378
16811
  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);
16379
16812
  panel = this.createPanel(options, group);
16380
16813
  group.model.openPanel(panel, {
16381
- skipSetActive: options.inactive,
16814
+ skipSetActive: shouldSkipSetActive(group),
16382
16815
  skipSetGroupActive: options.inactive,
16383
16816
  index
16384
16817
  });
@@ -16395,7 +16828,7 @@ react_dom = __toESM(react_dom);
16395
16828
  }));
16396
16829
  panel = this.createPanel(options, group);
16397
16830
  group.model.openPanel(panel, {
16398
- skipSetActive: options.inactive,
16831
+ skipSetActive: shouldSkipSetActive(group),
16399
16832
  skipSetGroupActive: options.inactive,
16400
16833
  index
16401
16834
  });
@@ -16403,7 +16836,7 @@ react_dom = __toESM(react_dom);
16403
16836
  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);
16404
16837
  panel = this.createPanel(options, group);
16405
16838
  group.model.openPanel(panel, {
16406
- skipSetActive: options.inactive,
16839
+ skipSetActive: shouldSkipSetActive(group),
16407
16840
  skipSetGroupActive: options.inactive,
16408
16841
  index
16409
16842
  });
@@ -16627,22 +17060,100 @@ react_dom = __toESM(react_dom);
16627
17060
  * outermost mutation.
16628
17061
  */
16629
17062
  mutation(kind, func) {
16630
- const outer = this._mutationDepth === 0;
17063
+ const close = this.openMutation(kind);
17064
+ try {
17065
+ return func();
17066
+ } finally {
17067
+ close();
17068
+ }
17069
+ }
17070
+ /**
17071
+ * `mutation()` for an operation whose work continues after the synchronous
17072
+ * call returns. The transaction stays open until the returned promise
17073
+ * settles, so everything the operation does - the group it adds, the panels
17074
+ * it relocates - lands inside the bracket, matching the synchronous
17075
+ * move / float paths.
17076
+ */
17077
+ mutationAsync(kind, func) {
17078
+ var _this = this;
17079
+ return _asyncToGenerator(function* () {
17080
+ const close = _this.openMutation(kind);
17081
+ try {
17082
+ return yield func();
17083
+ } finally {
17084
+ close();
17085
+ }
17086
+ })();
17087
+ }
17088
+ /**
17089
+ * Open a transaction and return the function that closes it. Shared by the
17090
+ * synchronous and asynchronous brackets; the returned function must be
17091
+ * called exactly once.
17092
+ *
17093
+ * Both ends key off the depth counter reaching zero rather than off which
17094
+ * bracket opened first. For synchronous nesting the two are the same thing
17095
+ * - brackets close in the order they opened - but asynchronous transactions
17096
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17097
+ * once, or a restore staggering several), and there the first to open is
17098
+ * not the last to close. Reporting on the opener would fire `didMutate`
17099
+ * while the other transaction was still doing structural work, which is the
17100
+ * very thing the async bracket exists to prevent. Overlapping transactions
17101
+ * therefore report as one, tagged with the kind of the last to finish.
17102
+ */
17103
+ openMutation(kind) {
16631
17104
  const origin = this._origin;
16632
- if (outer) this._onWillMutateLayout.fire({
17105
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16633
17106
  kind,
16634
17107
  origin
16635
17108
  });
16636
17109
  this._mutationDepth++;
16637
- try {
16638
- return func();
16639
- } finally {
17110
+ return () => {
16640
17111
  this._mutationDepth--;
16641
- if (outer) this._onDidMutateLayout.fire({
16642
- kind,
16643
- origin
16644
- });
17112
+ if (this._mutationDepth === 0) {
17113
+ this.flushLocationChanges();
17114
+ this._onDidMutateLayout.fire({
17115
+ kind,
17116
+ origin
17117
+ });
17118
+ }
17119
+ };
17120
+ }
17121
+ /**
17122
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17123
+ * transaction, or fire it immediately when no transaction is in flight.
17124
+ *
17125
+ * Relocating a panel touches its location twice: once as it is reparented
17126
+ * into the destination group - which, when that group has just been created
17127
+ * for a floating or popout window, has not been told where it lives yet -
17128
+ * and once as the group is tagged with its final location. Reporting each
17129
+ * signal as it happens therefore leaks an intermediate `grid` location the
17130
+ * panel was never in, at a moment when it belongs to neither group's panel
17131
+ * list and so is missing from `api.panels`.
17132
+ *
17133
+ * Deferring collapses those signals into a single event carrying the
17134
+ * settled location. Note the signals are deliberately *not* compared
17135
+ * against the panel's previous location: a panel can move between two
17136
+ * floating windows without its location type changing, and this event is
17137
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17138
+ * z-index against its new host window.
17139
+ */
17140
+ deferLocationChange(key, fire) {
17141
+ if (this._mutationDepth === 0) {
17142
+ fire();
17143
+ return;
16645
17144
  }
17145
+ this._pendingLocationChanges.set(key, fire);
17146
+ }
17147
+ /**
17148
+ * Report every panel whose location moved during the transaction that has
17149
+ * just closed. Each callback reads the panel's location at this point, so
17150
+ * what it reports is where the panel ended up.
17151
+ */
17152
+ flushLocationChanges() {
17153
+ if (this._pendingLocationChanges.size === 0) return;
17154
+ const pending = Array.from(this._pendingLocationChanges.values());
17155
+ this._pendingLocationChanges.clear();
17156
+ for (const fire of pending) fire();
16646
17157
  }
16647
17158
  /**
16648
17159
  * The origin of the operation currently in progress (`'user'` by default).
@@ -16683,6 +17194,18 @@ react_dom = __toESM(react_dom);
16683
17194
  origin: this._origin
16684
17195
  });
16685
17196
  }
17197
+ /**
17198
+ * Announce that `panel` has finished relocating. Must be called once the
17199
+ * move has settled: `to` is read from the panel's current group, so an
17200
+ * early call would report the source group as the destination.
17201
+ */
17202
+ fireDidMovePanel(panel, from) {
17203
+ this._onDidMovePanel.fire({
17204
+ panel,
17205
+ from,
17206
+ to: panel.group
17207
+ });
17208
+ }
16686
17209
  moveGroupOrPanel(options) {
16687
17210
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
16688
17211
  }
@@ -16743,10 +17266,7 @@ react_dom = __toESM(react_dom);
16743
17266
  });
16744
17267
  });
16745
17268
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
16746
- this._onDidMovePanel.fire({
16747
- panel: removedPanel,
16748
- from: sourceGroup
16749
- });
17269
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16750
17270
  } else {
16751
17271
  /**
16752
17272
  * Dropping a panel to the extremities of a group which will place that panel
@@ -16765,10 +17285,7 @@ react_dom = __toESM(react_dom);
16765
17285
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
16766
17286
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
16767
17287
  this.gridview.moveView(sourceParentLocation, from, to);
16768
- this._onDidMovePanel.fire({
16769
- panel: this.getGroupPanel(sourceItemId),
16770
- from: sourceGroup
16771
- });
17288
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16772
17289
  return;
16773
17290
  }
16774
17291
  }
@@ -16799,10 +17316,7 @@ react_dom = __toESM(react_dom);
16799
17316
  const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
16800
17317
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
16801
17318
  this.doSetGroupAndPanelActive(newGroup);
16802
- this._onDidMovePanel.fire({
16803
- panel: this.getGroupPanel(sourceItemId),
16804
- from: sourceGroup
16805
- });
17319
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16806
17320
  return;
16807
17321
  }
16808
17322
  if (sourceGroup.api.location.type === "edge") {
@@ -16819,10 +17333,7 @@ react_dom = __toESM(react_dom);
16819
17333
  const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
16820
17334
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16821
17335
  this.doSetGroupAndPanelActive(newGroup);
16822
- this._onDidMovePanel.fire({
16823
- panel: removedPanel,
16824
- from: sourceGroup
16825
- });
17336
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16826
17337
  return;
16827
17338
  }
16828
17339
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -16834,10 +17345,7 @@ react_dom = __toESM(react_dom);
16834
17345
  this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
16835
17346
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
16836
17347
  this.doSetGroupAndPanelActive(targetGroup);
16837
- this._onDidMovePanel.fire({
16838
- panel: this.getGroupPanel(sourceItemId),
16839
- from: sourceGroup
16840
- });
17348
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16841
17349
  } else {
16842
17350
  /**
16843
17351
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -16852,10 +17360,7 @@ react_dom = __toESM(react_dom);
16852
17360
  const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
16853
17361
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16854
17362
  this.doSetGroupAndPanelActive(group);
16855
- this._onDidMovePanel.fire({
16856
- panel: removedPanel,
16857
- from: sourceGroup
16858
- });
17363
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16859
17364
  }
16860
17365
  }
16861
17366
  }
@@ -16890,10 +17395,7 @@ react_dom = __toESM(react_dom);
16890
17395
  });
16891
17396
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
16892
17397
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
16893
- for (const panel of removedPanels) this._onDidMovePanel.fire({
16894
- panel,
16895
- from: sourceGroup
16896
- });
17398
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
16897
17399
  };
16898
17400
  let targetGroup;
16899
17401
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
@@ -16914,10 +17416,12 @@ react_dom = __toESM(react_dom);
16914
17416
  this.mutation("maximize", () => super.exitMaximizedGroup());
16915
17417
  }
16916
17418
  _doMoveGroup(options) {
17419
+ var _mergedPanels;
16917
17420
  const from = options.from.group;
16918
17421
  const to = options.to.group;
16919
17422
  const target = options.to.position;
16920
17423
  let source = from;
17424
+ let mergedPanels;
16921
17425
  if (target === "center") {
16922
17426
  const activePanel = from.activePanel;
16923
17427
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -16935,6 +17439,7 @@ react_dom = __toESM(react_dom);
16935
17439
  skipSetGroupActive: true
16936
17440
  });
16937
17441
  });
17442
+ mergedPanels = panels;
16938
17443
  for (const snapshot of tabGroupSnapshots) {
16939
17444
  const newTabGroup = to.model.createTabGroup({
16940
17445
  label: snapshot.label,
@@ -17024,11 +17529,8 @@ react_dom = __toESM(react_dom);
17024
17529
  this.setGroupLocationForRoot(source, destGridview);
17025
17530
  }
17026
17531
  }
17027
- source.panels.forEach((panel) => {
17028
- this._onDidMovePanel.fire({
17029
- panel,
17030
- from
17031
- });
17532
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17533
+ this.fireDidMovePanel(panel, from);
17032
17534
  });
17033
17535
  this.debouncedUpdateAllPositions();
17034
17536
  if (options.skipSetActive === false) {