dockview-react 8.1.0 → 8.3.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.1.0
3
+ * @version 8.3.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -51,7 +51,7 @@ react_dom = __toESM(react_dom);
51
51
  //#region ../dockview-core/dist/package/main.esm.mjs
52
52
  /**
53
53
  * dockview-core
54
- * @version 8.1.0
54
+ * @version 8.3.0
55
55
  * @link https://github.com/dockview/dockview
56
56
  * @license MIT
57
57
  */
@@ -1077,8 +1077,14 @@ react_dom = __toESM(react_dom);
1077
1077
  container.appendChild(view.element);
1078
1078
  let viewSize;
1079
1079
  if (typeof size === "number") viewSize = size;
1080
- else if (size.type === "split") viewSize = this.getViewSize(size.index) / 2;
1081
- else if (size.type === "invisible") viewSize = { cachedVisibleSize: size.cachedVisibleSize };
1080
+ else if (size.type === "split") {
1081
+ const splitTarget = this.viewItems[size.index];
1082
+ if (splitTarget) {
1083
+ const half = Math.floor(splitTarget.size / 2);
1084
+ viewSize = splitTarget.size - half;
1085
+ splitTarget.size = half;
1086
+ } else viewSize = this.getViewSize(size.index) / 2;
1087
+ } else if (size.type === "invisible") viewSize = { cachedVisibleSize: size.cachedVisibleSize };
1082
1088
  else viewSize = view.minimumSize;
1083
1089
  const disposable = view.onDidChange((newSize) => this.onDidChange(viewItem, newSize.size));
1084
1090
  const viewItem = new ViewItem(container, view, viewSize, { dispose: () => {
@@ -5718,7 +5724,7 @@ react_dom = __toESM(react_dom);
5718
5724
  };
5719
5725
  case "auto":
5720
5726
  case void 0:
5721
- default: return isCoarsePrimaryInput$1() ? {
5727
+ default: return isCoarsePrimaryInput$2() ? {
5722
5728
  html5: false,
5723
5729
  pointer: true,
5724
5730
  pointerHandlesMouse: true
@@ -5729,7 +5735,7 @@ react_dom = __toESM(react_dom);
5729
5735
  };
5730
5736
  }
5731
5737
  }
5732
- function isCoarsePrimaryInput$1() {
5738
+ function isCoarsePrimaryInput$2() {
5733
5739
  if (globalThis.window === void 0 || !globalThis.matchMedia) return false;
5734
5740
  const coarse = globalThis.matchMedia("(pointer: coarse)").matches;
5735
5741
  const fine = globalThis.matchMedia("(pointer: fine)").matches;
@@ -7850,11 +7856,28 @@ react_dom = __toESM(react_dom);
7850
7856
  if (this._voidContainer && related && (related === this._voidContainer || this._voidContainer.contains(related))) return;
7851
7857
  this.resetTabTransforms();
7852
7858
  if (this._animState.sourceIndex === -1) {
7853
- var _this$group$model$dro2;
7854
- (_this$group$model$dro2 = this.group.model.dropTargetContainer) === null || _this$group$model$dro2 === void 0 || (_this$group$model$dro2 = _this$group$model$dro2.model) === null || _this$group$model$dro2 === void 0 || _this$group$model$dro2.clear();
7859
+ this.clearHeaderAnchorOverlay();
7855
7860
  this._animState = null;
7856
7861
  } else this._animState.currentInsertionIndex = null;
7857
7862
  }
7863
+ /**
7864
+ * Clear the anchored overlay only when this header is what rendered it.
7865
+ * The container is shared with the group's content drop target, which
7866
+ * renders into it on the same frame the cursor leaves the strip: the
7867
+ * pointer backend calls `handleDragOver` on the new target before the
7868
+ * global `onDragMove` that reaches us (#1612).
7869
+ */
7870
+ clearHeaderAnchorOverlay() {
7871
+ var _container$model;
7872
+ const container = this.group.model.dropTargetContainer;
7873
+ const outline = container === null || container === void 0 ? void 0 : container.renderedOutline;
7874
+ if (outline && ![
7875
+ this._tabsList,
7876
+ this._voidContainer,
7877
+ this._extendedDropZone
7878
+ ].some((scope) => scope === null || scope === void 0 ? void 0 : scope.contains(outline))) return;
7879
+ container === null || container === void 0 || (_container$model = container.model) === null || _container$model === void 0 || _container$model.clear();
7880
+ }
7858
7881
  handleDragOver(event) {
7859
7882
  var _this$accessor$option2;
7860
7883
  if (!this._animState) return;
@@ -10395,7 +10418,11 @@ react_dom = __toESM(react_dom);
10395
10418
  this.panels.forEach((panel) => {
10396
10419
  this.rerender(panel);
10397
10420
  });
10398
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10421
+ if (this._activePanel) {
10422
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10423
+ const { width, height } = this.contentDimensions();
10424
+ this._activePanel.layout(width, height);
10425
+ }
10399
10426
  }
10400
10427
  get renderContainer() {
10401
10428
  var _this$_overwriteRende;
@@ -11342,7 +11369,7 @@ react_dom = __toESM(react_dom);
11342
11369
  this._group = value;
11343
11370
  this._onDidGroupChange.fire({});
11344
11371
  this.setupGroupEventListeners(oldGroup);
11345
- this._onDidLocationChange.fire({ location: this.group.api.location });
11372
+ this.fireLocationChange();
11346
11373
  }
11347
11374
  }
11348
11375
  get group() {
@@ -11418,6 +11445,23 @@ react_dom = __toESM(react_dom);
11418
11445
  exitMaximized() {
11419
11446
  this.group.api.exitMaximized();
11420
11447
  }
11448
+ /**
11449
+ * Report that this panel's location may have moved.
11450
+ *
11451
+ * A relocation touches the location more than once - the panel is
11452
+ * reparented into the destination group, then that group is tagged with
11453
+ * the location it ends up at - so the event is coalesced to the end of the
11454
+ * enclosing layout mutation and reports where the panel actually settled.
11455
+ * Firing each signal as it happened surfaced an intermediate `grid`
11456
+ * location the panel was never in, at a point where it was in neither
11457
+ * group's panel list. Outside a mutation this fires straight away.
11458
+ */
11459
+ fireLocationChange() {
11460
+ this.accessor.deferLocationChange(this, () => {
11461
+ if (this.isDisposed) return;
11462
+ this._onDidLocationChange.fire({ location: this.location });
11463
+ });
11464
+ }
11421
11465
  setupGroupEventListeners(previousGroup) {
11422
11466
  var _previousGroup$isActi;
11423
11467
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11426,9 +11470,9 @@ react_dom = __toESM(react_dom);
11426
11470
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11427
11471
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11428
11472
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11429
- }), this.group.api.onDidLocationChange((event) => {
11473
+ }), this.group.api.onDidLocationChange(() => {
11430
11474
  if (this.group !== this.panel.group) return;
11431
- this._onDidLocationChange.fire(event);
11475
+ this.fireLocationChange();
11432
11476
  }), this.group.api.onDidActiveChange(() => {
11433
11477
  if (this.group !== this.panel.group) return;
11434
11478
  if (_trackGroupActive !== this.isGroupActive) {
@@ -12206,7 +12250,6 @@ react_dom = __toESM(react_dom);
12206
12250
  "AdvancedOverflow",
12207
12251
  "AutoEdgeGroup",
12208
12252
  "AutoHideEdgeGroup",
12209
- "ContextMenu",
12210
12253
  "DndCompass",
12211
12254
  "KeyboardDocking",
12212
12255
  "KeyboardNavigation",
@@ -12288,8 +12331,8 @@ react_dom = __toESM(react_dom);
12288
12331
  * case: calling `undo()` having never set the option at all.
12289
12332
  *
12290
12333
  * Interaction handlers are queries in this sense too: a right-click reaching an
12291
- * absent ContextMenu module means the app never asked for one, so it stays
12292
- * silent (`?.`) and the browser's own menu shows. Options are where intent is
12334
+ * absent module means the app never asked for that feature, so it stays silent
12335
+ * (`?.`) and the browser's own behaviour applies. Options are where intent is
12293
12336
  * declared; see `optionsModules.ts`.
12294
12337
  */
12295
12338
  function assertModule(service, moduleName, context) {
@@ -12366,10 +12409,9 @@ react_dom = __toESM(react_dom);
12366
12409
  *
12367
12410
  * This is the "declared intent" diagnostic. It fires when the user asks for a
12368
12411
  * feature via options, at construction and on every `updateOptions`. It
12369
- * deliberately does *not* fire on user interaction: right-clicking without the
12370
- * ContextMenu module stays silent (the browser's own menu shows), because the
12371
- * `?.` service-slot call sites can't tell a missing module from a feature the
12372
- * app never wanted.
12412
+ * deliberately does *not* fire on user interaction: an interaction reaching an
12413
+ * absent module stays silent, because the `?.` service-slot call sites can't
12414
+ * tell a missing module from a feature the app never wanted.
12373
12415
  *
12374
12416
  * Rules are a flat list rather than a `Record<keyof DockviewOptions, ...>`
12375
12417
  * because dockview's options are nested and value-gated: `overflow.mode:
@@ -12465,18 +12507,6 @@ react_dom = __toESM(react_dom);
12465
12507
  moduleName: "AutoEdgeGroup",
12466
12508
  when: (o) => isAnyEdgeGroupEnabled(o.dockToEdgeGroups)
12467
12509
  },
12468
- {
12469
- optionKey: "getTabContextMenuItems",
12470
- reason: "getTabContextMenuItems",
12471
- moduleName: "ContextMenu",
12472
- when: (o) => o.getTabContextMenuItems != null
12473
- },
12474
- {
12475
- optionKey: "getTabGroupChipContextMenuItems",
12476
- reason: "getTabGroupChipContextMenuItems",
12477
- moduleName: "ContextMenu",
12478
- when: (o) => o.getTabGroupChipContextMenuItems != null
12479
- },
12480
12510
  {
12481
12511
  optionKey: "dndCompass",
12482
12512
  reason: "dndCompass",
@@ -13343,6 +13373,267 @@ react_dom = __toESM(react_dom);
13343
13373
  } });
13344
13374
  }
13345
13375
  });
13376
+ function popoverZIndexFor(target, group) {
13377
+ var _group$api, _group$api$isPeeking;
13378
+ if ((_group$api = group.api) === null || _group$api === void 0 || (_group$api$isPeeking = _group$api.isPeeking) === null || _group$api$isPeeking === void 0 ? void 0 : _group$api$isPeeking.call(_group$api)) return "calc(var(--dv-overlay-z-index, 999) + 100)";
13379
+ if (!(target instanceof HTMLElement)) return;
13380
+ const relativeParent = findRelativeZIndexParent(target);
13381
+ return (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex) ? `calc(${relativeParent.style.zIndex} * 2)` : void 0;
13382
+ }
13383
+ let _nextId = 0;
13384
+ const nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
13385
+ function isItemConfig(item) {
13386
+ return typeof item === "object";
13387
+ }
13388
+ function buildItem(label, close, action, disabled) {
13389
+ const el = document.createElement("div");
13390
+ el.className = "dv-context-menu-item";
13391
+ el.setAttribute("role", "menuitem");
13392
+ if (disabled) {
13393
+ el.classList.add("dv-context-menu-item--disabled");
13394
+ el.setAttribute("aria-disabled", "true");
13395
+ }
13396
+ el.textContent = label;
13397
+ if (!disabled) el.addEventListener("click", () => {
13398
+ action();
13399
+ close();
13400
+ });
13401
+ return el;
13402
+ }
13403
+ function buildSeparator() {
13404
+ const el = document.createElement("div");
13405
+ el.className = "dv-context-menu-separator";
13406
+ el.setAttribute("role", "separator");
13407
+ return el;
13408
+ }
13409
+ function isCoarsePrimaryInput$1() {
13410
+ if (globalThis.window === void 0 || !globalThis.matchMedia) return false;
13411
+ const coarse = globalThis.matchMedia("(pointer: coarse)").matches;
13412
+ const fine = globalThis.matchMedia("(pointer: fine)").matches;
13413
+ return coarse && !fine;
13414
+ }
13415
+ function buildRenameInput(tabGroup) {
13416
+ const wrapper = document.createElement("div");
13417
+ wrapper.className = "dv-context-menu-rename";
13418
+ const input = document.createElement("input");
13419
+ input.className = "dv-context-menu-rename-input";
13420
+ input.type = "text";
13421
+ input.placeholder = "Name This Group";
13422
+ input.value = tabGroup.label;
13423
+ input.addEventListener("input", () => {
13424
+ tabGroup.setLabel(input.value);
13425
+ });
13426
+ input.addEventListener("keydown", (e) => {
13427
+ if (e.key !== "Escape" && e.key !== "Enter") e.stopPropagation();
13428
+ });
13429
+ input.addEventListener("click", (e) => {
13430
+ e.stopPropagation();
13431
+ });
13432
+ wrapper.appendChild(input);
13433
+ if (!isCoarsePrimaryInput$1()) requestAnimationFrame(() => {
13434
+ input.focus();
13435
+ input.select();
13436
+ });
13437
+ return wrapper;
13438
+ }
13439
+ function buildColorPicker(tabGroup, palette) {
13440
+ const wrapper = document.createElement("div");
13441
+ wrapper.className = "dv-context-menu-color-picker";
13442
+ if (!palette.enabled) return wrapper;
13443
+ for (const entry of palette.entries()) {
13444
+ const swatch = document.createElement("div");
13445
+ swatch.className = "dv-context-menu-color-swatch";
13446
+ swatch.style.setProperty("--dv-tab-group-color", entry.value);
13447
+ if (entry.label) swatch.title = entry.label;
13448
+ if (tabGroup.color === entry.id) swatch.classList.add("dv-context-menu-color-swatch--selected");
13449
+ swatch.addEventListener("click", () => {
13450
+ tabGroup.setColor(entry.id);
13451
+ });
13452
+ wrapper.appendChild(swatch);
13453
+ }
13454
+ return wrapper;
13455
+ }
13456
+ var ContextMenuController = class {
13457
+ constructor(accessor) {
13458
+ this.accessor = accessor;
13459
+ }
13460
+ /**
13461
+ * A single maximize/restore toggle whose label and behaviour track the
13462
+ * group's live state: it reads *Restore* + calls `exitMaximized` when the
13463
+ * group is maximized, otherwise *Maximize* + `maximize`. Only grid groups
13464
+ * can be maximized, so the item is disabled for floating / popout panels
13465
+ * that are not already maximized.
13466
+ */
13467
+ buildMaximizeItem(panel, close) {
13468
+ const isMaximized = panel.api.isMaximized();
13469
+ return buildItem(isMaximized ? "Restore" : "Maximize", close, isMaximized ? () => panel.api.exitMaximized() : () => panel.api.maximize(), !isMaximized && panel.api.location.type !== "grid");
13470
+ }
13471
+ /**
13472
+ * A single collapse/expand toggle whose label tracks the tab group's live
13473
+ * state: *Expand* when the group is already collapsed, otherwise *Collapse*.
13474
+ */
13475
+ buildCollapseItem(tabGroup, close) {
13476
+ return buildItem(tabGroup.collapsed ? "Expand" : "Collapse", close, () => tabGroup.toggle());
13477
+ }
13478
+ /**
13479
+ * Whether to auto-inject the built-in Pin/Unpin item at the top of the tab
13480
+ * menu. True only when the PinnedTabs module is registered, pinning is
13481
+ * enabled, and the app has not opted out via `pinnedTabs.contextMenuItem:
13482
+ * false` (the item is on by default). This keeps pinning reachable from the
13483
+ * menu without the app having to return `'pin'` from
13484
+ * `getTabContextMenuItems` itself.
13485
+ */
13486
+ _shouldInjectPin() {
13487
+ const pinnedTabs = this.accessor.options.pinnedTabs;
13488
+ return !!this.accessor.pinnedTabsService && !!(pinnedTabs === null || pinnedTabs === void 0 ? void 0 : pinnedTabs.enabled) && pinnedTabs.contextMenuItem !== false;
13489
+ }
13490
+ /**
13491
+ * The tab menu items: the app's own items (if any) with the built-in
13492
+ * `'pin'` item prepended when {@link _shouldInjectPin} applies, so the app
13493
+ * keeps full control of its list, and pinning is added without disturbing
13494
+ * it. Reuses the existing `'pin'` token rendering below.
13495
+ */
13496
+ _resolveTabItems(panel, group, event) {
13497
+ var _this$accessor$option, _this$accessor$option2, _this$accessor$option3;
13498
+ const appItems = (_this$accessor$option = (_this$accessor$option2 = (_this$accessor$option3 = this.accessor.options).getTabContextMenuItems) === null || _this$accessor$option2 === void 0 ? void 0 : _this$accessor$option2.call(_this$accessor$option3, {
13499
+ panel,
13500
+ group,
13501
+ api: this.accessor.api,
13502
+ event
13503
+ })) !== null && _this$accessor$option !== void 0 ? _this$accessor$option : [];
13504
+ return this._shouldInjectPin() ? ["pin", ...appItems] : appItems;
13505
+ }
13506
+ /**
13507
+ * Render a custom `component` menu item and append it to `menuEl`. Builds
13508
+ * the framework renderer via the host's `createContextMenuItemComponent`
13509
+ * factory and initialises it. Shared by the tab menu and the chip menu,
13510
+ * which differ only in `identity`: the tab menu passes the `panel`, the chip
13511
+ * menu passes the `tabGroup`. No-op when no renderer could be created (no
13512
+ * factory configured, or the factory declined the component).
13513
+ */
13514
+ appendComponentItem(menuEl, item, identity, group, close) {
13515
+ var _this$accessor$option4, _this$accessor$option5;
13516
+ const renderer = (_this$accessor$option4 = (_this$accessor$option5 = this.accessor.options).createContextMenuItemComponent) === null || _this$accessor$option4 === void 0 ? void 0 : _this$accessor$option4.call(_this$accessor$option5, {
13517
+ id: nextContextMenuItemId(),
13518
+ component: item.component
13519
+ });
13520
+ if (!renderer) return;
13521
+ renderer.init(_objectSpread2$1(_objectSpread2$1({}, identity), {}, {
13522
+ group,
13523
+ api: this.accessor.api,
13524
+ close,
13525
+ componentProps: item.componentProps
13526
+ }));
13527
+ menuEl.appendChild(renderer.element);
13528
+ }
13529
+ /**
13530
+ * The element for one built-in tab item, or `undefined` for a token this
13531
+ * version doesn't know (an app on a newer typings version, or plain JS):
13532
+ * an unrecognised token renders nothing rather than throwing.
13533
+ */
13534
+ buildBuiltInTabItem(item, panel, group, close) {
13535
+ switch (item) {
13536
+ case "separator": return buildSeparator();
13537
+ case "close": return buildItem("Close", close, () => panel.api.close());
13538
+ case "closeOthers": return buildItem("Close Others", close, () => {
13539
+ group.panels.filter((p) => p !== panel).forEach((p) => p.api.close());
13540
+ });
13541
+ case "closeAll": return buildItem("Close All", close, () => {
13542
+ [...group.panels].forEach((p) => p.api.close());
13543
+ });
13544
+ case "closeLeft": {
13545
+ const index = group.panels.indexOf(panel);
13546
+ return buildItem("Close to the Left", close, () => {
13547
+ group.panels.filter((_, i) => i < index).forEach((p) => p.api.close());
13548
+ }, index <= 0);
13549
+ }
13550
+ case "closeRight": {
13551
+ const index = group.panels.indexOf(panel);
13552
+ return buildItem("Close to the Right", close, () => {
13553
+ group.panels.filter((_, i) => i > index).forEach((p) => p.api.close());
13554
+ }, index === -1 || index >= group.panels.length - 1);
13555
+ }
13556
+ case "maximize": return this.buildMaximizeItem(panel, close);
13557
+ case "float": return buildItem("Float", close, () => this.accessor.api.addFloatingGroup(panel), panel.api.location.type === "floating");
13558
+ case "popout": return buildItem("Open in New Window", close, () => {
13559
+ this.accessor.api.addPopoutGroup(panel);
13560
+ }, panel.api.location.type === "popout");
13561
+ case "pin": return buildItem(panel.api.isPinned ? "Unpin tab" : "Pin tab", close, () => panel.api.setPinned(!panel.api.isPinned));
13562
+ default: return;
13563
+ }
13564
+ }
13565
+ /**
13566
+ * Append one app-supplied item. A raw `element` is used as-is, a
13567
+ * `component` goes through the framework renderer, and a `label` builds the
13568
+ * default row; an item carrying none of the three renders nothing. Shared
13569
+ * by both menus, which differ only in `identity`.
13570
+ */
13571
+ appendConfigItem(menuEl, item, identity, group, close) {
13572
+ if (item.element) menuEl.appendChild(item.element);
13573
+ else if (item.component) this.appendComponentItem(menuEl, item, identity, group, close);
13574
+ else if (item.label) menuEl.appendChild(buildItem(item.label, close, () => {
13575
+ var _item$action;
13576
+ return (_item$action = item.action) === null || _item$action === void 0 ? void 0 : _item$action.call(item);
13577
+ }, item.disabled));
13578
+ }
13579
+ show(panel, group, event) {
13580
+ const items = this._resolveTabItems(panel, group, event);
13581
+ if (items.length === 0) return;
13582
+ event.preventDefault();
13583
+ const popupService = this.accessor.getPopupServiceForGroup(group);
13584
+ const close = () => popupService.close();
13585
+ const menuEl = document.createElement("div");
13586
+ menuEl.className = "dv-context-menu";
13587
+ menuEl.setAttribute("role", "menu");
13588
+ for (const item of items) {
13589
+ if (isItemConfig(item)) {
13590
+ this.appendConfigItem(menuEl, item, { panel }, group, close);
13591
+ continue;
13592
+ }
13593
+ const el = this.buildBuiltInTabItem(item, panel, group, close);
13594
+ if (el) menuEl.appendChild(el);
13595
+ }
13596
+ popupService.openPopover(menuEl, {
13597
+ x: event.clientX,
13598
+ y: event.clientY,
13599
+ zIndex: popoverZIndexFor(event.target, group)
13600
+ });
13601
+ }
13602
+ showForChip(tabGroup, group, event) {
13603
+ if (!this.accessor.options.getTabGroupChipContextMenuItems) return;
13604
+ const items = this.accessor.options.getTabGroupChipContextMenuItems({
13605
+ tabGroup,
13606
+ group,
13607
+ api: this.accessor.api,
13608
+ event
13609
+ });
13610
+ if (items.length === 0) return;
13611
+ event.preventDefault();
13612
+ const popupService = this.accessor.getPopupServiceForGroup(group);
13613
+ const close = () => popupService.close();
13614
+ const menuEl = document.createElement("div");
13615
+ menuEl.className = "dv-context-menu";
13616
+ menuEl.setAttribute("role", "menu");
13617
+ for (const item of items) if (item === "separator") menuEl.appendChild(buildSeparator());
13618
+ else if (item === "rename") menuEl.appendChild(buildRenameInput(tabGroup));
13619
+ else if (item === "colorPicker") menuEl.appendChild(buildColorPicker(tabGroup, this.accessor.tabGroupColorPalette));
13620
+ else if (item === "collapse") menuEl.appendChild(this.buildCollapseItem(tabGroup, close));
13621
+ else if (item === "close") menuEl.appendChild(buildItem("Close All", close, () => {
13622
+ group.panels.filter((p) => tabGroup.containsPanel(p.id)).forEach((p) => p.api.close());
13623
+ }));
13624
+ else if (isItemConfig(item)) this.appendConfigItem(menuEl, item, { tabGroup }, group, close);
13625
+ popupService.openPopover(menuEl, {
13626
+ x: event.clientX,
13627
+ y: event.clientY,
13628
+ zIndex: popoverZIndexFor(event.target, group)
13629
+ });
13630
+ }
13631
+ };
13632
+ const ContextMenuModule = defineModule({
13633
+ name: "ContextMenu",
13634
+ serviceKey: "contextMenuService",
13635
+ create: (host) => new ContextMenuController(host)
13636
+ });
13346
13637
  /**
13347
13638
  * Internal list of the built-in modules that ship with the core. Registered
13348
13639
  * automatically by DockviewComponent at construction time; not exported from
@@ -13358,7 +13649,8 @@ react_dom = __toESM(react_dom);
13358
13649
  HeaderActionsModule,
13359
13650
  LiveRegionModule,
13360
13651
  AdvancedDnDModule,
13361
- TabGroupChipsModule
13652
+ TabGroupChipsModule,
13653
+ ContextMenuModule
13362
13654
  ];
13363
13655
  var PositionCache = class {
13364
13656
  constructor() {
@@ -13388,6 +13680,13 @@ react_dom = __toESM(react_dom);
13388
13680
  });
13389
13681
  }
13390
13682
  };
13683
+ /**
13684
+ * Placeholder handle marking "a reposition frame has been requested but the
13685
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13686
+ * practice — it is replaced within the same synchronous block — and harmless
13687
+ * if it were, since no frame carries this id.
13688
+ */
13689
+ const SCHEDULED = -1;
13391
13690
  function createFocusableElement() {
13392
13691
  const element = document.createElement("div");
13393
13692
  element.tabIndex = -1;
@@ -13400,12 +13699,13 @@ react_dom = __toESM(react_dom);
13400
13699
  this.accessor = accessor;
13401
13700
  this.map = {};
13402
13701
  this._disposed = false;
13702
+ this._generation = 0;
13403
13703
  this.positionCache = new PositionCache();
13404
- this.pendingUpdates = /* @__PURE__ */ new Set();
13405
13704
  this.addDisposables(Disposable.from(() => {
13406
13705
  for (const value of Object.values(this.map)) {
13407
13706
  value.disposable.dispose();
13408
13707
  value.destroy.dispose();
13708
+ this.cancelPendingUpdate(value);
13409
13709
  }
13410
13710
  this._disposed = true;
13411
13711
  }));
@@ -13432,11 +13732,24 @@ react_dom = __toESM(react_dom);
13432
13732
  this.positionCache.invalidate();
13433
13733
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13434
13734
  }
13735
+ /**
13736
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13737
+ * bound to the reference container that was current when it was scheduled,
13738
+ * so it must be discarded whenever that container stops being the one the
13739
+ * overlay should track.
13740
+ */
13741
+ cancelPendingUpdate(entry) {
13742
+ if (entry.pendingUpdate !== void 0) {
13743
+ cancelAnimationFrame(entry.pendingUpdate);
13744
+ entry.pendingUpdate = void 0;
13745
+ }
13746
+ }
13435
13747
  detatch(panel) {
13436
13748
  if (this.map[panel.api.id]) {
13437
- const { disposable, destroy } = this.map[panel.api.id];
13438
- disposable.dispose();
13439
- destroy.dispose();
13749
+ const entry = this.map[panel.api.id];
13750
+ entry.disposable.dispose();
13751
+ entry.destroy.dispose();
13752
+ this.cancelPendingUpdate(entry);
13440
13753
  delete this.map[panel.api.id];
13441
13754
  return true;
13442
13755
  }
@@ -13452,22 +13765,73 @@ react_dom = __toESM(react_dom);
13452
13765
  panel,
13453
13766
  disposable: Disposable.NONE,
13454
13767
  destroy: Disposable.NONE,
13455
- element
13768
+ element,
13769
+ retainPreviousGeometry: false,
13770
+ positioned: false,
13771
+ generation: ++this._generation
13456
13772
  };
13457
13773
  }
13458
- const focusContainer = this.map[panel.api.id].element;
13774
+ const mapEntry = this.map[panel.api.id];
13775
+ /**
13776
+ * A *change* of reference container supersedes the previous `attach`:
13777
+ * take a fresh generation so its `resize` closure can no longer run,
13778
+ * and drop the frame it queued against the old container. During
13779
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13780
+ * detached staging group measuring 0x0, so leaving its frame in flight
13781
+ * both wastes the update and delays the reposition against the real one.
13782
+ *
13783
+ * Re-attaching over the *same* container (re-open, active panel change)
13784
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13785
+ * (the auto-hide peek) schedules a frame carrying the sticky
13786
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
13787
+ * peeked panel's `api.isVisible` is false — so discarding that frame
13788
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
13789
+ * nothing. A newly created entry already holds a fresh generation, so
13790
+ * the stale closures a `detatch` left behind are fenced off either way.
13791
+ */
13792
+ if (mapEntry.referenceContainer !== referenceContainer) {
13793
+ mapEntry.generation = ++this._generation;
13794
+ this.cancelPendingUpdate(mapEntry);
13795
+ mapEntry.referenceContainer = referenceContainer;
13796
+ /**
13797
+ * Arm the retain flag only here: it means "a replacement reference
13798
+ * container is awaiting layout", so the 0x0 early-return below
13799
+ * holds the last good box rather than blanking the panel mid-move.
13800
+ * Arming it on every re-attach instead would make a container that
13801
+ * genuinely collapses to zero keep its stale geometry forever.
13802
+ *
13803
+ * Gated on `positioned` because an overlay that has never been
13804
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
13805
+ * defaults to 100%/100% — retaining "previous" geometry there would
13806
+ * pin the content over the whole container.
13807
+ */
13808
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
13809
+ }
13810
+ const generation = mapEntry.generation;
13811
+ const focusContainer = mapEntry.element;
13459
13812
  const contentElement = panel.view.content.element;
13460
13813
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13461
13814
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13462
13815
  const resize = () => {
13463
13816
  const panelId = panel.api.id;
13464
- if (this.pendingUpdates.has(panelId)) return;
13465
- this.pendingUpdates.add(panelId);
13466
- requestAnimationFrame(() => {
13817
+ const scheduling = this.map[panelId];
13818
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
13819
+ if (scheduling.pendingUpdate !== void 0) return;
13820
+ /**
13821
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
13822
+ * shimmed to run inline by some hosts (and by several suites here),
13823
+ * in which case the callback below clears `pendingUpdate` while
13824
+ * this call is still on the stack — assigning the handle afterwards
13825
+ * would resurrect a slot that is already spent and block every
13826
+ * later reposition. The sentinel is replaced by the real handle
13827
+ * only if the frame has not already run.
13828
+ */
13829
+ scheduling.pendingUpdate = SCHEDULED;
13830
+ const handle = requestAnimationFrame(() => {
13467
13831
  var _entry$forceVisible;
13468
- this.pendingUpdates.delete(panelId);
13469
13832
  const entry = this.map[panelId];
13470
- if (this.isDisposed || !entry) return;
13833
+ if (entry) entry.pendingUpdate = void 0;
13834
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13471
13835
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13472
13836
  const clip = entry.clip;
13473
13837
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13476,6 +13840,15 @@ react_dom = __toESM(react_dom);
13476
13840
  const top = box.top - box2.top;
13477
13841
  const width = box.width;
13478
13842
  const height = box.height;
13843
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
13844
+ if (!panel.api.isVisible && !forceVisible) {
13845
+ focusContainer.style.visibility = "hidden";
13846
+ focusContainer.style.pointerEvents = "none";
13847
+ }
13848
+ return;
13849
+ }
13850
+ entry.retainPreviousGeometry = false;
13851
+ entry.positioned = true;
13479
13852
  focusContainer.style.left = `${left}px`;
13480
13853
  focusContainer.style.top = `${top}px`;
13481
13854
  focusContainer.style.width = `${width}px`;
@@ -13504,11 +13877,30 @@ react_dom = __toESM(react_dom);
13504
13877
  } else focusContainer.style.clipPath = "";
13505
13878
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13506
13879
  });
13880
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13507
13881
  };
13508
13882
  const visibilityChanged = () => {
13509
13883
  if (panel.api.isVisible) {
13884
+ var _this$map$panel$api$i;
13510
13885
  this.positionCache.invalidate();
13511
13886
  resize();
13887
+ /**
13888
+ * Existing geometry is safe to show while its replacement lays
13889
+ * out. `retainPreviousGeometry` is only set when geometry has
13890
+ * actually been written (see `attach`), so this can never
13891
+ * un-hide an overlay whose left/top/width/height are unset —
13892
+ * `.dv-render-overlay` is 100%/100% by default and would
13893
+ * otherwise cover the whole dock.
13894
+ *
13895
+ * The retained geometry is the panel's position in the previous
13896
+ * layout, so restoring a layout that moves the panel does paint
13897
+ * it at stale coordinates until the reposition lands. That is
13898
+ * bounded to the next frame now that a superseded `attach` can
13899
+ * no longer swallow the reposition (see `attach`); showing
13900
+ * stale-but-real geometry for a frame is the deliberate
13901
+ * trade-off against blanking the panel for the whole rebuild.
13902
+ */
13903
+ 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 = "";
13512
13904
  focusContainer.style.pointerEvents = "";
13513
13905
  } else {
13514
13906
  focusContainer.style.visibility = "hidden";
@@ -13725,7 +14117,17 @@ react_dom = __toESM(react_dom);
13725
14117
  window: externalWindow
13726
14118
  });
13727
14119
  return new Promise((resolve, reject) => {
13728
- externalWindow.addEventListener("unload", (e) => {});
14120
+ externalWindow.addEventListener("unload", () => {});
14121
+ /**
14122
+ * `load` is the only event that resolves this promise with a
14123
+ * container, so a window that goes away first would leave the
14124
+ * caller awaiting a promise that never settles - and
14125
+ * `addPopoutGroup` holds its layout transaction open until it does.
14126
+ * Settle with `null` on close, the same signal a blocked popup
14127
+ * gives and one the caller already handles. `resolve` after the
14128
+ * fact is a no-op, so a `load` that arrived first still wins.
14129
+ */
14130
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13729
14131
  externalWindow.addEventListener("load", () => {
13730
14132
  /**
13731
14133
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -13919,6 +14321,14 @@ react_dom = __toESM(react_dom);
13919
14321
  }
13920
14322
  };
13921
14323
  var DropTargetAnchorContainer = class extends CompositeDisposable {
14324
+ /**
14325
+ * The element the live overlay was rendered for, `undefined` when nothing
14326
+ * is showing. The container is shared by every drop target, so a caller
14327
+ * tearing down only its own overlay must check this before `clear()`.
14328
+ */
14329
+ get renderedOutline() {
14330
+ return this._model ? this._outline : void 0;
14331
+ }
13922
14332
  get disabled() {
13923
14333
  return this._disabled;
13924
14334
  }
@@ -14220,6 +14630,10 @@ react_dom = __toESM(react_dom);
14220
14630
  const index = position === "top" ? this._topIndex : this._bottomIndex;
14221
14631
  if (index !== void 0) this._splitview.resizeView(index, size);
14222
14632
  }
14633
+ /** The inner splitview's primary-axis (height) extent; zero until laid out. */
14634
+ get axisSize() {
14635
+ return this._splitview.size;
14636
+ }
14223
14637
  updateMargin(gap) {
14224
14638
  this._splitview.margin = gap;
14225
14639
  }
@@ -14232,6 +14646,7 @@ react_dom = __toESM(react_dom);
14232
14646
  constructor(container, dockviewElement, layoutGrid, gap = 0, defaultCollapsedSize = 35) {
14233
14647
  this._disposables = new CompositeDisposable();
14234
14648
  this._viewConfigs = /* @__PURE__ */ new Map();
14649
+ this._pendingSizes = /* @__PURE__ */ new Map();
14235
14650
  this._currentWidth = 0;
14236
14651
  this._currentHeight = 0;
14237
14652
  this._gap = gap;
@@ -14322,11 +14737,13 @@ react_dom = __toESM(react_dom);
14322
14737
  break;
14323
14738
  }
14324
14739
  this._disposables.addDisposables(view);
14740
+ if (!view.isCollapsed && !this._canResize(position)) this._pendingSizes.set(position, initialSize);
14325
14741
  this.updateTheme(this._gap, this._defaultCollapsedSize);
14326
14742
  return view;
14327
14743
  }
14328
14744
  layout(width, height) {
14329
14745
  this._outerSplitview.layout(width, height);
14746
+ this._flushPendingSizes();
14330
14747
  }
14331
14748
  /**
14332
14749
  * Called when the active theme changes. Updates splitview margins and
@@ -14390,6 +14807,7 @@ react_dom = __toESM(react_dom);
14390
14807
  this._disposables.removeDisposable(view);
14391
14808
  view.dispose();
14392
14809
  this._viewConfigs.delete(position);
14810
+ this._pendingSizes.delete(position);
14393
14811
  this.updateTheme(this._gap, this._defaultCollapsedSize);
14394
14812
  }
14395
14813
  hasEdgeGroup(position) {
@@ -14413,6 +14831,7 @@ react_dom = __toESM(react_dom);
14413
14831
  this._middleColumn.setViewVisible(position, visible);
14414
14832
  break;
14415
14833
  }
14834
+ if (visible) this._flushPendingSizes();
14416
14835
  }
14417
14836
  isEdgeGroupVisible(position) {
14418
14837
  switch (position) {
@@ -14430,20 +14849,63 @@ react_dom = __toESM(react_dom);
14430
14849
  const view = this._getView(position);
14431
14850
  if (!view) return;
14432
14851
  view.setCollapsed(collapsed);
14433
- const targetSize = collapsed ? view.collapsedSize : view.lastExpandedSize;
14852
+ if (collapsed) {
14853
+ this._pendingSizes.delete(position);
14854
+ this._resizeView(position, view.collapsedSize);
14855
+ } else this.resizeEdgeGroup(position, view.lastExpandedSize);
14856
+ }
14857
+ /**
14858
+ * Resize the edge group at `position` along its primary axis (width for
14859
+ * `left`/`right`, height for `top`/`bottom`), clamped by the group's
14860
+ * constraints and the space available. Where `groupApi.setSize` lands.
14861
+ *
14862
+ * The size becomes the group's expanded size, so a collapsed group keeps
14863
+ * its strip and takes it on expand, and it survives a `toJSON` round-trip.
14864
+ */
14865
+ resizeEdgeGroup(position, size) {
14866
+ const view = this._getView(position);
14867
+ if (!view || !Number.isFinite(size)) return;
14868
+ const target = Math.round(size);
14869
+ if (target <= 0) return;
14870
+ view.restoreExpandedSize(target);
14871
+ if (view.isCollapsed) {
14872
+ this._pendingSizes.delete(position);
14873
+ return;
14874
+ }
14875
+ if (this._canResize(position)) {
14876
+ this._pendingSizes.delete(position);
14877
+ this._resizeView(position, target);
14878
+ } else this._pendingSizes.set(position, target);
14879
+ }
14880
+ /** A resize lands only on a visible view (a hidden one is pinned to zero)
14881
+ * whose splitview has extent to distribute. */
14882
+ _canResize(position) {
14883
+ if (!this.isEdgeGroupVisible(position)) return false;
14884
+ return (position === "left" || position === "right" ? this._outerSplitview.size : this._middleColumn.axisSize) > 0;
14885
+ }
14886
+ _resizeView(position, size) {
14434
14887
  switch (position) {
14435
14888
  case "left":
14436
- if (this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, targetSize);
14889
+ if (this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, size);
14437
14890
  break;
14438
14891
  case "right":
14439
- if (this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, targetSize);
14892
+ if (this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, size);
14440
14893
  break;
14441
14894
  case "top":
14442
14895
  case "bottom":
14443
- this._middleColumn.resizeView(position, targetSize);
14896
+ this._middleColumn.resizeView(position, size);
14444
14897
  break;
14445
14898
  }
14446
14899
  }
14900
+ _flushPendingSizes() {
14901
+ if (this._pendingSizes.size === 0) return;
14902
+ for (const [position, size] of this._pendingSizes) {
14903
+ const view = this._getView(position);
14904
+ if (!view || view.isCollapsed || !this._canResize(position)) continue;
14905
+ this._pendingSizes.delete(position);
14906
+ this._resizeView(position, size);
14907
+ }
14908
+ }
14447
14909
  isEdgeGroupCollapsed(position) {
14448
14910
  var _this$_getView$isColl, _this$_getView;
14449
14911
  return (_this$_getView$isColl = (_this$_getView = this._getView(position)) === null || _this$_getView === void 0 ? void 0 : _this$_getView.isCollapsed) !== null && _this$_getView$isColl !== void 0 ? _this$_getView$isColl : false;
@@ -14469,15 +14931,17 @@ react_dom = __toESM(react_dom);
14469
14931
  maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
14470
14932
  collapsedSize: view.configuredCollapsedSize
14471
14933
  });
14472
- const expandedSize = (view, isVisible, liveSize, cachedVisibleSize) => {
14934
+ const expandedSize = (view, position, isVisible, liveSize, cachedVisibleSize) => {
14473
14935
  if (view.isCollapsed) return view.lastExpandedSize;
14936
+ const pending = this._pendingSizes.get(position);
14937
+ if (pending !== void 0) return pending;
14474
14938
  if (!isVisible) return cachedVisibleSize !== null && cachedVisibleSize !== void 0 ? cachedVisibleSize : view.lastExpandedSize;
14475
14939
  return liveSize;
14476
14940
  };
14477
14941
  if (this._leftView && this._leftIndex !== void 0) {
14478
14942
  const visible = this._outerSplitview.isViewVisible(this._leftIndex);
14479
14943
  edgeGroups.left = _objectSpread2$1({
14480
- size: expandedSize(this._leftView, visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
14944
+ size: expandedSize(this._leftView, "left", visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
14481
14945
  visible,
14482
14946
  collapsed: this._leftView.isCollapsed || void 0
14483
14947
  }, constraints(this._leftView));
@@ -14485,7 +14949,7 @@ react_dom = __toESM(react_dom);
14485
14949
  if (this._rightView && this._rightIndex !== void 0) {
14486
14950
  const visible = this._outerSplitview.isViewVisible(this._rightIndex);
14487
14951
  edgeGroups.right = _objectSpread2$1({
14488
- size: expandedSize(this._rightView, visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
14952
+ size: expandedSize(this._rightView, "right", visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
14489
14953
  visible,
14490
14954
  collapsed: this._rightView.isCollapsed || void 0
14491
14955
  }, constraints(this._rightView));
@@ -14493,7 +14957,7 @@ react_dom = __toESM(react_dom);
14493
14957
  if (this._topView) {
14494
14958
  const visible = this._middleColumn.isViewVisible("top");
14495
14959
  edgeGroups.top = _objectSpread2$1({
14496
- size: expandedSize(this._topView, visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
14960
+ size: expandedSize(this._topView, "top", visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
14497
14961
  visible,
14498
14962
  collapsed: this._topView.isCollapsed || void 0
14499
14963
  }, constraints(this._topView));
@@ -14501,7 +14965,7 @@ react_dom = __toESM(react_dom);
14501
14965
  if (this._bottomView) {
14502
14966
  const visible = this._middleColumn.isViewVisible("bottom");
14503
14967
  edgeGroups.bottom = _objectSpread2$1({
14504
- size: expandedSize(this._bottomView, visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
14968
+ size: expandedSize(this._bottomView, "bottom", visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
14505
14969
  visible,
14506
14970
  collapsed: this._bottomView.isCollapsed || void 0
14507
14971
  }, constraints(this._bottomView));
@@ -14509,33 +14973,23 @@ react_dom = __toESM(react_dom);
14509
14973
  return edgeGroups;
14510
14974
  }
14511
14975
  fromJSON(data) {
14512
- if (data.left && this._leftIndex !== void 0) {
14513
- var _this$_leftView2, _this$_leftView3, _data$left$collapsed, _this$_leftView$colla, _this$_leftView4;
14514
- (_this$_leftView2 = this._leftView) === null || _this$_leftView2 === void 0 || _this$_leftView2.restoreExpandedSize(data.left.size);
14515
- (_this$_leftView3 = this._leftView) === null || _this$_leftView3 === void 0 || _this$_leftView3.setCollapsed((_data$left$collapsed = data.left.collapsed) !== null && _data$left$collapsed !== void 0 ? _data$left$collapsed : false);
14516
- this._outerSplitview.resizeView(this._leftIndex, data.left.collapsed ? (_this$_leftView$colla = (_this$_leftView4 = this._leftView) === null || _this$_leftView4 === void 0 ? void 0 : _this$_leftView4.collapsedSize) !== null && _this$_leftView$colla !== void 0 ? _this$_leftView$colla : data.left.size : data.left.size);
14517
- if (!data.left.visible) this._outerSplitview.setViewVisible(this._leftIndex, false);
14518
- }
14519
- if (data.right && this._rightIndex !== void 0) {
14520
- var _this$_rightView2, _this$_rightView3, _data$right$collapsed, _this$_rightView$coll, _this$_rightView4;
14521
- (_this$_rightView2 = this._rightView) === null || _this$_rightView2 === void 0 || _this$_rightView2.restoreExpandedSize(data.right.size);
14522
- (_this$_rightView3 = this._rightView) === null || _this$_rightView3 === void 0 || _this$_rightView3.setCollapsed((_data$right$collapsed = data.right.collapsed) !== null && _data$right$collapsed !== void 0 ? _data$right$collapsed : false);
14523
- this._outerSplitview.resizeView(this._rightIndex, data.right.collapsed ? (_this$_rightView$coll = (_this$_rightView4 = this._rightView) === null || _this$_rightView4 === void 0 ? void 0 : _this$_rightView4.collapsedSize) !== null && _this$_rightView$coll !== void 0 ? _this$_rightView$coll : data.right.size : data.right.size);
14524
- if (!data.right.visible) this._outerSplitview.setViewVisible(this._rightIndex, false);
14525
- }
14526
- if (data.top) {
14527
- var _this$_topView2, _this$_topView3, _data$top$collapsed, _this$_topView$collap, _this$_topView4;
14528
- (_this$_topView2 = this._topView) === null || _this$_topView2 === void 0 || _this$_topView2.restoreExpandedSize(data.top.size);
14529
- (_this$_topView3 = this._topView) === null || _this$_topView3 === void 0 || _this$_topView3.setCollapsed((_data$top$collapsed = data.top.collapsed) !== null && _data$top$collapsed !== void 0 ? _data$top$collapsed : false);
14530
- this._middleColumn.resizeView("top", data.top.collapsed ? (_this$_topView$collap = (_this$_topView4 = this._topView) === null || _this$_topView4 === void 0 ? void 0 : _this$_topView4.collapsedSize) !== null && _this$_topView$collap !== void 0 ? _this$_topView$collap : data.top.size : data.top.size);
14531
- if (!data.top.visible) this._middleColumn.setViewVisible("top", false);
14532
- }
14533
- if (data.bottom) {
14534
- var _this$_bottomView2, _this$_bottomView3, _data$bottom$collapse, _this$_bottomView$col, _this$_bottomView4;
14535
- (_this$_bottomView2 = this._bottomView) === null || _this$_bottomView2 === void 0 || _this$_bottomView2.restoreExpandedSize(data.bottom.size);
14536
- (_this$_bottomView3 = this._bottomView) === null || _this$_bottomView3 === void 0 || _this$_bottomView3.setCollapsed((_data$bottom$collapse = data.bottom.collapsed) !== null && _data$bottom$collapse !== void 0 ? _data$bottom$collapse : false);
14537
- this._middleColumn.resizeView("bottom", data.bottom.collapsed ? (_this$_bottomView$col = (_this$_bottomView4 = this._bottomView) === null || _this$_bottomView4 === void 0 ? void 0 : _this$_bottomView4.collapsedSize) !== null && _this$_bottomView$col !== void 0 ? _this$_bottomView$col : data.bottom.size : data.bottom.size);
14538
- if (!data.bottom.visible) this._middleColumn.setViewVisible("bottom", false);
14976
+ for (const position of [
14977
+ "left",
14978
+ "right",
14979
+ "top",
14980
+ "bottom"
14981
+ ]) {
14982
+ var _state$collapsed;
14983
+ const state = data[position];
14984
+ const view = this._getView(position);
14985
+ if (!state || !view) continue;
14986
+ view.restoreExpandedSize(state.size);
14987
+ view.setCollapsed((_state$collapsed = state.collapsed) !== null && _state$collapsed !== void 0 ? _state$collapsed : false);
14988
+ if (state.collapsed) {
14989
+ this._pendingSizes.delete(position);
14990
+ this._resizeView(position, view.collapsedSize);
14991
+ } else this.resizeEdgeGroup(position, state.size);
14992
+ this.setEdgeGroupVisible(position, !!state.visible);
14539
14993
  }
14540
14994
  }
14541
14995
  dispose() {
@@ -15125,6 +15579,7 @@ react_dom = __toESM(react_dom);
15125
15579
  this._onWillDrop = new Emitter();
15126
15580
  this.onWillDrop = this._onWillDrop.event;
15127
15581
  this._mutationDepth = 0;
15582
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15128
15583
  this._origin = "user";
15129
15584
  this._originDepth = 0;
15130
15585
  this._onWillMutateLayout = new Emitter();
@@ -15222,7 +15677,7 @@ react_dom = __toESM(react_dom);
15222
15677
  toggleClass(this.element, "dv-debug", !!options.debug);
15223
15678
  this.updateTheme();
15224
15679
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15225
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15680
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15226
15681
  var _this$rootDropTargetC2, _this$floatingDropTar;
15227
15682
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15228
15683
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15296,7 +15751,7 @@ react_dom = __toESM(react_dom);
15296
15751
  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;
15297
15752
  }
15298
15753
  addPopoutGroup(itemToPopout, options) {
15299
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15754
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15300
15755
  }
15301
15756
  /** Enumerate the popout groups currently open in their own windows. */
15302
15757
  getPopouts() {
@@ -15393,15 +15848,32 @@ react_dom = __toESM(react_dom);
15393
15848
  }
15394
15849
  };
15395
15850
  let floatingBox;
15396
- 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(() => {
15397
- const panel = referenceGroup.model.removePanel(itemToPopout);
15398
- group.model.openPanel(panel);
15399
- });
15400
- else {
15851
+ /**
15852
+ * Popping out rehomes panels into `group`, so like the floating
15853
+ * case it owes the caller an `onDidMovePanel` per panel. The
15854
+ * events are deferred until the window is wired up so listeners
15855
+ * observe the panels' final `popout` location.
15856
+ */
15857
+ let movedPanels = [];
15858
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
15859
+ const sourceGroup = itemToPopout.group;
15860
+ this.movingLock(() => {
15861
+ const panel = referenceGroup.model.removePanel(itemToPopout);
15862
+ group.model.openPanel(panel);
15863
+ });
15864
+ movedPanels = [{
15865
+ panel: itemToPopout,
15866
+ from: sourceGroup
15867
+ }];
15868
+ } else {
15401
15869
  this.movingLock(() => moveGroupWithoutDestroying({
15402
15870
  from: referenceGroup,
15403
15871
  to: group
15404
15872
  }));
15873
+ movedPanels = group.panels.map((panel) => ({
15874
+ panel,
15875
+ from: referenceGroup
15876
+ }));
15405
15877
  switch (referenceLocation) {
15406
15878
  case "grid":
15407
15879
  referenceGroup.api.setVisible(false);
@@ -15510,6 +15982,7 @@ react_dom = __toESM(react_dom);
15510
15982
  group: value.popoutGroup,
15511
15983
  window: value.getWindow()
15512
15984
  });
15985
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15513
15986
  return true;
15514
15987
  }).catch((err) => {
15515
15988
  console.error("dockview: failed to create popout.", err);
@@ -15587,6 +16060,16 @@ react_dom = __toESM(react_dom);
15587
16060
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15588
16061
  const anchorPresent = members.includes(group);
15589
16062
  const anchorIsSoleMember = anchorPresent && members.length === 1;
16063
+ /**
16064
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
16065
+ * window owes the caller the same: every panel in it is rehomed, into
16066
+ * the reference group the popout left behind or into a fresh grid slot.
16067
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
16068
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
16069
+ * events are deferred to the end so listeners observe the panels at
16070
+ * their final group and location.
16071
+ */
16072
+ const movedPanels = [];
15590
16073
  if (genuineClose) for (const member of members) {
15591
16074
  if (member === group) continue;
15592
16075
  this.movingLock(() => {
@@ -15597,12 +16080,21 @@ react_dom = __toESM(react_dom);
15597
16080
  });
15598
16081
  this.redockGroupToMainGrid(member);
15599
16082
  });
16083
+ for (const panel of member.panels) movedPanels.push({
16084
+ panel,
16085
+ from: member
16086
+ });
15600
16087
  }
15601
16088
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16089
+ const rehomed = [...group.panels];
15602
16090
  this.movingLock(() => moveGroupWithoutDestroying({
15603
16091
  from: group,
15604
16092
  to: referenceGroup
15605
16093
  }));
16094
+ for (const panel of rehomed) movedPanels.push({
16095
+ panel,
16096
+ from: group
16097
+ });
15606
16098
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15607
16099
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15608
16100
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15633,10 +16125,15 @@ react_dom = __toESM(react_dom);
15633
16125
  this.movingLock(() => {
15634
16126
  this.doAddGroup(group, [0]);
15635
16127
  });
16128
+ for (const panel of group.panels) movedPanels.push({
16129
+ panel,
16130
+ from: group
16131
+ });
15636
16132
  }
15637
16133
  this.doSetGroupAndPanelActive(group);
15638
16134
  }
15639
16135
  disposePopoutGridview();
16136
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15640
16137
  }
15641
16138
  addFloatingGroup(item, options) {
15642
16139
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15645,7 +16142,15 @@ react_dom = __toESM(react_dom);
15645
16142
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15646
16143
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15647
16144
  let group;
16145
+ /**
16146
+ * Floating relocates panels exactly like a drop onto the grid does, so
16147
+ * it owes the caller an `onDidMovePanel` per panel. The events are
16148
+ * deferred until the window is mounted below: only then do the panels
16149
+ * report their final `floating` location.
16150
+ */
16151
+ let movedPanels = [];
15648
16152
  if (item instanceof DockviewPanel) {
16153
+ const sourceGroup = item.group;
15649
16154
  group = this.createGroup();
15650
16155
  this._onDidAddGroup.fire(group);
15651
16156
  this.movingLock(() => this.removePanel(item, {
@@ -15654,6 +16159,10 @@ react_dom = __toESM(react_dom);
15654
16159
  skipSetActiveGroup: true
15655
16160
  }));
15656
16161
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16162
+ movedPanels = [{
16163
+ panel: item,
16164
+ from: sourceGroup
16165
+ }];
15657
16166
  } else {
15658
16167
  var _this$_popoutWindowSe8;
15659
16168
  group = item;
@@ -15670,11 +16179,21 @@ react_dom = __toESM(react_dom);
15670
16179
  });
15671
16180
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15672
16181
  group = popoutReferenceGroup;
15673
- } else this.doRemoveGroup(item, {
15674
- skipDispose: true,
15675
- skipPopoutReturn: true,
15676
- skipPopoutAssociated: false
15677
- });
16182
+ movedPanels = group.panels.map((panel) => ({
16183
+ panel,
16184
+ from: item
16185
+ }));
16186
+ } else {
16187
+ this.doRemoveGroup(item, {
16188
+ skipDispose: true,
16189
+ skipPopoutReturn: true,
16190
+ skipPopoutAssociated: false
16191
+ });
16192
+ movedPanels = group.panels.map((panel) => ({
16193
+ panel,
16194
+ from: group
16195
+ }));
16196
+ }
15678
16197
  }
15679
16198
  function getAnchoredBox() {
15680
16199
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15707,6 +16226,7 @@ react_dom = __toESM(react_dom);
15707
16226
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15708
16227
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15709
16228
  });
16229
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15710
16230
  }
15711
16231
  /**
15712
16232
  * Build an empty gridview configured to match the main grid's styling, for
@@ -15851,6 +16371,13 @@ react_dom = __toESM(react_dom);
15851
16371
  position
15852
16372
  };
15853
16373
  group.model.headerPosition = position;
16374
+ const resizeDisposable = group.onDidChange((event) => {
16375
+ var _this$_shellManager3;
16376
+ if (!event) return;
16377
+ const size = position === "left" || position === "right" ? event.width : event.height;
16378
+ if (typeof size !== "number") return;
16379
+ (_this$_shellManager3 = this._shellManager) === null || _this$_shellManager3 === void 0 || _this$_shellManager3.resizeEdgeGroup(position, size);
16380
+ });
15854
16381
  const autoCollapseDisposable = group.model.onDidRemovePanel(() => {
15855
16382
  if (!group.model.isEmpty) return;
15856
16383
  if (service.isAutoReveal(group)) queueMicrotask(() => {
@@ -15859,7 +16386,7 @@ react_dom = __toESM(react_dom);
15859
16386
  });
15860
16387
  else this.setEdgeGroupCollapsed(group, true);
15861
16388
  });
15862
- service.add(position, group, autoCollapseDisposable);
16389
+ service.add(position, group, new CompositeDisposable(autoCollapseDisposable, resizeDisposable));
15863
16390
  if (options.autoHide !== void 0) service.setAutoHide(group, options.autoHide);
15864
16391
  if (options.autoReveal !== void 0) service.setAutoReveal(group, options.autoReveal);
15865
16392
  this._onDidAddGroup.fire(group);
@@ -15954,8 +16481,8 @@ react_dom = __toESM(react_dom);
15954
16481
  }
15955
16482
  /** The size an edge group expands to (pre-collapse); sizes the peek. */
15956
16483
  getEdgeGroupExpandedSize(position) {
15957
- var _this$_shellManager$g, _this$_shellManager3;
15958
- return (_this$_shellManager$g = (_this$_shellManager3 = this._shellManager) === null || _this$_shellManager3 === void 0 ? void 0 : _this$_shellManager3.getEdgeGroupExpandedSize(position)) !== null && _this$_shellManager$g !== void 0 ? _this$_shellManager$g : 0;
16484
+ var _this$_shellManager$g, _this$_shellManager4;
16485
+ return (_this$_shellManager$g = (_this$_shellManager4 = this._shellManager) === null || _this$_shellManager4 === void 0 ? void 0 : _this$_shellManager4.getEdgeGroupExpandedSize(position)) !== null && _this$_shellManager$g !== void 0 ? _this$_shellManager$g : 0;
15959
16486
  }
15960
16487
  /** Reposition a single `renderer:'always'` panel's overlay over its
15961
16488
  * reference container, optionally forcing it visible (the auto-hide peek
@@ -16138,35 +16665,119 @@ react_dom = __toESM(react_dom);
16138
16665
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16139
16666
  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");
16140
16667
  const existingPanels = /* @__PURE__ */ new Map();
16141
- let tempGroup;
16668
+ const temporaryGroups = /* @__PURE__ */ new Map();
16669
+ const stagedPanels = [];
16670
+ const temporaryGroupDisposables = [];
16671
+ /**
16672
+ * The staging groups are detached from `_groups` and never enter the
16673
+ * grid, so nothing else will ever tear them down.
16674
+ *
16675
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16676
+ * throwing renderer must not abort the rest of the cleanup — that would
16677
+ * re-introduce the very leak this reclaims — nor replace the
16678
+ * deserialization error the caller is being given. Draining the list
16679
+ * also makes this safe to call more than once.
16680
+ */
16681
+ const disposeTemporaryGroups = () => {
16682
+ for (const disposable of temporaryGroupDisposables) try {
16683
+ disposable.dispose();
16684
+ } catch (err) {
16685
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16686
+ }
16687
+ temporaryGroupDisposables.length = 0;
16688
+ };
16142
16689
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16143
16690
  /**
16144
- * What are we doing here?
16145
- *
16146
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16147
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16691
+ * Visible, always-rendered panels need individual staging groups
16692
+ * to remain active. Other reused panels can share a staging group.
16693
+ * The staging groups are excluded from the layout clear below.
16148
16694
  */
16149
- tempGroup = this.createGroup();
16150
- this._groups.delete(tempGroup.api.id);
16151
16695
  const newPanels = Object.keys(data.panels);
16152
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16153
- this.movingLock(() => {
16154
- Array.from(existingPanels.values()).forEach((panel) => {
16155
- this.moveGroupOrPanel({
16156
- from: {
16157
- groupId: panel.api.group.api.id,
16158
- panelId: panel.api.id
16159
- },
16160
- to: {
16161
- group: tempGroup,
16162
- position: "center"
16163
- },
16164
- keepEmptyGroups: true
16696
+ let sharedTemporaryGroup;
16697
+ const createTemporaryGroup = () => {
16698
+ const temporaryGroup = this.createGroup();
16699
+ /**
16700
+ * Removing the group from `_groups` also drops the record's
16701
+ * `CompositeDisposable`, so capture it first and tear both it
16702
+ * and the group down in the `finally` below. Left undisposed a
16703
+ * staging group leaks its `ResizeObserver`, its
16704
+ * `onDidOptionsChange` subscription (retained by this
16705
+ * component's emitter) and the watermark its model mounts when
16706
+ * the group empties — once per reused panel, per `fromJSON`.
16707
+ */
16708
+ const record = this._groups.get(temporaryGroup.api.id);
16709
+ this._groups.delete(temporaryGroup.api.id);
16710
+ temporaryGroupDisposables.push(Disposable.from(() => {
16711
+ /**
16712
+ * Reclaim the group's own resources, never its panels.
16713
+ * Staging is driven by the ids in `data.panels` while
16714
+ * reclaiming is driven by the group `views` that
16715
+ * reference them, so a panel whose state is present but
16716
+ * unreferenced is still staged here — and `dispose()`
16717
+ * on a non-empty group reaches the consumer's
16718
+ * `IContentRenderer.dispose()`. Emptying the group
16719
+ * first leaves those panels exactly as they were before
16720
+ * this teardown existed, which also keeps this safe
16721
+ * against any future path that rebuilds a panel under
16722
+ * an id that is live in the new layout.
16723
+ */
16724
+ this.movingLock(() => {
16725
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16726
+ });
16727
+ record === null || record === void 0 || record.disposable.dispose();
16728
+ temporaryGroup.dispose();
16729
+ }));
16730
+ return temporaryGroup;
16731
+ };
16732
+ /**
16733
+ * Staging and the clear below run before the deserialization
16734
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16735
+ * renderer teardown during `clear`) would escape without reclaiming
16736
+ * the staging groups already created.
16737
+ *
16738
+ * The creation loop is inside the guard too, not just the moves and
16739
+ * the clear: `createGroup()` reaches consumer code of its own —
16740
+ * `initialize()` mounts the watermark through
16741
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16742
+ * otherwise strand the n-1 staging groups already built.
16743
+ */
16744
+ try {
16745
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16746
+ existingPanels.set(panel.api.id, panel);
16747
+ let temporaryGroup;
16748
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16749
+ else {
16750
+ var _sharedTemporaryGroup;
16751
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16752
+ temporaryGroup = sharedTemporaryGroup;
16753
+ }
16754
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16755
+ stagedPanels.push({
16756
+ panel,
16757
+ temporaryGroup
16758
+ });
16759
+ }
16760
+ this.movingLock(() => {
16761
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
16762
+ this.moveGroupOrPanel({
16763
+ from: {
16764
+ groupId: panel.api.group.api.id,
16765
+ panelId: panel.api.id
16766
+ },
16767
+ to: {
16768
+ group: temporaryGroup,
16769
+ position: "center"
16770
+ },
16771
+ keepEmptyGroups: true
16772
+ });
16165
16773
  });
16166
16774
  });
16167
- });
16168
- }
16169
- this.clear();
16775
+ this.clear();
16776
+ } catch (err) {
16777
+ disposeTemporaryGroups();
16778
+ throw err;
16779
+ }
16780
+ } else this.clear();
16170
16781
  const { grid, panels, activeGroup } = data;
16171
16782
  try {
16172
16783
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16198,9 +16809,10 @@ react_dom = __toESM(react_dom);
16198
16809
  * due to a corruption of input data.
16199
16810
  */
16200
16811
  const existingPanel = existingPanels.get(child);
16201
- if (tempGroup && existingPanel) {
16812
+ const temporaryGroup = temporaryGroups.get(child);
16813
+ if (temporaryGroup && existingPanel) {
16202
16814
  this.movingLock(() => {
16203
- tempGroup.model.removePanel(existingPanel);
16815
+ temporaryGroup.model.removePanel(existingPanel);
16204
16816
  });
16205
16817
  createdPanels.push(existingPanel);
16206
16818
  existingPanel.updateFromStateModel(panels[child]);
@@ -16230,7 +16842,7 @@ react_dom = __toESM(react_dom);
16230
16842
  return createGroupFromSerializedState(node.data);
16231
16843
  } });
16232
16844
  this._layoutFromShell(width, height);
16233
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
16845
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16234
16846
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16235
16847
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16236
16848
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16267,6 +16879,8 @@ react_dom = __toESM(react_dom);
16267
16879
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16268
16880
  */
16269
16881
  throw err;
16882
+ } finally {
16883
+ disposeTemporaryGroups();
16270
16884
  }
16271
16885
  this.debouncedUpdateAllPositions();
16272
16886
  this._onDidLayoutFromJSON.fire();
@@ -16289,7 +16903,7 @@ react_dom = __toESM(react_dom);
16289
16903
  members
16290
16904
  };
16291
16905
  }
16292
- deserializeEdgeGroups(edgeGroups, panels) {
16906
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16293
16907
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16294
16908
  if (!edgeService) return;
16295
16909
  for (const _position of [
@@ -16319,13 +16933,40 @@ react_dom = __toESM(react_dom);
16319
16933
  if (groupState) {
16320
16934
  const { views, activeView } = groupState;
16321
16935
  const createdPanels = [];
16322
- for (const panelId of views) if (panels[panelId]) {
16323
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16324
- createdPanels.push(panel);
16936
+ for (const panelId of views) {
16937
+ if (!panels[panelId]) continue;
16938
+ /**
16939
+ * Reclaim a staged panel rather than rebuilding it, the
16940
+ * same way the grid path does. Deserializing here instead
16941
+ * would honour `reuseExistingPanels` everywhere except edge
16942
+ * groups: the live panel keeps sitting in its staging group
16943
+ * while a second panel is built under the same id, so the
16944
+ * consumer's renderer for the original is orphaned — never
16945
+ * disposed, and its element left inside the id-keyed
16946
+ * overlay entry the replacement now shares.
16947
+ */
16948
+ const existingPanel = existingPanels.get(panelId);
16949
+ const temporaryGroup = temporaryGroups.get(panelId);
16950
+ if (temporaryGroup && existingPanel) {
16951
+ this.movingLock(() => {
16952
+ temporaryGroup.model.removePanel(existingPanel);
16953
+ });
16954
+ createdPanels.push(existingPanel);
16955
+ existingPanel.updateFromStateModel(panels[panelId]);
16956
+ } else {
16957
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16958
+ createdPanels.push(panel);
16959
+ }
16325
16960
  }
16326
16961
  for (const panel of createdPanels) {
16327
16962
  const isActive = activeView === panel.id;
16328
- edgeGroup.model.openPanel(panel, {
16963
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
16964
+ edgeGroup.model.openPanel(panel, {
16965
+ skipSetActive: !isActive,
16966
+ skipSetGroupActive: true
16967
+ });
16968
+ });
16969
+ else edgeGroup.model.openPanel(panel, {
16329
16970
  skipSetActive: !isActive,
16330
16971
  skipSetGroupActive: true
16331
16972
  });
@@ -16749,22 +17390,100 @@ react_dom = __toESM(react_dom);
16749
17390
  * outermost mutation.
16750
17391
  */
16751
17392
  mutation(kind, func) {
16752
- const outer = this._mutationDepth === 0;
17393
+ const close = this.openMutation(kind);
17394
+ try {
17395
+ return func();
17396
+ } finally {
17397
+ close();
17398
+ }
17399
+ }
17400
+ /**
17401
+ * `mutation()` for an operation whose work continues after the synchronous
17402
+ * call returns. The transaction stays open until the returned promise
17403
+ * settles, so everything the operation does - the group it adds, the panels
17404
+ * it relocates - lands inside the bracket, matching the synchronous
17405
+ * move / float paths.
17406
+ */
17407
+ mutationAsync(kind, func) {
17408
+ var _this = this;
17409
+ return _asyncToGenerator(function* () {
17410
+ const close = _this.openMutation(kind);
17411
+ try {
17412
+ return yield func();
17413
+ } finally {
17414
+ close();
17415
+ }
17416
+ })();
17417
+ }
17418
+ /**
17419
+ * Open a transaction and return the function that closes it. Shared by the
17420
+ * synchronous and asynchronous brackets; the returned function must be
17421
+ * called exactly once.
17422
+ *
17423
+ * Both ends key off the depth counter reaching zero rather than off which
17424
+ * bracket opened first. For synchronous nesting the two are the same thing
17425
+ * - brackets close in the order they opened - but asynchronous transactions
17426
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17427
+ * once, or a restore staggering several), and there the first to open is
17428
+ * not the last to close. Reporting on the opener would fire `didMutate`
17429
+ * while the other transaction was still doing structural work, which is the
17430
+ * very thing the async bracket exists to prevent. Overlapping transactions
17431
+ * therefore report as one, tagged with the kind of the last to finish.
17432
+ */
17433
+ openMutation(kind) {
16753
17434
  const origin = this._origin;
16754
- if (outer) this._onWillMutateLayout.fire({
17435
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16755
17436
  kind,
16756
17437
  origin
16757
17438
  });
16758
17439
  this._mutationDepth++;
16759
- try {
16760
- return func();
16761
- } finally {
17440
+ return () => {
16762
17441
  this._mutationDepth--;
16763
- if (outer) this._onDidMutateLayout.fire({
16764
- kind,
16765
- origin
16766
- });
17442
+ if (this._mutationDepth === 0) {
17443
+ this.flushLocationChanges();
17444
+ this._onDidMutateLayout.fire({
17445
+ kind,
17446
+ origin
17447
+ });
17448
+ }
17449
+ };
17450
+ }
17451
+ /**
17452
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17453
+ * transaction, or fire it immediately when no transaction is in flight.
17454
+ *
17455
+ * Relocating a panel touches its location twice: once as it is reparented
17456
+ * into the destination group - which, when that group has just been created
17457
+ * for a floating or popout window, has not been told where it lives yet -
17458
+ * and once as the group is tagged with its final location. Reporting each
17459
+ * signal as it happens therefore leaks an intermediate `grid` location the
17460
+ * panel was never in, at a moment when it belongs to neither group's panel
17461
+ * list and so is missing from `api.panels`.
17462
+ *
17463
+ * Deferring collapses those signals into a single event carrying the
17464
+ * settled location. Note the signals are deliberately *not* compared
17465
+ * against the panel's previous location: a panel can move between two
17466
+ * floating windows without its location type changing, and this event is
17467
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17468
+ * z-index against its new host window.
17469
+ */
17470
+ deferLocationChange(key, fire) {
17471
+ if (this._mutationDepth === 0) {
17472
+ fire();
17473
+ return;
16767
17474
  }
17475
+ this._pendingLocationChanges.set(key, fire);
17476
+ }
17477
+ /**
17478
+ * Report every panel whose location moved during the transaction that has
17479
+ * just closed. Each callback reads the panel's location at this point, so
17480
+ * what it reports is where the panel ended up.
17481
+ */
17482
+ flushLocationChanges() {
17483
+ if (this._pendingLocationChanges.size === 0) return;
17484
+ const pending = Array.from(this._pendingLocationChanges.values());
17485
+ this._pendingLocationChanges.clear();
17486
+ for (const fire of pending) fire();
16768
17487
  }
16769
17488
  /**
16770
17489
  * The origin of the operation currently in progress (`'user'` by default).
@@ -16805,6 +17524,18 @@ react_dom = __toESM(react_dom);
16805
17524
  origin: this._origin
16806
17525
  });
16807
17526
  }
17527
+ /**
17528
+ * Announce that `panel` has finished relocating. Must be called once the
17529
+ * move has settled: `to` is read from the panel's current group, so an
17530
+ * early call would report the source group as the destination.
17531
+ */
17532
+ fireDidMovePanel(panel, from) {
17533
+ this._onDidMovePanel.fire({
17534
+ panel,
17535
+ from,
17536
+ to: panel.group
17537
+ });
17538
+ }
16808
17539
  moveGroupOrPanel(options) {
16809
17540
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
16810
17541
  }
@@ -16865,10 +17596,7 @@ react_dom = __toESM(react_dom);
16865
17596
  });
16866
17597
  });
16867
17598
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
16868
- this._onDidMovePanel.fire({
16869
- panel: removedPanel,
16870
- from: sourceGroup
16871
- });
17599
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16872
17600
  } else {
16873
17601
  /**
16874
17602
  * Dropping a panel to the extremities of a group which will place that panel
@@ -16887,10 +17615,7 @@ react_dom = __toESM(react_dom);
16887
17615
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
16888
17616
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
16889
17617
  this.gridview.moveView(sourceParentLocation, from, to);
16890
- this._onDidMovePanel.fire({
16891
- panel: this.getGroupPanel(sourceItemId),
16892
- from: sourceGroup
16893
- });
17618
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16894
17619
  return;
16895
17620
  }
16896
17621
  }
@@ -16918,13 +17643,10 @@ react_dom = __toESM(react_dom);
16918
17643
  }));
16919
17644
  this.doRemoveGroup(sourceGroup, { skipActive: true });
16920
17645
  const updatedTargetLocation = getRelativeLocation(destinationGridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
16921
- const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17646
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation, this.dropSizing(getGridLocation(destinationGroup.element)), void 0, destinationGridview);
16922
17647
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
16923
17648
  this.doSetGroupAndPanelActive(newGroup);
16924
- this._onDidMovePanel.fire({
16925
- panel: this.getGroupPanel(sourceItemId),
16926
- from: sourceGroup
16927
- });
17649
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16928
17650
  return;
16929
17651
  }
16930
17652
  if (sourceGroup.api.location.type === "edge") {
@@ -16938,13 +17660,10 @@ react_dom = __toESM(react_dom);
16938
17660
  skipSetActiveGroup: true
16939
17661
  }));
16940
17662
  if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
16941
- const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17663
+ const newGroup = this.createGroupAtLocation(targetLocation, this.dropSizing(referenceLocation), void 0, destinationGridview);
16942
17664
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16943
17665
  this.doSetGroupAndPanelActive(newGroup);
16944
- this._onDidMovePanel.fire({
16945
- panel: removedPanel,
16946
- from: sourceGroup
16947
- });
17666
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16948
17667
  return;
16949
17668
  }
16950
17669
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -16953,13 +17672,10 @@ react_dom = __toESM(react_dom);
16953
17672
  }));
16954
17673
  const updatedReferenceLocation = getGridLocation(destinationGroup.element);
16955
17674
  const location = getRelativeLocation(destinationGridview.orientation, updatedReferenceLocation, destinationTarget);
16956
- this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17675
+ this.movingLock(() => this.doAddGroup(targetGroup, location, this.dropSizing(updatedReferenceLocation), destinationGridview));
16957
17676
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
16958
17677
  this.doSetGroupAndPanelActive(targetGroup);
16959
- this._onDidMovePanel.fire({
16960
- panel: this.getGroupPanel(sourceItemId),
16961
- from: sourceGroup
16962
- });
17678
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
16963
17679
  } else {
16964
17680
  /**
16965
17681
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -16971,13 +17687,10 @@ react_dom = __toESM(react_dom);
16971
17687
  }));
16972
17688
  if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
16973
17689
  const dropLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
16974
- const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17690
+ const group = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation), void 0, destinationGridview);
16975
17691
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
16976
17692
  this.doSetGroupAndPanelActive(group);
16977
- this._onDidMovePanel.fire({
16978
- panel: removedPanel,
16979
- from: sourceGroup
16980
- });
17693
+ this.fireDidMovePanel(removedPanel, sourceGroup);
16981
17694
  }
16982
17695
  }
16983
17696
  }
@@ -17012,16 +17725,13 @@ react_dom = __toESM(react_dom);
17012
17725
  });
17013
17726
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17014
17727
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17015
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17016
- panel,
17017
- from: sourceGroup
17018
- });
17728
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17019
17729
  };
17020
17730
  let targetGroup;
17021
17731
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
17022
17732
  else {
17023
17733
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
17024
- targetGroup = this.createGroupAtLocation(dropLocation);
17734
+ targetGroup = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation));
17025
17735
  }
17026
17736
  if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
17027
17737
  addPanelsToGroup(targetGroup);
@@ -17036,10 +17746,12 @@ react_dom = __toESM(react_dom);
17036
17746
  this.mutation("maximize", () => super.exitMaximizedGroup());
17037
17747
  }
17038
17748
  _doMoveGroup(options) {
17749
+ var _mergedPanels;
17039
17750
  const from = options.from.group;
17040
17751
  const to = options.to.group;
17041
17752
  const target = options.to.position;
17042
17753
  let source = from;
17754
+ let mergedPanels;
17043
17755
  if (target === "center") {
17044
17756
  const activePanel = from.activePanel;
17045
17757
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17057,6 +17769,7 @@ react_dom = __toESM(react_dom);
17057
17769
  skipSetGroupActive: true
17058
17770
  });
17059
17771
  });
17772
+ mergedPanels = panels;
17060
17773
  for (const snapshot of tabGroupSnapshots) {
17061
17774
  const newTabGroup = to.model.createTabGroup({
17062
17775
  label: snapshot.label,
@@ -17069,6 +17782,21 @@ react_dom = __toESM(react_dom);
17069
17782
  if (options.skipSetActive !== true) this.doSetGroupAndPanelActive(to);
17070
17783
  else if (!this.activePanel) this.doSetGroupAndPanelActive(to);
17071
17784
  } else {
17785
+ const isReorderWithinBranch = (() => {
17786
+ if (from.api.location.type === "edge" || to.api.location.type === "edge") return false;
17787
+ const root = this.getGridviewForGroup(from);
17788
+ if (root !== this.getGridviewForGroup(to)) return false;
17789
+ let fromLocation;
17790
+ let toLocation;
17791
+ try {
17792
+ fromLocation = getGridLocation(from.element);
17793
+ toLocation = getGridLocation(to.element);
17794
+ } catch (_unused) {
17795
+ return false;
17796
+ }
17797
+ if (fromLocation.length === 0 || toLocation.length === 0) return false;
17798
+ return sequenceEquals(tail(fromLocation)[0], tail(toLocation)[0]) && getLocationOrientation(root.orientation, toLocation) === getDirectionOrientation(target);
17799
+ })();
17072
17800
  if (from.api.location.type === "edge") {
17073
17801
  /**
17074
17802
  * Edge groups are permanent structural elements and must
@@ -17134,23 +17862,17 @@ react_dom = __toESM(react_dom);
17134
17862
  const referenceLocation = getGridLocation(to.element);
17135
17863
  const dropLocation = getRelativeLocation(destGridview.orientation, referenceLocation, target);
17136
17864
  let size;
17137
- switch (destGridview.orientation) {
17138
- case "VERTICAL":
17139
- size = referenceLocation.length % 2 == 0 ? from.api.width : from.api.height;
17140
- break;
17141
- case "HORIZONTAL":
17142
- size = referenceLocation.length % 2 == 0 ? from.api.height : from.api.width;
17143
- break;
17865
+ if (isReorderWithinBranch) size = getDirectionOrientation(target) === "HORIZONTAL" ? from.api.width : from.api.height;
17866
+ else {
17867
+ var _referenceLocation;
17868
+ size = Sizing.Split((_referenceLocation = referenceLocation[referenceLocation.length - 1]) !== null && _referenceLocation !== void 0 ? _referenceLocation : 0);
17144
17869
  }
17145
17870
  destGridview.addView(source, size, dropLocation);
17146
17871
  this.setGroupLocationForRoot(source, destGridview);
17147
17872
  }
17148
17873
  }
17149
- source.panels.forEach((panel) => {
17150
- this._onDidMovePanel.fire({
17151
- panel,
17152
- from
17153
- });
17874
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
17875
+ this.fireDidMovePanel(panel, from);
17154
17876
  });
17155
17877
  this.debouncedUpdateAllPositions();
17156
17878
  if (options.skipSetActive === false) {
@@ -17268,6 +17990,16 @@ react_dom = __toESM(react_dom);
17268
17990
  });
17269
17991
  return panel;
17270
17992
  }
17993
+ /**
17994
+ * Sizing for a group created by a drop next to `referenceLocation`: half
17995
+ * of the group the overlay was drawn over, leaving its siblings alone, so
17996
+ * the panel lands in the region the overlay indicated (#1612). Gridview
17997
+ * rewrites the index to 0 when a cross-axis drop wraps the reference in a
17998
+ * new branch, so one value serves both paths.
17999
+ */
18000
+ dropSizing(referenceLocation) {
18001
+ return Sizing.Split(referenceLocation.length === 0 ? 0 : referenceLocation[referenceLocation.length - 1]);
18002
+ }
17271
18003
  createGroupAtLocation(location, size, options, gridview = this.gridview) {
17272
18004
  const group = this.createGroup(options);
17273
18005
  this.doAddGroup(group, location, size, gridview);
@@ -17327,22 +18059,22 @@ react_dom = __toESM(react_dom);
17327
18059
  return location.length % 2 == 1 ? rootOrientation : orthogonal(rootOrientation);
17328
18060
  }
17329
18061
  updateTheme() {
17330
- var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe23, _this$_popoutWindowSe24, _this$_shellManager4, _theme$edgeGroupColla, _theme$tabGroupIndica;
18062
+ var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe23, _this$_popoutWindowSe24, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
17331
18063
  const theme = (_this$_options$theme = this._options.theme) !== null && _this$_options$theme !== void 0 ? _this$_options$theme : themeAbyss;
17332
18064
  (_this$_shellThemeClas = this._shellThemeClassnames) === null || _this$_shellThemeClas === void 0 || _this$_shellThemeClas.setClassNames(theme.className);
17333
18065
  const gap = (_theme$gap = theme.gap) !== null && _theme$gap !== void 0 ? _theme$gap : 0;
17334
18066
  this.gridview.margin = gap;
17335
18067
  for (const floating of this.floatingGroups) floating.gridview.margin = gap;
17336
18068
  for (const entry of (_this$_popoutWindowSe23 = (_this$_popoutWindowSe24 = this._popoutWindowService) === null || _this$_popoutWindowSe24 === void 0 ? void 0 : _this$_popoutWindowSe24.entries) !== null && _this$_popoutWindowSe23 !== void 0 ? _this$_popoutWindowSe23 : []) entry.gridview.margin = gap;
17337
- (_this$_shellManager4 = this._shellManager) === null || _this$_shellManager4 === void 0 || _this$_shellManager4.updateTheme(gap, (_theme$edgeGroupColla = theme.edgeGroupCollapsedSize) !== null && _theme$edgeGroupColla !== void 0 ? _theme$edgeGroupColla : 35);
18069
+ (_this$_shellManager5 = this._shellManager) === null || _this$_shellManager5 === void 0 || _this$_shellManager5.updateTheme(gap, (_theme$edgeGroupColla = theme.edgeGroupCollapsedSize) !== null && _theme$edgeGroupColla !== void 0 ? _theme$edgeGroupColla : 35);
17338
18070
  if (theme.dndOverlayBorder === void 0) {
17339
- var _this$_shellManager5;
18071
+ var _this$_shellManager6;
17340
18072
  this.element.style.removeProperty("--dv-drag-over-border");
17341
- (_this$_shellManager5 = this._shellManager) === null || _this$_shellManager5 === void 0 || _this$_shellManager5.element.style.removeProperty("--dv-drag-over-border");
18073
+ (_this$_shellManager6 = this._shellManager) === null || _this$_shellManager6 === void 0 || _this$_shellManager6.element.style.removeProperty("--dv-drag-over-border");
17342
18074
  } else {
17343
- var _this$_shellManager6;
18075
+ var _this$_shellManager7;
17344
18076
  this.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
17345
- (_this$_shellManager6 = this._shellManager) === null || _this$_shellManager6 === void 0 || _this$_shellManager6.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
18077
+ (_this$_shellManager7 = this._shellManager) === null || _this$_shellManager7 === void 0 || _this$_shellManager7.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
17346
18078
  }
17347
18079
  switch (theme.dndOverlayMounting) {
17348
18080
  case "absolute":
@@ -19196,6 +19928,8 @@ exports.AdvancedDnDModule = AdvancedDnDModule;
19196
19928
  exports.AdvancedDnDService = AdvancedDnDService;
19197
19929
  exports.BaseGrid = BaseGrid;
19198
19930
  exports.ContentContainer = ContentContainer;
19931
+ exports.ContextMenuController = ContextMenuController;
19932
+ exports.ContextMenuModule = ContextMenuModule;
19199
19933
  exports.DEFAULT_TAB_GROUP_COLORS = DEFAULT_TAB_GROUP_COLORS;
19200
19934
  exports.DefaultDockviewDeserialzier = DefaultDockviewDeserialzier;
19201
19935
  exports.DefaultTab = DefaultTab;