dockview-core 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-core
3
- * @version 8.1.0
3
+ * @version 8.3.0
4
4
  * @link https://github.com/dockview/dockview
5
5
  * @license MIT
6
6
  */
@@ -1050,8 +1050,14 @@ var Splitview = class {
1050
1050
  container.appendChild(view.element);
1051
1051
  let viewSize;
1052
1052
  if (typeof size === "number") viewSize = size;
1053
- else if (size.type === "split") viewSize = this.getViewSize(size.index) / 2;
1054
- else if (size.type === "invisible") viewSize = { cachedVisibleSize: size.cachedVisibleSize };
1053
+ else if (size.type === "split") {
1054
+ const splitTarget = this.viewItems[size.index];
1055
+ if (splitTarget) {
1056
+ const half = Math.floor(splitTarget.size / 2);
1057
+ viewSize = splitTarget.size - half;
1058
+ splitTarget.size = half;
1059
+ } else viewSize = this.getViewSize(size.index) / 2;
1060
+ } else if (size.type === "invisible") viewSize = { cachedVisibleSize: size.cachedVisibleSize };
1055
1061
  else viewSize = view.minimumSize;
1056
1062
  const disposable = view.onDidChange((newSize) => this.onDidChange(viewItem, newSize.size));
1057
1063
  const viewItem = new ViewItem(container, view, viewSize, { dispose: () => {
@@ -5796,7 +5802,7 @@ function resolveDndCapabilities(options) {
5796
5802
  };
5797
5803
  case "auto":
5798
5804
  case void 0:
5799
- default: return isCoarsePrimaryInput$1() ? {
5805
+ default: return isCoarsePrimaryInput$2() ? {
5800
5806
  html5: false,
5801
5807
  pointer: true,
5802
5808
  pointerHandlesMouse: true
@@ -5807,7 +5813,7 @@ function resolveDndCapabilities(options) {
5807
5813
  };
5808
5814
  }
5809
5815
  }
5810
- function isCoarsePrimaryInput$1() {
5816
+ function isCoarsePrimaryInput$2() {
5811
5817
  if (globalThis.window === void 0 || !globalThis.matchMedia) return false;
5812
5818
  const coarse = globalThis.matchMedia("(pointer: coarse)").matches;
5813
5819
  const fine = globalThis.matchMedia("(pointer: fine)").matches;
@@ -7962,11 +7968,28 @@ var TabReorderController = class extends CompositeDisposable {
7962
7968
  if (this._voidContainer && related && (related === this._voidContainer || this._voidContainer.contains(related))) return;
7963
7969
  this.resetTabTransforms();
7964
7970
  if (this._animState.sourceIndex === -1) {
7965
- var _this$group$model$dro2;
7966
- (_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();
7971
+ this.clearHeaderAnchorOverlay();
7967
7972
  this._animState = null;
7968
7973
  } else this._animState.currentInsertionIndex = null;
7969
7974
  }
7975
+ /**
7976
+ * Clear the anchored overlay only when this header is what rendered it.
7977
+ * The container is shared with the group's content drop target, which
7978
+ * renders into it on the same frame the cursor leaves the strip: the
7979
+ * pointer backend calls `handleDragOver` on the new target before the
7980
+ * global `onDragMove` that reaches us (#1612).
7981
+ */
7982
+ clearHeaderAnchorOverlay() {
7983
+ var _container$model;
7984
+ const container = this.group.model.dropTargetContainer;
7985
+ const outline = container === null || container === void 0 ? void 0 : container.renderedOutline;
7986
+ if (outline && ![
7987
+ this._tabsList,
7988
+ this._voidContainer,
7989
+ this._extendedDropZone
7990
+ ].some((scope) => scope === null || scope === void 0 ? void 0 : scope.contains(outline))) return;
7991
+ container === null || container === void 0 || (_container$model = container.model) === null || _container$model === void 0 || _container$model.clear();
7992
+ }
7970
7993
  handleDragOver(event) {
7971
7994
  var _this$accessor$option2;
7972
7995
  if (!this._animState) return;
@@ -10522,7 +10545,11 @@ var DockviewGroupPanelModel = class extends CompositeDisposable {
10522
10545
  this.panels.forEach((panel) => {
10523
10546
  this.rerender(panel);
10524
10547
  });
10525
- if (this._activePanel) this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10548
+ if (this._activePanel) {
10549
+ this.contentContainer.renderPanel(this._activePanel, { asActive: true });
10550
+ const { width, height } = this.contentDimensions();
10551
+ this._activePanel.layout(width, height);
10552
+ }
10526
10553
  }
10527
10554
  get renderContainer() {
10528
10555
  var _this$_overwriteRende;
@@ -11487,7 +11514,7 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11487
11514
  this._group = value;
11488
11515
  this._onDidGroupChange.fire({});
11489
11516
  this.setupGroupEventListeners(oldGroup);
11490
- this._onDidLocationChange.fire({ location: this.group.api.location });
11517
+ this.fireLocationChange();
11491
11518
  }
11492
11519
  }
11493
11520
  get group() {
@@ -11563,6 +11590,23 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11563
11590
  exitMaximized() {
11564
11591
  this.group.api.exitMaximized();
11565
11592
  }
11593
+ /**
11594
+ * Report that this panel's location may have moved.
11595
+ *
11596
+ * A relocation touches the location more than once - the panel is
11597
+ * reparented into the destination group, then that group is tagged with
11598
+ * the location it ends up at - so the event is coalesced to the end of the
11599
+ * enclosing layout mutation and reports where the panel actually settled.
11600
+ * Firing each signal as it happened surfaced an intermediate `grid`
11601
+ * location the panel was never in, at a point where it was in neither
11602
+ * group's panel list. Outside a mutation this fires straight away.
11603
+ */
11604
+ fireLocationChange() {
11605
+ this.accessor.deferLocationChange(this, () => {
11606
+ if (this.isDisposed) return;
11607
+ this._onDidLocationChange.fire({ location: this.location });
11608
+ });
11609
+ }
11566
11610
  setupGroupEventListeners(previousGroup) {
11567
11611
  var _previousGroup$isActi;
11568
11612
  let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
@@ -11571,9 +11615,9 @@ var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
11571
11615
  const hasBecomeVisible = event.isVisible && !this.isVisible;
11572
11616
  const isActivePanel = this.group.model.isPanelActive(this.panel);
11573
11617
  if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
11574
- }), this.group.api.onDidLocationChange((event) => {
11618
+ }), this.group.api.onDidLocationChange(() => {
11575
11619
  if (this.group !== this.panel.group) return;
11576
- this._onDidLocationChange.fire(event);
11620
+ this.fireLocationChange();
11577
11621
  }), this.group.api.onDidActiveChange(() => {
11578
11622
  if (this.group !== this.panel.group) return;
11579
11623
  if (_trackGroupActive !== this.isGroupActive) {
@@ -12375,7 +12419,6 @@ const ENTERPRISE_MODULE_NAMES = /* @__PURE__ */ new Set([
12375
12419
  "AdvancedOverflow",
12376
12420
  "AutoEdgeGroup",
12377
12421
  "AutoHideEdgeGroup",
12378
- "ContextMenu",
12379
12422
  "DndCompass",
12380
12423
  "KeyboardDocking",
12381
12424
  "KeyboardNavigation",
@@ -12457,8 +12500,8 @@ function logMissingModule(moduleName, reason) {
12457
12500
  * case: calling `undo()` having never set the option at all.
12458
12501
  *
12459
12502
  * Interaction handlers are queries in this sense too: a right-click reaching an
12460
- * absent ContextMenu module means the app never asked for one, so it stays
12461
- * silent (`?.`) and the browser's own menu shows. Options are where intent is
12503
+ * absent module means the app never asked for that feature, so it stays silent
12504
+ * (`?.`) and the browser's own behaviour applies. Options are where intent is
12462
12505
  * declared; see `optionsModules.ts`.
12463
12506
  */
12464
12507
  function assertModule(service, moduleName, context) {
@@ -12538,10 +12581,9 @@ function clearRegisteredModules() {
12538
12581
  *
12539
12582
  * This is the "declared intent" diagnostic. It fires when the user asks for a
12540
12583
  * feature via options, at construction and on every `updateOptions`. It
12541
- * deliberately does *not* fire on user interaction: right-clicking without the
12542
- * ContextMenu module stays silent (the browser's own menu shows), because the
12543
- * `?.` service-slot call sites can't tell a missing module from a feature the
12544
- * app never wanted.
12584
+ * deliberately does *not* fire on user interaction: an interaction reaching an
12585
+ * absent module stays silent, because the `?.` service-slot call sites can't
12586
+ * tell a missing module from a feature the app never wanted.
12545
12587
  *
12546
12588
  * Rules are a flat list rather than a `Record<keyof DockviewOptions, ...>`
12547
12589
  * because dockview's options are nested and value-gated: `overflow.mode:
@@ -12637,18 +12679,6 @@ const OPTION_MODULE_RULES = [
12637
12679
  moduleName: "AutoEdgeGroup",
12638
12680
  when: (o) => isAnyEdgeGroupEnabled(o.dockToEdgeGroups)
12639
12681
  },
12640
- {
12641
- optionKey: "getTabContextMenuItems",
12642
- reason: "getTabContextMenuItems",
12643
- moduleName: "ContextMenu",
12644
- when: (o) => o.getTabContextMenuItems != null
12645
- },
12646
- {
12647
- optionKey: "getTabGroupChipContextMenuItems",
12648
- reason: "getTabGroupChipContextMenuItems",
12649
- moduleName: "ContextMenu",
12650
- when: (o) => o.getTabGroupChipContextMenuItems != null
12651
- },
12652
12682
  {
12653
12683
  optionKey: "dndCompass",
12654
12684
  reason: "dndCompass",
@@ -13549,6 +13579,270 @@ const TabGroupChipsModule = defineModule({
13549
13579
  }
13550
13580
  });
13551
13581
 
13582
+ //#endregion
13583
+ //#region src/dockview/contextMenuService.ts
13584
+ function popoverZIndexFor(target, group) {
13585
+ var _group$api, _group$api$isPeeking;
13586
+ 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)";
13587
+ if (!(target instanceof HTMLElement)) return;
13588
+ const relativeParent = findRelativeZIndexParent(target);
13589
+ return (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex) ? `calc(${relativeParent.style.zIndex} * 2)` : void 0;
13590
+ }
13591
+ let _nextId = 0;
13592
+ const nextContextMenuItemId = () => `dv-ctx-menu-item-${_nextId++}`;
13593
+ function isItemConfig(item) {
13594
+ return typeof item === "object";
13595
+ }
13596
+ function buildItem(label, close, action, disabled) {
13597
+ const el = document.createElement("div");
13598
+ el.className = "dv-context-menu-item";
13599
+ el.setAttribute("role", "menuitem");
13600
+ if (disabled) {
13601
+ el.classList.add("dv-context-menu-item--disabled");
13602
+ el.setAttribute("aria-disabled", "true");
13603
+ }
13604
+ el.textContent = label;
13605
+ if (!disabled) el.addEventListener("click", () => {
13606
+ action();
13607
+ close();
13608
+ });
13609
+ return el;
13610
+ }
13611
+ function buildSeparator() {
13612
+ const el = document.createElement("div");
13613
+ el.className = "dv-context-menu-separator";
13614
+ el.setAttribute("role", "separator");
13615
+ return el;
13616
+ }
13617
+ function isCoarsePrimaryInput$1() {
13618
+ if (globalThis.window === void 0 || !globalThis.matchMedia) return false;
13619
+ const coarse = globalThis.matchMedia("(pointer: coarse)").matches;
13620
+ const fine = globalThis.matchMedia("(pointer: fine)").matches;
13621
+ return coarse && !fine;
13622
+ }
13623
+ function buildRenameInput(tabGroup) {
13624
+ const wrapper = document.createElement("div");
13625
+ wrapper.className = "dv-context-menu-rename";
13626
+ const input = document.createElement("input");
13627
+ input.className = "dv-context-menu-rename-input";
13628
+ input.type = "text";
13629
+ input.placeholder = "Name This Group";
13630
+ input.value = tabGroup.label;
13631
+ input.addEventListener("input", () => {
13632
+ tabGroup.setLabel(input.value);
13633
+ });
13634
+ input.addEventListener("keydown", (e) => {
13635
+ if (e.key !== "Escape" && e.key !== "Enter") e.stopPropagation();
13636
+ });
13637
+ input.addEventListener("click", (e) => {
13638
+ e.stopPropagation();
13639
+ });
13640
+ wrapper.appendChild(input);
13641
+ if (!isCoarsePrimaryInput$1()) requestAnimationFrame(() => {
13642
+ input.focus();
13643
+ input.select();
13644
+ });
13645
+ return wrapper;
13646
+ }
13647
+ function buildColorPicker(tabGroup, palette) {
13648
+ const wrapper = document.createElement("div");
13649
+ wrapper.className = "dv-context-menu-color-picker";
13650
+ if (!palette.enabled) return wrapper;
13651
+ for (const entry of palette.entries()) {
13652
+ const swatch = document.createElement("div");
13653
+ swatch.className = "dv-context-menu-color-swatch";
13654
+ swatch.style.setProperty("--dv-tab-group-color", entry.value);
13655
+ if (entry.label) swatch.title = entry.label;
13656
+ if (tabGroup.color === entry.id) swatch.classList.add("dv-context-menu-color-swatch--selected");
13657
+ swatch.addEventListener("click", () => {
13658
+ tabGroup.setColor(entry.id);
13659
+ });
13660
+ wrapper.appendChild(swatch);
13661
+ }
13662
+ return wrapper;
13663
+ }
13664
+ var ContextMenuController = class {
13665
+ constructor(accessor) {
13666
+ this.accessor = accessor;
13667
+ }
13668
+ /**
13669
+ * A single maximize/restore toggle whose label and behaviour track the
13670
+ * group's live state: it reads *Restore* + calls `exitMaximized` when the
13671
+ * group is maximized, otherwise *Maximize* + `maximize`. Only grid groups
13672
+ * can be maximized, so the item is disabled for floating / popout panels
13673
+ * that are not already maximized.
13674
+ */
13675
+ buildMaximizeItem(panel, close) {
13676
+ const isMaximized = panel.api.isMaximized();
13677
+ return buildItem(isMaximized ? "Restore" : "Maximize", close, isMaximized ? () => panel.api.exitMaximized() : () => panel.api.maximize(), !isMaximized && panel.api.location.type !== "grid");
13678
+ }
13679
+ /**
13680
+ * A single collapse/expand toggle whose label tracks the tab group's live
13681
+ * state: *Expand* when the group is already collapsed, otherwise *Collapse*.
13682
+ */
13683
+ buildCollapseItem(tabGroup, close) {
13684
+ return buildItem(tabGroup.collapsed ? "Expand" : "Collapse", close, () => tabGroup.toggle());
13685
+ }
13686
+ /**
13687
+ * Whether to auto-inject the built-in Pin/Unpin item at the top of the tab
13688
+ * menu. True only when the PinnedTabs module is registered, pinning is
13689
+ * enabled, and the app has not opted out via `pinnedTabs.contextMenuItem:
13690
+ * false` (the item is on by default). This keeps pinning reachable from the
13691
+ * menu without the app having to return `'pin'` from
13692
+ * `getTabContextMenuItems` itself.
13693
+ */
13694
+ _shouldInjectPin() {
13695
+ const pinnedTabs = this.accessor.options.pinnedTabs;
13696
+ return !!this.accessor.pinnedTabsService && !!(pinnedTabs === null || pinnedTabs === void 0 ? void 0 : pinnedTabs.enabled) && pinnedTabs.contextMenuItem !== false;
13697
+ }
13698
+ /**
13699
+ * The tab menu items: the app's own items (if any) with the built-in
13700
+ * `'pin'` item prepended when {@link _shouldInjectPin} applies, so the app
13701
+ * keeps full control of its list, and pinning is added without disturbing
13702
+ * it. Reuses the existing `'pin'` token rendering below.
13703
+ */
13704
+ _resolveTabItems(panel, group, event) {
13705
+ var _this$accessor$option, _this$accessor$option2, _this$accessor$option3;
13706
+ 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, {
13707
+ panel,
13708
+ group,
13709
+ api: this.accessor.api,
13710
+ event
13711
+ })) !== null && _this$accessor$option !== void 0 ? _this$accessor$option : [];
13712
+ return this._shouldInjectPin() ? ["pin", ...appItems] : appItems;
13713
+ }
13714
+ /**
13715
+ * Render a custom `component` menu item and append it to `menuEl`. Builds
13716
+ * the framework renderer via the host's `createContextMenuItemComponent`
13717
+ * factory and initialises it. Shared by the tab menu and the chip menu,
13718
+ * which differ only in `identity`: the tab menu passes the `panel`, the chip
13719
+ * menu passes the `tabGroup`. No-op when no renderer could be created (no
13720
+ * factory configured, or the factory declined the component).
13721
+ */
13722
+ appendComponentItem(menuEl, item, identity, group, close) {
13723
+ var _this$accessor$option4, _this$accessor$option5;
13724
+ 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, {
13725
+ id: nextContextMenuItemId(),
13726
+ component: item.component
13727
+ });
13728
+ if (!renderer) return;
13729
+ renderer.init(_objectSpread2(_objectSpread2({}, identity), {}, {
13730
+ group,
13731
+ api: this.accessor.api,
13732
+ close,
13733
+ componentProps: item.componentProps
13734
+ }));
13735
+ menuEl.appendChild(renderer.element);
13736
+ }
13737
+ /**
13738
+ * The element for one built-in tab item, or `undefined` for a token this
13739
+ * version doesn't know (an app on a newer typings version, or plain JS):
13740
+ * an unrecognised token renders nothing rather than throwing.
13741
+ */
13742
+ buildBuiltInTabItem(item, panel, group, close) {
13743
+ switch (item) {
13744
+ case "separator": return buildSeparator();
13745
+ case "close": return buildItem("Close", close, () => panel.api.close());
13746
+ case "closeOthers": return buildItem("Close Others", close, () => {
13747
+ group.panels.filter((p) => p !== panel).forEach((p) => p.api.close());
13748
+ });
13749
+ case "closeAll": return buildItem("Close All", close, () => {
13750
+ [...group.panels].forEach((p) => p.api.close());
13751
+ });
13752
+ case "closeLeft": {
13753
+ const index = group.panels.indexOf(panel);
13754
+ return buildItem("Close to the Left", close, () => {
13755
+ group.panels.filter((_, i) => i < index).forEach((p) => p.api.close());
13756
+ }, index <= 0);
13757
+ }
13758
+ case "closeRight": {
13759
+ const index = group.panels.indexOf(panel);
13760
+ return buildItem("Close to the Right", close, () => {
13761
+ group.panels.filter((_, i) => i > index).forEach((p) => p.api.close());
13762
+ }, index === -1 || index >= group.panels.length - 1);
13763
+ }
13764
+ case "maximize": return this.buildMaximizeItem(panel, close);
13765
+ case "float": return buildItem("Float", close, () => this.accessor.api.addFloatingGroup(panel), panel.api.location.type === "floating");
13766
+ case "popout": return buildItem("Open in New Window", close, () => {
13767
+ this.accessor.api.addPopoutGroup(panel);
13768
+ }, panel.api.location.type === "popout");
13769
+ case "pin": return buildItem(panel.api.isPinned ? "Unpin tab" : "Pin tab", close, () => panel.api.setPinned(!panel.api.isPinned));
13770
+ default: return;
13771
+ }
13772
+ }
13773
+ /**
13774
+ * Append one app-supplied item. A raw `element` is used as-is, a
13775
+ * `component` goes through the framework renderer, and a `label` builds the
13776
+ * default row; an item carrying none of the three renders nothing. Shared
13777
+ * by both menus, which differ only in `identity`.
13778
+ */
13779
+ appendConfigItem(menuEl, item, identity, group, close) {
13780
+ if (item.element) menuEl.appendChild(item.element);
13781
+ else if (item.component) this.appendComponentItem(menuEl, item, identity, group, close);
13782
+ else if (item.label) menuEl.appendChild(buildItem(item.label, close, () => {
13783
+ var _item$action;
13784
+ return (_item$action = item.action) === null || _item$action === void 0 ? void 0 : _item$action.call(item);
13785
+ }, item.disabled));
13786
+ }
13787
+ show(panel, group, event) {
13788
+ const items = this._resolveTabItems(panel, group, event);
13789
+ if (items.length === 0) return;
13790
+ event.preventDefault();
13791
+ const popupService = this.accessor.getPopupServiceForGroup(group);
13792
+ const close = () => popupService.close();
13793
+ const menuEl = document.createElement("div");
13794
+ menuEl.className = "dv-context-menu";
13795
+ menuEl.setAttribute("role", "menu");
13796
+ for (const item of items) {
13797
+ if (isItemConfig(item)) {
13798
+ this.appendConfigItem(menuEl, item, { panel }, group, close);
13799
+ continue;
13800
+ }
13801
+ const el = this.buildBuiltInTabItem(item, panel, group, close);
13802
+ if (el) menuEl.appendChild(el);
13803
+ }
13804
+ popupService.openPopover(menuEl, {
13805
+ x: event.clientX,
13806
+ y: event.clientY,
13807
+ zIndex: popoverZIndexFor(event.target, group)
13808
+ });
13809
+ }
13810
+ showForChip(tabGroup, group, event) {
13811
+ if (!this.accessor.options.getTabGroupChipContextMenuItems) return;
13812
+ const items = this.accessor.options.getTabGroupChipContextMenuItems({
13813
+ tabGroup,
13814
+ group,
13815
+ api: this.accessor.api,
13816
+ event
13817
+ });
13818
+ if (items.length === 0) return;
13819
+ event.preventDefault();
13820
+ const popupService = this.accessor.getPopupServiceForGroup(group);
13821
+ const close = () => popupService.close();
13822
+ const menuEl = document.createElement("div");
13823
+ menuEl.className = "dv-context-menu";
13824
+ menuEl.setAttribute("role", "menu");
13825
+ for (const item of items) if (item === "separator") menuEl.appendChild(buildSeparator());
13826
+ else if (item === "rename") menuEl.appendChild(buildRenameInput(tabGroup));
13827
+ else if (item === "colorPicker") menuEl.appendChild(buildColorPicker(tabGroup, this.accessor.tabGroupColorPalette));
13828
+ else if (item === "collapse") menuEl.appendChild(this.buildCollapseItem(tabGroup, close));
13829
+ else if (item === "close") menuEl.appendChild(buildItem("Close All", close, () => {
13830
+ group.panels.filter((p) => tabGroup.containsPanel(p.id)).forEach((p) => p.api.close());
13831
+ }));
13832
+ else if (isItemConfig(item)) this.appendConfigItem(menuEl, item, { tabGroup }, group, close);
13833
+ popupService.openPopover(menuEl, {
13834
+ x: event.clientX,
13835
+ y: event.clientY,
13836
+ zIndex: popoverZIndexFor(event.target, group)
13837
+ });
13838
+ }
13839
+ };
13840
+ const ContextMenuModule = defineModule({
13841
+ name: "ContextMenu",
13842
+ serviceKey: "contextMenuService",
13843
+ create: (host) => new ContextMenuController(host)
13844
+ });
13845
+
13552
13846
  //#endregion
13553
13847
  //#region src/dockview/allModules.ts
13554
13848
  /**
@@ -13566,7 +13860,8 @@ const AllModules = [
13566
13860
  HeaderActionsModule,
13567
13861
  LiveRegionModule,
13568
13862
  AdvancedDnDModule,
13569
- TabGroupChipsModule
13863
+ TabGroupChipsModule,
13864
+ ContextMenuModule
13570
13865
  ];
13571
13866
 
13572
13867
  //#endregion
@@ -13599,6 +13894,13 @@ var PositionCache = class {
13599
13894
  });
13600
13895
  }
13601
13896
  };
13897
+ /**
13898
+ * Placeholder handle marking "a reposition frame has been requested but the
13899
+ * real handle is not known yet". Never passed to `cancelAnimationFrame` in
13900
+ * practice — it is replaced within the same synchronous block — and harmless
13901
+ * if it were, since no frame carries this id.
13902
+ */
13903
+ const SCHEDULED = -1;
13602
13904
  function createFocusableElement() {
13603
13905
  const element = document.createElement("div");
13604
13906
  element.tabIndex = -1;
@@ -13611,12 +13913,13 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13611
13913
  this.accessor = accessor;
13612
13914
  this.map = {};
13613
13915
  this._disposed = false;
13916
+ this._generation = 0;
13614
13917
  this.positionCache = new PositionCache();
13615
- this.pendingUpdates = /* @__PURE__ */ new Set();
13616
13918
  this.addDisposables(Disposable.from(() => {
13617
13919
  for (const value of Object.values(this.map)) {
13618
13920
  value.disposable.dispose();
13619
13921
  value.destroy.dispose();
13922
+ this.cancelPendingUpdate(value);
13620
13923
  }
13621
13924
  this._disposed = true;
13622
13925
  }));
@@ -13643,11 +13946,24 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13643
13946
  this.positionCache.invalidate();
13644
13947
  (_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
13645
13948
  }
13949
+ /**
13950
+ * Drop the reposition frame queued for a panel, if any. A queued frame is
13951
+ * bound to the reference container that was current when it was scheduled,
13952
+ * so it must be discarded whenever that container stops being the one the
13953
+ * overlay should track.
13954
+ */
13955
+ cancelPendingUpdate(entry) {
13956
+ if (entry.pendingUpdate !== void 0) {
13957
+ cancelAnimationFrame(entry.pendingUpdate);
13958
+ entry.pendingUpdate = void 0;
13959
+ }
13960
+ }
13646
13961
  detatch(panel) {
13647
13962
  if (this.map[panel.api.id]) {
13648
- const { disposable, destroy } = this.map[panel.api.id];
13649
- disposable.dispose();
13650
- destroy.dispose();
13963
+ const entry = this.map[panel.api.id];
13964
+ entry.disposable.dispose();
13965
+ entry.destroy.dispose();
13966
+ this.cancelPendingUpdate(entry);
13651
13967
  delete this.map[panel.api.id];
13652
13968
  return true;
13653
13969
  }
@@ -13663,22 +13979,73 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13663
13979
  panel,
13664
13980
  disposable: Disposable.NONE,
13665
13981
  destroy: Disposable.NONE,
13666
- element
13982
+ element,
13983
+ retainPreviousGeometry: false,
13984
+ positioned: false,
13985
+ generation: ++this._generation
13667
13986
  };
13668
13987
  }
13669
- const focusContainer = this.map[panel.api.id].element;
13988
+ const mapEntry = this.map[panel.api.id];
13989
+ /**
13990
+ * A *change* of reference container supersedes the previous `attach`:
13991
+ * take a fresh generation so its `resize` closure can no longer run,
13992
+ * and drop the frame it queued against the old container. During
13993
+ * `fromJSON({ reuseExistingPanels: true })` that old container is a
13994
+ * detached staging group measuring 0x0, so leaving its frame in flight
13995
+ * both wastes the update and delays the reposition against the real one.
13996
+ *
13997
+ * Re-attaching over the *same* container (re-open, active panel change)
13998
+ * deliberately leaves scheduled work alone: `repositionPanelOverlay`
13999
+ * (the auto-hide peek) schedules a frame carrying the sticky
14000
+ * `forceVisible`/`clip` state which `attach` does not re-apply, and a
14001
+ * peeked panel's `api.isVisible` is false — so discarding that frame
14002
+ * leaves `visibilityChanged` to hide the overlay and the peek renders
14003
+ * nothing. A newly created entry already holds a fresh generation, so
14004
+ * the stale closures a `detatch` left behind are fenced off either way.
14005
+ */
14006
+ if (mapEntry.referenceContainer !== referenceContainer) {
14007
+ mapEntry.generation = ++this._generation;
14008
+ this.cancelPendingUpdate(mapEntry);
14009
+ mapEntry.referenceContainer = referenceContainer;
14010
+ /**
14011
+ * Arm the retain flag only here: it means "a replacement reference
14012
+ * container is awaiting layout", so the 0x0 early-return below
14013
+ * holds the last good box rather than blanking the panel mid-move.
14014
+ * Arming it on every re-attach instead would make a container that
14015
+ * genuinely collapses to zero keep its stale geometry forever.
14016
+ *
14017
+ * Gated on `positioned` because an overlay that has never been
14018
+ * positioned has no left/top/width/height, and `.dv-render-overlay`
14019
+ * defaults to 100%/100% — retaining "previous" geometry there would
14020
+ * pin the content over the whole container.
14021
+ */
14022
+ mapEntry.retainPreviousGeometry = mapEntry.positioned;
14023
+ }
14024
+ const generation = mapEntry.generation;
14025
+ const focusContainer = mapEntry.element;
13670
14026
  const contentElement = panel.view.content.element;
13671
14027
  if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
13672
14028
  if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
13673
14029
  const resize = () => {
13674
14030
  const panelId = panel.api.id;
13675
- if (this.pendingUpdates.has(panelId)) return;
13676
- this.pendingUpdates.add(panelId);
13677
- requestAnimationFrame(() => {
14031
+ const scheduling = this.map[panelId];
14032
+ if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
14033
+ if (scheduling.pendingUpdate !== void 0) return;
14034
+ /**
14035
+ * Claim the slot *before* scheduling. `requestAnimationFrame` is
14036
+ * shimmed to run inline by some hosts (and by several suites here),
14037
+ * in which case the callback below clears `pendingUpdate` while
14038
+ * this call is still on the stack — assigning the handle afterwards
14039
+ * would resurrect a slot that is already spent and block every
14040
+ * later reposition. The sentinel is replaced by the real handle
14041
+ * only if the frame has not already run.
14042
+ */
14043
+ scheduling.pendingUpdate = SCHEDULED;
14044
+ const handle = requestAnimationFrame(() => {
13678
14045
  var _entry$forceVisible;
13679
- this.pendingUpdates.delete(panelId);
13680
14046
  const entry = this.map[panelId];
13681
- if (this.isDisposed || !entry) return;
14047
+ if (entry) entry.pendingUpdate = void 0;
14048
+ if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
13682
14049
  const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
13683
14050
  const clip = entry.clip;
13684
14051
  const box = this.positionCache.getPosition(referenceContainer.element);
@@ -13687,6 +14054,15 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13687
14054
  const top = box.top - box2.top;
13688
14055
  const width = box.width;
13689
14056
  const height = box.height;
14057
+ if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
14058
+ if (!panel.api.isVisible && !forceVisible) {
14059
+ focusContainer.style.visibility = "hidden";
14060
+ focusContainer.style.pointerEvents = "none";
14061
+ }
14062
+ return;
14063
+ }
14064
+ entry.retainPreviousGeometry = false;
14065
+ entry.positioned = true;
13690
14066
  focusContainer.style.left = `${left}px`;
13691
14067
  focusContainer.style.top = `${top}px`;
13692
14068
  focusContainer.style.width = `${width}px`;
@@ -13715,11 +14091,30 @@ var OverlayRenderContainer = class extends CompositeDisposable {
13715
14091
  } else focusContainer.style.clipPath = "";
13716
14092
  toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
13717
14093
  });
14094
+ if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
13718
14095
  };
13719
14096
  const visibilityChanged = () => {
13720
14097
  if (panel.api.isVisible) {
14098
+ var _this$map$panel$api$i;
13721
14099
  this.positionCache.invalidate();
13722
14100
  resize();
14101
+ /**
14102
+ * Existing geometry is safe to show while its replacement lays
14103
+ * out. `retainPreviousGeometry` is only set when geometry has
14104
+ * actually been written (see `attach`), so this can never
14105
+ * un-hide an overlay whose left/top/width/height are unset —
14106
+ * `.dv-render-overlay` is 100%/100% by default and would
14107
+ * otherwise cover the whole dock.
14108
+ *
14109
+ * The retained geometry is the panel's position in the previous
14110
+ * layout, so restoring a layout that moves the panel does paint
14111
+ * it at stale coordinates until the reposition lands. That is
14112
+ * bounded to the next frame now that a superseded `attach` can
14113
+ * no longer swallow the reposition (see `attach`); showing
14114
+ * stale-but-real geometry for a frame is the deliberate
14115
+ * trade-off against blanking the panel for the whole rebuild.
14116
+ */
14117
+ if ((_this$map$panel$api$i = this.map[panel.api.id]) === null || _this$map$panel$api$i === void 0 ? void 0 : _this$map$panel$api$i.retainPreviousGeometry) focusContainer.style.visibility = "";
13723
14118
  focusContainer.style.pointerEvents = "";
13724
14119
  } else {
13725
14120
  focusContainer.style.visibility = "hidden";
@@ -13942,7 +14337,17 @@ var PopoutWindow = class extends CompositeDisposable {
13942
14337
  window: externalWindow
13943
14338
  });
13944
14339
  return new Promise((resolve, reject) => {
13945
- externalWindow.addEventListener("unload", (e) => {});
14340
+ externalWindow.addEventListener("unload", () => {});
14341
+ /**
14342
+ * `load` is the only event that resolves this promise with a
14343
+ * container, so a window that goes away first would leave the
14344
+ * caller awaiting a promise that never settles - and
14345
+ * `addPopoutGroup` holds its layout transaction open until it does.
14346
+ * Settle with `null` on close, the same signal a blocked popup
14347
+ * gives and one the caller already handles. `resolve` after the
14348
+ * fact is a no-op, so a `load` that arrived first still wins.
14349
+ */
14350
+ disposable.addDisposables(_this.onWillClose(() => resolve(null)));
13946
14351
  externalWindow.addEventListener("load", () => {
13947
14352
  /**
13948
14353
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
@@ -14148,6 +14553,14 @@ var PopupService = class extends CompositeDisposable {
14148
14553
  //#endregion
14149
14554
  //#region src/dnd/dropTargetAnchorContainer.ts
14150
14555
  var DropTargetAnchorContainer = class extends CompositeDisposable {
14556
+ /**
14557
+ * The element the live overlay was rendered for, `undefined` when nothing
14558
+ * is showing. The container is shared by every drop target, so a caller
14559
+ * tearing down only its own overlay must check this before `clear()`.
14560
+ */
14561
+ get renderedOutline() {
14562
+ return this._model ? this._outline : void 0;
14563
+ }
14151
14564
  get disabled() {
14152
14565
  return this._disabled;
14153
14566
  }
@@ -14452,6 +14865,10 @@ var MiddleColumnView = class {
14452
14865
  const index = position === "top" ? this._topIndex : this._bottomIndex;
14453
14866
  if (index !== void 0) this._splitview.resizeView(index, size);
14454
14867
  }
14868
+ /** The inner splitview's primary-axis (height) extent; zero until laid out. */
14869
+ get axisSize() {
14870
+ return this._splitview.size;
14871
+ }
14455
14872
  updateMargin(gap) {
14456
14873
  this._splitview.margin = gap;
14457
14874
  }
@@ -14464,6 +14881,7 @@ var ShellManager = class {
14464
14881
  constructor(container, dockviewElement, layoutGrid, gap = 0, defaultCollapsedSize = 35) {
14465
14882
  this._disposables = new CompositeDisposable();
14466
14883
  this._viewConfigs = /* @__PURE__ */ new Map();
14884
+ this._pendingSizes = /* @__PURE__ */ new Map();
14467
14885
  this._currentWidth = 0;
14468
14886
  this._currentHeight = 0;
14469
14887
  this._gap = gap;
@@ -14554,11 +14972,13 @@ var ShellManager = class {
14554
14972
  break;
14555
14973
  }
14556
14974
  this._disposables.addDisposables(view);
14975
+ if (!view.isCollapsed && !this._canResize(position)) this._pendingSizes.set(position, initialSize);
14557
14976
  this.updateTheme(this._gap, this._defaultCollapsedSize);
14558
14977
  return view;
14559
14978
  }
14560
14979
  layout(width, height) {
14561
14980
  this._outerSplitview.layout(width, height);
14981
+ this._flushPendingSizes();
14562
14982
  }
14563
14983
  /**
14564
14984
  * Called when the active theme changes. Updates splitview margins and
@@ -14622,6 +15042,7 @@ var ShellManager = class {
14622
15042
  this._disposables.removeDisposable(view);
14623
15043
  view.dispose();
14624
15044
  this._viewConfigs.delete(position);
15045
+ this._pendingSizes.delete(position);
14625
15046
  this.updateTheme(this._gap, this._defaultCollapsedSize);
14626
15047
  }
14627
15048
  hasEdgeGroup(position) {
@@ -14645,6 +15066,7 @@ var ShellManager = class {
14645
15066
  this._middleColumn.setViewVisible(position, visible);
14646
15067
  break;
14647
15068
  }
15069
+ if (visible) this._flushPendingSizes();
14648
15070
  }
14649
15071
  isEdgeGroupVisible(position) {
14650
15072
  switch (position) {
@@ -14662,20 +15084,63 @@ var ShellManager = class {
14662
15084
  const view = this._getView(position);
14663
15085
  if (!view) return;
14664
15086
  view.setCollapsed(collapsed);
14665
- const targetSize = collapsed ? view.collapsedSize : view.lastExpandedSize;
15087
+ if (collapsed) {
15088
+ this._pendingSizes.delete(position);
15089
+ this._resizeView(position, view.collapsedSize);
15090
+ } else this.resizeEdgeGroup(position, view.lastExpandedSize);
15091
+ }
15092
+ /**
15093
+ * Resize the edge group at `position` along its primary axis (width for
15094
+ * `left`/`right`, height for `top`/`bottom`), clamped by the group's
15095
+ * constraints and the space available. Where `groupApi.setSize` lands.
15096
+ *
15097
+ * The size becomes the group's expanded size, so a collapsed group keeps
15098
+ * its strip and takes it on expand, and it survives a `toJSON` round-trip.
15099
+ */
15100
+ resizeEdgeGroup(position, size) {
15101
+ const view = this._getView(position);
15102
+ if (!view || !Number.isFinite(size)) return;
15103
+ const target = Math.round(size);
15104
+ if (target <= 0) return;
15105
+ view.restoreExpandedSize(target);
15106
+ if (view.isCollapsed) {
15107
+ this._pendingSizes.delete(position);
15108
+ return;
15109
+ }
15110
+ if (this._canResize(position)) {
15111
+ this._pendingSizes.delete(position);
15112
+ this._resizeView(position, target);
15113
+ } else this._pendingSizes.set(position, target);
15114
+ }
15115
+ /** A resize lands only on a visible view (a hidden one is pinned to zero)
15116
+ * whose splitview has extent to distribute. */
15117
+ _canResize(position) {
15118
+ if (!this.isEdgeGroupVisible(position)) return false;
15119
+ return (position === "left" || position === "right" ? this._outerSplitview.size : this._middleColumn.axisSize) > 0;
15120
+ }
15121
+ _resizeView(position, size) {
14666
15122
  switch (position) {
14667
15123
  case "left":
14668
- if (this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, targetSize);
15124
+ if (this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, size);
14669
15125
  break;
14670
15126
  case "right":
14671
- if (this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, targetSize);
15127
+ if (this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, size);
14672
15128
  break;
14673
15129
  case "top":
14674
15130
  case "bottom":
14675
- this._middleColumn.resizeView(position, targetSize);
15131
+ this._middleColumn.resizeView(position, size);
14676
15132
  break;
14677
15133
  }
14678
15134
  }
15135
+ _flushPendingSizes() {
15136
+ if (this._pendingSizes.size === 0) return;
15137
+ for (const [position, size] of this._pendingSizes) {
15138
+ const view = this._getView(position);
15139
+ if (!view || view.isCollapsed || !this._canResize(position)) continue;
15140
+ this._pendingSizes.delete(position);
15141
+ this._resizeView(position, size);
15142
+ }
15143
+ }
14679
15144
  isEdgeGroupCollapsed(position) {
14680
15145
  var _this$_getView$isColl, _this$_getView;
14681
15146
  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;
@@ -14701,15 +15166,17 @@ var ShellManager = class {
14701
15166
  maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
14702
15167
  collapsedSize: view.configuredCollapsedSize
14703
15168
  });
14704
- const expandedSize = (view, isVisible, liveSize, cachedVisibleSize) => {
15169
+ const expandedSize = (view, position, isVisible, liveSize, cachedVisibleSize) => {
14705
15170
  if (view.isCollapsed) return view.lastExpandedSize;
15171
+ const pending = this._pendingSizes.get(position);
15172
+ if (pending !== void 0) return pending;
14706
15173
  if (!isVisible) return cachedVisibleSize !== null && cachedVisibleSize !== void 0 ? cachedVisibleSize : view.lastExpandedSize;
14707
15174
  return liveSize;
14708
15175
  };
14709
15176
  if (this._leftView && this._leftIndex !== void 0) {
14710
15177
  const visible = this._outerSplitview.isViewVisible(this._leftIndex);
14711
15178
  edgeGroups.left = _objectSpread2({
14712
- size: expandedSize(this._leftView, visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
15179
+ size: expandedSize(this._leftView, "left", visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
14713
15180
  visible,
14714
15181
  collapsed: this._leftView.isCollapsed || void 0
14715
15182
  }, constraints(this._leftView));
@@ -14717,7 +15184,7 @@ var ShellManager = class {
14717
15184
  if (this._rightView && this._rightIndex !== void 0) {
14718
15185
  const visible = this._outerSplitview.isViewVisible(this._rightIndex);
14719
15186
  edgeGroups.right = _objectSpread2({
14720
- size: expandedSize(this._rightView, visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
15187
+ size: expandedSize(this._rightView, "right", visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
14721
15188
  visible,
14722
15189
  collapsed: this._rightView.isCollapsed || void 0
14723
15190
  }, constraints(this._rightView));
@@ -14725,7 +15192,7 @@ var ShellManager = class {
14725
15192
  if (this._topView) {
14726
15193
  const visible = this._middleColumn.isViewVisible("top");
14727
15194
  edgeGroups.top = _objectSpread2({
14728
- size: expandedSize(this._topView, visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
15195
+ size: expandedSize(this._topView, "top", visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
14729
15196
  visible,
14730
15197
  collapsed: this._topView.isCollapsed || void 0
14731
15198
  }, constraints(this._topView));
@@ -14733,7 +15200,7 @@ var ShellManager = class {
14733
15200
  if (this._bottomView) {
14734
15201
  const visible = this._middleColumn.isViewVisible("bottom");
14735
15202
  edgeGroups.bottom = _objectSpread2({
14736
- size: expandedSize(this._bottomView, visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
15203
+ size: expandedSize(this._bottomView, "bottom", visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
14737
15204
  visible,
14738
15205
  collapsed: this._bottomView.isCollapsed || void 0
14739
15206
  }, constraints(this._bottomView));
@@ -14741,33 +15208,23 @@ var ShellManager = class {
14741
15208
  return edgeGroups;
14742
15209
  }
14743
15210
  fromJSON(data) {
14744
- if (data.left && this._leftIndex !== void 0) {
14745
- var _this$_leftView2, _this$_leftView3, _data$left$collapsed, _this$_leftView$colla, _this$_leftView4;
14746
- (_this$_leftView2 = this._leftView) === null || _this$_leftView2 === void 0 || _this$_leftView2.restoreExpandedSize(data.left.size);
14747
- (_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);
14748
- 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);
14749
- if (!data.left.visible) this._outerSplitview.setViewVisible(this._leftIndex, false);
14750
- }
14751
- if (data.right && this._rightIndex !== void 0) {
14752
- var _this$_rightView2, _this$_rightView3, _data$right$collapsed, _this$_rightView$coll, _this$_rightView4;
14753
- (_this$_rightView2 = this._rightView) === null || _this$_rightView2 === void 0 || _this$_rightView2.restoreExpandedSize(data.right.size);
14754
- (_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);
14755
- 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);
14756
- if (!data.right.visible) this._outerSplitview.setViewVisible(this._rightIndex, false);
14757
- }
14758
- if (data.top) {
14759
- var _this$_topView2, _this$_topView3, _data$top$collapsed, _this$_topView$collap, _this$_topView4;
14760
- (_this$_topView2 = this._topView) === null || _this$_topView2 === void 0 || _this$_topView2.restoreExpandedSize(data.top.size);
14761
- (_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);
14762
- 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);
14763
- if (!data.top.visible) this._middleColumn.setViewVisible("top", false);
14764
- }
14765
- if (data.bottom) {
14766
- var _this$_bottomView2, _this$_bottomView3, _data$bottom$collapse, _this$_bottomView$col, _this$_bottomView4;
14767
- (_this$_bottomView2 = this._bottomView) === null || _this$_bottomView2 === void 0 || _this$_bottomView2.restoreExpandedSize(data.bottom.size);
14768
- (_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);
14769
- 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);
14770
- if (!data.bottom.visible) this._middleColumn.setViewVisible("bottom", false);
15211
+ for (const position of [
15212
+ "left",
15213
+ "right",
15214
+ "top",
15215
+ "bottom"
15216
+ ]) {
15217
+ var _state$collapsed;
15218
+ const state = data[position];
15219
+ const view = this._getView(position);
15220
+ if (!state || !view) continue;
15221
+ view.restoreExpandedSize(state.size);
15222
+ view.setCollapsed((_state$collapsed = state.collapsed) !== null && _state$collapsed !== void 0 ? _state$collapsed : false);
15223
+ if (state.collapsed) {
15224
+ this._pendingSizes.delete(position);
15225
+ this._resizeView(position, view.collapsedSize);
15226
+ } else this.resizeEdgeGroup(position, state.size);
15227
+ this.setEdgeGroupVisible(position, !!state.visible);
14771
15228
  }
14772
15229
  }
14773
15230
  dispose() {
@@ -15360,6 +15817,7 @@ var DockviewComponent = class extends BaseGrid {
15360
15817
  this._onWillDrop = new Emitter();
15361
15818
  this.onWillDrop = this._onWillDrop.event;
15362
15819
  this._mutationDepth = 0;
15820
+ this._pendingLocationChanges = /* @__PURE__ */ new Map();
15363
15821
  this._origin = "user";
15364
15822
  this._originDepth = 0;
15365
15823
  this._onWillMutateLayout = new Emitter();
@@ -15457,7 +15915,7 @@ var DockviewComponent = class extends BaseGrid {
15457
15915
  toggleClass(this.element, "dv-debug", !!options.debug);
15458
15916
  this.updateTheme();
15459
15917
  if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
15460
- this.addDisposables(this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15918
+ this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
15461
15919
  var _this$rootDropTargetC2, _this$floatingDropTar;
15462
15920
  (_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
15463
15921
  (_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
@@ -15531,7 +15989,7 @@ var DockviewComponent = class extends BaseGrid {
15531
15989
  return (_this$_popoutWindowSe3 = (_this$_popoutWindowSe4 = this._popoutWindowService) === null || _this$_popoutWindowSe4 === void 0 || (_this$_popoutWindowSe4 = _this$_popoutWindowSe4.findByGroup(group)) === null || _this$_popoutWindowSe4 === void 0 ? void 0 : _this$_popoutWindowSe4.popupService) !== null && _this$_popoutWindowSe3 !== void 0 ? _this$_popoutWindowSe3 : this.popupService;
15532
15990
  }
15533
15991
  addPopoutGroup(itemToPopout, options) {
15534
- return this.mutation("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15992
+ return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
15535
15993
  }
15536
15994
  /** Enumerate the popout groups currently open in their own windows. */
15537
15995
  getPopouts() {
@@ -15628,15 +16086,32 @@ var DockviewComponent = class extends BaseGrid {
15628
16086
  }
15629
16087
  };
15630
16088
  let floatingBox;
15631
- if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) this.movingLock(() => {
15632
- const panel = referenceGroup.model.removePanel(itemToPopout);
15633
- group.model.openPanel(panel);
15634
- });
15635
- else {
16089
+ /**
16090
+ * Popping out rehomes panels into `group`, so like the floating
16091
+ * case it owes the caller an `onDidMovePanel` per panel. The
16092
+ * events are deferred until the window is wired up so listeners
16093
+ * observe the panels' final `popout` location.
16094
+ */
16095
+ let movedPanels = [];
16096
+ if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
16097
+ const sourceGroup = itemToPopout.group;
16098
+ this.movingLock(() => {
16099
+ const panel = referenceGroup.model.removePanel(itemToPopout);
16100
+ group.model.openPanel(panel);
16101
+ });
16102
+ movedPanels = [{
16103
+ panel: itemToPopout,
16104
+ from: sourceGroup
16105
+ }];
16106
+ } else {
15636
16107
  this.movingLock(() => moveGroupWithoutDestroying({
15637
16108
  from: referenceGroup,
15638
16109
  to: group
15639
16110
  }));
16111
+ movedPanels = group.panels.map((panel) => ({
16112
+ panel,
16113
+ from: referenceGroup
16114
+ }));
15640
16115
  switch (referenceLocation) {
15641
16116
  case "grid":
15642
16117
  referenceGroup.api.setVisible(false);
@@ -15745,6 +16220,7 @@ var DockviewComponent = class extends BaseGrid {
15745
16220
  group: value.popoutGroup,
15746
16221
  window: value.getWindow()
15747
16222
  });
16223
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15748
16224
  return true;
15749
16225
  }).catch((err) => {
15750
16226
  console.error("dockview: failed to create popout.", err);
@@ -15822,6 +16298,16 @@ var DockviewComponent = class extends BaseGrid {
15822
16298
  const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
15823
16299
  const anchorPresent = members.includes(group);
15824
16300
  const anchorIsSoleMember = anchorPresent && members.length === 1;
16301
+ /**
16302
+ * Popping a group out fires `onDidMovePanel` per panel, so closing the
16303
+ * window owes the caller the same: every panel in it is rehomed, into
16304
+ * the reference group the popout left behind or into a fresh grid slot.
16305
+ * `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
16306
+ * paths, so `onDidMovePanel` is the only event that can carry it. The
16307
+ * events are deferred to the end so listeners observe the panels at
16308
+ * their final group and location.
16309
+ */
16310
+ const movedPanels = [];
15825
16311
  if (genuineClose) for (const member of members) {
15826
16312
  if (member === group) continue;
15827
16313
  this.movingLock(() => {
@@ -15832,12 +16318,21 @@ var DockviewComponent = class extends BaseGrid {
15832
16318
  });
15833
16319
  this.redockGroupToMainGrid(member);
15834
16320
  });
16321
+ for (const panel of member.panels) movedPanels.push({
16322
+ panel,
16323
+ from: member
16324
+ });
15835
16325
  }
15836
16326
  if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
16327
+ const rehomed = [...group.panels];
15837
16328
  this.movingLock(() => moveGroupWithoutDestroying({
15838
16329
  from: group,
15839
16330
  to: referenceGroup
15840
16331
  }));
16332
+ for (const panel of rehomed) movedPanels.push({
16333
+ panel,
16334
+ from: group
16335
+ });
15841
16336
  if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
15842
16337
  if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
15843
16338
  } else if (anchorPresent && this.getPanel(group.id)) {
@@ -15868,10 +16363,15 @@ var DockviewComponent = class extends BaseGrid {
15868
16363
  this.movingLock(() => {
15869
16364
  this.doAddGroup(group, [0]);
15870
16365
  });
16366
+ for (const panel of group.panels) movedPanels.push({
16367
+ panel,
16368
+ from: group
16369
+ });
15871
16370
  }
15872
16371
  this.doSetGroupAndPanelActive(group);
15873
16372
  }
15874
16373
  disposePopoutGridview();
16374
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15875
16375
  }
15876
16376
  addFloatingGroup(item, options) {
15877
16377
  this.mutation("float", () => this._doAddFloatingGroup(item, options));
@@ -15880,7 +16380,15 @@ var DockviewComponent = class extends BaseGrid {
15880
16380
  if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
15881
16381
  if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
15882
16382
  let group;
16383
+ /**
16384
+ * Floating relocates panels exactly like a drop onto the grid does, so
16385
+ * it owes the caller an `onDidMovePanel` per panel. The events are
16386
+ * deferred until the window is mounted below: only then do the panels
16387
+ * report their final `floating` location.
16388
+ */
16389
+ let movedPanels = [];
15883
16390
  if (item instanceof DockviewPanel) {
16391
+ const sourceGroup = item.group;
15884
16392
  group = this.createGroup();
15885
16393
  this._onDidAddGroup.fire(group);
15886
16394
  this.movingLock(() => this.removePanel(item, {
@@ -15889,6 +16397,10 @@ var DockviewComponent = class extends BaseGrid {
15889
16397
  skipSetActiveGroup: true
15890
16398
  }));
15891
16399
  this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
16400
+ movedPanels = [{
16401
+ panel: item,
16402
+ from: sourceGroup
16403
+ }];
15892
16404
  } else {
15893
16405
  var _this$_popoutWindowSe8;
15894
16406
  group = item;
@@ -15905,11 +16417,21 @@ var DockviewComponent = class extends BaseGrid {
15905
16417
  });
15906
16418
  this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
15907
16419
  group = popoutReferenceGroup;
15908
- } else this.doRemoveGroup(item, {
15909
- skipDispose: true,
15910
- skipPopoutReturn: true,
15911
- skipPopoutAssociated: false
15912
- });
16420
+ movedPanels = group.panels.map((panel) => ({
16421
+ panel,
16422
+ from: item
16423
+ }));
16424
+ } else {
16425
+ this.doRemoveGroup(item, {
16426
+ skipDispose: true,
16427
+ skipPopoutReturn: true,
16428
+ skipPopoutAssociated: false
16429
+ });
16430
+ movedPanels = group.panels.map((panel) => ({
16431
+ panel,
16432
+ from: group
16433
+ }));
16434
+ }
15913
16435
  }
15914
16436
  function getAnchoredBox() {
15915
16437
  if (options === null || options === void 0 ? void 0 : options.position) {
@@ -15942,6 +16464,7 @@ var DockviewComponent = class extends BaseGrid {
15942
16464
  skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
15943
16465
  disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
15944
16466
  });
16467
+ for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
15945
16468
  }
15946
16469
  /**
15947
16470
  * Build an empty gridview configured to match the main grid's styling, for
@@ -16086,6 +16609,13 @@ var DockviewComponent = class extends BaseGrid {
16086
16609
  position
16087
16610
  };
16088
16611
  group.model.headerPosition = position;
16612
+ const resizeDisposable = group.onDidChange((event) => {
16613
+ var _this$_shellManager3;
16614
+ if (!event) return;
16615
+ const size = position === "left" || position === "right" ? event.width : event.height;
16616
+ if (typeof size !== "number") return;
16617
+ (_this$_shellManager3 = this._shellManager) === null || _this$_shellManager3 === void 0 || _this$_shellManager3.resizeEdgeGroup(position, size);
16618
+ });
16089
16619
  const autoCollapseDisposable = group.model.onDidRemovePanel(() => {
16090
16620
  if (!group.model.isEmpty) return;
16091
16621
  if (service.isAutoReveal(group)) queueMicrotask(() => {
@@ -16094,7 +16624,7 @@ var DockviewComponent = class extends BaseGrid {
16094
16624
  });
16095
16625
  else this.setEdgeGroupCollapsed(group, true);
16096
16626
  });
16097
- service.add(position, group, autoCollapseDisposable);
16627
+ service.add(position, group, new CompositeDisposable(autoCollapseDisposable, resizeDisposable));
16098
16628
  if (options.autoHide !== void 0) service.setAutoHide(group, options.autoHide);
16099
16629
  if (options.autoReveal !== void 0) service.setAutoReveal(group, options.autoReveal);
16100
16630
  this._onDidAddGroup.fire(group);
@@ -16189,8 +16719,8 @@ var DockviewComponent = class extends BaseGrid {
16189
16719
  }
16190
16720
  /** The size an edge group expands to (pre-collapse); sizes the peek. */
16191
16721
  getEdgeGroupExpandedSize(position) {
16192
- var _this$_shellManager$g, _this$_shellManager3;
16193
- 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;
16722
+ var _this$_shellManager$g, _this$_shellManager4;
16723
+ 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;
16194
16724
  }
16195
16725
  /** Reposition a single `renderer:'always'` panel's overlay over its
16196
16726
  * reference container, optionally forcing it visible (the auto-hide peek
@@ -16373,35 +16903,119 @@ var DockviewComponent = class extends BaseGrid {
16373
16903
  if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
16374
16904
  if (!data.grid || ((_data$grid$root = data.grid.root) === null || _data$grid$root === void 0 ? void 0 : _data$grid$root.type) !== "branch" || !Array.isArray(data.grid.root.data)) throw new Error("dockview: root must be of type branch");
16375
16905
  const existingPanels = /* @__PURE__ */ new Map();
16376
- let tempGroup;
16906
+ const temporaryGroups = /* @__PURE__ */ new Map();
16907
+ const stagedPanels = [];
16908
+ const temporaryGroupDisposables = [];
16909
+ /**
16910
+ * The staging groups are detached from `_groups` and never enter the
16911
+ * grid, so nothing else will ever tear them down.
16912
+ *
16913
+ * `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
16914
+ * throwing renderer must not abort the rest of the cleanup — that would
16915
+ * re-introduce the very leak this reclaims — nor replace the
16916
+ * deserialization error the caller is being given. Draining the list
16917
+ * also makes this safe to call more than once.
16918
+ */
16919
+ const disposeTemporaryGroups = () => {
16920
+ for (const disposable of temporaryGroupDisposables) try {
16921
+ disposable.dispose();
16922
+ } catch (err) {
16923
+ console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
16924
+ }
16925
+ temporaryGroupDisposables.length = 0;
16926
+ };
16377
16927
  if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
16378
16928
  /**
16379
- * What are we doing here?
16380
- *
16381
- * 1. Create a temporary group to hold any panels that currently exist and that also exist in the new layout
16382
- * 2. Remove that temporary group from the group mapping so that it doesn't get cleared when we clear the layout
16929
+ * Visible, always-rendered panels need individual staging groups
16930
+ * to remain active. Other reused panels can share a staging group.
16931
+ * The staging groups are excluded from the layout clear below.
16383
16932
  */
16384
- tempGroup = this.createGroup();
16385
- this._groups.delete(tempGroup.api.id);
16386
16933
  const newPanels = Object.keys(data.panels);
16387
- for (const panel of this.panels) if (newPanels.includes(panel.api.id)) existingPanels.set(panel.api.id, panel);
16388
- this.movingLock(() => {
16389
- Array.from(existingPanels.values()).forEach((panel) => {
16390
- this.moveGroupOrPanel({
16391
- from: {
16392
- groupId: panel.api.group.api.id,
16393
- panelId: panel.api.id
16394
- },
16395
- to: {
16396
- group: tempGroup,
16397
- position: "center"
16398
- },
16399
- keepEmptyGroups: true
16934
+ let sharedTemporaryGroup;
16935
+ const createTemporaryGroup = () => {
16936
+ const temporaryGroup = this.createGroup();
16937
+ /**
16938
+ * Removing the group from `_groups` also drops the record's
16939
+ * `CompositeDisposable`, so capture it first and tear both it
16940
+ * and the group down in the `finally` below. Left undisposed a
16941
+ * staging group leaks its `ResizeObserver`, its
16942
+ * `onDidOptionsChange` subscription (retained by this
16943
+ * component's emitter) and the watermark its model mounts when
16944
+ * the group empties — once per reused panel, per `fromJSON`.
16945
+ */
16946
+ const record = this._groups.get(temporaryGroup.api.id);
16947
+ this._groups.delete(temporaryGroup.api.id);
16948
+ temporaryGroupDisposables.push(Disposable.from(() => {
16949
+ /**
16950
+ * Reclaim the group's own resources, never its panels.
16951
+ * Staging is driven by the ids in `data.panels` while
16952
+ * reclaiming is driven by the group `views` that
16953
+ * reference them, so a panel whose state is present but
16954
+ * unreferenced is still staged here — and `dispose()`
16955
+ * on a non-empty group reaches the consumer's
16956
+ * `IContentRenderer.dispose()`. Emptying the group
16957
+ * first leaves those panels exactly as they were before
16958
+ * this teardown existed, which also keeps this safe
16959
+ * against any future path that rebuilds a panel under
16960
+ * an id that is live in the new layout.
16961
+ */
16962
+ this.movingLock(() => {
16963
+ for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
16964
+ });
16965
+ record === null || record === void 0 || record.disposable.dispose();
16966
+ temporaryGroup.dispose();
16967
+ }));
16968
+ return temporaryGroup;
16969
+ };
16970
+ /**
16971
+ * Staging and the clear below run before the deserialization
16972
+ * `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
16973
+ * renderer teardown during `clear`) would escape without reclaiming
16974
+ * the staging groups already created.
16975
+ *
16976
+ * The creation loop is inside the guard too, not just the moves and
16977
+ * the clear: `createGroup()` reaches consumer code of its own —
16978
+ * `initialize()` mounts the watermark through
16979
+ * `createWatermarkComponent()` — so a throw on the n-th panel would
16980
+ * otherwise strand the n-1 staging groups already built.
16981
+ */
16982
+ try {
16983
+ for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
16984
+ existingPanels.set(panel.api.id, panel);
16985
+ let temporaryGroup;
16986
+ if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
16987
+ else {
16988
+ var _sharedTemporaryGroup;
16989
+ (_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
16990
+ temporaryGroup = sharedTemporaryGroup;
16991
+ }
16992
+ temporaryGroups.set(panel.api.id, temporaryGroup);
16993
+ stagedPanels.push({
16994
+ panel,
16995
+ temporaryGroup
16996
+ });
16997
+ }
16998
+ this.movingLock(() => {
16999
+ stagedPanels.forEach(({ panel, temporaryGroup }) => {
17000
+ this.moveGroupOrPanel({
17001
+ from: {
17002
+ groupId: panel.api.group.api.id,
17003
+ panelId: panel.api.id
17004
+ },
17005
+ to: {
17006
+ group: temporaryGroup,
17007
+ position: "center"
17008
+ },
17009
+ keepEmptyGroups: true
17010
+ });
16400
17011
  });
16401
17012
  });
16402
- });
16403
- }
16404
- this.clear();
17013
+ this.clear();
17014
+ } catch (err) {
17015
+ disposeTemporaryGroups();
17016
+ throw err;
17017
+ }
17018
+ } else this.clear();
16405
17019
  const { grid, panels, activeGroup } = data;
16406
17020
  try {
16407
17021
  var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
@@ -16433,9 +17047,10 @@ var DockviewComponent = class extends BaseGrid {
16433
17047
  * due to a corruption of input data.
16434
17048
  */
16435
17049
  const existingPanel = existingPanels.get(child);
16436
- if (tempGroup && existingPanel) {
17050
+ const temporaryGroup = temporaryGroups.get(child);
17051
+ if (temporaryGroup && existingPanel) {
16437
17052
  this.movingLock(() => {
16438
- tempGroup.model.removePanel(existingPanel);
17053
+ temporaryGroup.model.removePanel(existingPanel);
16439
17054
  });
16440
17055
  createdPanels.push(existingPanel);
16441
17056
  existingPanel.updateFromStateModel(panels[child]);
@@ -16465,7 +17080,7 @@ var DockviewComponent = class extends BaseGrid {
16465
17080
  return createGroupFromSerializedState(node.data);
16466
17081
  } });
16467
17082
  this._layoutFromShell(width, height);
16468
- if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels);
17083
+ if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
16469
17084
  this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
16470
17085
  const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
16471
17086
  (_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
@@ -16502,6 +17117,8 @@ var DockviewComponent = class extends BaseGrid {
16502
17117
  * expect trying to load a corrupted layout to result in an error and not silently fail...
16503
17118
  */
16504
17119
  throw err;
17120
+ } finally {
17121
+ disposeTemporaryGroups();
16505
17122
  }
16506
17123
  this.debouncedUpdateAllPositions();
16507
17124
  this._onDidLayoutFromJSON.fire();
@@ -16524,7 +17141,7 @@ var DockviewComponent = class extends BaseGrid {
16524
17141
  members
16525
17142
  };
16526
17143
  }
16527
- deserializeEdgeGroups(edgeGroups, panels) {
17144
+ deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
16528
17145
  const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
16529
17146
  if (!edgeService) return;
16530
17147
  for (const _position of [
@@ -16554,13 +17171,40 @@ var DockviewComponent = class extends BaseGrid {
16554
17171
  if (groupState) {
16555
17172
  const { views, activeView } = groupState;
16556
17173
  const createdPanels = [];
16557
- for (const panelId of views) if (panels[panelId]) {
16558
- const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
16559
- createdPanels.push(panel);
17174
+ for (const panelId of views) {
17175
+ if (!panels[panelId]) continue;
17176
+ /**
17177
+ * Reclaim a staged panel rather than rebuilding it, the
17178
+ * same way the grid path does. Deserializing here instead
17179
+ * would honour `reuseExistingPanels` everywhere except edge
17180
+ * groups: the live panel keeps sitting in its staging group
17181
+ * while a second panel is built under the same id, so the
17182
+ * consumer's renderer for the original is orphaned — never
17183
+ * disposed, and its element left inside the id-keyed
17184
+ * overlay entry the replacement now shares.
17185
+ */
17186
+ const existingPanel = existingPanels.get(panelId);
17187
+ const temporaryGroup = temporaryGroups.get(panelId);
17188
+ if (temporaryGroup && existingPanel) {
17189
+ this.movingLock(() => {
17190
+ temporaryGroup.model.removePanel(existingPanel);
17191
+ });
17192
+ createdPanels.push(existingPanel);
17193
+ existingPanel.updateFromStateModel(panels[panelId]);
17194
+ } else {
17195
+ const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
17196
+ createdPanels.push(panel);
17197
+ }
16560
17198
  }
16561
17199
  for (const panel of createdPanels) {
16562
17200
  const isActive = activeView === panel.id;
16563
- edgeGroup.model.openPanel(panel, {
17201
+ if (existingPanels.has(panel.api.id)) this.movingLock(() => {
17202
+ edgeGroup.model.openPanel(panel, {
17203
+ skipSetActive: !isActive,
17204
+ skipSetGroupActive: true
17205
+ });
17206
+ });
17207
+ else edgeGroup.model.openPanel(panel, {
16564
17208
  skipSetActive: !isActive,
16565
17209
  skipSetGroupActive: true
16566
17210
  });
@@ -16984,22 +17628,100 @@ var DockviewComponent = class extends BaseGrid {
16984
17628
  * outermost mutation.
16985
17629
  */
16986
17630
  mutation(kind, func) {
16987
- const outer = this._mutationDepth === 0;
17631
+ const close = this.openMutation(kind);
17632
+ try {
17633
+ return func();
17634
+ } finally {
17635
+ close();
17636
+ }
17637
+ }
17638
+ /**
17639
+ * `mutation()` for an operation whose work continues after the synchronous
17640
+ * call returns. The transaction stays open until the returned promise
17641
+ * settles, so everything the operation does - the group it adds, the panels
17642
+ * it relocates - lands inside the bracket, matching the synchronous
17643
+ * move / float paths.
17644
+ */
17645
+ mutationAsync(kind, func) {
17646
+ var _this = this;
17647
+ return _asyncToGenerator(function* () {
17648
+ const close = _this.openMutation(kind);
17649
+ try {
17650
+ return yield func();
17651
+ } finally {
17652
+ close();
17653
+ }
17654
+ })();
17655
+ }
17656
+ /**
17657
+ * Open a transaction and return the function that closes it. Shared by the
17658
+ * synchronous and asynchronous brackets; the returned function must be
17659
+ * called exactly once.
17660
+ *
17661
+ * Both ends key off the depth counter reaching zero rather than off which
17662
+ * bracket opened first. For synchronous nesting the two are the same thing
17663
+ * - brackets close in the order they opened - but asynchronous transactions
17664
+ * can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
17665
+ * once, or a restore staggering several), and there the first to open is
17666
+ * not the last to close. Reporting on the opener would fire `didMutate`
17667
+ * while the other transaction was still doing structural work, which is the
17668
+ * very thing the async bracket exists to prevent. Overlapping transactions
17669
+ * therefore report as one, tagged with the kind of the last to finish.
17670
+ */
17671
+ openMutation(kind) {
16988
17672
  const origin = this._origin;
16989
- if (outer) this._onWillMutateLayout.fire({
17673
+ if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
16990
17674
  kind,
16991
17675
  origin
16992
17676
  });
16993
17677
  this._mutationDepth++;
16994
- try {
16995
- return func();
16996
- } finally {
17678
+ return () => {
16997
17679
  this._mutationDepth--;
16998
- if (outer) this._onDidMutateLayout.fire({
16999
- kind,
17000
- origin
17001
- });
17680
+ if (this._mutationDepth === 0) {
17681
+ this.flushLocationChanges();
17682
+ this._onDidMutateLayout.fire({
17683
+ kind,
17684
+ origin
17685
+ });
17686
+ }
17687
+ };
17688
+ }
17689
+ /**
17690
+ * Coalesce a panel's `onDidLocationChange` to the end of the enclosing
17691
+ * transaction, or fire it immediately when no transaction is in flight.
17692
+ *
17693
+ * Relocating a panel touches its location twice: once as it is reparented
17694
+ * into the destination group - which, when that group has just been created
17695
+ * for a floating or popout window, has not been told where it lives yet -
17696
+ * and once as the group is tagged with its final location. Reporting each
17697
+ * signal as it happens therefore leaks an intermediate `grid` location the
17698
+ * panel was never in, at a moment when it belongs to neither group's panel
17699
+ * list and so is missing from `api.panels`.
17700
+ *
17701
+ * Deferring collapses those signals into a single event carrying the
17702
+ * settled location. Note the signals are deliberately *not* compared
17703
+ * against the panel's previous location: a panel can move between two
17704
+ * floating windows without its location type changing, and this event is
17705
+ * the only signal `OverlayRenderContainer` has to re-resolve the panel's
17706
+ * z-index against its new host window.
17707
+ */
17708
+ deferLocationChange(key, fire) {
17709
+ if (this._mutationDepth === 0) {
17710
+ fire();
17711
+ return;
17002
17712
  }
17713
+ this._pendingLocationChanges.set(key, fire);
17714
+ }
17715
+ /**
17716
+ * Report every panel whose location moved during the transaction that has
17717
+ * just closed. Each callback reads the panel's location at this point, so
17718
+ * what it reports is where the panel ended up.
17719
+ */
17720
+ flushLocationChanges() {
17721
+ if (this._pendingLocationChanges.size === 0) return;
17722
+ const pending = Array.from(this._pendingLocationChanges.values());
17723
+ this._pendingLocationChanges.clear();
17724
+ for (const fire of pending) fire();
17003
17725
  }
17004
17726
  /**
17005
17727
  * The origin of the operation currently in progress (`'user'` by default).
@@ -17040,6 +17762,18 @@ var DockviewComponent = class extends BaseGrid {
17040
17762
  origin: this._origin
17041
17763
  });
17042
17764
  }
17765
+ /**
17766
+ * Announce that `panel` has finished relocating. Must be called once the
17767
+ * move has settled: `to` is read from the panel's current group, so an
17768
+ * early call would report the source group as the destination.
17769
+ */
17770
+ fireDidMovePanel(panel, from) {
17771
+ this._onDidMovePanel.fire({
17772
+ panel,
17773
+ from,
17774
+ to: panel.group
17775
+ });
17776
+ }
17043
17777
  moveGroupOrPanel(options) {
17044
17778
  this.mutation("move", () => this._doMoveGroupOrPanel(options));
17045
17779
  }
@@ -17100,10 +17834,7 @@ var DockviewComponent = class extends BaseGrid {
17100
17834
  });
17101
17835
  });
17102
17836
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
17103
- this._onDidMovePanel.fire({
17104
- panel: removedPanel,
17105
- from: sourceGroup
17106
- });
17837
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17107
17838
  } else {
17108
17839
  /**
17109
17840
  * Dropping a panel to the extremities of a group which will place that panel
@@ -17122,10 +17853,7 @@ var DockviewComponent = class extends BaseGrid {
17122
17853
  const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
17123
17854
  if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
17124
17855
  this.gridview.moveView(sourceParentLocation, from, to);
17125
- this._onDidMovePanel.fire({
17126
- panel: this.getGroupPanel(sourceItemId),
17127
- from: sourceGroup
17128
- });
17856
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17129
17857
  return;
17130
17858
  }
17131
17859
  }
@@ -17153,13 +17881,10 @@ var DockviewComponent = class extends BaseGrid {
17153
17881
  }));
17154
17882
  this.doRemoveGroup(sourceGroup, { skipActive: true });
17155
17883
  const updatedTargetLocation = getRelativeLocation(destinationGridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
17156
- const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
17884
+ const newGroup = this.createGroupAtLocation(updatedTargetLocation, this.dropSizing(getGridLocation(destinationGroup.element)), void 0, destinationGridview);
17157
17885
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
17158
17886
  this.doSetGroupAndPanelActive(newGroup);
17159
- this._onDidMovePanel.fire({
17160
- panel: this.getGroupPanel(sourceItemId),
17161
- from: sourceGroup
17162
- });
17887
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17163
17888
  return;
17164
17889
  }
17165
17890
  if (sourceGroup.api.location.type === "edge") {
@@ -17173,13 +17898,10 @@ var DockviewComponent = class extends BaseGrid {
17173
17898
  skipSetActiveGroup: true
17174
17899
  }));
17175
17900
  if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
17176
- const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
17901
+ const newGroup = this.createGroupAtLocation(targetLocation, this.dropSizing(referenceLocation), void 0, destinationGridview);
17177
17902
  this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17178
17903
  this.doSetGroupAndPanelActive(newGroup);
17179
- this._onDidMovePanel.fire({
17180
- panel: removedPanel,
17181
- from: sourceGroup
17182
- });
17904
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17183
17905
  return;
17184
17906
  }
17185
17907
  const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
@@ -17188,13 +17910,10 @@ var DockviewComponent = class extends BaseGrid {
17188
17910
  }));
17189
17911
  const updatedReferenceLocation = getGridLocation(destinationGroup.element);
17190
17912
  const location = getRelativeLocation(destinationGridview.orientation, updatedReferenceLocation, destinationTarget);
17191
- this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
17913
+ this.movingLock(() => this.doAddGroup(targetGroup, location, this.dropSizing(updatedReferenceLocation), destinationGridview));
17192
17914
  this.setGroupLocationForRoot(targetGroup, destinationGridview);
17193
17915
  this.doSetGroupAndPanelActive(targetGroup);
17194
- this._onDidMovePanel.fire({
17195
- panel: this.getGroupPanel(sourceItemId),
17196
- from: sourceGroup
17197
- });
17916
+ this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
17198
17917
  } else {
17199
17918
  /**
17200
17919
  * The group we are removing from has many panels, we need to remove the panels we are moving,
@@ -17206,13 +17925,10 @@ var DockviewComponent = class extends BaseGrid {
17206
17925
  }));
17207
17926
  if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
17208
17927
  const dropLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
17209
- const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
17928
+ const group = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation), void 0, destinationGridview);
17210
17929
  this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
17211
17930
  this.doSetGroupAndPanelActive(group);
17212
- this._onDidMovePanel.fire({
17213
- panel: removedPanel,
17214
- from: sourceGroup
17215
- });
17931
+ this.fireDidMovePanel(removedPanel, sourceGroup);
17216
17932
  }
17217
17933
  }
17218
17934
  }
@@ -17247,16 +17963,13 @@ var DockviewComponent = class extends BaseGrid {
17247
17963
  });
17248
17964
  for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
17249
17965
  if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
17250
- for (const panel of removedPanels) this._onDidMovePanel.fire({
17251
- panel,
17252
- from: sourceGroup
17253
- });
17966
+ for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
17254
17967
  };
17255
17968
  let targetGroup;
17256
17969
  if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
17257
17970
  else {
17258
17971
  const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
17259
- targetGroup = this.createGroupAtLocation(dropLocation);
17972
+ targetGroup = this.createGroupAtLocation(dropLocation, this.dropSizing(referenceLocation));
17260
17973
  }
17261
17974
  if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
17262
17975
  addPanelsToGroup(targetGroup);
@@ -17271,10 +17984,12 @@ var DockviewComponent = class extends BaseGrid {
17271
17984
  this.mutation("maximize", () => super.exitMaximizedGroup());
17272
17985
  }
17273
17986
  _doMoveGroup(options) {
17987
+ var _mergedPanels;
17274
17988
  const from = options.from.group;
17275
17989
  const to = options.to.group;
17276
17990
  const target = options.to.position;
17277
17991
  let source = from;
17992
+ let mergedPanels;
17278
17993
  if (target === "center") {
17279
17994
  const activePanel = from.activePanel;
17280
17995
  const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
@@ -17292,6 +18007,7 @@ var DockviewComponent = class extends BaseGrid {
17292
18007
  skipSetGroupActive: true
17293
18008
  });
17294
18009
  });
18010
+ mergedPanels = panels;
17295
18011
  for (const snapshot of tabGroupSnapshots) {
17296
18012
  const newTabGroup = to.model.createTabGroup({
17297
18013
  label: snapshot.label,
@@ -17304,6 +18020,21 @@ var DockviewComponent = class extends BaseGrid {
17304
18020
  if (options.skipSetActive !== true) this.doSetGroupAndPanelActive(to);
17305
18021
  else if (!this.activePanel) this.doSetGroupAndPanelActive(to);
17306
18022
  } else {
18023
+ const isReorderWithinBranch = (() => {
18024
+ if (from.api.location.type === "edge" || to.api.location.type === "edge") return false;
18025
+ const root = this.getGridviewForGroup(from);
18026
+ if (root !== this.getGridviewForGroup(to)) return false;
18027
+ let fromLocation;
18028
+ let toLocation;
18029
+ try {
18030
+ fromLocation = getGridLocation(from.element);
18031
+ toLocation = getGridLocation(to.element);
18032
+ } catch (_unused) {
18033
+ return false;
18034
+ }
18035
+ if (fromLocation.length === 0 || toLocation.length === 0) return false;
18036
+ return sequenceEquals(tail(fromLocation)[0], tail(toLocation)[0]) && getLocationOrientation(root.orientation, toLocation) === getDirectionOrientation(target);
18037
+ })();
17307
18038
  if (from.api.location.type === "edge") {
17308
18039
  /**
17309
18040
  * Edge groups are permanent structural elements and must
@@ -17369,23 +18100,17 @@ var DockviewComponent = class extends BaseGrid {
17369
18100
  const referenceLocation = getGridLocation(to.element);
17370
18101
  const dropLocation = getRelativeLocation(destGridview.orientation, referenceLocation, target);
17371
18102
  let size;
17372
- switch (destGridview.orientation) {
17373
- case "VERTICAL":
17374
- size = referenceLocation.length % 2 == 0 ? from.api.width : from.api.height;
17375
- break;
17376
- case "HORIZONTAL":
17377
- size = referenceLocation.length % 2 == 0 ? from.api.height : from.api.width;
17378
- break;
18103
+ if (isReorderWithinBranch) size = getDirectionOrientation(target) === "HORIZONTAL" ? from.api.width : from.api.height;
18104
+ else {
18105
+ var _referenceLocation;
18106
+ size = Sizing.Split((_referenceLocation = referenceLocation[referenceLocation.length - 1]) !== null && _referenceLocation !== void 0 ? _referenceLocation : 0);
17379
18107
  }
17380
18108
  destGridview.addView(source, size, dropLocation);
17381
18109
  this.setGroupLocationForRoot(source, destGridview);
17382
18110
  }
17383
18111
  }
17384
- source.panels.forEach((panel) => {
17385
- this._onDidMovePanel.fire({
17386
- panel,
17387
- from
17388
- });
18112
+ ((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
18113
+ this.fireDidMovePanel(panel, from);
17389
18114
  });
17390
18115
  this.debouncedUpdateAllPositions();
17391
18116
  if (options.skipSetActive === false) {
@@ -17503,6 +18228,16 @@ var DockviewComponent = class extends BaseGrid {
17503
18228
  });
17504
18229
  return panel;
17505
18230
  }
18231
+ /**
18232
+ * Sizing for a group created by a drop next to `referenceLocation`: half
18233
+ * of the group the overlay was drawn over, leaving its siblings alone, so
18234
+ * the panel lands in the region the overlay indicated (#1612). Gridview
18235
+ * rewrites the index to 0 when a cross-axis drop wraps the reference in a
18236
+ * new branch, so one value serves both paths.
18237
+ */
18238
+ dropSizing(referenceLocation) {
18239
+ return Sizing.Split(referenceLocation.length === 0 ? 0 : referenceLocation[referenceLocation.length - 1]);
18240
+ }
17506
18241
  createGroupAtLocation(location, size, options, gridview = this.gridview) {
17507
18242
  const group = this.createGroup(options);
17508
18243
  this.doAddGroup(group, location, size, gridview);
@@ -17562,22 +18297,22 @@ var DockviewComponent = class extends BaseGrid {
17562
18297
  return location.length % 2 == 1 ? rootOrientation : orthogonal(rootOrientation);
17563
18298
  }
17564
18299
  updateTheme() {
17565
- var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe23, _this$_popoutWindowSe24, _this$_shellManager4, _theme$edgeGroupColla, _theme$tabGroupIndica;
18300
+ var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe23, _this$_popoutWindowSe24, _this$_shellManager5, _theme$edgeGroupColla, _theme$tabGroupIndica;
17566
18301
  const theme = (_this$_options$theme = this._options.theme) !== null && _this$_options$theme !== void 0 ? _this$_options$theme : themeAbyss;
17567
18302
  (_this$_shellThemeClas = this._shellThemeClassnames) === null || _this$_shellThemeClas === void 0 || _this$_shellThemeClas.setClassNames(theme.className);
17568
18303
  const gap = (_theme$gap = theme.gap) !== null && _theme$gap !== void 0 ? _theme$gap : 0;
17569
18304
  this.gridview.margin = gap;
17570
18305
  for (const floating of this.floatingGroups) floating.gridview.margin = gap;
17571
18306
  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;
17572
- (_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);
18307
+ (_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);
17573
18308
  if (theme.dndOverlayBorder === void 0) {
17574
- var _this$_shellManager5;
18309
+ var _this$_shellManager6;
17575
18310
  this.element.style.removeProperty("--dv-drag-over-border");
17576
- (_this$_shellManager5 = this._shellManager) === null || _this$_shellManager5 === void 0 || _this$_shellManager5.element.style.removeProperty("--dv-drag-over-border");
18311
+ (_this$_shellManager6 = this._shellManager) === null || _this$_shellManager6 === void 0 || _this$_shellManager6.element.style.removeProperty("--dv-drag-over-border");
17577
18312
  } else {
17578
- var _this$_shellManager6;
18313
+ var _this$_shellManager7;
17579
18314
  this.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
17580
- (_this$_shellManager6 = this._shellManager) === null || _this$_shellManager6 === void 0 || _this$_shellManager6.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
18315
+ (_this$_shellManager7 = this._shellManager) === null || _this$_shellManager7 === void 0 || _this$_shellManager7.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
17581
18316
  }
17582
18317
  switch (theme.dndOverlayMounting) {
17583
18318
  case "absolute":
@@ -18480,6 +19215,8 @@ exports.AdvancedDnDModule = AdvancedDnDModule;
18480
19215
  exports.AdvancedDnDService = AdvancedDnDService;
18481
19216
  exports.BaseGrid = BaseGrid;
18482
19217
  exports.ContentContainer = ContentContainer;
19218
+ exports.ContextMenuController = ContextMenuController;
19219
+ exports.ContextMenuModule = ContextMenuModule;
18483
19220
  exports.DEFAULT_TAB_GROUP_COLORS = DEFAULT_TAB_GROUP_COLORS;
18484
19221
  exports.DefaultDockviewDeserialzier = DefaultDockviewDeserialzier;
18485
19222
  exports.DefaultTab = DefaultTab;