dockview
Version:
Docking layout manager — tabs, groups, grids, splitviews, drag and drop, floating panels
18,703 lines • 919 kB
JavaScript
/**
* dockview
* @version 8.2.0
* @link https://github.com/dockview/dockview
* @license MIT
*/
(function(global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.dockview = {})));
})(this, function(exports) {
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
//#region \0cssinject:/home/runner/work/dockview/dockview/packages/dockview/dist/styles/dockview.css.mjs
if (typeof document !== "undefined") {
const s = document.createElement("style");
s.textContent = ".dv-drop-target-container {\n position: absolute;\n z-index: 9999;\n top: 0px;\n left: 0px;\n height: 100%;\n width: 100%;\n pointer-events: none;\n overflow: hidden;\n --dv-transition-duration: 300ms;\n}\n.dv-drop-target-container .dv-drop-target-anchor {\n position: relative;\n border: var(--dv-drag-over-border);\n background-color: var(--dv-drag-over-background-color);\n opacity: 1;\n /* GPU optimizations */\n will-change: transform, opacity;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n contain: layout paint;\n transition: opacity var(--dv-transition-duration) ease-in, top var(--dv-transition-duration) ease-out, left var(--dv-transition-duration) ease-out, width var(--dv-transition-duration) ease-out, height var(--dv-transition-duration) ease-out;\n}\n.dv-drop-target {\n position: relative;\n --dv-transition-duration: 70ms;\n}\n.dv-drop-target > .dv-drop-target-dropzone {\n position: absolute;\n left: 0px;\n top: 0px;\n height: 100%;\n width: 100%;\n z-index: 1000;\n pointer-events: none;\n}\n.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection {\n position: relative;\n box-sizing: border-box;\n height: 100%;\n width: 100%;\n border: var(--dv-drag-over-border);\n background-color: var(--dv-drag-over-background-color);\n transition: top var(--dv-transition-duration) ease-out, left var(--dv-transition-duration) ease-out, width var(--dv-transition-duration) ease-out, height var(--dv-transition-duration) ease-out, opacity var(--dv-transition-duration) ease-out;\n will-change: transform;\n pointer-events: none;\n}\n.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection.dv-drop-target-top.dv-drop-target-small-vertical {\n border-top: 1px solid var(--dv-drag-over-border-color);\n}\n.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection.dv-drop-target-bottom.dv-drop-target-small-vertical {\n border-bottom: 1px solid var(--dv-drag-over-border-color);\n}\n.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection.dv-drop-target-left.dv-drop-target-small-horizontal {\n border-left: 1px solid var(--dv-drag-over-border-color);\n}\n.dv-drop-target > .dv-drop-target-dropzone > .dv-drop-target-selection.dv-drop-target-right.dv-drop-target-small-horizontal {\n border-right: 1px solid var(--dv-drag-over-border-color);\n}\n\n.dv-dnd-compass {\n z-index: 1001;\n}\n\n.dv-dnd-compass-cell {\n box-sizing: border-box;\n border-radius: 2px;\n border: 1px solid var(--dv-dnd-compass-color, #1f9cf0);\n background-color: var(--dv-dnd-compass-cell-color, rgba(31, 156, 240, 0.25));\n}\n\n.dv-dnd-compass-cell-edge {\n border-style: dashed;\n background-color: var(--dv-dnd-compass-edge-cell-color, rgba(31, 156, 240, 0.12));\n}\n\n.dv-dnd-compass-cell-active {\n background-color: var(--dv-dnd-compass-active-cell-color, rgba(31, 156, 240, 0.5));\n border-style: solid;\n}\n\n.dv-dnd-compass-edge-preview {\n z-index: 1000;\n box-sizing: border-box;\n background-color: var(--dv-drag-over-background-color);\n border: var(--dv-drag-over-border);\n}\n.dv-dragged {\n transform: translate3d(0px, 0px, 0px); /* forces tab to be drawn on a separate layer (see https://github.com/microsoft/vscode/issues/18733) */\n}\n\n/* Applied to the ghost tab clone during drag so it shows the same border as a focused tab */\n.dv-tab-ghost-drag {\n position: relative;\n}\n.dv-tab-ghost-drag::after {\n position: absolute;\n content: \"\";\n height: 100%;\n width: 100%;\n top: 0px;\n left: 0px;\n pointer-events: none;\n outline: 1px solid var(--dv-tab-divider-color) !important;\n outline-offset: -1px;\n z-index: 5;\n}\n\n.dv-tab {\n flex-shrink: 0;\n}\n.dv-tab:focus-visible, .dv-tab:has(:focus-visible) {\n position: relative;\n}\n.dv-tab:focus-visible::after, .dv-tab:has(:focus-visible)::after {\n position: absolute;\n content: \"\";\n height: 100%;\n width: 100%;\n top: 0px;\n left: 0px;\n pointer-events: none;\n outline: 1px solid var(--dv-tab-divider-color) !important;\n outline-offset: -1px;\n z-index: 5;\n}\n.dv-tab.dv-tab-dragging .dv-default-tab-action {\n background-color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n.dv-tab.dv-active-tab .dv-default-tab .dv-default-tab-action {\n visibility: visible;\n}\n.dv-tab.dv-inactive-tab .dv-default-tab .dv-default-tab-action {\n visibility: hidden;\n}\n.dv-tab.dv-inactive-tab .dv-default-tab:hover .dv-default-tab-action {\n visibility: visible;\n}\n@media (hover: none) {\n .dv-tab.dv-inactive-tab .dv-default-tab .dv-default-tab-action {\n visibility: visible;\n }\n}\n.dv-tab .dv-default-tab {\n position: relative;\n height: 100%;\n width: 100%;\n display: flex;\n align-items: center;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.dv-tab .dv-default-tab .dv-default-tab-content {\n flex-grow: 1;\n margin-right: 4px;\n}\n.dv-tab .dv-default-tab .dv-default-tab-action {\n padding: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n border: none;\n background: none;\n color: inherit;\n font: inherit;\n cursor: pointer;\n}\n.dv-tab .dv-default-tab .dv-default-tab-action:hover {\n border-radius: 2px;\n background-color: var(--dv-icon-hover-background-color);\n}\n@media (pointer: coarse) {\n .dv-tab .dv-default-tab .dv-default-tab-action {\n padding: 8px;\n }\n}\n.dv-tabs-overflow-dropdown-default {\n height: 100%;\n box-sizing: border-box;\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n margin: var(--dv-tab-margin);\n display: flex;\n align-items: center;\n flex-shrink: 0;\n padding: 0.25rem 0.5rem;\n cursor: pointer;\n}\n.dv-tabs-overflow-dropdown-default > span {\n padding-left: 0.25rem;\n}\n.dv-tabs-overflow-dropdown-default > svg {\n transform: rotate(90deg);\n}\n.dv-tabs-overflow-dropdown-default:hover {\n border-radius: 2px;\n background-color: var(--dv-icon-hover-background-color);\n}\n.dv-tabs-container {\n display: flex;\n position: relative;\n height: 100%;\n overflow: auto;\n scrollbar-width: thin;\n /**\n * Multi-row (wrapping) tabs. Inert until the `MultiRowTabsModule` toggles\n * `.dv-tabs-container--wrap` on this element (`overflow.mode: 'wrap'`):\n * tabs wrap onto multiple rows instead of clipping into the dropdown, and\n * the strip grows to fit (the header grows via the `:has()` rule in\n * tabsContainer.scss; the free header-aware content-sizing seam then shrinks\n * the content area to match). Restricted to horizontal headers in v1.\n */\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) {\n flex-wrap: wrap;\n height: auto;\n overflow: visible;\n align-content: flex-start;\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: var(--dv-tabs-and-actions-container-height);\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical).dv-tabs-container--wrap-capped {\n max-height: calc(var(--dv-tabs-and-actions-container-height) * var(--dv-max-tab-rows));\n overflow: hidden;\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab--reorder-before::after,\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab--reorder-after::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n z-index: 10;\n pointer-events: none;\n background-color: var(--dv-drag-over-border-color);\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab--reorder-before::after {\n left: 0;\n}\n.dv-tabs-container.dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab--reorder-after::after {\n right: 0;\n}\n.dv-tabs-container {\n /**\n * Multi-column (wrapping) tabs for a vertical (edge-group) header: the\n * mirror of the horizontal rule above with the axes swapped. The main axis\n * is vertical (bounded by the header height, so overflowing tabs wrap) and\n * the cross axis is horizontal (grows into additional columns). The header\n * grows in width via the `:has()` rule in tabsContainer.scss.\n *\n * Column flow direction under `writing-mode: vertical-rl` differs\n * between left and right header positions; this needs visual verification\n * in a real browser (jsdom lays out neither writing-mode nor flex-wrap).\n */\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical {\n flex-wrap: wrap;\n width: auto;\n height: 100%;\n max-height: 100%;\n overflow: visible;\n align-content: flex-start;\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: var(--dv-tabs-and-actions-container-height);\n height: var(--dv-wrap-vertical-tab-height, auto);\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical.dv-tabs-container--wrap-capped {\n max-width: calc(var(--dv-tabs-and-actions-container-height) * var(--dv-max-tab-rows));\n overflow: hidden;\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab--reorder-before::after,\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab--reorder-after::after {\n content: \"\";\n position: absolute;\n left: 0;\n right: 0;\n height: 2px;\n z-index: 10;\n pointer-events: none;\n background-color: var(--dv-drag-over-border-color);\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab--reorder-before::after {\n top: 0;\n}\n.dv-tabs-container.dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab--reorder-after::after {\n bottom: 0;\n}\n.dv-tabs-container {\n /* GPU optimizations for smooth scrolling */\n will-change: scroll-position;\n transform: translate3d(0, 0, 0);\n /**\n * Stop scroll-chaining at the tab strip so that wheel / trackpad\n * overscroll past the strip's edges doesn't trigger the browser's\n * swipe-to-go-back-or-forward gesture (and doesn't scroll the page\n * either). `contain` keeps the native bounce visuals; `none` would\n * also disable them.\n */\n overscroll-behavior: contain;\n /**\n * Empty space between tabs (and the scrollbar lane) keeps pan-x so a\n * flick on those areas produces native momentum scroll. The tab and\n * chip elements themselves opt out (`touch-action: none`) so the\n * pointer drag source owns the gesture from pointerdown - a flick on\n * a tab or chip always becomes a drag, regardless of direction.\n */\n touch-action: pan-x;\n}\n.dv-tabs-container.dv-tabs-container-vertical {\n width: 100%;\n height: fit-content;\n max-height: 100%;\n writing-mode: vertical-rl;\n touch-action: pan-y;\n}\n.dv-tabs-container.dv-horizontal .dv-tab:not(:first-child)::before, .dv-tabs-container.dv-vertical .dv-tab:not(:first-child)::before {\n content: \" \";\n position: absolute;\n top: 0;\n left: 0;\n z-index: 5;\n pointer-events: none;\n background-color: var(--dv-tab-divider-color);\n}\n.dv-tabs-container.dv-horizontal .dv-tab:not(:first-child)::before {\n width: 1px;\n height: 100%;\n}\n.dv-tabs-container.dv-vertical .dv-tab:not(:first-child)::before {\n width: 100%;\n height: 1px;\n}\n.dv-tabs-container::-webkit-scrollbar {\n height: 3px;\n}\n.dv-tabs-container {\n /* Track */\n}\n.dv-tabs-container::-webkit-scrollbar-track {\n background: transparent;\n}\n.dv-tabs-container {\n /* Handle */\n}\n.dv-tabs-container::-webkit-scrollbar-thumb {\n background: var(--dv-tabs-container-scrollbar-color);\n}\n\n.dv-scrollable > .dv-tabs-container {\n overflow: hidden;\n}\n\n.dv-tab {\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n outline: none;\n padding: 0.25rem 0.5rem;\n cursor: pointer;\n position: relative;\n box-sizing: border-box;\n font-size: var(--dv-tab-font-size);\n margin: var(--dv-tab-margin);\n touch-action: none;\n}\n.dv-tab.dv-tab--shifting {\n will-change: transform, margin-left, margin-right, margin-top, margin-bottom;\n transition: transform var(--dv-transition-duration, 200ms) ease-out, margin-left var(--dv-transition-duration, 200ms) ease-out, margin-right var(--dv-transition-duration, 200ms) ease-out, margin-top var(--dv-transition-duration, 200ms) ease-out, margin-bottom var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tab.dv-tab--dragging {\n width: 0 !important;\n min-width: 0 !important;\n padding: 0 !important;\n margin: 0 !important;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n transition: width var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tab.dv-tab--group-collapsed {\n width: 0 !important;\n min-width: 0 !important;\n padding: 0 !important;\n margin: 0 !important;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n transition: width var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tab.dv-tab--group-expanding {\n transition: width var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n\n.dv-tab.dv-tab--pinned:has(.dv-tab-pin) {\n display: flex;\n align-items: center;\n}\n.dv-tab.dv-tab--pinned .dv-tab-pin {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n margin-right: 4px;\n opacity: 0.7;\n pointer-events: none;\n}\n.dv-tab.dv-tab--pinned .dv-tab-pin .dv-svg {\n width: 11px;\n height: 11px;\n}\n.dv-tab.dv-tab--pinned .dv-default-tab {\n min-width: 0;\n}\n.dv-tab.dv-tab--pinned .dv-default-tab-action {\n display: none;\n}\n\n.dv-tab.dv-tab--pinned-compact .dv-default-tab-content {\n display: none;\n}\n.dv-tab.dv-tab--pinned-compact .dv-tab-pin {\n margin-right: 0;\n}\n\n.dv-tabs-container:not(.dv-tabs-container--wrap):not(.dv-tabs-container-vertical) > .dv-tab.dv-tab--pinned-sticky {\n position: sticky;\n left: var(--dv-pinned-sticky-left, 0);\n z-index: 2;\n}\n\n.dv-tabs-and-actions-container.dv-tabs-and-actions-container--pinned-row {\n flex-wrap: wrap;\n height: auto;\n min-height: var(--dv-tabs-and-actions-container-height);\n align-content: flex-start;\n}\n.dv-tabs-and-actions-container.dv-tabs-and-actions-container--pinned-row .dv-pinned-row {\n flex: 0 0 100%;\n order: -1;\n}\n.dv-tabs-and-actions-container.dv-tabs-and-actions-container--pinned-row .dv-tab.dv-tab--pinned {\n display: none;\n}\n\n.dv-pinned-row {\n display: flex;\n align-items: center;\n gap: 2px;\n box-sizing: border-box;\n width: 100%;\n min-height: var(--dv-tabs-and-actions-container-height);\n padding: 2px 4px;\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom: 1px solid var(--dv-tab-divider-color);\n}\n\n.dv-pinned-tab {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n box-sizing: border-box;\n padding: 0.25rem 0.5rem;\n font-size: var(--dv-tab-font-size);\n cursor: pointer;\n user-select: none;\n background-color: var(--dv-activegroup-hiddenpanel-tab-background-color);\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n}\n.dv-pinned-tab.dv-pinned-tab--active {\n background-color: var(--dv-activegroup-visiblepanel-tab-background-color);\n color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n.dv-pinned-tab .dv-pinned-tab-label {\n max-width: 120px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.dv-pinned-tab .dv-pinned-tab-unpin {\n flex-shrink: 0;\n opacity: 0.6;\n}\n.dv-pinned-tab .dv-pinned-tab-unpin:hover {\n opacity: 1;\n}\n.dv-pinned-tab.dv-pinned-tab--dragging {\n opacity: 0.5;\n}\n.dv-pinned-tab.dv-pinned-tab--drop-before, .dv-pinned-tab.dv-pinned-tab--drop-after {\n position: relative;\n}\n.dv-pinned-tab.dv-pinned-tab--drop-before::after, .dv-pinned-tab.dv-pinned-tab--drop-after::after {\n content: \"\";\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n z-index: 10;\n pointer-events: none;\n background-color: var(--dv-drag-over-border-color);\n}\n.dv-pinned-tab.dv-pinned-tab--drop-before::after {\n left: 0;\n}\n.dv-pinned-tab.dv-pinned-tab--drop-after::after {\n right: 0;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .dv-tab {\n transition: none !important;\n }\n .dv-tab-group-chip {\n transition: none !important;\n }\n}\n.dv-tab-group-chip {\n display: inline-flex;\n align-items: center;\n align-self: center;\n padding: var(--dv-tab-group-chip-padding);\n margin: 0 8px 0 8px;\n border-radius: var(--dv-tab-group-chip-border-radius);\n font-size: var(--dv-tab-group-chip-font-size);\n cursor: pointer;\n user-select: none;\n white-space: nowrap;\n box-sizing: border-box;\n line-height: 1;\n touch-action: none;\n background-color: var(--dv-tab-group-color);\n color: white;\n}\n.dv-tab-group-chip.dv-tab-group-chip--accent-off {\n background-color: transparent;\n color: inherit;\n}\n.dv-tab-group-chip.dv-tab-group-chip--shifting {\n will-change: margin-left;\n transition: margin-left var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tab-group-chip.dv-tab-group-chip--dragging {\n width: 0 !important;\n min-width: 0 !important;\n padding: 0 !important;\n margin: 0 !important;\n overflow: hidden;\n opacity: 0;\n pointer-events: none;\n transition: width var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tab-group-chip .dv-tab-group-chip-label--empty {\n display: none;\n}\n.dv-tab-group-chip:has(.dv-tab-group-chip-label--empty) {\n position: relative;\n width: 12px;\n height: 12px;\n padding: 0;\n border-radius: 50%;\n}\n.dv-tab-group-chip:has(.dv-tab-group-chip-label--empty)::before {\n content: \"\";\n position: absolute;\n inset: -8px;\n}\n\n.dv-tab-group-underline {\n position: absolute;\n bottom: 0;\n opacity: var(--dv-tab-group-line-opacity);\n pointer-events: none;\n z-index: 10;\n}\n\n.dv-tab-group-chip-continuation {\n position: absolute;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n opacity: var(--dv-tab-group-line-opacity);\n pointer-events: none;\n z-index: 10;\n}\n\n.dv-groupview-header-bottom .dv-tab-group-underline {\n bottom: auto;\n top: 0;\n}\n\n.dv-tabs-container-vertical .dv-tab-group-underline {\n bottom: auto;\n left: 0;\n}\n\n.dv-tabs-container-vertical .dv-tab-group-chip {\n margin: 8px 0 8px 0;\n}\n.dv-tabs-container-vertical .dv-tab {\n padding: 0.5rem 0.25rem;\n}\n.dv-tabs-container-vertical .dv-tab.dv-tab--group-collapsed {\n height: 0 !important;\n min-height: 0 !important;\n width: auto !important;\n min-width: initial !important;\n transition: height var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tabs-container-vertical .dv-tab.dv-tab--group-expanding {\n transition: height var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n.dv-tabs-container-vertical .dv-tab.dv-tab--dragging {\n height: 0 !important;\n min-height: 0 !important;\n width: auto !important;\n min-width: initial !important;\n transition: height var(--dv-transition-duration, 200ms) ease-out, padding var(--dv-transition-duration, 200ms) ease-out, margin var(--dv-transition-duration, 200ms) ease-out, opacity var(--dv-transition-duration, 200ms) ease-out;\n}\n\n.dv-tabs-overflow-container {\n flex-direction: column;\n height: unset;\n font-size: var(--dv-tabs-and-actions-container-font-size);\n max-height: min(50vh, 400px);\n overflow-y: auto;\n border: 1px solid var(--dv-tab-divider-color);\n background-color: var(--dv-group-view-background-color);\n /* Scrollbar styling for webkit browsers */\n}\n.dv-tabs-overflow-container::-webkit-scrollbar {\n width: 6px;\n}\n.dv-tabs-overflow-container::-webkit-scrollbar-track {\n background: transparent;\n}\n.dv-tabs-overflow-container::-webkit-scrollbar-thumb {\n background: var(--dv-tabs-container-scrollbar-color);\n border-radius: 3px;\n}\n.dv-tabs-overflow-container {\n /* Firefox scrollbar */\n scrollbar-width: thin;\n}\n.dv-tabs-overflow-container .dv-tab:not(:last-child) {\n border-bottom: 1px solid var(--dv-tab-divider-color);\n}\n.dv-tabs-overflow-container .dv-active-tab {\n background-color: var(--dv-activegroup-visiblepanel-tab-background-color);\n color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n.dv-tabs-overflow-container .dv-inactive-tab {\n background-color: var(--dv-activegroup-hiddenpanel-tab-background-color);\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-group-header {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n font-size: 0.8em;\n font-weight: 600;\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n cursor: pointer;\n border-bottom: 1px solid var(--dv-tab-divider-color);\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-group-header:hover {\n background-color: var(--dv-icon-hover-background-color);\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-group-color {\n display: inline-block;\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n background-color: var(--dv-tab-group-color);\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-group-label {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-group-collapsed-badge {\n font-size: 0.75em;\n font-weight: 400;\n opacity: 0.7;\n padding: 1px 4px;\n border-radius: 3px;\n background-color: var(--dv-tab-divider-color);\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-pinned-header {\n cursor: default;\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-pinned-header:hover {\n background-color: transparent;\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-pinned-header .dv-tabs-overflow-pinned-icon {\n display: inline-flex;\n align-items: center;\n flex-shrink: 0;\n opacity: 0.7;\n}\n.dv-tabs-overflow-container .dv-tabs-overflow-pinned-header .dv-tabs-overflow-pinned-icon .dv-svg {\n width: 11px;\n height: 11px;\n}\n.dv-tabs-overflow-container .dv-tab.dv-tab--grouped {\n padding-left: 16px;\n}\n\n/*\n * Advanced overflow popover (AdvancedOverflowModule): the same container as the\n * free list, upgraded in place with a search input, a scrollable listbox, and a\n * keyboard-highlighted active row. Styles live in core so the module ships no\n * CSS; they are inert unless the module renders this body.\n */\n.dv-tabs-overflow-container.dv-tabs-overflow-advanced {\n min-width: 220px;\n}\n.dv-tabs-overflow-container.dv-tabs-overflow-advanced .dv-tabs-overflow-search {\n box-sizing: border-box;\n width: 100%;\n padding: 6px 8px;\n border: none;\n border-bottom: 1px solid var(--dv-tab-divider-color);\n outline: none;\n font-size: inherit;\n font-family: inherit;\n color: var(--dv-activegroup-visiblepanel-tab-color);\n background-color: var(--dv-group-view-background-color);\n}\n.dv-tabs-overflow-container.dv-tabs-overflow-advanced .dv-tabs-overflow-list {\n display: flex;\n flex-direction: column;\n outline: none;\n}\n.dv-tabs-overflow-container.dv-tabs-overflow-advanced .dv-tab.dv-tabs-overflow-option--focused {\n outline: 1px solid var(--dv-tab-divider-color);\n outline-offset: -1px;\n background-color: var(--dv-icon-hover-background-color);\n}\n.dv-tabs-and-actions-container {\n display: flex;\n background-color: var(--dv-tabs-and-actions-container-background-color);\n flex-shrink: 0;\n box-sizing: border-box;\n height: var(--dv-tabs-and-actions-container-height);\n font-size: var(--dv-tabs-and-actions-container-font-size);\n /**\n * Multi-row (wrapping) tabs: when the tab list wraps onto multiple rows\n * (`.dv-tabs-container--wrap`, toggled by the `MultiRowTabsModule`), the\n * header grows to fit instead of clipping. Inert otherwise: the single-row\n * fixed height above is unchanged.\n */\n}\n.dv-tabs-and-actions-container:has(.dv-tabs-container--wrap) {\n height: auto;\n min-height: var(--dv-tabs-and-actions-container-height);\n align-items: flex-start;\n}\n.dv-tabs-and-actions-container:has(.dv-tabs-container--wrap) > .dv-pre-actions-container,\n.dv-tabs-and-actions-container:has(.dv-tabs-container--wrap) > .dv-left-actions-container,\n.dv-tabs-and-actions-container:has(.dv-tabs-container--wrap) > .dv-right-actions-container,\n.dv-tabs-and-actions-container:has(.dv-tabs-container--wrap) > .dv-void-container {\n height: var(--dv-tabs-and-actions-container-height);\n}\n.dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-scrollable {\n flex-grow: 1;\n}\n.dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-tabs-container {\n flex-grow: 1;\n}\n.dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-tabs-container .dv-tab {\n flex-grow: 1;\n padding: 0px;\n}\n.dv-tabs-and-actions-container.dv-single-tab.dv-full-width-single-tab .dv-void-container {\n flex-grow: 0;\n}\n.dv-tabs-and-actions-container .dv-void-container {\n display: flex;\n flex-grow: 1;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n touch-action: none;\n}\n.dv-tabs-and-actions-container .dv-void-container.dv-draggable {\n cursor: grab;\n}\n.dv-tabs-and-actions-container .dv-right-actions-container {\n display: flex;\n}\n.dv-tabs-and-actions-container .dv-right-actions-container.dv-right-actions-container-vertical {\n flex-direction: column;\n}\n.dv-tabs-and-actions-container.dv-groupview-header-vertical {\n flex-direction: column;\n height: auto;\n width: var(--dv-tabs-and-actions-container-height);\n}\n.dv-tabs-and-actions-container.dv-groupview-header-vertical:has(.dv-tabs-container--wrap) {\n width: auto;\n min-width: var(--dv-tabs-and-actions-container-height);\n}\n.dv-tabs-and-actions-container.dv-groupview-header-vertical:has(.dv-tabs-container--wrap) > .dv-scrollable {\n align-self: stretch;\n}\n.dv-watermark {\n display: flex;\n height: 100%;\n}\n.dv-dockview {\n position: relative;\n background-color: var(--dv-group-view-background-color);\n}\n.dv-dockview .dv-watermark-container {\n position: absolute;\n top: 0px;\n left: 0px;\n height: 100%;\n width: 100%;\n z-index: 1;\n}\n.dv-dockview .dv-overlay-render-container {\n position: relative;\n}\n\n.dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n background-color: var(--dv-activegroup-visiblepanel-tab-background-color);\n color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n.dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-inactive-tab {\n background-color: var(--dv-activegroup-hiddenpanel-tab-background-color);\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n}\n.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n background-color: var(--dv-inactivegroup-visiblepanel-tab-background-color);\n color: var(--dv-inactivegroup-visiblepanel-tab-color);\n}\n.dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-inactive-tab {\n background-color: var(--dv-inactivegroup-hiddenpanel-tab-background-color);\n color: var(--dv-inactivegroup-hiddenpanel-tab-color);\n}\n\n/**\n * when a tab is dragged we lose the above stylings because they are conditional on parent elements\n * therefore we also set some stylings for the dragging event\n **/\n.dv-tab.dv-tab-dragging {\n background-color: var(--dv-activegroup-visiblepanel-tab-background-color);\n color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n\n.dv-keyboard-docking-hint {\n position: absolute;\n left: 50%;\n bottom: 8px;\n transform: translateX(-50%);\n z-index: 100;\n max-width: 90%;\n padding: 4px 10px;\n border-radius: 4px;\n font-size: 12px;\n line-height: 1.4;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n pointer-events: none;\n background-color: var(--dv-context-menu-background-color, var(--dv-group-view-background-color));\n color: var(--dv-activegroup-visiblepanel-tab-color);\n border: 1px solid var(--dv-tab-divider-color);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);\n}\n.dv-groupview {\n display: flex;\n height: 100%;\n background-color: var(--dv-group-view-background-color);\n overflow: hidden;\n flex-direction: column;\n}\n.dv-groupview:focus {\n outline: none;\n}\n.dv-groupview > .dv-content-container {\n flex-grow: 1;\n min-height: 0;\n outline: none;\n}\n.dv-groupview.dv-groupview-header-bottom {\n flex-direction: column-reverse;\n}\n.dv-groupview.dv-groupview-header-left {\n flex-direction: row;\n}\n.dv-groupview.dv-groupview-header-right {\n flex-direction: row-reverse;\n}\n.dv-groupview.dv-groupview-edge.dv-edge-collapsed > .dv-content-container {\n display: none;\n}\n.dv-root-wrapper {\n height: 100%;\n width: 100%;\n}\n.dv-grid-view,\n.dv-branch-node {\n height: 100%;\n width: 100%;\n}\n.dv-debug .dv-resize-container .dv-resize-handle-top {\n background-color: red;\n}\n.dv-debug .dv-resize-container .dv-resize-handle-bottom {\n background-color: green;\n}\n.dv-debug .dv-resize-container .dv-resize-handle-left {\n background-color: yellow;\n}\n.dv-debug .dv-resize-container .dv-resize-handle-right {\n background-color: blue;\n}\n.dv-debug .dv-resize-container .dv-resize-handle-topleft,\n.dv-debug .dv-resize-container .dv-resize-handle-topright,\n.dv-debug .dv-resize-container .dv-resize-handle-bottomleft,\n.dv-debug .dv-resize-container .dv-resize-handle-bottomright {\n background-color: cyan;\n}\n\n.dv-floating-overlay-host {\n position: absolute;\n pointer-events: none;\n}\n.dv-floating-overlay-host > .dv-resize-container {\n pointer-events: auto;\n}\n\n.dv-edge-peek {\n background-color: var(--dv-group-view-background-color);\n border: 1px solid var(--dv-separator-border);\n box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4);\n}\n.dv-edge-peek > * {\n width: 100%;\n height: 100%;\n overflow: auto;\n}\n\n.dv-auto-edge-band {\n position: absolute;\n pointer-events: none;\n z-index: var(--dv-overlay-z-index, 999);\n background-color: var(--dv-edge-dock-indicator-color);\n box-shadow: 0 0 6px var(--dv-edge-dock-indicator-color);\n}\n\n.dv-edge-peek-header {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 0 4px 0 8px;\n box-sizing: border-box;\n background-color: var(--dv-group-view-background-color);\n border: 1px solid var(--dv-separator-border);\n border-bottom: none;\n color: var(--dv-activegroup-visiblepanel-tab-color);\n font-size: 12px;\n}\n.dv-edge-peek-header .dv-edge-peek-pin,\n.dv-edge-peek-header .dv-edge-peek-close {\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n cursor: pointer;\n border: none;\n padding: 4px;\n color: inherit;\n background: transparent;\n outline: none;\n}\n.dv-edge-peek-header .dv-edge-peek-pin:hover,\n.dv-edge-peek-header .dv-edge-peek-close:hover {\n border-radius: 2px;\n background-color: var(--dv-icon-hover-background-color);\n}\n.dv-edge-peek-header .dv-edge-peek-pin:focus-visible,\n.dv-edge-peek-header .dv-edge-peek-close:focus-visible {\n border-radius: 2px;\n outline: 1px solid var(--dv-tab-divider-color);\n outline-offset: -1px;\n}\n\n.dv-edge-peek-title {\n flex: 1 1 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.dv-resize-container > .dv-grid-view {\n width: 100%;\n height: 100%;\n}\n\n.dv-resize-container-with-titlebar {\n display: flex;\n flex-direction: column;\n}\n.dv-resize-container-with-titlebar > .dv-floating-titlebar {\n flex: 0 0 auto;\n}\n.dv-resize-container-with-titlebar > .dv-grid-view {\n height: auto;\n flex: 1 1 0;\n min-height: 0;\n}\n\n.dv-floating-titlebar {\n box-sizing: border-box;\n flex-shrink: 0;\n height: var(--dv-floating-titlebar-height, 22px);\n background-color: var(--dv-floating-titlebar-background-color);\n border-bottom: var(--dv-floating-titlebar-border-bottom, none);\n user-select: none;\n touch-action: none;\n}\n.dv-floating-titlebar.dv-draggable {\n cursor: grab;\n}\n\n.dv-resize-container {\n --dv-overlay-z-index: var(--dv-overlay-z-index, 999);\n position: absolute;\n z-index: calc(var(--dv-overlay-z-index) - 2);\n background-color: var(--dv-group-view-background-color);\n border: var(--dv-floating-border);\n box-shadow: var(--dv-floating-box-shadow);\n /* GPU optimizations for floating group movement */\n will-change: transform, opacity;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n}\n.dv-resize-container.dv-hidden {\n display: none;\n}\n.dv-resize-container.dv-resize-container-dragging {\n opacity: var(--dv-floating-group-dragging-opacity);\n /* Enhanced GPU acceleration during drag */\n will-change: transform, opacity;\n}\n.dv-resize-container .dv-resize-handle-top,\n.dv-resize-container .dv-resize-handle-bottom,\n.dv-resize-container .dv-resize-handle-left,\n.dv-resize-container .dv-resize-handle-right,\n.dv-resize-container .dv-resize-handle-topleft,\n.dv-resize-container .dv-resize-handle-topright,\n.dv-resize-container .dv-resize-handle-bottomleft,\n.dv-resize-container .dv-resize-handle-bottomright {\n touch-action: none;\n}\n.dv-resize-container .dv-resize-handle-top {\n height: 4px;\n width: calc(100% - 8px);\n left: 4px;\n top: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: ns-resize;\n}\n.dv-resize-container .dv-resize-handle-bottom {\n height: 4px;\n width: calc(100% - 8px);\n left: 4px;\n bottom: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: ns-resize;\n}\n.dv-resize-container .dv-resize-handle-left {\n height: calc(100% - 8px);\n width: 4px;\n left: -2px;\n top: 4px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: ew-resize;\n}\n.dv-resize-container .dv-resize-handle-right {\n height: calc(100% - 8px);\n width: 4px;\n right: -2px;\n top: 4px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: ew-resize;\n}\n.dv-resize-container .dv-resize-handle-topleft {\n height: 4px;\n width: 4px;\n top: -2px;\n left: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: nw-resize;\n}\n.dv-resize-container .dv-resize-handle-topright {\n height: 4px;\n width: 4px;\n right: -2px;\n top: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: ne-resize;\n}\n.dv-resize-container .dv-resize-handle-bottomleft {\n height: 4px;\n width: 4px;\n left: -2px;\n bottom: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: sw-resize;\n}\n.dv-resize-container .dv-resize-handle-bottomright {\n height: 4px;\n width: 4px;\n right: -2px;\n bottom: -2px;\n z-index: var(--dv-overlay-z-index);\n position: absolute;\n cursor: se-resize;\n}\n@media (pointer: coarse) {\n .dv-resize-container .dv-resize-handle-top,\n .dv-resize-container .dv-resize-handle-bottom {\n height: 16px;\n width: calc(100% - 48px);\n left: 24px;\n }\n .dv-resize-container .dv-resize-handle-top {\n top: -10px;\n }\n .dv-resize-container .dv-resize-handle-bottom {\n bottom: -10px;\n }\n .dv-resize-container .dv-resize-handle-left,\n .dv-resize-container .dv-resize-handle-right {\n width: 16px;\n height: calc(100% - 48px);\n top: 24px;\n }\n .dv-resize-container .dv-resize-handle-left {\n left: -10px;\n }\n .dv-resize-container .dv-resize-handle-right {\n right: -10px;\n }\n .dv-resize-container .dv-resize-handle-topleft,\n .dv-resize-container .dv-resize-handle-topright,\n .dv-resize-container .dv-resize-handle-bottomleft,\n .dv-resize-container .dv-resize-handle-bottomright {\n height: 24px;\n width: 24px;\n }\n .dv-resize-container .dv-resize-handle-topleft {\n top: -12px;\n left: -12px;\n }\n .dv-resize-container .dv-resize-handle-topright {\n top: -12px;\n right: -12px;\n }\n .dv-resize-container .dv-resize-handle-bottomleft {\n bottom: -12px;\n left: -12px;\n }\n .dv-resize-container .dv-resize-handle-bottomright {\n bottom: -12px;\n right: -12px;\n }\n}\n\n.dv-smart-guides {\n pointer-events: none;\n z-index: calc(var(--dv-overlay-z-index, 999) + 100);\n}\n\n.dv-smart-guide {\n background-color: var(--dv-smart-guides-color, #1f9cf0);\n}\n\n.dv-smart-guide-preview {\n box-sizing: border-box;\n background-color: var(--dv-smart-guides-preview-color, rgba(31, 156, 240, 0.18));\n border: 1px solid var(--dv-smart-guides-color, #1f9cf0);\n}\n.dv-render-overlay {\n --dv-overlay-z-index: var(--dv-overlay-z-index, 999);\n position: absolute;\n z-index: 1;\n width: 100%;\n height: 100%;\n contain: layout paint;\n isolation: isolate;\n /* GPU optimizations */\n will-change: transform;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n}\n.dv-render-overlay.dv-render-overlay-float {\n z-index: calc(var(--dv-overlay-z-index) - 1);\n}\n\n.dv-debug .dv-render-overlay {\n outline: 1px solid red;\n outline-offset: -1;\n}\n.dv-pane-container {\n height: 100%;\n width: 100%;\n}\n.dv-pane-container.dv-animated .dv-view {\n /* GPU optimizations for smooth pane animations */\n will-change: transform;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n transition: transform 0.15s ease-out;\n}\n.dv-pane-container .dv-view {\n overflow: hidden;\n display: flex;\n flex-direction: column;\n padding: 0px !important;\n}\n.dv-pane-container .dv-view:not(:first-child)::before {\n background-color: transparent !important;\n}\n.dv-pane-container .dv-view:not(:first-child) .dv-pane > .dv-pane-header {\n border-top: 1px solid var(--dv-paneview-header-border-color);\n}\n.dv-pane-container .dv-view .dv-default-header {\n background-color: var(--dv-group-view-background-color);\n color: var(--dv-activegroup-visiblepanel-tab-color);\n display: flex;\n padding: 0px 8px;\n cursor: pointer;\n}\n.dv-pane-container .dv-view .dv-default-header .dv-pane-header-icon {\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.dv-pane-container .dv-view .dv-default-header > span {\n padding-left: 8px;\n flex-grow: 1;\n}\n.dv-pane-container:first-of-type > .dv-pane > .dv-pane-header {\n border-top: none !important;\n}\n.dv-pane-container .dv-pane {\n display: flex;\n flex-direction: column;\n overflow: hidden;\n height: 100%;\n}\n.dv-pane-container .dv-pane .dv-pane-header {\n box-sizing: border-box;\n user-select: none;\n position: relative;\n outline: none;\n}\n.dv-pane-container .dv-pane .dv-pane-header.dv-pane-draggable {\n cursor: pointer;\n}\n.dv-pane-container .dv-pane .dv-pane-header:focus-visible:before, .dv-pane-container .dv-pane .dv-pane-header:has(:focus-visible):before {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 5;\n content: \"\";\n pointer-events: none;\n outline: 1px solid;\n outline-width: -1px;\n outline-style: solid;\n outline-offset: -1px;\n outline-color: var(--dv-paneview-active-outline-color);\n}\n.dv-pane-container .dv-pane .dv-pane-body {\n overflow-y: auto;\n overflow-x: hidden;\n flex-grow: 1;\n position: relative;\n outline: none;\n}\n.dv-pane-container .dv-pane .dv-pane-body:focus-visible:before, .dv-pane-container .dv-pane .dv-pane-body:has(:focus-visible):before {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 5;\n content: \"\";\n pointer-events: none;\n outline: 1px solid;\n outline-width: -1px;\n outline-style: solid;\n outline-offset: -1px;\n outline-color: var(--dv-paneview-active-outline-color);\n}\n.dv-scrollable {\n position: relative;\n overflow: hidden;\n}\n.dv-scrollable .dv-scrollbar {\n position: absolute;\n border-radius: 2px;\n background-color: transparent;\n /* GPU optimizations */\n will-change: background-color, transform;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n transition-property: background-color;\n transition-timing-function: ease-in-out;\n transition-duration: 1s;\n transition-delay: 0s;\n}\n.dv-scrollable .dv-scrollbar-horizontal {\n bottom: 0px;\n left: 0px;\n height: 4px;\n}\n.dv-scrollable .dv-scrollbar-vertical {\n right: 0px;\n top: 0px;\n width: 4px;\n}\n.dv-scrollable:hover .dv-scrollbar, .dv-scrollable.dv-scrollable-resizing .dv-scrollbar, .dv-scrollable.dv-scrollable-scrolling .dv-scrollbar {\n background-color: var(--dv-scrollbar-background-color, rgba(255, 255, 255, 0.25));\n}\n.dv-debug .dv-split-view-container .dv-sash-container .dv-sash.dv-enabled {\n background-color: black;\n}\n.dv-debug .dv-split-view-container .dv-sash-container .dv-sash.dv-disabled {\n background-color: orange;\n}\n.dv-debug .dv-split-view-container .dv-sash-container .dv-sash.dv-maximum {\n background-color: green;\n}\n.dv-debug .dv-split-view-container .dv-sash-container .dv-sash.dv-minimum {\n background-color: red;\n}\n\n.dv-split-view-container {\n position: relative;\n overflow: hidden;\n height: 100%;\n width: 100%;\n}\n.dv-split-view-container.dv-splitview-disabled > .dv-sash-container > .dv-sash {\n pointer-events: none;\n}\n.dv-split-view-container.dv-animation .dv-view,\n.dv-split-view-container.dv-animation .dv-sash {\n /* GPU optimizations for smooth animations */\n will-change: transform;\n transform: translate3d(0, 0, 0);\n backface-visibility: hidden;\n transition: transform 0.15s ease-out;\n}\n.dv-split-view-container.dv-horizontal {\n height: 100%;\n}\n.dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash {\n height: 100%;\n width: 4px;\n}\n.dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash.dv-enabled {\n cursor: ew-resize;\n}\n.dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash.dv-disabled {\n cursor: default;\n}\n.dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash.dv-maximum {\n cursor: w-resize;\n}\n.dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash.dv-minimum {\n cursor: e-resize;\n}\n.dv-split-view-container.dv-horizontal > .dv-view-container > .dv-view:not(:first-child)::before {\n height: 100%;\n width: 1px;\n}\n.dv-split-view-container.dv-vertical {\n width: 100%;\n}\n.dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash {\n width: 100%;\n height: 4px;\n}\n.dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash.dv-enabled {\n cursor: ns-resize;\n}\n.dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash.dv-disabled {\n cursor: default;\n}\n.dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash.dv-maximum {\n cursor: n-resize;\n}\n.dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash.dv-minimum {\n cursor: s-resize;\n}\n.dv-split-view-container.dv-vertical > .dv-view-container > .dv-view {\n width: 100%;\n}\n.dv-split-view-container.dv-vertical > .dv-view-container > .dv-view:not(:first-child)::before {\n height: 1px;\n width: 100%;\n}\n.dv-split-view-container .dv-sash-container {\n height: 100%;\n width: 100%;\n position: absolute;\n}\n.dv-split-view-container .dv-sash-container .dv-sash {\n position: absolute;\n z-index: 99;\n outline: none;\n user-select: none;\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n touch-action: none;\n background-color: var(--dv-sash-color, transparent);\n}\n.dv-split-view-container .dv-sash-container .dv-sash:not(.disabled):active, .dv-split-view-container .dv-sash-container .dv-sash:not(.disabled):hover {\n background-color: var(--dv-active-sash-color, transparent);\n transition-property: background-color;\n transition-timing-function: ease-in-out;\n transition-duration: var(--dv-active-sash-transition-duration, 0.1s);\n transition-delay: var(--dv-active-sash-transition-delay, 0.5s);\n}\n@media (pointer: coarse) {\n .dv-split-view-container .dv-sash-container > .dv-sash:not(.dv-disabled)::before {\n content: \"\";\n position: absolute;\n background: transparent;\n }\n .dv-split-view-container.dv-horizontal > .dv-sash-container > .dv-sash:not(.dv-disabled)::before {\n top: 0;\n bottom: 0;\n left: -10px;\n right: -10px;\n }\n .dv-split-view-container.dv-vertical > .dv-sash-container > .dv-sash:not(.dv-disabled)::before {\n left: 0;\n right: 0;\n top: -10px;\n bottom: -10px;\n }\n}\n.dv-split-view-container .dv-view-container {\n position: relative;\n height: 100%;\n width: 100%;\n}\n.dv-split-view-container .dv-view-container .dv-view {\n height: 100%;\n box-sizing: border-box;\n overflow: auto;\n position: absolute;\n}\n.dv-split-view-container.dv-separator-border .dv-view:not(:first-child)::before {\n content: \" \";\n position: absolute;\n top: 0;\n left: 0;\n z-index: 5;\n pointer-events: none;\n background-color: var(--dv-separator-border);\n}\n.dv-svg {\n display: inline-block;\n fill: currentcolor;\n line-height: 1;\n stroke: currentcolor;\n stroke-width: 0;\n}\n.dockview-theme-dark {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-dark .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-dark {\n --dv-group-view-background-color: #1e1e1e;\n --dv-tabs-and-actions-container-background-color: #252526;\n --dv-activegroup-visiblepanel-tab-background-color: #1e1e1e;\n --dv-activegroup-hiddenpanel-tab-background-color: #2d2d2d;\n --dv-inactivegroup-visiblepanel-tab-background-color: #1e1e1e;\n --dv-inactivegroup-hiddenpanel-tab-background-color: #2d2d2d;\n --dv-tab-divider-color: #1e1e1e;\n --dv-activegroup-visiblepanel-tab-color: white;\n --dv-activegroup-hiddenpanel-tab-color: #969696;\n --dv-inactivegroup-visiblepanel-tab-color: #8f8f8f;\n --dv-inactivegroup-hiddenpanel-tab-color: #626262;\n --dv-separator-border: rgb(68, 68, 68);\n --dv-paneview-header-border-color: rgba(204, 204, 204, 0.2);\n}\n\n.dockview-theme-light {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-light .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-light {\n --dv-group-view-background-color: white;\n --dv-tabs-and-actions-container-background-color: #f3f3f3;\n --dv-activegroup-visiblepanel-tab-background-color: white;\n --dv-activegroup-hiddenpanel-tab-background-color: #ececec;\n --dv-inactivegroup-visiblepanel-tab-background-color: white;\n --dv-inactivegroup-hiddenpanel-tab-background-color: #ececec;\n --dv-tab-divider-color: white;\n --dv-activegroup-visiblepanel-tab-color: rgb(51, 51, 51);\n --dv-activegroup-hiddenpanel-tab-color: rgba(51, 51, 51, 0.7);\n --dv-inactivegroup-visiblepanel-tab-color: rgba(51, 51, 51, 0.7);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(51, 51, 51, 0.35);\n --dv-separator-border: rgba(128, 128, 128, 0.35);\n --dv-paneview-header-border-color: rgb(51, 51, 51);\n --dv-scrollbar-background-color: rgba(0, 0, 0, 0.25);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n.dockview-theme-vs {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-vs .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-vs {\n --dv-group-view-background-color: #1e1e1e;\n --dv-tabs-and-actions-container-background-color: #252526;\n --dv-activegroup-visiblepanel-tab-background-color: #1e1e1e;\n --dv-activegroup-hiddenpanel-tab-background-color: #2d2d2d;\n --dv-inactivegroup-visiblepanel-tab-background-color: #1e1e1e;\n --dv-inactivegroup-hiddenpanel-tab-background-color: #2d2d2d;\n --dv-tab-divider-color: #1e1e1e;\n --dv-activegroup-visiblepanel-tab-color: white;\n --dv-activegroup-hiddenpanel-tab-color: #969696;\n --dv-inactivegroup-visiblepanel-tab-color: #8f8f8f;\n --dv-inactivegroup-hiddenpanel-tab-color: #626262;\n --dv-separator-border: rgb(68, 68, 68);\n --dv-paneview-header-border-color: rgba(204, 204, 204, 0.2);\n --dv-tabs-and-actions-container-background-color: #2d2d30;\n --dv-tabs-and-actions-container-height: 20px;\n --dv-tabs-and-actions-container-font-size: 11px;\n --dv-activegroup-visiblepanel-tab-background-color: #007acc;\n --dv-inactivegroup-visiblepanel-tab-background-color: #3f3f46;\n --dv-activegroup-visiblepanel-tab-color: white;\n --dv-activegroup-hiddenpanel-tab-color: white;\n --dv-inactivegroup-visiblepanel-tab-color: white;\n --dv-inactivegroup-hiddenpanel-tab-color: white;\n}\n.dockview-theme-vs .dv-groupview.dv-active-group > .dv-tabs-and-actions-container {\n box-sizing: content-box;\n border-bottom: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color);\n}\n.dockview-theme-vs .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tab.dv-active-tab {\n border-top: 2px solid var(--dv-activegroup-visiblepanel-tab-background-color);\n}\n.dockview-theme-vs .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tab.dv-inactive-tab {\n border-top: 2px solid var(--dv-activegroup-hiddenpanel-tab-background-color);\n}\n.dockview-theme-vs .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container {\n box-sizing: content-box;\n border-bottom: 2px solid var(--dv-inactivegroup-visiblepanel-tab-background-color);\n}\n.dockview-theme-vs .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tab.dv-active-tab {\n border-top: 2px solid var(--dv-inactivegroup-visiblepanel-tab-background-color);\n}\n.dockview-theme-vs .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tab.dv-inactive-tab {\n border-top: 2px solid var(--dv-inactivegroup-hiddenpanel-tab-background-color);\n}\n\n.dockview-theme-abyss {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-abyss .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-abyss {\n --dv-color-abyss-dark: #000c18;\n --dv-color-abyss: #10192c;\n --dv-color-abyss-light: #1c1c2a;\n --dv-color-abyss-lighter: #2b2b4a;\n --dv-color-abyss-accent: rgb(91, 30, 207);\n --dv-color-abyss-primary-text: white;\n --dv-color-abyss-secondary-text: rgb(148, 151, 169);\n --dv-group-view-background-color: var(--dv-color-abyss-dark);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-abyss-light\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-abyss-dark\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(--dv-color-abyss);\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-abyss-dark\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(--dv-color-abyss);\n --dv-tab-divider-color: var(--dv-color-abyss-lighter);\n --dv-activegroup-visiblepanel-tab-color: white;\n --dv-activegroup-hiddenpanel-tab-color: rgba(255, 255, 255, 0.5);\n --dv-inactivegroup-visiblepanel-tab-color: rgba(255, 255, 255, 0.5);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(255, 255, 255, 0.25);\n --dv-separator-border: var(--dv-color-abyss-lighter);\n --dv-paneview-header-border-color: var(--dv-color-abyss-lighter);\n --dv-paneview-active-outline-color: #596f99;\n}\n\n.dockview-theme-dracula {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-dracula .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-dracula {\n --dv-group-view-background-color: #282a36;\n --dv-tabs-and-actions-container-background-color: #191a21;\n --dv-activegroup-visiblepanel-tab-background-color: #282a36;\n --dv-activegroup-hiddenpanel-tab-background-color: #21222c;\n --dv-inactivegroup-visiblepanel-tab-background-color: #282a36;\n --dv-inactivegroup-hiddenpanel-tab-background-color: #21222c;\n --dv-tab-divider-color: #191a21;\n --dv-activegroup-visiblepanel-tab-color: rgb(248, 248, 242);\n --dv-activegroup-hiddenpanel-tab-color: rgb(98, 114, 164);\n --dv-inactivegroup-visiblepanel-tab-color: rgba(248, 248, 242, 0.5);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(98, 114, 164, 0.5);\n --dv-separator-border: #bd93f9;\n --dv-paneview-header-border-color: #bd93f9;\n --dv-paneview-active-outline-color: #6272a4;\n}\n.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-dracula .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 1px;\n background-color: #94527e;\n z-index: 999;\n}\n.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-dracula .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 1px;\n background-color: #5e3d5a;\n z-index: 999;\n}\n\n.dockview-theme-nord {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-nord .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-nord {\n --dv-color-nord-polar-0: #2e3440;\n --dv-color-nord-polar-1: #3b4252;\n --dv-color-nord-polar-2: #434c5e;\n --dv-color-nord-polar-3: #4c566a;\n --dv-color-nord-frost: #88c0d0;\n --dv-color-nord-frost-2: #81a1c1;\n --dv-color-nord-snow-0: #eceff4;\n --dv-color-nord-snow-1: #d8dee9;\n --dv-group-view-background-color: var(--dv-color-nord-polar-0);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-nord-polar-1\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-nord-polar-0\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-nord-polar-2\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-nord-polar-1\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-nord-polar-2\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-nord-snow-0);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-nord-snow-1);\n --dv-inactivegroup-visiblepanel-tab-color: #8a9bbf;\n --dv-inactivegroup-hiddenpanel-tab-color: #5e6f8e;\n --dv-separator-border: var(--dv-color-nord-polar-3);\n --dv-paneview-active-outline-color: var(--dv-color-nord-frost);\n --dv-active-sash-color: var(--dv-color-nord-frost);\n --dv-scrollbar-background-color: rgba(76, 86, 106, 0.5);\n}\n.dockview-theme-nord .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-nord .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: var(--dv-color-nord-frost);\n z-index: 999;\n}\n.dockview-theme-nord .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-nord .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: var(--dv-color-nord-frost-2);\n z-index: 999;\n}\n\n.dockview-theme-nord-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-nord-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-nord-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-nord-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-nord-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-nord-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-nord-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-nord-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-nord-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-nord-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-tabs-overflow-container,\n.dockview-theme-nord-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-nord-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-nord-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-nord-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-nord-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-nord-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-nord-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-nord-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-nord-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-nord-spaced {\n --dv-color-nord-polar-0: #2e3440;\n --dv-color-nord-polar-1: #3b4252;\n --dv-color-nord-polar-2: #434c5e;\n --dv-color-nord-polar-3: #4c566a;\n --dv-color-nord-frost: #88c0d0;\n --dv-color-nord-frost-2: #81a1c1;\n --dv-color-nord-snow-0: #eceff4;\n --dv-color-nord-snow-1: #d8dee9;\n --dv-group-view-background-color: var(--dv-color-nord-polar-0);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-nord-polar-1\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-nord-polar-2\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-nord-polar-1\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-nord-polar-2\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-nord-polar-1\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-nord-snow-0);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-nord-snow-1);\n --dv-inactivegroup-visiblepanel-tab-color: #8a9bbf;\n --dv-inactivegroup-hiddenpanel-tab-color: #5e6f8e;\n --dv-separator-border: transparent;\n --dv-paneview-active-outline-color: var(--dv-color-nord-frost);\n --dv-active-sash-color: var(--dv-color-nord-frost);\n --dv-scrollbar-background-color: rgba(76, 86, 106, 0.5);\n --dv-floating-group-border: 2px solid var(--dv-color-nord-polar-0);\n}\n\n.dockview-theme-catppuccin-mocha {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-catppuccin-mocha .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-catppuccin-mocha {\n --dv-color-mocha-crust: #11111b;\n --dv-color-mocha-mantle: #181825;\n --dv-color-mocha-base: #1e1e2e;\n --dv-color-mocha-surface0: #313244;\n --dv-color-mocha-surface1: #45475a;\n --dv-color-mocha-text: #cdd6f4;\n --dv-color-mocha-subtext1: #bac2de;\n --dv-color-mocha-subtext0: #a6adc8;\n --dv-color-mocha-mauve: #cba6f7;\n --dv-color-mocha-lavender: #b4befe;\n --dv-group-view-background-color: var(--dv-color-mocha-base);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-mocha-mantle\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-mocha-base\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-mocha-surface0\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-mocha-mantle\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-mocha-crust\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-mocha-text);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-mocha-subtext1);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-mocha-subtext0);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(166, 173, 200, 0.5);\n --dv-separator-border: var(--dv-color-mocha-surface1);\n --dv-paneview-active-outline-color: var(--dv-color-mocha-mauve);\n --dv-active-sash-color: var(--dv-color-mocha-mauve);\n --dv-scrollbar-background-color: rgba(49, 50, 68, 0.8);\n}\n.dockview-theme-catppuccin-mocha .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-catppuccin-mocha .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: var(--dv-color-mocha-mauve);\n z-index: 999;\n}\n.dockview-theme-catppuccin-mocha .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-catppuccin-mocha .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: rgba(180, 190, 254, 0.4);\n z-index: 999;\n}\n\n.dockview-theme-catppuccin-mocha-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tabs-overflow-container,\n.dockview-theme-catppuccin-mocha-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-catppuccin-mocha-spaced {\n --dv-color-mocha-crust: #11111b;\n --dv-color-mocha-mantle: #181825;\n --dv-color-mocha-base: #1e1e2e;\n --dv-color-mocha-surface0: #313244;\n --dv-color-mocha-surface1: #45475a;\n --dv-color-mocha-text: #cdd6f4;\n --dv-color-mocha-subtext1: #bac2de;\n --dv-color-mocha-subtext0: #a6adc8;\n --dv-color-mocha-mauve: #cba6f7;\n --dv-color-mocha-lavender: #b4befe;\n --dv-group-view-background-color: var(--dv-color-mocha-crust);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-mocha-mantle\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-mocha-surface0\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-mocha-mantle\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-mocha-surface0\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-mocha-mantle\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-mocha-text);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-mocha-subtext1);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-mocha-subtext0);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(166, 173, 200, 0.5);\n --dv-separator-border: transparent;\n --dv-paneview-active-outline-color: var(--dv-color-mocha-mauve);\n --dv-active-sash-color: var(--dv-color-mocha-mauve);\n --dv-scrollbar-background-color: rgba(49, 50, 68, 0.8);\n --dv-floating-group-border: 2px solid var(--dv-color-mocha-crust);\n}\n\n.dockview-theme-monokai {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-monokai .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-monokai {\n --dv-color-monokai-bg: #272822;\n --dv-color-monokai-bg-light: #3e3d32;\n --dv-color-monokai-comment: #75715e;\n --dv-color-monokai-fg: #f8f8f2;\n --dv-color-monokai-green: #a6e22e;\n --dv-group-view-background-color: var(--dv-color-monokai-bg);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-monokai-bg-light\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-monokai-bg\n );\n --dv-activegroup-hiddenpanel-tab-background-color: #2d2c25;\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-monokai-bg\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: #2d2c25;\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-monokai-fg);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-monokai-comment);\n --dv-inactivegroup-visiblepanel-tab-color: rgba(248, 248, 242, 0.5);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(117, 113, 94, 0.5);\n --dv-separator-border: var(--dv-color-monokai-bg-light);\n --dv-paneview-active-outline-color: var(--dv-color-monokai-green);\n --dv-active-sash-color: var(--dv-color-monokai-green);\n --dv-scrollbar-background-color: rgba(117, 113, 94, 0.5);\n}\n.dockview-theme-monokai .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-monokai .dv-groupview.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: var(--dv-color-monokai-green);\n z-index: 999;\n}\n.dockview-theme-monokai .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab {\n position: relative;\n}\n.dockview-theme-monokai .dv-groupview.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n position: absolute;\n left: 0px;\n bottom: 0px;\n content: \"\";\n width: 100%;\n height: 2px;\n background-color: rgba(166, 226, 46, 0.35);\n z-index: 999;\n}\n\n.dockview-theme-solarized-light {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-solarized-light .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-solarized-light {\n --dv-color-sol-base3: #fdf6e3;\n --dv-color-sol-base2: #eee8d5;\n --dv-color-sol-base1: #93a1a1;\n --dv-color-sol-base00: #657b83;\n --dv-color-sol-base01: #586e75;\n --dv-color-sol-blue: #268bd2;\n --dv-group-view-background-color: var(--dv-color-sol-base3);\n --dv-tabs-and-actions-container-background-color: var(--dv-color-sol-base2);\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-sol-base3\n );\n --dv-activegroup-hiddenpanel-tab-background-color: #e8e2d0;\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-sol-base3\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: #e8e2d0;\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-sol-base01);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-sol-base00);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-sol-base1);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(147, 161, 161, 0.6);\n --dv-separator-border: var(--dv-color-sol-base2);\n --dv-paneview-active-outline-color: var(--dv-color-sol-blue);\n --dv-active-sash-color: var(--dv-color-sol-blue);\n --dv-scrollbar-background-color: rgba(101, 123, 131, 0.25);\n --dv-drag-over-background-color: rgba(38, 139, 210, 0.15);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n.dockview-theme-solarized-light-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-solarized-light-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-solarized-light-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-solarized-light-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-solarized-light-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-tabs-overflow-container,\n.dockview-theme-solarized-light-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-solarized-light-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-solarized-light-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-solarized-light-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-solarized-light-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-solarized-light-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-solarized-light-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-solarized-light-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-solarized-light-spaced {\n --dv-color-sol-base3: #fdf6e3;\n --dv-color-sol-base2: #eee8d5;\n --dv-color-sol-base1: #93a1a1;\n --dv-color-sol-base00: #657b83;\n --dv-color-sol-base01: #586e75;\n --dv-color-sol-blue: #268bd2;\n --dv-drag-over-background-color: rgba(38, 139, 210, 0.1);\n --dv-group-view-background-color: var(--dv-color-sol-base2);\n --dv-tabs-and-actions-container-background-color: var(--dv-color-sol-base3);\n --dv-activegroup-visiblepanel-tab-background-color: #e8e2d0;\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-sol-base3\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: #e8e2d0;\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-sol-base3\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-sol-base01);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-sol-base00);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-sol-base1);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(147, 161, 161, 0.6);\n --dv-separator-border: transparent;\n --dv-paneview-active-outline-color: var(--dv-color-sol-blue);\n --dv-active-sash-color: var(--dv-color-sol-blue);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n --dv-scrollbar-background-color: rgba(101, 123, 131, 0.25);\n --dv-floating-group-border: 2px solid rgba(238, 232, 213, 0.5);\n}\n\n.dockview-theme-github-dark {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-github-dark .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-github-dark {\n --dv-color-gh-canvas-default: #0d1117;\n --dv-color-gh-canvas-subtle: #161b22;\n --dv-color-gh-canvas-inset: #010409;\n --dv-color-gh-border: #30363d;\n --dv-color-gh-border-muted: #21262d;\n --dv-color-gh-fg-default: #e6edf3;\n --dv-color-gh-fg-muted: #8b949e;\n --dv-color-gh-fg-subtle: #6e7681;\n --dv-color-gh-accent: #58a6ff;\n --dv-group-view-background-color: var(--dv-color-gh-canvas-default);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-canvas-default\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-canvas-default\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-gh-fg-default);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-gh-fg-muted);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-gh-fg-subtle);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(110, 118, 129, 0.5);\n --dv-separator-border: var(--dv-color-gh-border);\n --dv-paneview-active-outline-color: var(--dv-color-gh-accent);\n --dv-active-sash-color: var(--dv-color-gh-accent);\n --dv-scrollbar-background-color: rgba(48, 54, 61, 0.7);\n --dv-drag-over-background-color: rgba(88, 166, 255, 0.15);\n}\n\n.dockview-theme-github-dark-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-github-dark-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-github-dark-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-github-dark-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-github-dark-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-github-dark-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-github-dark-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-github-dark-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-github-dark-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-tabs-overflow-container,\n.dockview-theme-github-dark-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-github-dark-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-github-dark-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-github-dark-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-github-dark-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-github-dark-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-github-dark-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-github-dark-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-dark-spaced {\n --dv-color-gh-canvas-default: #0d1117;\n --dv-color-gh-canvas-subtle: #161b22;\n --dv-color-gh-canvas-inset: #010409;\n --dv-color-gh-border: #30363d;\n --dv-color-gh-border-muted: #21262d;\n --dv-color-gh-fg-default: #e6edf3;\n --dv-color-gh-fg-muted: #8b949e;\n --dv-color-gh-fg-subtle: #6e7681;\n --dv-color-gh-accent: #58a6ff;\n --dv-drag-over-background-color: rgba(88, 166, 255, 0.1);\n --dv-group-view-background-color: var(--dv-color-gh-canvas-inset);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-border\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-border\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-gh-fg-default);\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-gh-fg-muted);\n --dv-inactivegroup-visiblepanel-tab-color: var(--dv-color-gh-fg-subtle);\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(110, 118, 129, 0.5);\n --dv-separator-border: transparent;\n --dv-paneview-active-outline-color: var(--dv-color-gh-accent);\n --dv-active-sash-color: var(--dv-color-gh-accent);\n --dv-scrollbar-background-color: rgba(48, 54, 61, 0.7);\n --dv-floating-group-border: 2px solid var(--dv-color-gh-canvas-inset);\n}\n\n.dockview-theme-github-light {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n}\n.dockview-theme-github-light .dv-drop-target-container .dv-drop-target-anchor.dv-drop-target-anchor-container-changed {\n opacity: 0;\n transition: none;\n}\n.dockview-theme-github-light {\n --dv-color-gh-light-canvas-default: #ffffff;\n --dv-color-gh-light-canvas-subtle: #f6f8fa;\n --dv-color-gh-light-canvas-inset: #f0f6ff;\n --dv-color-gh-light-border: #d0d7de;\n --dv-color-gh-light-fg-default: #1f2328;\n --dv-color-gh-light-fg-muted: #656d76;\n --dv-color-gh-light-fg-subtle: #6e7781;\n --dv-color-gh-light-accent: #0969da;\n --dv-group-view-background-color: var(--dv-color-gh-light-canvas-default);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-gh-light-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-light-canvas-default\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-light-canvas-subtle\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-light-canvas-default\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-light-canvas-subtle\n );\n --dv-activegroup-visiblepanel-tab-color: var(\n --dv-color-gh-light-fg-default\n );\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-gh-light-fg-muted);\n --dv-inactivegroup-visiblepanel-tab-color: var(\n --dv-color-gh-light-fg-subtle\n );\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(110, 118, 129, 0.4);\n --dv-separator-border: var(--dv-color-gh-light-border);\n --dv-paneview-active-outline-color: var(--dv-color-gh-light-accent);\n --dv-active-sash-color: var(--dv-color-gh-light-accent);\n --dv-scrollbar-background-color: rgba(208, 215, 222, 0.5);\n --dv-drag-over-background-color: rgba(9, 105, 218, 0.1);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n}\n\n.dockview-theme-github-light-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-github-light-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-github-light-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-github-light-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-github-light-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-github-light-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-github-light-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-github-light-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-github-light-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-tabs-overflow-container,\n.dockview-theme-github-light-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-github-light-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-github-light-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-github-light-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-github-light-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-github-light-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-github-light-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-github-light-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-github-light-spaced {\n --dv-color-gh-light-canvas-default: #ffffff;\n --dv-color-gh-light-canvas-subtle: #f6f8fa;\n --dv-color-gh-light-border: #d0d7de;\n --dv-color-gh-light-fg-default: #1f2328;\n --dv-color-gh-light-fg-muted: #656d76;\n --dv-color-gh-light-fg-subtle: #6e7781;\n --dv-color-gh-light-accent: #0969da;\n --dv-drag-over-background-color: rgba(9, 105, 218, 0.08);\n --dv-group-view-background-color: var(--dv-color-gh-light-canvas-subtle);\n --dv-tabs-and-actions-container-background-color: var(\n --dv-color-gh-light-canvas-default\n );\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-light-border\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-light-canvas-default\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-gh-light-border\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-gh-light-canvas-default\n );\n --dv-activegroup-visiblepanel-tab-color: var(\n --dv-color-gh-light-fg-default\n );\n --dv-activegroup-hiddenpanel-tab-color: var(--dv-color-gh-light-fg-muted);\n --dv-inactivegroup-visiblepanel-tab-color: var(\n --dv-color-gh-light-fg-subtle\n );\n --dv-inactivegroup-hiddenpanel-tab-color: rgba(110, 118, 129, 0.4);\n --dv-separator-border: transparent;\n --dv-paneview-active-outline-color: var(--dv-color-gh-light-accent);\n --dv-active-sash-color: var(--dv-color-gh-light-accent);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n --dv-scrollbar-background-color: rgba(208, 215, 222, 0.5);\n --dv-floating-group-border: 2px solid rgba(208, 215, 222, 0.5);\n}\n\n.dockview-theme-abyss-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-abyss-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-abyss-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-abyss-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-abyss-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-abyss-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-abyss-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-abyss-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-abyss-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-tabs-overflow-container,\n.dockview-theme-abyss-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-abyss-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-abyss-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-abyss-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-abyss-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-abyss-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-abyss-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-abyss-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-abyss-spaced {\n --dv-color-abyss-dark: rgb(11, 6, 17);\n --dv-color-abyss: #16121f;\n --dv-color-abyss-light: #201d2b;\n --dv-color-abyss-lighter: #2a2837;\n --dv-color-abyss-accent: rgb(91, 30, 207);\n --dv-color-abyss-primary-text: white;\n --dv-color-abyss-secondary-text: rgb(148, 151, 169);\n --dv-drag-over-background-color: '';\n --dv-group-view-background-color: var(--dv-color-abyss-dark);\n --dv-tabs-and-actions-container-background-color: var(--dv-color-abyss);\n --dv-activegroup-visiblepanel-tab-background-color: var(\n --dv-color-abyss-lighter\n );\n --dv-activegroup-hiddenpanel-tab-background-color: var(\n --dv-color-abyss-light\n );\n --dv-inactivegroup-visiblepanel-tab-background-color: var(\n --dv-color-abyss-lighter\n );\n --dv-inactivegroup-hiddenpanel-tab-background-color: var(\n --dv-color-abyss-light\n );\n --dv-tab-divider-color: transparent;\n --dv-activegroup-visiblepanel-tab-color: var(--dv-color-abyss-primary-text);\n --dv-activegroup-hiddenpanel-tab-color: var(\n --dv-color-abyss-secondary-text\n );\n --dv-inactivegroup-visiblepanel-tab-color: var(\n --dv-color-abyss-primary-text\n );\n --dv-inactivegroup-hiddenpanel-tab-color: var(\n --dv-color-abyss-secondary-text\n );\n --dv-separator-border: transparent;\n --dv-paneview-header-border-color: rgb(51, 51, 51);\n --dv-active-sash-color: var(--dv-color-abyss-accent);\n --dv-floating-group-border: 2px solid var(--dv-color-abyss-dark);\n}\n\n.dockview-theme-light-spaced {\n --dv-paneview-active-outline-color: dodgerblue;\n --dv-tabs-and-actions-container-font-size: 13px;\n --dv-tabs-and-actions-container-height: 35px;\n --dv-drag-over-background-color: rgba(83, 89, 93, 0.5);\n --dv-drag-over-border-color: transparent;\n --dv-edge-dock-indicator-color: rgba(56, 139, 253, 0.9);\n --dv-tabs-container-scrollbar-color: #888;\n --dv-icon-hover-background-color: rgba(90, 93, 94, 0.31);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.4), 0 2px 8px rgba(0, 0, 0, 0.25);\n --dv-floating-border: 1px solid rgba(255, 255, 255, 0.1);\n --dv-overlay-z-index: 999;\n --dv-tab-font-size: inherit;\n --dv-border-radius: 0px;\n --dv-tab-margin: 0;\n --dv-sash-color: transparent;\n --dv-active-sash-color: transparent;\n --dv-active-sash-transition-duration: 0.1s;\n --dv-active-sash-transition-delay: 0.5s;\n --dv-spacing-padding: 0px;\n --dv-tab-border-radius: 0px;\n --dv-sash-border-radius: 0px;\n --dv-dropdown-border-radius: 0px;\n --dv-tab-close-icon-size: inherit;\n --dv-floating-group-border: none;\n --dv-drag-over-border: none;\n --dv-floating-group-dragging-opacity: 0.5;\n --dv-floating-titlebar-height: 22px;\n --dv-floating-titlebar-background-color: var(\n --dv-tabs-and-actions-container-background-color\n );\n --dv-floating-titlebar-border-bottom: var(--dv-floating-border);\n --dv-tab-group-color-grey: #5f6368;\n --dv-tab-group-color-blue: #1a73e8;\n --dv-tab-group-color-red: #d93025;\n --dv-tab-group-color-yellow: #f9ab00;\n --dv-tab-group-color-green: #188038;\n --dv-tab-group-color-pink: #d01884;\n --dv-tab-group-color-purple: #a142f4;\n --dv-tab-group-color-cyan: #007b83;\n --dv-tab-group-color-orange: #e8710a;\n --dv-tab-group-chip-padding: 4px 8px;\n --dv-tab-group-chip-border-radius: 6px;\n --dv-tab-group-chip-font-size: 11px;\n --dv-tab-group-line-height: 2px;\n --dv-tab-group-line-opacity: 0.6;\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-theme-light-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-theme-light-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-theme-light-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-theme-light-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-theme-light-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-theme-light-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-theme-light-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-theme-light-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-theme-light-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-tabs-overflow-container,\n.dockview-theme-light-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-theme-light-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-theme-light-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-theme-light-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-light-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-theme-light-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-theme-light-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-theme-light-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-theme-light-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-theme-light-spaced {\n --dv-drag-over-background-color: '';\n --dv-group-view-background-color: #f6f5f9;\n --dv-tabs-and-actions-container-background-color: white;\n --dv-activegroup-visiblepanel-tab-background-color: #ededf0;\n --dv-activegroup-hiddenpanel-tab-background-color: #f9f9fa;\n --dv-inactivegroup-visiblepanel-tab-background-color: #ededf0;\n --dv-inactivegroup-hiddenpanel-tab-background-color: #f9f9fa;\n --dv-tab-divider-color: transparent;\n --dv-activegroup-visiblepanel-tab-color: rgb(104, 107, 130);\n --dv-activegroup-hiddenpanel-tab-color: rgb(148, 151, 169);\n --dv-inactivegroup-visiblepanel-tab-color: rgb(104, 107, 130);\n --dv-inactivegroup-hiddenpanel-tab-color: rgb(148, 151, 169);\n --dv-separator-border: transparent;\n --dv-paneview-header-border-color: rgb(51, 51, 51);\n --dv-active-sash-color: rgb(91, 30, 207);\n --dv-floating-box-shadow:\n 0 8px 32px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08);\n --dv-floating-border: 1px solid rgba(0, 0, 0, 0.1);\n --dv-scrollbar-background-color: rgba(0, 0, 0, 0.25);\n --dv-floating-group-border: 2px solid rgba(255, 255, 255, 0.1);\n}\n\n.dockview-spaced {\n --dv-spacing-padding: 10px;\n --dv-tab-font-size: 12px;\n --dv-border-radius: 12px;\n --dv-tab-margin-block: 0.5rem;\n --dv-tab-margin-inline: 0.25rem;\n --dv-tab-margin: var(--dv-tab-margin-block) var(--dv-tab-margin-inline);\n --dv-tabs-and-actions-container-height: 44px;\n --dv-tab-border-radius: 8px;\n --dv-sash-border-radius: 4px;\n --dv-dropdown-border-radius: 8px;\n --dv-tab-close-icon-size: 8px;\n --dv-floating-group-border: 2px solid var(--dv-group-view-background-color);\n --dv-floating-titlebar-background-color: var(\n --dv-group-view-background-color\n );\n --dv-floating-titlebar-border-bottom: none;\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n background-color: var(--dv-group-view-background-color);\n}\n.dockview-spaced .dv-dockview {\n padding: 0;\n}\n.dockview-spaced .dv-resize-container:has(> .dv-groupview) {\n border-radius: 8px;\n}\n.dockview-spaced .dv-sash {\n border-radius: var(--dv-sash-border-radius);\n}\n.dockview-spaced .dv-drop-target-anchor {\n border-radius: calc(var(--dv-border-radius) / 4);\n}\n.dockview-spaced .dv-drop-target-anchor.dv-drop-target-content {\n border-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-resize-container {\n border-radius: var(--dv-border-radius) !important;\n border: none;\n}\n.dockview-spaced .dv-resize-container .dv-groupview {\n border: var(--dv-floating-group-border);\n}\n.dockview-spaced .dv-resize-container > .dv-grid-view {\n box-sizing: border-box;\n padding: var(--dv-spacing-padding);\n}\n.dockview-spaced .dv-resize-container-with-titlebar > .dv-grid-view {\n padding-top: 0;\n}\n.dockview-spaced .dv-resize-container-with-titlebar > .dv-floating-titlebar {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-tabs-overflow-container,\n.dockview-spaced .dv-tabs-overflow-dropdown-default {\n border-radius: var(--dv-dropdown-border-radius);\n height: unset !important;\n}\n.dockview-spaced .dv-render-overlay {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-tab {\n border-radius: var(--dv-tab-border-radius);\n}\n.dockview-spaced .dv-tab .dv-svg {\n height: var(--dv-tab-close-icon-size);\n width: var(--dv-tab-close-icon-size);\n}\n.dockview-spaced .dv-tabs-container--wrap:not(.dv-tabs-container-vertical) .dv-tab {\n height: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-spaced .dv-tabs-container-vertical .dv-tab {\n margin: var(--dv-tab-margin-inline) var(--dv-tab-margin-block);\n}\n.dockview-spaced .dv-tabs-container--wrap.dv-tabs-container-vertical .dv-tab {\n width: calc(var(--dv-tabs-and-actions-container-height) - 2 * var(--dv-tab-margin-block));\n}\n.dockview-spaced .dv-groupview {\n border-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-groupview .dv-tabs-and-actions-container {\n padding: 0px calc(var(--dv-border-radius) / 2);\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-groupview .dv-tabs-and-actions-container.dv-groupview-header-vertical {\n padding: calc(var(--dv-border-radius) / 2) 0;\n}\n.dockview-spaced .dv-groupview .dv-content-container {\n background-color: var(--dv-tabs-and-actions-container-background-color);\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-groupview.dv-edge-tool-window .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-groupview.dv-edge-tool-window .dv-content-container {\n border-radius: 0;\n}\n.dockview-spaced .dv-groupview.dv-edge-tool-window .dv-tabs-and-actions-container {\n border-top-left-radius: 0;\n border-top-right-radius: 0;\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-edge-peek {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-edge-peek-clip {\n border-bottom-left-radius: var(--dv-border-radius);\n border-bottom-right-radius: var(--dv-border-radius);\n}\n.dockview-spaced .dv-edge-peek-header {\n border-top-left-radius: var(--dv-border-radius);\n border-top-right-radius: var(--dv-border-radius);\n}\n\n.dv-context-menu {\n min-width: 160px;\n overflow: hidden;\n background: var(--dv-context-menu-background-color, var(--dv-activegroup-hiddenpanel-tab-background-color));\n color: var(--dv-context-menu-color, var(--dv-activegroup-hiddenpanel-tab-color));\n border: 1px solid var(--dv-tab-divider-color);\n border-radius: var(--dv-border-radius);\n box-shadow: var(--dv-floating-box-shadow);\n padding: 4px 0;\n}\n\n.dv-context-menu-item {\n height: 25px;\n padding: 0 12px;\n display: flex;\n align-items: center;\n cursor: pointer;\n font-size: var(--dv-tabs-and-actions-container-font-size);\n white-space: nowrap;\n user-select: none;\n}\n.dv-context-menu-item:hover {\n background: var(--dv-icon-hover-background-color);\n}\n.dv-context-menu-item.dv-context-menu-item--disabled {\n opacity: 0.4;\n cursor: default;\n pointer-events: none;\n}\n\n.dv-context-menu-separator {\n height: 1px;\n background: var(--dv-tab-divider-color);\n margin: 4px 0;\n}\n\n.dv-context-menu-rename {\n padding: 8px 12px 4px;\n}\n\n.dv-context-menu-rename-input {\n width: 100%;\n box-sizing: border-box;\n padding: 8px 10px;\n border: 1px solid var(--dv-tab-divider-color);\n border-radius: var(--dv-border-radius);\n background: inherit;\n color: var(--dv-activegroup-visiblepanel-tab-color);\n font-size: var(--dv-tabs-and-actions-container-font-size);\n outline: none;\n}\n.dv-context-menu-rename-input:focus {\n border-color: var(--dv-activegroup-visiblepanel-tab-color);\n}\n.dv-context-menu-rename-input::placeholder {\n color: var(--dv-activegroup-hiddenpanel-tab-color);\n}\n\n.dv-context-menu-color-picker {\n display: flex;\n flex-direction: row;\n gap: 6px;\n padding: 8px 12px;\n align-items: center;\n}\n\n.dv-context-menu-color-swatch {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n cursor: pointer;\n border: 2px solid transparent;\n flex-shrink: 0;\n background-color: var(--dv-tab-group-color);\n}\n.dv-context-menu-color-swatch:hover {\n opacity: 0.85;\n}\n.dv-context-menu-color-swatch.dv-context-menu-color-swatch--selected {\n outline: 2px solid var(--dv-tab-divider-color);\n outline-offset: 2px;\n}\n\n.dv-tab-group-indicator-none .dv-groupview-header-bottom .dv-tab-group-underline {\n top: auto;\n bottom: 0;\n}\n\n.dv-groupview.dv-groupview-header-bottom.dv-active-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after, .dv-groupview.dv-groupview-header-bottom.dv-inactive-group > .dv-tabs-and-actions-container .dv-tabs-container > .dv-tab.dv-active-tab::after {\n top: 0px;\n bottom: auto;\n}";
document.head.appendChild(s);
}
//#endregion
//#region ../dockview-core/dist/package/main.esm.mjs
/**
* dockview-core
* @version 8.2.0
* @link https://github.com/dockview/dockview
* @license MIT
*/
var _LocalSelectionTransfer;
var TransferObject = class {
constructor() {}
};
var PanelTransfer = class extends TransferObject {
constructor(viewId, groupId, panelId, tabGroupId) {
super();
this.viewId = viewId;
this.groupId = groupId;
this.panelId = panelId;
this.tabGroupId = tabGroupId;
}
};
var PaneTransfer = class extends TransferObject {
constructor(viewId, paneId) {
super();
this.viewId = viewId;
this.paneId = paneId;
}
};
/**
* A singleton to store transfer data during drag & drop operations that are only valid within the application.
*/
var LocalSelectionTransfer = class LocalSelectionTransfer {
constructor() {}
static getInstance() {
return LocalSelectionTransfer.INSTANCE;
}
hasData(proto) {
return proto && proto === this.proto;
}
clearData(proto) {
if (this.hasData(proto)) {
this.proto = void 0;
this.data = void 0;
}
}
getData(proto) {
if (this.hasData(proto)) return this.data;
}
setData(data, proto) {
if (proto) {
this.data = data;
this.proto = proto;
}
}
};
_LocalSelectionTransfer = LocalSelectionTransfer;
LocalSelectionTransfer.INSTANCE = new _LocalSelectionTransfer();
function getPanelData() {
const panelTransfer = LocalSelectionTransfer.getInstance();
if (!panelTransfer.hasData(PanelTransfer.prototype)) return;
return panelTransfer.getData(PanelTransfer.prototype)[0];
}
function getPaneData() {
const paneTransfer = LocalSelectionTransfer.getInstance();
if (!paneTransfer.hasData(PaneTransfer.prototype)) return;
return paneTransfer.getData(PaneTransfer.prototype)[0];
}
let Disposable;
(function(_Disposable) {
_Disposable.NONE = { dispose: () => {} };
function from(func) {
return { dispose: () => {
func();
} };
}
_Disposable.from = from;
})(Disposable || (Disposable = {}));
var CompositeDisposable = class {
get isDisposed() {
return this._isDisposed;
}
constructor(...args) {
this._isDisposed = false;
this._disposables = new Set(args);
}
addDisposables(...args) {
args.forEach((arg) => this._disposables.add(arg));
}
removeDisposable(disposable) {
this._disposables.delete(disposable);
}
dispose() {
if (this._isDisposed) return;
this._isDisposed = true;
this._disposables.forEach((arg) => arg.dispose());
this._disposables.clear();
}
};
var MutableDisposable = class {
constructor() {
this._disposable = Disposable.NONE;
}
get value() {
return this._disposable === Disposable.NONE ? void 0 : this._disposable;
}
set value(disposable) {
if (this._disposable) this._disposable.dispose();
this._disposable = disposable;
}
dispose() {
if (this._disposable) {
this._disposable.dispose();
this._disposable = Disposable.NONE;
}
}
};
let Event;
(function(_Event) {
_Event.any = (...children) => {
return (listener) => {
const disposables = children.map((child) => child(listener));
return { dispose: () => {
disposables.forEach((d) => {
d.dispose();
});
} };
};
};
})(Event || (Event = {}));
var DockviewEvent = class {
constructor() {
this._defaultPrevented = false;
}
get defaultPrevented() {
return this._defaultPrevented;
}
preventDefault() {
this._defaultPrevented = true;
}
};
var AcceptableEvent = class {
constructor() {
this._isAccepted = false;
}
get isAccepted() {
return this._isAccepted;
}
accept() {
this._isAccepted = true;
}
};
var LeakageMonitor = class {
constructor() {
this.events = /* @__PURE__ */ new Map();
}
get size() {
return this.events.size;
}
add(event, stacktrace) {
this.events.set(event, stacktrace);
}
delete(event) {
this.events.delete(event);
}
clear() {
this.events.clear();
}
};
var Stacktrace = class Stacktrace {
static create() {
var _Error$stack;
return new Stacktrace((_Error$stack = (/* @__PURE__ */ new Error("listener stacktrace")).stack) !== null && _Error$stack !== void 0 ? _Error$stack : "");
}
constructor(value) {
this.value = value;
}
print() {
console.warn("dockview: stacktrace", this.value);
}
};
var Listener = class {
constructor(callback, stacktrace) {
this.callback = callback;
this.stacktrace = stacktrace;
}
};
var Emitter = class Emitter {
static setLeakageMonitorEnabled(isEnabled) {
if (isEnabled !== Emitter.ENABLE_TRACKING) Emitter.MEMORY_LEAK_WATCHER.clear();
Emitter.ENABLE_TRACKING = isEnabled;
}
get value() {
return this._last;
}
constructor(options) {
this.options = options;
this._listeners = [];
this._disposed = false;
}
get event() {
if (!this._event) {
this._event = (callback) => {
var _this$options;
if (((_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.replay) && this._last !== void 0) callback(this._last);
const listener = new Listener(callback, Emitter.ENABLE_TRACKING ? Stacktrace.create() : void 0);
this._listeners.push(listener);
return { dispose: () => {
const index = this._listeners.indexOf(listener);
if (index > -1) this._listeners.splice(index, 1);
else if (Emitter.ENABLE_TRACKING) {}
} };
};
if (Emitter.ENABLE_TRACKING) Emitter.MEMORY_LEAK_WATCHER.add(this._event, Stacktrace.create());
}
return this._event;
}
fire(e) {
var _this$options2;
if (this._pauseTokens !== void 0 && this._pauseTokens.size > 0) return;
if ((_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.replay) this._last = e;
const listeners = this._listeners;
const length = listeners.length;
if (length === 0) return;
if (length === 1) {
listeners[0].callback(e);
return;
}
for (const listener of listeners.slice()) listener.callback(e);
}
pause() {
var _this$_pauseTokens;
const token = {};
(_this$_pauseTokens = this._pauseTokens) !== null && _this$_pauseTokens !== void 0 || (this._pauseTokens = /* @__PURE__ */ new Set());
this._pauseTokens.add(token);
return Disposable.from(() => {
var _this$_pauseTokens2;
return (_this$_pauseTokens2 = this._pauseTokens) === null || _this$_pauseTokens2 === void 0 ? void 0 : _this$_pauseTokens2.delete(token);
});
}
dispose() {
if (!this._disposed) {
this._disposed = true;
if (this._listeners.length > 0) if (Emitter.ENABLE_TRACKING) queueMicrotask(() => {
for (const listener of this._listeners) {
var _listener$stacktrace;
console.warn("dockview: stacktrace", (_listener$stacktrace = listener.stacktrace) === null || _listener$stacktrace === void 0 ? void 0 : _listener$stacktrace.print());
}
this._listeners = [];
});
else this._listeners = [];
if (Emitter.ENABLE_TRACKING && this._event) Emitter.MEMORY_LEAK_WATCHER.delete(this._event);
}
}
};
Emitter.ENABLE_TRACKING = false;
Emitter.MEMORY_LEAK_WATCHER = new LeakageMonitor();
function addDisposableListener(element, type, listener, options) {
element.addEventListener(type, listener, options);
return { dispose: () => {
element.removeEventListener(type, listener, options);
} };
}
/**
*
* Event Emitter that fires events from a Microtask callback, only one event will fire per event-loop cycle.
*
* It's kind of like using an `asapScheduler` in RxJs with additional logic to only fire once per event-loop cycle.
* This implementation exists to avoid external dependencies.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask
* @see https://rxjs.dev/api/index/const/asapScheduler
*/
var AsapEvent = class {
constructor() {
this._onFired = new Emitter();
this._currentFireCount = 0;
this._queued = false;
this.onEvent = (e) => {
/**
* when the event is first subscribed to take note of the current fire count
*/
const fireCountAtTimeOfEventSubscription = this._currentFireCount;
return this._onFired.event(() => {
/**
* if the current fire count is greater than the fire count at event subscription
* then the event has been fired since we subscribed and it's ok to "on_next" the event.
*
* if the count is not greater then what we are recieving is an event from the microtask
* queue that was triggered before we actually subscribed and therfore we should ignore it.
*/
if (this._currentFireCount > fireCountAtTimeOfEventSubscription) e();
});
};
}
fire() {
this._currentFireCount++;
if (this._queued) return;
this._queued = true;
queueMicrotask(() => {
this._queued = false;
this._onFired.fire();
});
}
dispose() {
this._onFired.dispose();
}
};
var OverflowObserver = class extends CompositeDisposable {
constructor(el) {
super();
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._value = null;
this.addDisposables(this._onDidChange, watchElementResize(el, (entry) => {
const hasScrollX = entry.target.scrollWidth > entry.target.clientWidth;
const hasScrollY = entry.target.scrollHeight > entry.target.clientHeight;
this._value = {
hasScrollX,
hasScrollY
};
this._onDidChange.fire(this._value);
}));
}
};
function watchElementResize(element, cb) {
const observer = new ResizeObserver((entires) => {
/**
* Fast browser window resize produces Error: ResizeObserver loop limit exceeded.
* The error isn't visible in browser console, doesn't affect functionality, but degrades performance.
* See https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded/58701523#58701523
*/
requestAnimationFrame(() => {
const firstEntry = entires[0];
cb(firstEntry);
});
});
observer.observe(element);
return { dispose: () => {
observer.unobserve(element);
observer.disconnect();
} };
}
const removeClasses = (element, ...classes) => {
for (const classname of classes) if (element.classList.contains(classname)) element.classList.remove(classname);
};
const addClasses = (element, ...classes) => {
for (const classname of classes) if (!element.classList.contains(classname)) element.classList.add(classname);
};
const toggleClass = (element, className, isToggled) => {
const hasClass = element.classList.contains(className);
if (isToggled && !hasClass) element.classList.add(className);
if (!isToggled && hasClass) element.classList.remove(className);
};
function isAncestor(testChild, testAncestor) {
while (testChild) {
if (testChild === testAncestor) return true;
testChild = testChild.parentNode;
}
return false;
}
function trackFocus(element) {
return new FocusTracker(element);
}
/**
* Track focus on an element. Ensure tabIndex is set when an HTMLElement is not focusable by default
*/
var FocusTracker = class extends CompositeDisposable {
constructor(element) {
super();
this._onDidFocus = new Emitter();
this.onDidFocus = this._onDidFocus.event;
this._onDidBlur = new Emitter();
this.onDidBlur = this._onDidBlur.event;
this.addDisposables(this._onDidFocus, this._onDidBlur);
let hasFocus = isAncestor(document.activeElement, element);
let loosingFocus = false;
const onFocus = () => {
loosingFocus = false;
if (!hasFocus) {
hasFocus = true;
this._onDidFocus.fire();
}
};
const onBlur = () => {
if (hasFocus) {
loosingFocus = true;
globalThis.setTimeout(() => {
if (loosingFocus) {
loosingFocus = false;
hasFocus = false;
this._onDidBlur.fire();
}
}, 0);
}
};
this._refreshStateHandler = () => {
if (isAncestor(document.activeElement, element) !== hasFocus) if (hasFocus) onBlur();
else onFocus();
};
this.addDisposables(addDisposableListener(element, "focus", onFocus, true));
this.addDisposables(addDisposableListener(element, "blur", onBlur, true));
}
refreshState() {
this._refreshStateHandler();
}
};
const QUASI_PREVENT_DEFAULT_KEY = "dv-quasiPreventDefault";
function quasiPreventDefault(event) {
event[QUASI_PREVENT_DEFAULT_KEY] = true;
}
function quasiDefaultPrevented(event) {
return event[QUASI_PREVENT_DEFAULT_KEY];
}
function addStyles(document, styleSheetList, options = {}) {
const styleSheets = Array.from(styleSheetList);
const { nonce } = options;
const resolvedNonce = typeof nonce === "function" ? nonce(document) : nonce;
for (const styleSheet of styleSheets) {
if (styleSheet.href) {
const link = document.createElement("link");
link.href = styleSheet.href;
link.type = styleSheet.type;
link.rel = "stylesheet";
document.head.appendChild(link);
continue;
}
let cssTexts = [];
try {
if (styleSheet.cssRules) cssTexts = Array.from(styleSheet.cssRules).map((rule) => rule.cssText);
} catch (err) {
console.warn("dockview: failed to access stylesheet rules due to security restrictions", err);
}
const fragment = document.createDocumentFragment();
for (const rule of cssTexts) {
const style = document.createElement("style");
if (resolvedNonce) style.setAttribute("nonce", resolvedNonce);
style.appendChild(document.createTextNode(rule));
fragment.appendChild(style);
}
document.head.appendChild(fragment);
}
}
function getDomNodePagePosition(domNode) {
const { left, top, width, height } = domNode.getBoundingClientRect();
return {
left: left + window.scrollX,
top: top + window.scrollY,
width,
height
};
}
/**
* Check whether an element is in the DOM (including the Shadow DOM)
* @see https://terodox.tech/how-to-tell-if-an-element-is-in-the-dom-including-the-shadow-dom/
*/
function isInDocument(element) {
let currentElement = element;
while (currentElement === null || currentElement === void 0 ? void 0 : currentElement.parentNode) if (currentElement.parentNode === document) return true;
else if (currentElement.parentNode instanceof DocumentFragment) currentElement = currentElement.parentNode.host;
else currentElement = currentElement.parentNode;
return false;
}
function addTestId(element, id) {
element.dataset.testid = id;
}
/**
* Should be more efficient than element.querySelectorAll("*") since there
* is no need to store every element in-memory using this approach
*/
function allTagsNamesInclusiveOfShadowDoms(tagNames, rootNode) {
const iframes = [];
function findIframesInNode(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (tagNames.includes(node.tagName)) iframes.push(node);
if (node.shadowRoot) findIframesInNode(node.shadowRoot);
for (const child of node.children) findIframesInNode(child);
}
}
findIframesInNode(rootNode instanceof Document ? rootNode.documentElement : rootNode);
return iframes;
}
function disableIframePointEvents(rootNode = document) {
const iframes = allTagsNamesInclusiveOfShadowDoms(["IFRAME", "WEBVIEW"], rootNode);
const original = /* @__PURE__ */ new WeakMap();
for (const iframe of iframes) {
original.set(iframe, iframe.style.pointerEvents);
iframe.style.pointerEvents = "none";
}
return { release: () => {
for (const iframe of iframes) {
var _original$get;
iframe.style.pointerEvents = (_original$get = original.get(iframe)) !== null && _original$get !== void 0 ? _original$get : "auto";
}
iframes.splice(0, iframes.length);
} };
}
function getDockviewTheme(element) {
function toClassList(element) {
const list = [];
for (let i = 0; i < element.classList.length; i++) list.push(element.classList.item(i));
return list;
}
let theme = void 0;
let parent = element;
while (parent !== null) {
theme = toClassList(parent).find((cls) => cls.startsWith("dockview-theme-"));
if (typeof theme === "string") break;
parent = parent.parentElement;
}
return theme;
}
var Classnames = class {
constructor(element) {
this.element = element;
this._classNames = [];
}
setClassNames(classNames) {
for (const className of this._classNames) toggleClass(this.element, className, false);
this._classNames = classNames.split(" ").filter((v) => v.trim().length > 0);
for (const className of this._classNames) toggleClass(this.element, className, true);
}
};
const DEBOUCE_DELAY = 100;
function isChildEntirelyVisibleWithinParent(child, parent) {
const childPosition = getDomNodePagePosition(child);
const parentPosition = getDomNodePagePosition(parent);
if (childPosition.left < parentPosition.left) return false;
if (childPosition.left + childPosition.width > parentPosition.left + parentPosition.width) return false;
if (childPosition.top < parentPosition.top) return false;
if (childPosition.top + childPosition.height > parentPosition.top + parentPosition.height) return false;
return true;
}
function onDidWindowMoveEnd(window) {
const emitter = new Emitter();
let previousScreenX = window.screenX;
let previousScreenY = window.screenY;
let timeout;
let rafHandle = 0;
let stopped = false;
const checkMovement = () => {
if (stopped || window.closed) return;
const currentScreenX = window.screenX;
const currentScreenY = window.screenY;
if (currentScreenX !== previousScreenX || currentScreenY !== previousScreenY) {
clearTimeout(timeout);
timeout = setTimeout(() => {
emitter.fire();
}, DEBOUCE_DELAY);
previousScreenX = currentScreenX;
previousScreenY = currentScreenY;
}
rafHandle = requestAnimationFrame(checkMovement);
};
checkMovement();
const disposeEmitter = emitter.dispose.bind(emitter);
emitter.dispose = () => {
if (!stopped) {
stopped = true;
cancelAnimationFrame(rafHandle);
clearTimeout(timeout);
}
disposeEmitter();
};
return emitter;
}
function onDidWindowResizeEnd(element, cb) {
let resizeTimeout;
return new CompositeDisposable(addDisposableListener(element, "resize", () => {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(() => {
cb();
}, DEBOUCE_DELAY);
}));
}
function shiftAbsoluteElementIntoView(element, root, options) {
var _options$buffer;
const buffer = (_options$buffer = options === null || options === void 0 ? void 0 : options.buffer) !== null && _options$buffer !== void 0 ? _options$buffer : 10;
const rect = element.getBoundingClientRect();
const rootRect = root.getBoundingClientRect();
let translateX = 0;
let translateY = 0;
const left = rect.left - rootRect.left;
const top = rect.top - rootRect.top;
const bottom = rect.bottom - rootRect.bottom;
const right = rect.right - rootRect.right;
if (left < buffer) translateX = buffer - left;
else if (right > buffer) translateX = -buffer - right;
if (top < buffer) translateY = buffer - top;
else if (bottom > buffer) translateY = -bottom - buffer;
if (translateX !== 0 || translateY !== 0) element.style.transform = `translate(${translateX}px, ${translateY}px)`;
}
function findRelativeZIndexParent(el) {
let tmp = el;
while (tmp && (tmp.style.zIndex === "auto" || tmp.style.zIndex === "")) tmp = tmp.parentElement;
return tmp;
}
/** Whether the user has requested reduced motion: the JS counterpart to the
* `prefers-reduced-motion` media query, for animations driven in script. */
function prefersReducedMotion(doc = document) {
var _doc$defaultView, _doc$defaultView$matc;
const mq = (_doc$defaultView = doc.defaultView) === null || _doc$defaultView === void 0 || (_doc$defaultView$matc = _doc$defaultView.matchMedia) === null || _doc$defaultView$matc === void 0 ? void 0 : _doc$defaultView$matc.call(_doc$defaultView, "(prefers-reduced-motion: reduce)");
return !!(mq === null || mq === void 0 ? void 0 : mq.matches);
}
/** The first opaque computed background colour walking up from `element` (the
* element itself, then its ancestors). Use to give a floating/overlapping
* surface a non-see-through backdrop derived from the live DOM. Returns `''`
* when nothing opaque is found (let the stylesheet decide). */
function resolveOpaqueBackground(element) {
const win = element.ownerDocument.defaultView;
if (!win) return "";
let el = element;
while (el) {
const bg = win.getComputedStyle(el).backgroundColor;
if (bg && bg !== "transparent" && !/,\s*0\)\s*$/.test(bg)) return bg;
el = el.parentElement;
}
return "";
}
function tail(arr) {
if (arr.length === 0) throw new Error("Invalid tail call");
return [arr.slice(0, arr.length - 1), arr[arr.length - 1]];
}
function sequenceEquals(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) if (arr1[i] !== arr2[i]) return false;
return true;
}
/**
* Pushes an element to the start of the array, if found.
*/
function pushToStart(arr, value) {
const index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
arr.unshift(value);
}
}
/**
* Pushes an element to the end of the array, if found.
*/
function pushToEnd(arr, value) {
const index = arr.indexOf(value);
if (index > -1) {
arr.splice(index, 1);
arr.push(value);
}
}
function firstIndex(array, fn) {
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (fn(element)) return i;
}
return -1;
}
function remove(array, value) {
const index = array.indexOf(value);
if (index > -1) {
array.splice(index, 1);
return true;
}
return false;
}
const clamp = (value, min, max) => {
if (min > max)
/**
* caveat: an error should be thrown here if this was a proper `clamp` function but we need to handle
* cases where `min` > `max` and in those cases return `min`.
*/
return min;
return Math.min(max, Math.max(value, min));
};
const sequentialNumberGenerator = () => {
let value = 1;
return { next: () => (value++).toString() };
};
const range = (from, to) => {
const result = [];
if (typeof to !== "number") {
to = from;
from = 0;
}
if (from <= to) for (let i = from; i < to; i++) result.push(i);
else for (let i = from; i > to; i--) result.push(i);
return result;
};
var ViewItem = class {
set size(size) {
this._size = size;
}
setContainerGeometry(prop, value) {
if (this._appliedStyles[prop] === value) return;
this._appliedStyles[prop] = value;
this.container.style[prop] = value;
}
get size() {
return this._size;
}
get cachedVisibleSize() {
return this._cachedVisibleSize;
}
get visible() {
return this._cachedVisibleSize === void 0;
}
get minimumSize() {
return this.visible ? this.view.minimumSize : 0;
}
get viewMinimumSize() {
return this.view.minimumSize;
}
get maximumSize() {
return this.visible ? this.view.maximumSize : 0;
}
get viewMaximumSize() {
return this.view.maximumSize;
}
get priority() {
return this.view.priority;
}
get snap() {
return !!this.view.snap;
}
set enabled(enabled) {
this.container.style.pointerEvents = enabled ? "" : "none";
}
constructor(container, view, size, disposable) {
this.container = container;
this.view = view;
this.disposable = disposable;
this._cachedVisibleSize = void 0;
this._appliedStyles = {};
if (typeof size === "number") {
this._size = size;
this._cachedVisibleSize = void 0;
container.classList.add("visible");
} else {
this._size = 0;
this._cachedVisibleSize = size.cachedVisibleSize;
}
}
setVisible(visible, size) {
if (visible === this.visible) return;
if (visible) {
var _this$_cachedVisibleS;
this.size = clamp((_this$_cachedVisibleS = this._cachedVisibleSize) !== null && _this$_cachedVisibleS !== void 0 ? _this$_cachedVisibleS : 0, this.viewMinimumSize, this.viewMaximumSize);
this._cachedVisibleSize = void 0;
} else {
this._cachedVisibleSize = typeof size === "number" ? size : this.size;
this.size = 0;
}
this.container.classList.toggle("visible", visible);
if (this.view.setVisible) this.view.setVisible(visible);
}
dispose() {
this.disposable.dispose();
return this.view;
}
};
let Orientation = /* @__PURE__ */ function(Orientation) {
Orientation["HORIZONTAL"] = "HORIZONTAL";
Orientation["VERTICAL"] = "VERTICAL";
return Orientation;
}({});
let SashState = /* @__PURE__ */ function(SashState) {
SashState[SashState["MAXIMUM"] = 0] = "MAXIMUM";
SashState[SashState["MINIMUM"] = 1] = "MINIMUM";
SashState[SashState["DISABLED"] = 2] = "DISABLED";
SashState[SashState["ENABLED"] = 3] = "ENABLED";
return SashState;
}({});
let LayoutPriority = /* @__PURE__ */ function(LayoutPriority) {
LayoutPriority["Low"] = "low";
LayoutPriority["High"] = "high";
LayoutPriority["Normal"] = "normal";
return LayoutPriority;
}({});
function setSashPosition(sash, left, top) {
if (sash.appliedLeft !== left) {
sash.appliedLeft = left;
sash.container.style.left = left;
}
if (sash.appliedTop !== top) {
sash.appliedTop = top;
sash.container.style.top = top;
}
}
let Sizing;
(function(_Sizing) {
_Sizing.Distribute = { type: "distribute" };
function Split(index) {
return {
type: "split",
index
};
}
_Sizing.Split = Split;
function Invisible(cachedVisibleSize) {
return {
type: "invisible",
cachedVisibleSize
};
}
_Sizing.Invisible = Invisible;
})(Sizing || (Sizing = {}));
var Splitview = class {
get contentSize() {
return this._contentSize;
}
get size() {
return this._size;
}
set size(value) {
this._size = value;
}
get orthogonalSize() {
return this._orthogonalSize;
}
set orthogonalSize(value) {
this._orthogonalSize = value;
}
get length() {
return this.viewItems.length;
}
get proportions() {
return this._proportions ? [...this._proportions] : void 0;
}
get orientation() {
return this._orientation;
}
set orientation(value) {
this._orientation = value;
const tmp = this.size;
this.size = this.orthogonalSize;
this.orthogonalSize = tmp;
removeClasses(this.element, "dv-horizontal", "dv-vertical");
this.element.classList.add(this.orientation == "HORIZONTAL" ? "dv-horizontal" : "dv-vertical");
}
get minimumSize() {
return this.viewItems.reduce((r, item) => r + item.minimumSize, 0);
}
get maximumSize() {
return this.length === 0 ? Number.POSITIVE_INFINITY : this.viewItems.reduce((r, item) => r + item.maximumSize, 0);
}
get startSnappingEnabled() {
return this._startSnappingEnabled;
}
set startSnappingEnabled(startSnappingEnabled) {
if (this._startSnappingEnabled === startSnappingEnabled) return;
this._startSnappingEnabled = startSnappingEnabled;
this.updateSashEnablement();
}
get endSnappingEnabled() {
return this._endSnappingEnabled;
}
set endSnappingEnabled(endSnappingEnabled) {
if (this._endSnappingEnabled === endSnappingEnabled) return;
this._endSnappingEnabled = endSnappingEnabled;
this.updateSashEnablement();
}
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = value;
toggleClass(this.element, "dv-splitview-disabled", value);
}
get margin() {
return this._margin;
}
set margin(value) {
this._margin = value;
toggleClass(this.element, "dv-splitview-has-margin", value !== 0);
}
constructor(container, options) {
var _options$orientation, _options$margin;
this.container = container;
this.viewItems = [];
this.sashes = [];
this._size = 0;
this._orthogonalSize = 0;
this._contentSize = 0;
this._proportions = void 0;
this._startSnappingEnabled = true;
this._endSnappingEnabled = true;
this._disabled = false;
this._margin = 0;
this._onDidSashEnd = new Emitter();
this.onDidSashEnd = this._onDidSashEnd.event;
this._onDidAddView = new Emitter();
this.onDidAddView = this._onDidAddView.event;
this._onDidRemoveView = new Emitter();
this.onDidRemoveView = this._onDidRemoveView.event;
this.resize = (index, delta, sizes = this.viewItems.map((x) => x.size), lowPriorityIndexes, highPriorityIndexes, overloadMinDelta = Number.NEGATIVE_INFINITY, overloadMaxDelta = Number.POSITIVE_INFINITY, snapBefore, snapAfter) => {
if (index < 0 || index > this.viewItems.length) return 0;
const upIndexes = range(index, -1);
const downIndexes = range(index + 1, this.viewItems.length);
if (highPriorityIndexes) for (const i of highPriorityIndexes) {
pushToStart(upIndexes, i);
pushToStart(downIndexes, i);
}
if (lowPriorityIndexes) for (const i of lowPriorityIndexes) {
pushToEnd(upIndexes, i);
pushToEnd(downIndexes, i);
}
const minDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].minimumSize - sizes[i], 0);
const maxDeltaUp = upIndexes.reduce((_, i) => _ + this.viewItems[i].maximumSize - sizes[i], 0);
const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((_, i) => _ + sizes[i] - this.viewItems[i].minimumSize, 0);
const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((_, i) => _ + sizes[i] - this.viewItems[i].maximumSize, 0);
const minDelta = Math.max(minDeltaUp, minDeltaDown);
const maxDelta = Math.min(maxDeltaDown, maxDeltaUp);
let snapped = false;
if (snapBefore) {
const snapView = this.viewItems[snapBefore.index];
const visible = delta >= snapBefore.limitDelta;
snapped = visible !== snapView.visible;
snapView.setVisible(visible, snapBefore.size);
}
if (!snapped && snapAfter) {
const snapView = this.viewItems[snapAfter.index];
const visible = delta < snapAfter.limitDelta;
snapped = visible !== snapView.visible;
snapView.setVisible(visible, snapAfter.size);
}
if (snapped) return this.resize(index, delta, sizes, lowPriorityIndexes, highPriorityIndexes, overloadMinDelta, overloadMaxDelta);
const tentativeDelta = clamp(delta, minDelta, maxDelta);
let actualDelta = 0;
let deltaUp = tentativeDelta;
for (const upIndex of upIndexes) {
const item = this.viewItems[upIndex];
const priorSize = sizes[upIndex];
const size = clamp(priorSize + deltaUp, item.minimumSize, item.maximumSize);
const viewDelta = size - priorSize;
actualDelta += viewDelta;
deltaUp -= viewDelta;
item.size = size;
}
let deltaDown = actualDelta;
for (const downIndex of downIndexes) {
const item = this.viewItems[downIndex];
const priorSize = sizes[downIndex];
const size = clamp(priorSize - deltaDown, item.minimumSize, item.maximumSize);
const viewDelta = size - priorSize;
deltaDown += viewDelta;
item.size = size;
}
return delta;
};
this._orientation = (_options$orientation = options.orientation) !== null && _options$orientation !== void 0 ? _options$orientation : "VERTICAL";
this.element = this.createContainer();
this.margin = (_options$margin = options.margin) !== null && _options$margin !== void 0 ? _options$margin : 0;
this.proportionalLayout = options.proportionalLayout === void 0 ? true : !!options.proportionalLayout;
this.viewContainer = this.createViewContainer();
this.sashContainer = this.createSashContainer();
this.element.appendChild(this.sashContainer);
this.element.appendChild(this.viewContainer);
this.container.appendChild(this.element);
this.style(options.styles);
if (options.descriptor) {
this._size = options.descriptor.size;
options.descriptor.views.forEach((viewDescriptor, index) => {
const sizing = viewDescriptor.visible === void 0 || viewDescriptor.visible ? viewDescriptor.size : {
type: "invisible",
cachedVisibleSize: viewDescriptor.size
};
const view = viewDescriptor.view;
this.addView(view, sizing, index, true);
});
this._contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.saveProportions();
}
}
style(styles) {
if ((styles === null || styles === void 0 ? void 0 : styles.separatorBorder) === "transparent") {
removeClasses(this.element, "dv-separator-border");
this.element.style.removeProperty("--dv-separator-border");
} else {
addClasses(this.element, "dv-separator-border");
if (styles === null || styles === void 0 ? void 0 : styles.separatorBorder) this.element.style.setProperty("--dv-separator-border", styles.separatorBorder);
}
}
isViewVisible(index) {
if (index < 0 || index >= this.viewItems.length) throw new Error("Index out of bounds");
return this.viewItems[index].visible;
}
setViewVisible(index, visible) {
if (index < 0 || index >= this.viewItems.length) throw new Error("Index out of bounds");
const viewItem = this.viewItems[index];
viewItem.setVisible(visible, viewItem.size);
this.distributeEmptySpace(index);
this.layoutViews();
this.saveProportions();
}
getViewSize(index) {
if (index < 0 || index >= this.viewItems.length) return -1;
return this.viewItems[index].size;
}
resizeView(index, size) {
if (index < 0 || index >= this.viewItems.length) return;
const indexes = range(this.viewItems.length).filter((i) => i !== index);
const lowPriorityIndexes = [...indexes.filter((i) => this.viewItems[i].priority === "low"), index];
const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
const item = this.viewItems[index];
size = Math.round(size);
size = clamp(size, item.minimumSize, Math.min(item.maximumSize, this._size));
item.size = size;
this.relayout(lowPriorityIndexes, highPriorityIndexes);
}
getViews() {
return this.viewItems.map((x) => x.view);
}
onDidChange(item, size) {
const index = this.viewItems.indexOf(item);
if (index < 0 || index >= this.viewItems.length) return;
size = typeof size === "number" ? size : item.size;
size = clamp(size, item.minimumSize, item.maximumSize);
item.size = size;
const indexes = range(this.viewItems.length).filter((i) => i !== index);
const lowPriorityIndexes = [...indexes.filter((i) => this.viewItems[i].priority === "low"), index];
const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
/**
* add this view we are changing to the low-index list since we have determined the size
* here and don't want it changed
*/
this.relayout([...lowPriorityIndexes, index], highPriorityIndexes);
}
addView(view, size = Sizing.Distribute, index = this.viewItems.length, skipLayout) {
const container = document.createElement("div");
container.className = "dv-view";
container.appendChild(view.element);
let viewSize;
if (typeof size === "number") viewSize = size;
else if (size.type === "split") viewSize = this.getViewSize(size.index) / 2;
else if (size.type === "invisible") viewSize = { cachedVisibleSize: size.cachedVisibleSize };
else viewSize = view.minimumSize;
const disposable = view.onDidChange((newSize) => this.onDidChange(viewItem, newSize.size));
const viewItem = new ViewItem(container, view, viewSize, { dispose: () => {
disposable.dispose();
container.remove();
} });
if (index === this.viewItems.length) this.viewContainer.appendChild(container);
else this.viewContainer.insertBefore(container, this.viewContainer.children.item(index));
this.viewItems.splice(index, 0, viewItem);
if (this.viewItems.length > 1) {
const sash = document.createElement("div");
sash.className = "dv-sash";
const onPointerStart = (event) => {
var _sash$ownerDocument;
for (const item of this.viewItems) item.enabled = false;
const doc = (_sash$ownerDocument = sash.ownerDocument) !== null && _sash$ownerDocument !== void 0 ? _sash$ownerDocument : document;
const iframes = disableIframePointEvents(doc);
const start = this._orientation === "HORIZONTAL" ? event.clientX : event.clientY;
const sashIndex = firstIndex(this.sashes, (s) => s.container === sash);
const sizes = this.viewItems.map((x) => x.size);
let snapBefore;
let snapAfter;
const upIndexes = range(sashIndex, -1);
const downIndexes = range(sashIndex + 1, this.viewItems.length);
const minDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].minimumSize - sizes[i]), 0);
const maxDeltaUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].viewMaximumSize - sizes[i]), 0);
const maxDeltaDown = downIndexes.length === 0 ? Number.POSITIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].minimumSize), 0);
const minDeltaDown = downIndexes.length === 0 ? Number.NEGATIVE_INFINITY : downIndexes.reduce((r, i) => r + (sizes[i] - this.viewItems[i].viewMaximumSize), 0);
const minDelta = Math.max(minDeltaUp, minDeltaDown);
const maxDelta = Math.min(maxDeltaDown, maxDeltaUp);
const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
if (typeof snapBeforeIndex === "number") {
const snappedViewItem = this.viewItems[snapBeforeIndex];
const halfSize = Math.floor(snappedViewItem.viewMinimumSize / 2);
snapBefore = {
index: snapBeforeIndex,
limitDelta: snappedViewItem.visible ? minDelta - halfSize : minDelta + halfSize,
size: snappedViewItem.size
};
}
if (typeof snapAfterIndex === "number") {
const snappedViewItem = this.viewItems[snapAfterIndex];
const halfSize = Math.floor(snappedViewItem.viewMinimumSize / 2);
snapAfter = {
index: snapAfterIndex,
limitDelta: snappedViewItem.visible ? maxDelta + halfSize : maxDelta - halfSize,
size: snappedViewItem.size
};
}
const onPointerMove = (event) => {
const delta = (this._orientation === "HORIZONTAL" ? event.clientX : event.clientY) - start;
this.resize(sashIndex, delta, sizes, void 0, void 0, minDelta, maxDelta, snapBefore, snapAfter);
this.distributeEmptySpace();
this.layoutViews();
};
const end = () => {
for (const item of this.viewItems) item.enabled = true;
iframes.release();
this.saveProportions();
doc.removeEventListener("pointermove", onPointerMove);
doc.removeEventListener("pointerup", end);
doc.removeEventListener("pointercancel", end);
doc.removeEventListener("contextmenu", end);
this._onDidSashEnd.fire(void 0);
};
doc.addEventListener("pointermove", onPointerMove);
doc.addEventListener("pointerup", end);
doc.addEventListener("pointercancel", end);
doc.addEventListener("contextmenu", end);
};
sash.addEventListener("pointerdown", onPointerStart);
const sashItem = {
container: sash,
disposable: () => {
sash.removeEventListener("pointerdown", onPointerStart);
sash.remove();
}
};
this.sashContainer.appendChild(sash);
this.sashes.push(sashItem);
}
if (!skipLayout) this.relayout([index]);
if (!skipLayout && typeof size !== "number" && size.type === "distribute") this.distributeViewSizes();
this._onDidAddView.fire(view);
}
distributeViewSizes() {
const flexibleViewItems = [];
let flexibleSize = 0;
for (const item of this.viewItems) if (item.maximumSize - item.minimumSize > 0) {
flexibleViewItems.push(item);
flexibleSize += item.size;
}
const size = Math.floor(flexibleSize / flexibleViewItems.length);
for (const item of flexibleViewItems) item.size = clamp(size, item.minimumSize, item.maximumSize);
const indexes = range(this.viewItems.length);
const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
this.relayout(lowPriorityIndexes, highPriorityIndexes);
}
removeView(index, sizing, skipLayout = false) {
const viewItem = this.viewItems.splice(index, 1)[0];
viewItem.dispose();
if (this.viewItems.length >= 1) {
const sashIndex = Math.max(index - 1, 0);
this.sashes.splice(sashIndex, 1)[0].disposable();
}
if (!skipLayout) this.relayout();
if ((sizing === null || sizing === void 0 ? void 0 : sizing.type) === "distribute") this.distributeViewSizes();
this._onDidRemoveView.fire(viewItem.view);
return viewItem.view;
}
getViewCachedVisibleSize(index) {
if (index < 0 || index >= this.viewItems.length) throw new Error("Index out of bounds");
return this.viewItems[index].cachedVisibleSize;
}
moveView(from, to) {
const cachedVisibleSize = this.getViewCachedVisibleSize(from);
const sizing = cachedVisibleSize === void 0 ? this.getViewSize(from) : Sizing.Invisible(cachedVisibleSize);
const view = this.removeView(from, void 0, true);
this.addView(view, sizing, to);
}
layout(size, orthogonalSize) {
const previousSize = Math.max(this.size, this._contentSize);
this.size = size;
this.orthogonalSize = orthogonalSize;
if (this.proportions) {
let total = 0;
for (let i = 0; i < this.viewItems.length; i++) {
const item = this.viewItems[i];
const proportion = this.proportions[i];
if (typeof proportion === "number") total += proportion;
else size -= item.size;
}
for (let i = 0; i < this.viewItems.length; i++) {
const item = this.viewItems[i];
const proportion = this.proportions[i];
if (typeof proportion === "number" && total > 0) item.size = clamp(Math.round(proportion * size / total), item.minimumSize, item.maximumSize);
}
} else {
const indexes = range(this.viewItems.length);
const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
this.resize(this.viewItems.length - 1, size - previousSize, void 0, lowPriorityIndexes, highPriorityIndexes);
}
this.distributeEmptySpace();
this.layoutViews();
}
relayout(lowPriorityIndexes, highPriorityIndexes) {
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.resize(this.viewItems.length - 1, this._size - contentSize, void 0, lowPriorityIndexes, highPriorityIndexes);
this.distributeEmptySpace();
this.layoutViews();
this.saveProportions();
}
distributeEmptySpace(lowPriorityIndex) {
let contentSize = 0;
for (const item of this.viewItems) contentSize += item.size;
let emptyDelta = this.size - contentSize;
if (emptyDelta === 0) return;
const indexes = range(this.viewItems.length - 1, -1);
let hasPriority = false;
for (const item of this.viewItems) if (item.priority === "low" || item.priority === "high") {
hasPriority = true;
break;
}
if (hasPriority) {
const lowPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "low");
const highPriorityIndexes = indexes.filter((i) => this.viewItems[i].priority === "high");
for (const index of highPriorityIndexes) pushToStart(indexes, index);
for (const index of lowPriorityIndexes) pushToEnd(indexes, index);
}
if (typeof lowPriorityIndex === "number") pushToEnd(indexes, lowPriorityIndex);
for (let i = 0; emptyDelta !== 0 && i < indexes.length; i++) {
const item = this.viewItems[indexes[i]];
const size = clamp(item.size + emptyDelta, item.minimumSize, item.maximumSize);
const viewDelta = size - item.size;
emptyDelta -= viewDelta;
item.size = size;
}
}
saveProportions() {
if (this.proportionalLayout && this._contentSize > 0) this._proportions = this.viewItems.map((i) => i.visible ? i.size / this._contentSize : void 0);
}
/**
* Margin explain:
*
* For `n` views in a splitview there will be `n-1` margins `m`.
*
* To fit the margins each view must reduce in size by `(m * (n - 1)) / n`.
*
* For each view `i` the offet must be adjusted by `m * i/(n - 1)`.
*/
layoutViews() {
let contentSize = 0;
let visibleViewCount = 0;
for (const item of this.viewItems) {
contentSize += item.size;
if (item.visible) visibleViewCount++;
}
this._contentSize = contentSize;
this.updateSashEnablement();
if (this.viewItems.length === 0) return;
const sashCount = Math.max(0, visibleViewCount - 1);
const marginReducedSize = this.margin * sashCount / Math.max(1, visibleViewCount);
let totalLeftOffset = 0;
const viewLeftOffsets = [];
const sashWidth = 4;
let runningVisiblePanelCount = 0;
this.viewItems.forEach((view, i) => {
totalLeftOffset += this.viewItems[i].size;
viewLeftOffsets.push(totalLeftOffset);
runningVisiblePanelCount += view.visible ? 1 : 0;
const size = view.visible ? view.size - marginReducedSize : 0;
const visiblePanelsBeforeThisView = Math.max(0, runningVisiblePanelCount - 1);
const offset = i === 0 || visiblePanelsBeforeThisView === 0 ? 0 : viewLeftOffsets[i - 1] + visiblePanelsBeforeThisView / sashCount * marginReducedSize;
if (i < this.viewItems.length - 1) {
const newSize = view.visible ? offset + size - sashWidth / 2 + this.margin / 2 : offset;
const sash = this.sashes[i];
if (this._orientation === "HORIZONTAL") setSashPosition(sash, `${newSize}px`, `0px`);
if (this._orientation === "VERTICAL") setSashPosition(sash, `0px`, `${newSize}px`);
}
if (this._orientation === "HORIZONTAL") {
view.setContainerGeometry("width", `${size}px`);
view.setContainerGeometry("left", `${offset}px`);
view.setContainerGeometry("top", "");
view.setContainerGeometry("height", "");
}
if (this._orientation === "VERTICAL") {
view.setContainerGeometry("height", `${size}px`);
view.setContainerGeometry("top", `${offset}px`);
view.setContainerGeometry("width", "");
view.setContainerGeometry("left", "");
}
view.view.layout(view.size - marginReducedSize, this._orthogonalSize);
});
}
findFirstSnapIndex(indexes) {
for (const index of indexes) {
const viewItem = this.viewItems[index];
if (!viewItem.visible) continue;
if (viewItem.snap) return index;
}
for (const index of indexes) {
const viewItem = this.viewItems[index];
if (viewItem.visible && viewItem.maximumSize - viewItem.minimumSize > 0) return;
if (!viewItem.visible && viewItem.snap) return index;
}
}
updateSashEnablement() {
if (this.sashes.length === 0) return;
let previous = false;
const collapsesDown = this.viewItems.map((i) => previous = i.size - i.minimumSize > 0 || previous);
previous = false;
const expandsDown = this.viewItems.map((i) => previous = i.maximumSize - i.size > 0 || previous);
const reverseViews = [...this.viewItems].reverse();
previous = false;
const collapsesUp = reverseViews.map((i) => previous = i.size - i.minimumSize > 0 || previous).reverse();
previous = false;
const expandsUp = reverseViews.map((i) => previous = i.maximumSize - i.size > 0 || previous).reverse();
let position = 0;
for (let index = 0; index < this.sashes.length; index++) {
const sash = this.sashes[index];
const viewItem = this.viewItems[index];
position += viewItem.size;
const min = !(collapsesDown[index] && expandsUp[index + 1]);
const max = !(expandsDown[index] && collapsesUp[index + 1]);
if (min && max) {
const upIndexes = range(index, -1);
const downIndexes = range(index + 1, this.viewItems.length);
const snapBeforeIndex = this.findFirstSnapIndex(upIndexes);
const snapAfterIndex = this.findFirstSnapIndex(downIndexes);
const snappedBefore = typeof snapBeforeIndex === "number" && !this.viewItems[snapBeforeIndex].visible;
const snappedAfter = typeof snapAfterIndex === "number" && !this.viewItems[snapAfterIndex].visible;
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) this.updateSash(sash, 1);
else if (snappedAfter && collapsesDown[index] && (position < this._contentSize || this.endSnappingEnabled)) this.updateSash(sash, 0);
else this.updateSash(sash, 2);
} else if (min && !max) this.updateSash(sash, 1);
else if (!min && max) this.updateSash(sash, 0);
else this.updateSash(sash, 3);
}
}
updateSash(sash, state) {
toggleClass(sash.container, "dv-disabled", state === 2);
toggleClass(sash.container, "dv-enabled", state === 3);
toggleClass(sash.container, "dv-maximum", state === 0);
toggleClass(sash.container, "dv-minimum", state === 1);
}
createViewContainer() {
const element = document.createElement("div");
element.className = "dv-view-container";
return element;
}
createSashContainer() {
const element = document.createElement("div");
element.className = "dv-sash-container";
return element;
}
createContainer() {
const element = document.createElement("div");
element.className = `dv-split-view-container ${this._orientation === "HORIZONTAL" ? "dv-horizontal" : "dv-vertical"}`;
return element;
}
dispose() {
this._onDidSashEnd.dispose();
this._onDidAddView.dispose();
this._onDidRemoveView.dispose();
for (let i = 0; i < this.element.children.length; i++) if (this.element.children.item(i) === this.element) {
this.element.remove();
break;
}
for (const viewItem of this.viewItems) viewItem.dispose();
this.element.remove();
}
};
const PROPERTY_KEYS_SPLITVIEW = (() => {
return Object.keys({
orientation: void 0,
descriptor: void 0,
proportionalLayout: void 0,
styles: void 0,
margin: void 0,
disableAutoResizing: void 0,
className: void 0
});
})();
var Paneview = class extends CompositeDisposable {
get onDidAddView() {
return this.splitview.onDidAddView;
}
get onDidRemoveView() {
return this.splitview.onDidRemoveView;
}
get minimumSize() {
return this.splitview.minimumSize;
}
get maximumSize() {
return this.splitview.maximumSize;
}
get orientation() {
return this.splitview.orientation;
}
get size() {
return this.splitview.size;
}
get orthogonalSize() {
return this.splitview.orthogonalSize;
}
constructor(container, options) {
var _options$orientation;
super();
this.paneItems = [];
this.skipAnimation = false;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._orientation = (_options$orientation = options.orientation) !== null && _options$orientation !== void 0 ? _options$orientation : "VERTICAL";
this.element = document.createElement("div");
this.element.className = "dv-pane-container";
container.appendChild(this.element);
this.splitview = new Splitview(this.element, {
orientation: this._orientation,
proportionalLayout: false,
descriptor: options.descriptor
});
this.getPanes().forEach((pane) => {
const disposable = new CompositeDisposable(pane.onDidChangeExpansionState(() => {
this.setupAnimation();
this._onDidChange.fire(void 0);
}));
const paneItem = {
pane,
disposable: { dispose: () => {
disposable.dispose();
} }
};
this.paneItems.push(paneItem);
pane.orthogonalSize = this.splitview.orthogonalSize;
});
this.addDisposables(this._onDidChange, this.splitview.onDidSashEnd(() => {
this._onDidChange.fire(void 0);
}), this.splitview.onDidAddView(() => {
this._onDidChange.fire();
}), this.splitview.onDidRemoveView(() => {
this._onDidChange.fire();
}));
}
setViewVisible(index, visible) {
this.splitview.setViewVisible(index, visible);
}
addPane(pane, size, index = this.splitview.length, skipLayout = false) {
const disposable = pane.onDidChangeExpansionState(() => {
this.setupAnimation();
this._onDidChange.fire(void 0);
});
const paneItem = {
pane,
disposable: { dispose: () => {
disposable.dispose();
} }
};
this.paneItems.splice(index, 0, paneItem);
pane.orthogonalSize = this.splitview.orthogonalSize;
this.splitview.addView(pane, size, index, skipLayout);
}
getViewSize(index) {
return this.splitview.getViewSize(index);
}
getViewCachedVisibleSize(index) {
return this.splitview.getViewCachedVisibleSize(index);
}
getPanes() {
return this.splitview.getViews();
}
removePane(index, options) {
const paneItem = this.paneItems.splice(index, 1)[0];
this.splitview.removeView(index);
if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
paneItem.disposable.dispose();
paneItem.pane.dispose();
}
return paneItem;
}
moveView(from, to) {
if (from === to) return;
const view = this.removePane(from, { skipDispose: true });
view.disposable.dispose();
this.skipAnimation = true;
try {
this.addPane(view.pane, view.pane.size, to, false);
} finally {
this.skipAnimation = false;
}
}
layout(size, orthogonalSize) {
this.splitview.layout(size, orthogonalSize);
}
setupAnimation() {
if (this.skipAnimation) return;
if (this.animationTimer) {
clearTimeout(this.animationTimer);
this.animationTimer = void 0;
}
addClasses(this.element, "dv-animated");
this.animationTimer = setTimeout(() => {
this.animationTimer = void 0;
removeClasses(this.element, "dv-animated");
}, 200);
}
dispose() {
super.dispose();
if (this.animationTimer) {
clearTimeout(this.animationTimer);
this.animationTimer = void 0;
}
this.paneItems.forEach((paneItem) => {
paneItem.disposable.dispose();
paneItem.pane.dispose();
});
this.paneItems = [];
this.splitview.dispose();
this.element.remove();
}
};
var LeafNode = class {
get minimumWidth() {
return this.view.minimumWidth;
}
get maximumWidth() {
return this.view.maximumWidth;
}
get minimumHeight() {
return this.view.minimumHeight;
}
get maximumHeight() {
return this.view.maximumHeight;
}
get priority() {
return this.view.priority;
}
get snap() {
return this.view.snap;
}
get minimumSize() {
return this.orientation === "HORIZONTAL" ? this.minimumHeight : this.minimumWidth;
}
get maximumSize() {
return this.orientation === "HORIZONTAL" ? this.maximumHeight : this.maximumWidth;
}
get minimumOrthogonalSize() {
return this.orientation === "HORIZONTAL" ? this.minimumWidth : this.minimumHeight;
}
get maximumOrthogonalSize() {
return this.orientation === "HORIZONTAL" ? this.maximumWidth : this.maximumHeight;
}
get orthogonalSize() {
return this._orthogonalSize;
}
get size() {
return this._size;
}
get element() {
return this.view.element;
}
get width() {
return this.orientation === "HORIZONTAL" ? this.orthogonalSize : this.size;
}
get height() {
return this.orientation === "HORIZONTAL" ? this.size : this.orthogonalSize;
}
constructor(view, orientation, orthogonalSize, size = 0) {
this.view = view;
this.orientation = orientation;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._orthogonalSize = orthogonalSize;
this._size = size;
this._disposable = this.view.onDidChange((event) => {
if (event) this._onDidChange.fire({
size: this.orientation === "VERTICAL" ? event.width : event.height,
orthogonalSize: this.orientation === "VERTICAL" ? event.height : event.width
});
else this._onDidChange.fire({});
});
}
setVisible(visible) {
if (this.view.setVisible) this.view.setVisible(visible);
}
layout(size, orthogonalSize) {
this._size = size;
this._orthogonalSize = orthogonalSize;
this.view.layout(this.width, this.height);
}
dispose() {
this._onDidChange.dispose();
this._disposable.dispose();
}
};
var BranchNode = class BranchNode extends CompositeDisposable {
get width() {
return this.orientation === "HORIZONTAL" ? this.size : this.orthogonalSize;
}
get height() {
return this.orientation === "HORIZONTAL" ? this.orthogonalSize : this.size;
}
get minimumSize() {
if (this._cachedMinimumSize === void 0) {
let value = 0;
for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.max(value, this.children[index].minimumOrthogonalSize);
this._cachedMinimumSize = value;
}
return this._cachedMinimumSize;
}
get maximumSize() {
if (this._cachedMaximumSize === void 0) {
let value = Number.POSITIVE_INFINITY;
for (let index = 0; index < this.children.length; index++) if (this.splitview.isViewVisible(index)) value = Math.min(value, this.children[index].maximumOrthogonalSize);
this._cachedMaximumSize = value;
}
return this._cachedMaximumSize;
}
get minimumOrthogonalSize() {
return this.splitview.minimumSize;
}
get maximumOrthogonalSize() {
return this.splitview.maximumSize;
}
get orthogonalSize() {
return this._orthogonalSize;
}
get size() {
return this._size;
}
get minimumWidth() {
return this.orientation === "HORIZONTAL" ? this.minimumOrthogonalSize : this.minimumSize;
}
get minimumHeight() {
return this.orientation === "HORIZONTAL" ? this.minimumSize : this.minimumOrthogonalSize;
}
get maximumWidth() {
return this.orientation === "HORIZONTAL" ? this.maximumOrthogonalSize : this.maximumSize;
}
get maximumHeight() {
return this.orientation === "HORIZONTAL" ? this.maximumSize : this.maximumOrthogonalSize;
}
get priority() {
var _this$_cachedPriority;
(_this$_cachedPriority = this._cachedPriority) !== null && _this$_cachedPriority !== void 0 || (this._cachedPriority = this.computePriority());
return this._cachedPriority;
}
computePriority() {
if (this.children.length === 0) return "normal";
let hasHigh = false;
let hasLow = false;
for (const child of this.children) {
var _child$priority;
const priority = (_child$priority = child.priority) !== null && _child$priority !== void 0 ? _child$priority : "normal";
if (priority === "high") hasHigh = true;
else if (priority === "low") hasLow = true;
}
if (hasHigh) return "high";
else if (hasLow) return "low";
return "normal";
}
invalidateCachedSizes() {
this._cachedMinimumSize = void 0;
this._cachedMaximumSize = void 0;
this._cachedPriority = void 0;
}
get disabled() {
return this.splitview.disabled;
}
set disabled(value) {
this.splitview.disabled = value;
}
get margin() {
return this.splitview.margin;
}
set margin(value) {
this.splitview.margin = value;
this.children.forEach((child) => {
if (child instanceof BranchNode) child.margin = value;
});
}
constructor(orientation, proportionalLayout, styles, size, orthogonalSize, disabled, margin, childDescriptors) {
super();
this.orientation = orientation;
this.proportionalLayout = proportionalLayout;
this.styles = styles;
this._childrenDisposable = Disposable.NONE;
this.children = [];
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._onDidVisibilityChange = new Emitter();
this.onDidVisibilityChange = this._onDidVisibilityChange.event;
this._orthogonalSize = orthogonalSize;
this._size = size;
this.element = document.createElement("div");
this.element.className = "dv-branch-node";
if (childDescriptors) {
const descriptor = {
views: childDescriptors.map((childDescriptor) => {
var _childDescriptor$visi;
return {
view: childDescriptor.node,
size: childDescriptor.node.size,
visible: (_childDescriptor$visi = childDescriptor.visible) !== null && _childDescriptor$visi !== void 0 ? _childDescriptor$visi : true
};
}),
size: this.orthogonalSize
};
this.children = childDescriptors.map((c) => c.node);
this.splitview = new Splitview(this.element, {
orientation: this.orientation,
descriptor,
proportionalLayout,
styles,
margin
});
} else {
this.splitview = new Splitview(this.element, {
orientation: this.orientation,
proportionalLayout,
styles,
margin
});
this.splitview.layout(this.size, this.orthogonalSize);
}
this.disabled = disabled;
this.addDisposables(this._onDidChange, this._onDidVisibilityChange, this.splitview.onDidSashEnd(() => {
this._onDidChange.fire({});
}));
this.setupChildrenEvents();
}
setVisible(_visible) {}
isChildVisible(index) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
return this.splitview.isViewVisible(index);
}
setChildVisible(index, visible) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
if (this.splitview.isViewVisible(index) === visible) return;
const wereAllChildrenHidden = this.splitview.contentSize === 0;
this.splitview.setViewVisible(index, visible);
this.invalidateCachedSizes();
const areAllChildrenHidden = this.splitview.contentSize === 0;
if (visible && wereAllChildrenHidden || !visible && areAllChildrenHidden) this._onDidVisibilityChange.fire({ visible });
}
moveChild(from, to) {
if (from === to) return;
if (from < 0 || from >= this.children.length) throw new Error("Invalid from index");
if (from < to) to--;
this.splitview.moveView(from, to);
const child = this._removeChild(from);
this._addChild(child, to);
}
getChildSize(index) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
return this.splitview.getViewSize(index);
}
resizeChild(index, size) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
this.splitview.resizeView(index, size);
}
layout(size, orthogonalSize) {
this._size = orthogonalSize;
this._orthogonalSize = size;
this.splitview.layout(orthogonalSize, size);
}
addChild(node, size, index, skipLayout) {
if (index < 0 || index > this.children.length) throw new Error("Invalid index");
this.splitview.addView(node, size, index, skipLayout);
this._addChild(node, index);
}
getChildCachedVisibleSize(index) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
return this.splitview.getViewCachedVisibleSize(index);
}
removeChild(index, sizing) {
if (index < 0 || index >= this.children.length) throw new Error("Invalid index");
this.splitview.removeView(index, sizing);
return this._removeChild(index);
}
_addChild(node, index) {
this.children.splice(index, 0, node);
this.setupChildrenEvents();
}
_removeChild(index) {
const [child] = this.children.splice(index, 1);
this.setupChildrenEvents();
return child;
}
setupChildrenEvents() {
this.invalidateCachedSizes();
this._childrenDisposable.dispose();
this._childrenDisposable = new CompositeDisposable(Event.any(...this.children.map((c) => c.onDidChange))((e) => {
/**
* indicate a change has occured to allows any re-rendering but don't bubble
* event because that was specific to this branch
*/
this.invalidateCachedSizes();
this._onDidChange.fire({ size: e.orthogonalSize });
}), ...this.children.map((c, i) => {
if (c instanceof BranchNode) return c.onDidVisibilityChange(({ visible }) => {
this.setChildVisible(i, visible);
});
return Disposable.NONE;
}));
}
dispose() {
this._childrenDisposable.dispose();
this.splitview.dispose();
this.children.forEach((child) => child.dispose());
super.dispose();
}
};
function findLeaf(candiateNode, last) {
if (candiateNode instanceof LeafNode) return candiateNode;
if (candiateNode instanceof BranchNode) return findLeaf(candiateNode.children[last ? candiateNode.children.length - 1 : 0], last);
throw new Error("invalid node");
}
function cloneNode(node, size, orthogonalSize) {
if (node instanceof BranchNode) {
const result = new BranchNode(node.orientation, node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
for (let i = node.children.length - 1; i >= 0; i--) {
const child = node.children[i];
result.addChild(cloneNode(child, child.size, child.orthogonalSize), child.size, 0, true);
}
return result;
} else return new LeafNode(node.view, node.orientation, orthogonalSize);
}
function flipNode(node, size, orthogonalSize) {
if (node instanceof BranchNode) {
const result = new BranchNode(orthogonal(node.orientation), node.proportionalLayout, node.styles, size, orthogonalSize, node.disabled, node.margin);
let totalSize = 0;
for (let i = node.children.length - 1; i >= 0; i--) {
const child = node.children[i];
const childSize = child instanceof BranchNode ? child.orthogonalSize : child.size;
let newSize = node.size === 0 ? 0 : Math.round(size * childSize / node.size);
totalSize += newSize;
if (i === 0) newSize += size - totalSize;
result.addChild(flipNode(child, orthogonalSize, newSize), newSize, 0, true);
}
return result;
} else return new LeafNode(node.view, orthogonal(node.orientation), orthogonalSize);
}
function indexInParent(element) {
const parentElement = element.parentElement;
if (!parentElement) throw new Error("Invalid grid element");
let el = parentElement.firstElementChild;
let index = 0;
while (el !== element && el !== parentElement.lastElementChild && el) {
el = el.nextElementSibling;
index++;
}
return index;
}
/**
* Find the grid location of a specific DOM element by traversing the parent
* chain and finding each child index on the way.
*
* This will break as soon as DOM structures of the Splitview or Gridview change.
*/
function getGridLocation(element) {
const parentElement = element.parentElement;
if (!parentElement) throw new Error("Invalid grid element");
if (/\bdv-grid-view\b/.test(parentElement.className)) return [];
const index = indexInParent(parentElement);
const ancestor = parentElement.parentElement.parentElement.parentElement;
return [...getGridLocation(ancestor), index];
}
function getRelativeLocation(rootOrientation, location, direction) {
if (getLocationOrientation(rootOrientation, location) === getDirectionOrientation(direction)) {
const [rest, _index] = tail(location);
let index = _index;
if (direction === "right" || direction === "bottom") index += 1;
return [...rest, index];
} else {
const index = direction === "right" || direction === "bottom" ? 1 : 0;
return [...location, index];
}
}
function getDirectionOrientation(direction) {
return direction === "top" || direction === "bottom" ? "VERTICAL" : "HORIZONTAL";
}
function getLocationOrientation(rootOrientation, location) {
return location.length % 2 === 0 ? orthogonal(rootOrientation) : rootOrientation;
}
const orthogonal = (orientation) => orientation === "HORIZONTAL" ? "VERTICAL" : "HORIZONTAL";
function isGridBranchNode(node) {
return !!node.children;
}
const serializeBranchNode = (node, orientation) => {
const size = orientation === "VERTICAL" ? node.box.width : node.box.height;
if (!isGridBranchNode(node)) {
if (typeof node.cachedVisibleSize === "number") return {
type: "leaf",
data: node.view.toJSON(),
size: node.cachedVisibleSize,
visible: false
};
return {
type: "leaf",
data: node.view.toJSON(),
size
};
}
const data = node.children.map((c) => serializeBranchNode(c, orthogonal(orientation)));
if (typeof node.cachedVisibleSize === "number") return {
type: "branch",
data,
size: node.cachedVisibleSize,
visible: false
};
return {
type: "branch",
data,
size
};
};
var Gridview = class {
get length() {
return this._root ? this._root.children.length : 0;
}
get orientation() {
return this.root.orientation;
}
set orientation(orientation) {
if (this.root.orientation === orientation) return;
const { size, orthogonalSize } = this.root;
this.root = flipNode(this.root, orthogonalSize, size);
this.root.layout(size, orthogonalSize);
}
get width() {
return this.root.width;
}
get height() {
return this.root.height;
}
get minimumWidth() {
return this.root.minimumWidth;
}
get minimumHeight() {
return this.root.minimumHeight;
}
get maximumWidth() {
return this.root.maximumWidth;
}
get maximumHeight() {
return this.root.maximumHeight;
}
get locked() {
return this._locked;
}
set locked(value) {
this._locked = value;
const branch = [this.root];
/**
* simple depth-first-search to cover all nodes
*
* @see https://en.wikipedia.org/wiki/Depth-first_search
*/
while (branch.length > 0) {
const node = branch.pop();
if (node instanceof BranchNode) {
node.disabled = value;
branch.push(...node.children);
}
}
}
get margin() {
return this._margin;
}
set margin(value) {
this._margin = value;
this.root.margin = value;
}
maximizedView() {
var _this$_maximizedNode;
return (_this$_maximizedNode = this._maximizedNode) === null || _this$_maximizedNode === void 0 ? void 0 : _this$_maximizedNode.leaf.view;
}
hasMaximizedView() {
return this._maximizedNode !== void 0;
}
maximizeView(view) {
var _this$_maximizedNode2;
const location = getGridLocation(view.element);
const [_, node] = this.getNode(location);
if (!(node instanceof LeafNode)) return;
if (((_this$_maximizedNode2 = this._maximizedNode) === null || _this$_maximizedNode2 === void 0 ? void 0 : _this$_maximizedNode2.leaf) === node) return;
if (this.hasMaximizedView()) this.exitMaximizedView();
serializeBranchNode(this.getView(), this.orientation);
const hiddenOnMaximize = [];
function hideAllViewsBut(parent, exclude) {
for (let i = 0; i < parent.children.length; i++) {
const child = parent.children[i];
if (child instanceof LeafNode) {
if (child !== exclude) if (parent.isChildVisible(i)) parent.setChildVisible(i, false);
else hiddenOnMaximize.push(child);
} else hideAllViewsBut(child, exclude);
}
}
hideAllViewsBut(this.root, node);
this._maximizedNode = {
leaf: node,
hiddenOnMaximize
};
this._onDidMaximizedNodeChange.fire({
view: node.view,
isMaximized: true
});
}
exitMaximizedView() {
if (!this._maximizedNode) return;
const hiddenOnMaximize = this._maximizedNode.hiddenOnMaximize;
function showViewsInReverseOrder(parent) {
for (let index = parent.children.length - 1; index >= 0; index--) {
const child = parent.children[index];
if (child instanceof LeafNode) {
if (!hiddenOnMaximize.includes(child)) parent.setChildVisible(index, true);
} else showViewsInReverseOrder(child);
}
}
showViewsInReverseOrder(this.root);
const tmp = this._maximizedNode.leaf;
this._maximizedNode = void 0;
this._onDidMaximizedNodeChange.fire({
view: tmp.view,
isMaximized: false
});
}
serialize() {
const maximizedView = this.maximizedView();
let maxmizedViewLocation;
if (maximizedView)
/**
* The minimum information we can get away with in order to serialize a maxmized view is it's location within the grid
* which is represented as a branch of indices
*/
maxmizedViewLocation = getGridLocation(maximizedView.element);
/**
* We pause the onDidMaximizedNodeChange events because this method needs to
* call `this.exitMaximizedView()`. We don't want this to invoke any listeners
* since we undo it before leaving this method
*/
const pauseToken = this._onDidMaximizedNodeChange.pause();
try {
if (this.hasMaximizedView())
/**
* the saved layout cannot be in its maxmized state otherwise all of the underlying
* view dimensions will be wrong
*
* To counteract this we temporaily remove the maximized view to compute the serialized output
* of the grid before adding back the maxmized view as to not alter the layout from the users
* perspective when `.toJSON()` is called
*/
this.exitMaximizedView();
const result = {
root: serializeBranchNode(this.getView(), this.orientation),
width: this.width,
height: this.height,
orientation: this.orientation
};
if (maxmizedViewLocation) result.maximizedNode = { location: maxmizedViewLocation };
if (maximizedView) this.maximizeView(maximizedView);
return result;
} finally {
pauseToken.dispose();
}
}
dispose() {
this.disposable.dispose();
this._onDidChange.dispose();
this._onDidMaximizedNodeChange.dispose();
this._onDidViewVisibilityChange.dispose();
this.root.dispose();
this._maximizedNode = void 0;
this.element.remove();
}
clear() {
const orientation = this.root.orientation;
this.root = new BranchNode(orientation, this.proportionalLayout, this.styles, this.root.size, this.root.orthogonalSize, this.locked, this.margin);
}
deserialize(json, deserializer) {
const orientation = json.orientation;
const height = orientation === "VERTICAL" ? json.height : json.width;
this._deserialize(json.root, orientation, deserializer, height);
/**
* The deserialied layout must be positioned through this.layout(...)
* before any maximizedNode can be positioned
*/
this.layout(json.width, json.height);
if (json.maximizedNode) {
const location = json.maximizedNode.location;
const [_, node] = this.getNode(location);
if (!(node instanceof LeafNode)) return;
this.maximizeView(node.view);
}
}
_deserialize(root, orientation, deserializer, orthogonalSize) {
this.root = this._deserializeNode(root, orientation, deserializer, orthogonalSize);
}
_deserializeNode(node, orientation, deserializer, orthogonalSize) {
let result;
if (node.type === "branch") {
const children = node.data.map((serializedChild) => {
return {
node: this._deserializeNode(serializedChild, orthogonal(orientation), deserializer, node.size),
visible: serializedChild.visible
};
});
result = new BranchNode(orientation, this.proportionalLayout, this.styles, node.size, orthogonalSize, this.locked, this.margin, children);
} else {
const view = deserializer.fromJSON(node);
if (typeof node.visible === "boolean") {
var _view$setVisible;
(_view$setVisible = view.setVisible) === null || _view$setVisible === void 0 || _view$setVisible.call(view, node.visible);
}
result = new LeafNode(view, orientation, orthogonalSize, node.size);
}
return result;
}
get root() {
return this._root;
}
set root(root) {
const oldRoot = this._root;
if (oldRoot) {
oldRoot.dispose();
this._maximizedNode = void 0;
oldRoot.element.remove();
}
this._root = root;
this.element.appendChild(this._root.element);
this.disposable.value = this._root.onDidChange((e) => {
this._onDidChange.fire(e);
});
}
normalize() {
if (!this._root) return;
if (this._root.children.length !== 1) return;
const oldRoot = this.root;
const childReference = oldRoot.children[0];
if (childReference instanceof LeafNode) return;
oldRoot.element.remove();
const child = oldRoot.removeChild(0);
oldRoot.dispose();
child.dispose();
this._root = cloneNode(childReference, childReference.size, childReference.orthogonalSize);
this.element.appendChild(this._root.element);
this.disposable.value = this._root.onDidChange((e) => {
this._onDidChange.fire(e);
});
}
/**
* If the root is orientated as a VERTICAL node then nest the existing root within a new HORIZIONTAL root node
* If the root is orientated as a HORIZONTAL node then nest the existing root within a new VERITCAL root node
*/
insertOrthogonalSplitviewAtRoot() {
if (!this._root) return;
const oldRoot = this.root;
oldRoot.element.remove();
this._root = new BranchNode(orthogonal(oldRoot.orientation), this.proportionalLayout, this.styles, this.root.orthogonalSize, this.root.size, this.locked, this.margin);
if (oldRoot.children.length === 0) {} else if (oldRoot.children.length === 1) {
const childReference = oldRoot.children[0];
oldRoot.removeChild(0).dispose();
oldRoot.dispose();
this._root.addChild(
/**
* the child node will have the same orientation as the new root since
* we are removing the inbetween node.
* the entire 'tree' must be flipped recursively to ensure that the orientation
* flips at each level
*/
flipNode(childReference, childReference.orthogonalSize, childReference.size),
Sizing.Distribute,
0
);
} else this._root.addChild(oldRoot, Sizing.Distribute, 0);
this.element.appendChild(this._root.element);
this.disposable.value = this._root.onDidChange((e) => {
this._onDidChange.fire(e);
});
}
next(location) {
return this.progmaticSelect(location);
}
previous(location) {
return this.progmaticSelect(location, true);
}
getView(location) {
const node = location ? this.getNode(location)[1] : this.root;
return this._getViews(node, this.orientation);
}
_getViews(node, orientation, cachedVisibleSize) {
const box = {
height: node.height,
width: node.width
};
if (node instanceof LeafNode) return {
box,
view: node.view,
cachedVisibleSize
};
const children = [];
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const nodeCachedVisibleSize = node.getChildCachedVisibleSize(i);
children.push(this._getViews(child, orthogonal(orientation), nodeCachedVisibleSize));
}
return {
box,
children,
cachedVisibleSize
};
}
progmaticSelect(location, reverse = false) {
const [path, node] = this.getNode(location);
if (!(node instanceof LeafNode)) throw new TypeError("invalid location");
for (let i = path.length - 1; i > -1; i--) {
const n = path[i];
const l = location[i] || 0;
if (reverse ? l - 1 > -1 : l + 1 < n.children.length) return findLeaf(n.children[reverse ? l - 1 : l + 1], reverse);
}
return findLeaf(this.root, reverse);
}
constructor(proportionalLayout, styles, orientation, locked, margin) {
this.proportionalLayout = proportionalLayout;
this.styles = styles;
this._locked = false;
this._margin = 0;
this._maximizedNode = void 0;
this.disposable = new MutableDisposable();
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._onDidViewVisibilityChange = new Emitter();
this.onDidViewVisibilityChange = this._onDidViewVisibilityChange.event;
this._onDidMaximizedNodeChange = new Emitter();
this.onDidMaximizedNodeChange = this._onDidMaximizedNodeChange.event;
this.element = document.createElement("div");
this.element.className = "dv-grid-view";
this._locked = locked !== null && locked !== void 0 ? locked : false;
this._margin = margin !== null && margin !== void 0 ? margin : 0;
this.root = new BranchNode(orientation, proportionalLayout, styles, 0, 0, this.locked, this.margin);
}
isViewVisible(location) {
const [rest, index] = tail(location);
const [, parent] = this.getNode(rest);
if (!(parent instanceof BranchNode)) throw new TypeError("Invalid from location");
return parent.isChildVisible(index);
}
setViewVisible(location, visible) {
if (this.hasMaximizedView()) this.exitMaximizedView();
const [rest, index] = tail(location);
const [, parent] = this.getNode(rest);
if (!(parent instanceof BranchNode)) throw new TypeError("Invalid from location");
this._onDidViewVisibilityChange.fire();
parent.setChildVisible(index, visible);
}
moveView(parentLocation, from, to) {
if (this.hasMaximizedView()) this.exitMaximizedView();
const [, parent] = this.getNode(parentLocation);
if (!(parent instanceof BranchNode)) throw new TypeError("Invalid location");
parent.moveChild(from, to);
}
addView(view, size, location) {
if (this.hasMaximizedView()) this.exitMaximizedView();
const [rest, index] = tail(location);
const [pathToParent, parent] = this.getNode(rest);
if (parent instanceof BranchNode) {
const node = new LeafNode(view, orthogonal(parent.orientation), parent.orthogonalSize);
parent.addChild(node, size, index);
} else {
const [grandParent, ..._] = [...pathToParent].reverse();
const [parentIndex, ...__] = [...rest].reverse();
let newSiblingSize = 0;
const newSiblingCachedVisibleSize = grandParent.getChildCachedVisibleSize(parentIndex);
if (typeof newSiblingCachedVisibleSize === "number") newSiblingSize = Sizing.Invisible(newSiblingCachedVisibleSize);
grandParent.removeChild(parentIndex).dispose();
const newParent = new BranchNode(parent.orientation, this.proportionalLayout, this.styles, parent.size, parent.orthogonalSize, this.locked, this.margin);
grandParent.addChild(newParent, parent.size, parentIndex);
const newSibling = new LeafNode(parent.view, grandParent.orientation, parent.size);
newParent.addChild(newSibling, newSiblingSize, 0);
if (typeof size !== "number" && size.type === "split") size = {
type: "split",
index: 0
};
const node = new LeafNode(view, grandParent.orientation, parent.size);
newParent.addChild(node, size, index);
}
}
remove(view, sizing) {
const location = getGridLocation(view.element);
return this.removeView(location, sizing);
}
removeView(location, sizing) {
if (this.hasMaximizedView()) this.exitMaximizedView();
const [rest, index] = tail(location);
const [pathToParent, parent] = this.getNode(rest);
if (!(parent instanceof BranchNode)) throw new TypeError("Invalid location");
const nodeToRemove = parent.children[index];
if (!(nodeToRemove instanceof LeafNode)) throw new TypeError("Invalid location");
parent.removeChild(index, sizing);
nodeToRemove.dispose();
if (parent.children.length !== 1) return nodeToRemove.view;
const sibling = parent.children[0];
if (pathToParent.length === 0) {
if (sibling instanceof LeafNode) return nodeToRemove.view;
parent.removeChild(0, sizing);
this.root = sibling;
return nodeToRemove.view;
}
const [grandParent, ..._] = [...pathToParent].reverse();
const [parentIndex, ...__] = [...rest].reverse();
const isSiblingVisible = parent.isChildVisible(0);
parent.removeChild(0, sizing);
const sizes = grandParent.children.map((_size, i) => grandParent.getChildSize(i));
grandParent.removeChild(parentIndex, sizing).dispose();
if (sibling instanceof BranchNode) {
sizes.splice(parentIndex, 1, ...sibling.children.map((c) => c.size));
for (let i = 0; i < sibling.children.length; i++) {
const child = sibling.children[i];
grandParent.addChild(child, child.size, parentIndex + i);
}
/**
* clean down the branch node since we need to dipose of it and
* when .dispose() it called on a branch it will dispose of any
* views it is holding onto.
*/
while (sibling.children.length > 0) sibling.removeChild(0);
} else {
const newSibling = new LeafNode(sibling.view, orthogonal(sibling.orientation), sibling.size);
const siblingSizing = isSiblingVisible ? sibling.orthogonalSize : Sizing.Invisible(sibling.orthogonalSize);
grandParent.addChild(newSibling, siblingSizing, parentIndex);
}
sibling.dispose();
for (let i = 0; i < sizes.length; i++) grandParent.resizeChild(i, sizes[i]);
return nodeToRemove.view;
}
layout(width, height) {
const [size, orthogonalSize] = this.root.orientation === "HORIZONTAL" ? [height, width] : [width, height];
this.root.layout(size, orthogonalSize);
}
getNode(location, node = this.root, path = []) {
if (location.length === 0) return [path, node];
if (!(node instanceof BranchNode)) throw new TypeError("Invalid location");
const [index, ...rest] = location;
if (index < 0 || index >= node.children.length) throw new Error("Invalid location");
const child = node.children[index];
path.push(node);
return this.getNode(rest, child, path);
}
};
const PROPERTY_KEYS_GRIDVIEW = (() => {
return Object.keys({
disableAutoResizing: void 0,
proportionalLayout: void 0,
orientation: void 0,
hideBorders: void 0,
className: void 0
});
})();
var Resizable = class extends CompositeDisposable {
get element() {
return this._element;
}
get disableResizing() {
return this._disableResizing;
}
set disableResizing(value) {
this._disableResizing = value;
}
constructor(parentElement, disableResizing = false) {
super();
this._lastWidth = -1;
this._lastHeight = -1;
this._disableResizing = disableResizing;
this._element = parentElement;
this.addDisposables(watchElementResize(this._element, (entry) => {
if (this.isDisposed)
/**
* resize is delayed through requestAnimationFrame so there is a small chance
* the component has already been disposed of
*/
return;
if (this.disableResizing) return;
if (!this._element.offsetParent)
/**
* offsetParent === null is equivalent to display: none being set on the element or one
* of it's parents. In the display: none case the size will become (0, 0) which we do
* not want to propagate.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
*
* You could use checkVisibility() but at the time of writing it's not supported across
* all Browsers
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility
*/
return;
if (!isInDocument(this._element))
/**
* since the event is dispatched through requestAnimationFrame there is a small chance
* the component is no longer attached to the DOM, if that is the case the dimensions
* are mostly likely all zero and meaningless. we should skip this case.
*/
return;
const width = Math.round(entry.contentRect.width);
const height = Math.round(entry.contentRect.height);
if (width === this._lastWidth && height === this._lastHeight) return;
this._lastWidth = width;
this._lastHeight = height;
this.layout(width, height);
}));
}
};
const nextLayoutId$1 = sequentialNumberGenerator();
function toTarget(direction) {
switch (direction) {
case "left": return "left";
case "right": return "right";
case "above": return "top";
case "below": return "bottom";
default: return "center";
}
}
var BaseGrid = class extends Resizable {
get id() {
return this._id;
}
get size() {
return this._groups.size;
}
get groups() {
return Array.from(this._groups.values()).map((_) => _.value);
}
get width() {
return this.gridview.width;
}
get height() {
return this.gridview.height;
}
get minimumHeight() {
return this.gridview.minimumHeight;
}
get maximumHeight() {
return this.gridview.maximumHeight;
}
get minimumWidth() {
return this.gridview.minimumWidth;
}
get maximumWidth() {
return this.gridview.maximumWidth;
}
get activeGroup() {
return this._activeGroup;
}
get locked() {
return this.gridview.locked;
}
set locked(value) {
this.gridview.locked = value;
}
constructor(container, options) {
var _options$className;
super(document.createElement("div"), options.disableAutoResizing);
this._id = nextLayoutId$1.next();
this._groups = /* @__PURE__ */ new Map();
this._onDidRemove = new Emitter();
this.onDidRemove = this._onDidRemove.event;
this._onDidAdd = new Emitter();
this.onDidAdd = this._onDidAdd.event;
this._onDidMaximizedChange = new Emitter();
this.onDidMaximizedChange = this._onDidMaximizedChange.event;
this._onDidActiveChange = new Emitter();
this.onDidActiveChange = this._onDidActiveChange.event;
this._bufferOnDidLayoutChange = new AsapEvent();
this.onDidLayoutChange = this._bufferOnDidLayoutChange.onEvent;
this._onDidViewVisibilityChangeMicroTaskQueue = new AsapEvent();
this.onDidViewVisibilityChangeMicroTaskQueue = this._onDidViewVisibilityChangeMicroTaskQueue.onEvent;
this.element.style.height = "100%";
this.element.style.width = "100%";
this._classNames = new Classnames(this.element);
this._classNames.setClassNames((_options$className = options.className) !== null && _options$className !== void 0 ? _options$className : "");
container.appendChild(this.element);
this.gridview = new Gridview(!!options.proportionalLayout, options.styles, options.orientation, options.locked, options.margin);
this.gridview.locked = !!options.locked;
this.element.appendChild(this.gridview.element);
this.layout(0, 0, true);
this.addDisposables(this.gridview.onDidMaximizedNodeChange((event) => {
this._onDidMaximizedChange.fire({
panel: event.view,
isMaximized: event.isMaximized
});
}), this.gridview.onDidViewVisibilityChange(() => this._onDidViewVisibilityChangeMicroTaskQueue.fire()), this.onDidViewVisibilityChangeMicroTaskQueue(() => {
this.forceRelayout();
}), Disposable.from(() => {
this.element.remove();
}), this.gridview.onDidChange(() => {
this._bufferOnDidLayoutChange.fire();
}), Event.any(this.onDidAdd, this.onDidRemove, this.onDidActiveChange)(() => {
this._bufferOnDidLayoutChange.fire();
}), this._onDidMaximizedChange, this._onDidViewVisibilityChangeMicroTaskQueue, this._bufferOnDidLayoutChange);
}
setVisible(panel, visible) {
this.gridview.setViewVisible(getGridLocation(panel.element), visible);
this._bufferOnDidLayoutChange.fire();
}
isVisible(panel) {
return this.gridview.isViewVisible(getGridLocation(panel.element));
}
updateOptions(options) {
if (typeof options.proportionalLayout === "boolean") {}
if (options.orientation) this.gridview.orientation = options.orientation;
if ("styles" in options) {}
if ("disableAutoResizing" in options) {
var _options$disableAutoR;
this.disableResizing = (_options$disableAutoR = options.disableAutoResizing) !== null && _options$disableAutoR !== void 0 ? _options$disableAutoR : false;
}
if ("locked" in options) {
var _options$locked;
this.locked = (_options$locked = options.locked) !== null && _options$locked !== void 0 ? _options$locked : false;
}
if ("margin" in options) {
var _options$margin;
this.gridview.margin = (_options$margin = options.margin) !== null && _options$margin !== void 0 ? _options$margin : 0;
}
if ("className" in options) {
var _options$className2;
this._classNames.setClassNames((_options$className2 = options.className) !== null && _options$className2 !== void 0 ? _options$className2 : "");
}
}
maximizeGroup(panel) {
this.gridview.maximizeView(panel);
this.doSetGroupActive(panel);
}
isMaximizedGroup(panel) {
return this.gridview.maximizedView() === panel;
}
exitMaximizedGroup() {
this.gridview.exitMaximizedView();
}
hasMaximizedGroup() {
return this.gridview.hasMaximizedView();
}
doAddGroup(group, location = [0], size, gridview = this.gridview) {
gridview.addView(group, size !== null && size !== void 0 ? size : Sizing.Distribute, location);
this._onDidAdd.fire(group);
}
doRemoveGroup(group, options) {
if (!this._groups.has(group.id)) throw new Error("invalid operation");
const item = this._groups.get(group.id);
const view = this.gridview.remove(group, Sizing.Distribute);
if (item && !(options === null || options === void 0 ? void 0 : options.skipDispose)) {
item.disposable.dispose();
item.value.dispose();
this._groups.delete(group.id);
this._onDidRemove.fire(group);
}
if (!(options === null || options === void 0 ? void 0 : options.skipActive) && this._activeGroup === group) {
const groups = Array.from(this._groups.values());
this.doSetGroupActive(groups.length > 0 ? groups[0].value : void 0);
}
return view;
}
getPanel(id) {
var _this$_groups$get;
return (_this$_groups$get = this._groups.get(id)) === null || _this$_groups$get === void 0 ? void 0 : _this$_groups$get.value;
}
doSetGroupActive(group) {
if (this._activeGroup === group) return;
if (this._activeGroup) this._activeGroup.setActive(false);
if (group) group.setActive(true);
this._activeGroup = group;
this._onDidActiveChange.fire(group);
}
removeGroup(group) {
this.doRemoveGroup(group);
}
activateNext(options) {
var _options, _this$gridview$next;
(_options = options) !== null && _options !== void 0 || (options = {});
if (!options.group) {
if (!this.activeGroup) return;
options.group = this.activeGroup;
}
const location = getGridLocation(options.group.element);
const next = (_this$gridview$next = this.gridview.next(location)) === null || _this$gridview$next === void 0 ? void 0 : _this$gridview$next.view;
this.doSetGroupActive(next);
}
activatePrevious(options) {
var _options2, _this$gridview$previo;
(_options2 = options) !== null && _options2 !== void 0 || (options = {});
if (!options.group) {
if (!this.activeGroup) return;
options.group = this.activeGroup;
}
const location = getGridLocation(options.group.element);
const next = (_this$gridview$previo = this.gridview.previous(location)) === null || _this$gridview$previo === void 0 ? void 0 : _this$gridview$previo.view;
this.doSetGroupActive(next);
}
forceRelayout() {
this.layout(this.width, this.height, true);
}
layout(width, height, forceResize) {
if (!(forceResize || width !== this.width || height !== this.height)) return;
this.gridview.element.style.height = `${height}px`;
this.gridview.element.style.width = `${width}px`;
this.gridview.layout(width, height);
}
dispose() {
this._onDidActiveChange.dispose();
this._onDidAdd.dispose();
this._onDidRemove.dispose();
for (const group of this.groups) group.dispose();
this.gridview.dispose();
super.dispose();
}
};
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
return typeof o;
} : function(o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function toPropertyKey(t) {
var i = toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _defineProperty(e, r, t) {
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function ownKeys(e, r) {
var t = Object.keys(e);
if (Object.getOwnPropertySymbols) {
var o = Object.getOwnPropertySymbols(e);
r && (o = o.filter(function(r) {
return Object.getOwnPropertyDescriptor(e, r).enumerable;
})), t.push.apply(t, o);
}
return t;
}
function _objectSpread2(e) {
for (var r = 1; r < arguments.length; r++) {
var t = null != arguments[r] ? arguments[r] : {};
r % 2 ? ownKeys(Object(t), !0).forEach(function(r) {
_defineProperty(e, r, t[r]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r) {
Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
});
}
return e;
}
/** Where a drop position lands, phrased for the *edge prompt*. */
function edgeWhere(position, target) {
return position === "center" ? `Tab into ${target}` : `Split ${position} of ${target}`;
}
/** Where a drop position landed, phrased for the *commit* sentence. */
function committedWhere(position, target) {
return position === "center" ? `docked into ${target}` : `split ${position} of ${target}`;
}
const DEFAULT_MESSAGES = {
panelOpened: (title) => `${title} opened`,
panelClosed: (title) => `${title} closed`,
groupMaximized: (title) => `${title} maximized`,
groupRestored: (title) => `${title} restored`,
groupFloated: (title) => `${title} floated`,
groupDocked: (title) => `${title} docked`,
groupPoppedOut: (title) => `${title} opened in a new window`,
closeTab: (title) => `Close ${title}`,
closeTabPlain: () => `Close`,
movePickTarget: (source, target, current, total) => `Moving ${source}. Target ${target}, ${current} of ${total}. Enter to choose where, Escape to cancel.`,
movePickEdge: (position, target) => `${edgeWhere(position, target)}. Arrows to change, Enter to confirm, Escape to go back.`,
moveCommitted: (source, target, position) => `${source} ${committedWhere(position, target)}.`,
moveCancelled: () => `Move cancelled.`,
moveNotAllowed: () => `That move is not allowed.`,
moveFloated: (source) => `${source} floated.`
};
/** Merge an app's partial overrides over the English defaults. */
function resolveMessages(overrides) {
return overrides ? _objectSpread2(_objectSpread2({}, DEFAULT_MESSAGES), overrides) : DEFAULT_MESSAGES;
}
var SplitviewApi = class {
/**
* The minimum size the component can reach where size is measured in the direction of orientation provided.
*/
get minimumSize() {
return this.component.minimumSize;
}
/**
* The maximum size the component can reach where size is measured in the direction of orientation provided.
*/
get maximumSize() {
return this.component.maximumSize;
}
/**
* Width of the component.
*/
get width() {
return this.component.width;
}
/**
* Height of the component.
*/
get height() {
return this.component.height;
}
/**
* The current number of panels.
*/
get length() {
return this.component.length;
}
/**
* The current orientation of the component.
*/
get orientation() {
return this.component.orientation;
}
/**
* The list of current panels.
*/
get panels() {
return this.component.panels;
}
/**
* Invoked after a layout is loaded through the `fromJSON` method.
*/
get onDidLayoutFromJSON() {
return this.component.onDidLayoutFromJSON;
}
/**
* Invoked whenever any aspect of the layout changes.
* If listening to this event it may be worth debouncing ouputs.
*/
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
/**
* Invoked when a view is added.
*/
get onDidAddView() {
return this.component.onDidAddView;
}
/**
* Invoked when a view is removed.
*/
get onDidRemoveView() {
return this.component.onDidRemoveView;
}
constructor(component) {
this.component = component;
}
/**
* Removes an existing panel and optionally provide a `Sizing` method
* for the subsequent resize.
*/
removePanel(panel, sizing) {
this.component.removePanel(panel, sizing);
}
/**
* Focus the component.
*/
focus() {
this.component.focus();
}
/**
* Get the reference to a panel given it's `string` id.
*/
getPanel(id) {
return this.component.getPanel(id);
}
/**
* Layout the panel with a width and height.
*/
layout(width, height) {
return this.component.layout(width, height);
}
/**
* Add a new panel and return the created instance.
*/
addPanel(options) {
return this.component.addPanel(options);
}
/**
* Move a panel given it's current and desired index.
*/
movePanel(from, to) {
this.component.movePanel(from, to);
}
/**
* Deserialize a layout to built a splitivew.
*/
fromJSON(data) {
this.component.fromJSON(data);
}
/** Serialize a layout */
toJSON() {
return this.component.toJSON();
}
/**
* Remove all panels and clear the component.
*/
clear() {
this.component.clear();
}
/**
* Update configuratable options.
*/
updateOptions(options) {
this.component.updateOptions(options);
}
/**
* Release resources and teardown component. Do not call when using framework versions of dockview.
*/
dispose() {
this.component.dispose();
}
};
var PaneviewApi = class {
/**
* The minimum size the component can reach where size is measured in the direction of orientation provided.
*/
get minimumSize() {
return this.component.minimumSize;
}
/**
* The maximum size the component can reach where size is measured in the direction of orientation provided.
*/
get maximumSize() {
return this.component.maximumSize;
}
/**
* Width of the component.
*/
get width() {
return this.component.width;
}
/**
* Height of the component.
*/
get height() {
return this.component.height;
}
/**
* All panel objects.
*/
get panels() {
return this.component.panels;
}
/**
* Invoked when any layout change occures, an aggregation of many events.
*/
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
/**
* Invoked after a layout is deserialzied using the `fromJSON` method.
*/
get onDidLayoutFromJSON() {
return this.component.onDidLayoutFromJSON;
}
/**
* Invoked when a panel is added. May be called multiple times when moving panels.
*/
get onDidAddView() {
return this.component.onDidAddView;
}
/**
* Invoked when a panel is removed. May be called multiple times when moving panels.
*/
get onDidRemoveView() {
return this.component.onDidRemoveView;
}
/**
* Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
*/
get onDidDrop() {
return this.component.onDidDrop;
}
get onUnhandledDragOver() {
return this.component.onUnhandledDragOver;
}
constructor(component) {
this.component = component;
}
/**
* Remove a panel given the panel object.
*/
removePanel(panel) {
this.component.removePanel(panel);
}
/**
* Get a panel object given a `string` id. May return `undefined`.
*/
getPanel(id) {
return this.component.getPanel(id);
}
/**
* Move a panel given it's current and desired index.
*/
movePanel(from, to) {
this.component.movePanel(from, to);
}
/**
* Focus the component. Will try to focus an active panel if one exists.
*/
focus() {
this.component.focus();
}
/**
* Force resize the component to an exact width and height. Read about auto-resizing before using.
*/
layout(width, height) {
this.component.layout(width, height);
}
/**
* Add a panel and return the created object.
*/
addPanel(options) {
return this.component.addPanel(options);
}
/**
* Create a component from a serialized object.
*/
fromJSON(data) {
this.component.fromJSON(data);
}
/**
* Create a serialized object of the current component.
*/
toJSON() {
return this.component.toJSON();
}
/**
* Reset the component back to an empty and default state.
*/
clear() {
this.component.clear();
}
/**
* Update configuratable options.
*/
updateOptions(options) {
this.component.updateOptions(options);
}
/**
* Release resources and teardown component. Do not call when using framework versions of dockview.
*/
dispose() {
this.component.dispose();
}
};
var GridviewApi = class {
/**
* Width of the component.
*/
get width() {
return this.component.width;
}
/**
* Height of the component.
*/
get height() {
return this.component.height;
}
/**
* Minimum height of the component.
*/
get minimumHeight() {
return this.component.minimumHeight;
}
/**
* Maximum height of the component.
*/
get maximumHeight() {
return this.component.maximumHeight;
}
/**
* Minimum width of the component.
*/
get minimumWidth() {
return this.component.minimumWidth;
}
/**
* Maximum width of the component.
*/
get maximumWidth() {
return this.component.maximumWidth;
}
/**
* Invoked when any layout change occures, an aggregation of many events.
*/
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
/**
* Invoked when a panel is added. May be called multiple times when moving panels.
*/
get onDidAddPanel() {
return this.component.onDidAddGroup;
}
/**
* Invoked when a panel is removed. May be called multiple times when moving panels.
*/
get onDidRemovePanel() {
return this.component.onDidRemoveGroup;
}
/**
* Invoked when the active panel changes. May be undefined if no panel is active.
*/
get onDidActivePanelChange() {
return this.component.onDidActiveGroupChange;
}
/**
* Invoked after a layout is deserialzied using the `fromJSON` method.
*/
get onDidLayoutFromJSON() {
return this.component.onDidLayoutFromJSON;
}
/**
* All panel objects.
*/
get panels() {
return this.component.groups;
}
/**
* Current orientation. Can be changed after initialization.
*/
get orientation() {
return this.component.orientation;
}
set orientation(value) {
this.component.updateOptions({ orientation: value });
}
constructor(component) {
this.component = component;
}
/**
* Focus the component. Will try to focus an active panel if one exists.
*/
focus() {
this.component.focus();
}
/**
* Force resize the component to an exact width and height. Read about auto-resizing before using.
*/
layout(width, height, force = false) {
this.component.layout(width, height, force);
}
/**
* Add a panel and return the created object.
*/
addPanel(options) {
return this.component.addPanel(options);
}
/**
* Remove a panel given the panel object.
*/
removePanel(panel, sizing) {
this.component.removePanel(panel, sizing);
}
/**
* Move a panel in a particular direction relative to another panel.
*/
movePanel(panel, options) {
this.component.movePanel(panel, options);
}
/**
* Get a panel object given a `string` id. May return `undefined`.
*/
getPanel(id) {
return this.component.getPanel(id);
}
/**
* Create a component from a serialized object.
*/
fromJSON(data) {
return this.component.fromJSON(data);
}
/**
* Create a serialized object of the current component.
*/
toJSON() {
return this.component.toJSON();
}
/**
* Reset the component back to an empty and default state.
*/
clear() {
this.component.clear();
}
updateOptions(options) {
this.component.updateOptions(options);
}
/**
* Release resources and teardown component. Do not call when using framework versions of dockview.
*/
dispose() {
this.component.dispose();
}
};
var DockviewApi = class {
/**
* The unique identifier for this instance. Used to manage scope of Drag'n'Drop events.
*/
get id() {
return this.component.id;
}
/**
* Width of the component.
*/
get width() {
return this.component.width;
}
/**
* Height of the component.
*/
get height() {
return this.component.height;
}
/**
* Minimum height of the component.
*/
get minimumHeight() {
return this.component.minimumHeight;
}
/**
* Maximum height of the component.
*/
get maximumHeight() {
return this.component.maximumHeight;
}
/**
* Minimum width of the component.
*/
get minimumWidth() {
return this.component.minimumWidth;
}
/**
* Maximum width of the component.
*/
get maximumWidth() {
return this.component.maximumWidth;
}
/**
* Total number of groups.
*/
get size() {
return this.component.size;
}
/**
* The active tab-group color palette. Reflects the configured
* `tabGroupColors` option, or the built-in defaults when unset.
* Useful for custom chip renderers that want to roll their own
* picker UI.
*/
get tabGroupColors() {
return this.component.tabGroupColorPalette.entries();
}
/**
* Total number of panels.
*/
get totalPanels() {
return this.component.totalPanels;
}
/**
* Invoked when the active group changes. May be undefined if no group is active.
*/
get onDidActiveGroupChange() {
return this.component.onDidActiveGroupChange;
}
/**
* Invoked when a group is added. May be called multiple times when moving groups.
*/
get onDidAddGroup() {
return this.component.onDidAddGroup;
}
/**
* Invoked when a group is removed. May be called multiple times when moving groups.
*/
get onDidRemoveGroup() {
return this.component.onDidRemoveGroup;
}
/**
* Invoked when the active panel changes. The event carries the active
* `panel` (may be undefined if no panel is active) and the
* {@link DockviewOrigin} (`'user'` vs `'api'`) of the change.
*/
get onDidActivePanelChange() {
return this.component.onDidActivePanelChange;
}
/**
* Fired when a panel is pinned or unpinned (PinnedTabs module). Carries the
* panel and its new `isPinned` state.
*/
get onDidPanelPinnedChange() {
return this.component.onDidPanelPinnedChange;
}
/**
* Invoked when a panel is added. May be called multiple times when moving panels.
*/
get onDidAddPanel() {
return this.component.onDidAddPanel;
}
/**
* Invoked when a panel is removed. May be called multiple times when moving panels.
*/
get onDidRemovePanel() {
return this.component.onDidRemovePanel;
}
get onDidMovePanel() {
return this.component.onDidMovePanel;
}
/**
* Invoked after a layout is deserialzied using the `fromJSON` method.
*/
get onDidLayoutFromJSON() {
return this.component.onDidLayoutFromJSON;
}
/**
* Invoked when any layout change occures, an aggregation of many events.
*/
get onDidLayoutChange() {
return this.component.onDidLayoutChange;
}
/**
* Invoked when a Drag'n'Drop event occurs that the component was unable to handle. Exposed for custom Drag'n'Drop functionality.
*/
get onDidDrop() {
return this.component.onDidDrop;
}
/**
* Invoked when a Drag'n'Drop event occurs but before dockview handles it giving the user an opportunity to intecept and
* prevent the event from occuring using the standard `preventDefault()` syntax.
*
* Preventing certain events may causes unexpected behaviours, use carefully.
*/
get onWillDrop() {
return this.component.onWillDrop;
}
/**
* Fires before each top-level structural layout mutation (add / remove /
* move / float / popout / maximize / load / clear). Compound operations
* (e.g. a drag) fire once. Pair with `onDidMutateLayout` to bracket a
* change, which is useful for undo/redo, autosave and dirty-tracking.
*/
get onWillMutateLayout() {
return this.component.onWillMutateLayout;
}
/** Fires after each top-level structural layout mutation. See `onWillMutateLayout`. */
get onDidMutateLayout() {
return this.component.onDidMutateLayout;
}
/**
* Invoked before an overlay is shown indicating a drop target.
*
* Calling `event.preventDefault()` will prevent the overlay being shown and prevent
* the any subsequent drop event.
*/
get onWillShowOverlay() {
return this.component.onWillShowOverlay;
}
/**
* Invoked before a group is dragged.
*
* Calling `event.nativeEvent.preventDefault()` will prevent the group drag starting.
*
*/
get onWillDragGroup() {
return this.component.onWillDragGroup;
}
/**
* Invoked before a panel is dragged.
*
* Calling `event.nativeEvent.preventDefault()` will prevent the panel drag starting.
*/
get onWillDragPanel() {
return this.component.onWillDragPanel;
}
get onUnhandledDragOver() {
return this.component.onUnhandledDragOver;
}
get onDidPopoutGroupSizeChange() {
return this.component.onDidPopoutGroupSizeChange;
}
get onDidPopoutGroupPositionChange() {
return this.component.onDidPopoutGroupPositionChange;
}
/**
* Fires when a popout group successfully opens in its own window, carrying
* the live `Window` handle. Use it to route focus or attach per-document
* listeners. Enumerate the current popouts at any time with `getPopouts()`.
*/
get onDidAddPopoutGroup() {
return this.component.onDidAddPopoutGroup;
}
/**
* Fires when a popout group is removed, whether the user closed its window
* or it was docked back programmatically. Symmetric with
* {@link onDidAddPopoutGroup}; not fired during component disposal.
*/
get onDidRemovePopoutGroup() {
return this.component.onDidRemovePopoutGroup;
}
get onDidOpenPopoutWindowFail() {
return this.component.onDidOpenPopoutWindowFail;
}
/** Enumerate the popout groups currently open in their own windows. */
getPopouts() {
return this.component.getPopouts();
}
/**
* Invoked when a tab group is created in any group.
*/
get onDidCreateTabGroup() {
return this.component.onDidCreateTabGroup;
}
/**
* Invoked when a tab group is destroyed in any group.
*/
get onDidDestroyTabGroup() {
return this.component.onDidDestroyTabGroup;
}
/**
* Invoked when a panel is added to a tab group.
*/
get onDidAddPanelToTabGroup() {
return this.component.onDidAddPanelToTabGroup;
}
/**
* Invoked when a panel is removed from a tab group.
*/
get onDidRemovePanelFromTabGroup() {
return this.component.onDidRemovePanelFromTabGroup;
}
/**
* Invoked when a tab group's properties (label, color) change.
*/
get onDidTabGroupChange() {
return this.component.onDidTabGroupChange;
}
/**
* Invoked when a tab group is collapsed or expanded.
*/
get onDidTabGroupCollapsedChange() {
return this.component.onDidTabGroupCollapsedChange;
}
/**
* All panel objects.
*/
get panels() {
return this.component.panels;
}
/**
* All group objects.
*/
get groups() {
return this.component.groups;
}
/**
* The nearest grid group in a spatial direction from `group`, comparing
* group centre points, e.g. the group visually to the left. Floating and
* popout groups are ignored. Returns `undefined` when there is no group in
* that direction. Pair with `group.api.boundingBox` to build your own
* spatial navigation.
*/
adjacentGroupInDirection(group, direction) {
return this.component.adjacentGroupInDirection(group, direction);
}
/**
* Active panel object.
*/
get activePanel() {
return this.component.activePanel;
}
/**
* Active group object.
*/
get activeGroup() {
return this.component.activeGroup;
}
/**
* The resolved accessibility message catalog (the app's `messages`
* overrides merged over the English defaults). Used by parts that surface
* localisable AT strings, e.g. the default tab's close-button label.
*/
get messages() {
return resolveMessages(this.component.options.messages);
}
constructor(component) {
this.component = component;
}
/**
* Focus the component. Will try to focus an active panel if one exists.
*/
focus() {
this.component.focus();
}
/**
* Get a panel object given a `string` id. May return `undefined`.
*/
getPanel(id) {
return this.component.getGroupPanel(id);
}
/**
* Force resize the component to an exact width and height. Read about auto-resizing before using.
*/
layout(width, height, force = false) {
this.component.layout(width, height, force);
}
/**
* Add a panel and return the created object.
*/
addPanel(options) {
return this.component.withOrigin("api", () => this.component.addPanel(options));
}
/**
* Remove a panel given the panel object.
*/
removePanel(panel) {
this.component.withOrigin("api", () => this.component.removePanel(panel));
}
/**
* Add a group and return the created object.
*/
addGroup(options) {
return this.component.withOrigin("api", () => this.component.addGroup(options));
}
/**
* Close all groups and panels.
*/
closeAllGroups() {
return this.component.withOrigin("api", () => this.component.closeAllGroups());
}
/**
* Remove a group and any panels within the group.
*/
removeGroup(group) {
this.component.withOrigin("api", () => this.component.removeGroup(group));
}
/**
* Get a group object given a `string` id. May return undefined.
*/
getGroup(id) {
return this.component.getPanel(id);
}
/**
* Add a floating group
*/
addFloatingGroup(item, options) {
return this.component.withOrigin("api", () => this.component.addFloatingGroup(item, options));
}
/**
* Create a component from a serialized object.
*/
fromJSON(data, options) {
this.component.withOrigin("api", () => this.component.fromJSON(data, options));
}
/**
* Create a serialized object of the current component.
*/
toJSON() {
return this.component.toJSON();
}
/**
* Reset the component back to an empty and default state.
*/
clear() {
this.component.withOrigin("api", () => this.component.clear());
}
/**
* Undo the previous recorded layout mutation. No-op when there is nothing
* to undo, when `layoutHistory.enabled` is not set, or when the
* LayoutHistory module is absent.
*/
undo() {
this.component.undo();
}
/** Re-apply the next layout mutation undone via {@link undo}. */
redo() {
this.component.redo();
}
/** Whether {@link undo} would do something. Reactive via {@link onDidChangeHistory}. */
get canUndo() {
return this.component.canUndo;
}
/** Whether {@link redo} would do something. */
get canRedo() {
return this.component.canRedo;
}
/** Drop both undo and redo stacks (e.g. on document switch). */
clearHistory() {
this.component.clearHistory();
}
/** Fires whenever the undo/redo stacks change. */
get onDidChangeHistory() {
return this.component.onDidChangeHistory;
}
/**
* Whether Smart Guides snapping is active (the `smartGuides` option is
* present + enabled and the module is registered). Reactive via
* {@link setSmartGuidesEnabled}.
*/
get smartGuidesEnabled() {
return this.component.smartGuidesEnabled;
}
/** Toggle Smart Guides snapping at runtime (no-op when the module is absent). */
setSmartGuidesEnabled(enabled) {
this.component.setSmartGuidesEnabled(enabled);
}
/** Merge a partial Smart Guides option override in at runtime. */
updateSmartGuidesOptions(options) {
this.component.updateSmartGuidesOptions(options);
}
/** Fires when a dragged floating group commits an alignment snap on drop. */
get onDidSnapFloat() {
return this.component.onDidSnapFloat;
}
/** Fires when a dragged floating group docks/merges into another on drop. */
get onDidSnapTogether() {
return this.component.onDidSnapTogether;
}
/**
* Resolves once any in-flight popout-window restoration completes. Popout
* windows re-open asynchronously, so after an {@link undo} / {@link redo} (or
* {@link fromJSON}) that re-opens a popout, await this to know the window is
* ready. Already-resolved when nothing is restoring.
*/
get popoutRestorationPromise() {
return this.component.popoutRestorationPromise;
}
/**
* Activate the next panel or group, moving focus programmatically. Pass
* `{ includePanel: true }` to step through the panels of the active group
* before advancing to the next group.
*/
activateNext(options) {
this.component.activateNext(options);
}
/**
* Activate the previous panel or group, moving focus programmatically. Pass
* `{ includePanel: true }` to step through the panels of the active group
* before advancing to the previous group.
*/
activatePrevious(options) {
this.component.activatePrevious(options);
}
/**
* @deprecated Use {@link DockviewApi.activateNext} instead. Renamed because
* this advances the active panel/group (focus), it does not relocate a
* panel. Removal planned for a future major release.
*/
moveToNext(options) {
this.activateNext(options);
}
/**
* @deprecated Use {@link DockviewApi.activatePrevious} instead. Renamed
* because this advances the active panel/group (focus), it does not
* relocate a panel. Removal planned for a future major release.
*/
moveToPrevious(options) {
this.activatePrevious(options);
}
maximizeGroup(panel) {
this.component.withOrigin("api", () => this.component.maximizeGroup(panel.group));
}
hasMaximizedGroup() {
return this.component.hasMaximizedGroup();
}
exitMaximizedGroup() {
this.component.withOrigin("api", () => this.component.exitMaximizedGroup());
}
get onDidMaximizedGroupChange() {
return this.component.onDidMaximizedGroupChange;
}
/**
* Add a popout group in a new Window
*/
addPopoutGroup(item, options) {
return this.component.withOrigin("api", () => this.component.addPopoutGroup(item, options));
}
/**
* Add an edge group at the given position. Returns the group panel API
* for the newly created group. Throws if a group already exists there.
*/
addEdgeGroup(position, options) {
return this.component.addEdgeGroup(position, options);
}
/**
* Reveal (create-or-fill) the edge group at `position` and move the dragged
* item described by `data` into it. A newly created edge group tears down to
* zero footprint when later emptied. Drives the dock-to-edge groups behind
* the `dockToEdgeGroups` option; a no-op if edge groups are unavailable.
*/
revealEdgeGroupWithData(position, data, options) {
this.component.revealEdgeGroupWithData(position, data, options);
}
/**
* Get the group panel API for an edge group at the given position.
* Returns `undefined` if no edge group is configured at that position.
*/
getEdgeGroup(position) {
return this.component.getEdgeGroup(position);
}
/**
* Set the visibility of an edge group.
*/
setEdgeGroupVisible(position, visible) {
this.component.setEdgeGroupVisible(position, visible);
}
/**
* Check whether an edge group is currently visible.
*/
isEdgeGroupVisible(position) {
return this.component.isEdgeGroupVisible(position);
}
/**
* Remove an edge group and reclaim its slot in the layout.
* All panels inside the group are disposed. Throws if no group exists at position.
*/
removeEdgeGroup(position) {
this.component.removeEdgeGroup(position);
}
/**
* Pin (expand) the collapsed edge group at `position`. Requires the
* auto-hide edge groups module; no-op when it is absent.
*/
pinEdgeGroup(position) {
this.component.pinEdgeGroup(position);
}
/** Auto-hide (collapse to a strip) the edge group at `position`. */
autoHideEdgeGroup(position) {
this.component.autoHideEdgeGroup(position);
}
/**
* Peek (slide out as an overlay, without reflowing the grid) or close the
* collapsed edge group at `position`. No-op when the auto-hide module is
* absent or the group is not collapsed.
*/
peekEdgeGroup(position, peek) {
this.component.peekEdgeGroup(position, peek);
}
updateOptions(options) {
this.component.updateOptions(options);
}
_getGroupModel(groupId) {
const group = this.component.getPanel(groupId);
if (!group) throw new Error(`dockview: group '${groupId}' not found`);
return group.model;
}
createTabGroup(options) {
const model = this._getGroupModel(options.groupId);
return this.component.withOrigin("api", () => model.createTabGroup({
label: options.label,
color: options.color,
componentParams: options.componentParams
}));
}
dissolveTabGroup(options) {
const model = this._getGroupModel(options.groupId);
this.component.withOrigin("api", () => model.dissolveTabGroup(options.tabGroupId));
}
addPanelToTabGroup(options) {
const model = this._getGroupModel(options.groupId);
this.component.withOrigin("api", () => model.addPanelToTabGroup(options.tabGroupId, options.panelId, options.index));
}
removePanelFromTabGroup(options) {
const model = this._getGroupModel(options.groupId);
this.component.withOrigin("api", () => model.removePanelFromTabGroup(options.panelId));
}
getTabGroups(options) {
return this._getGroupModel(options.groupId).getTabGroups();
}
getTabGroupForPanel(options) {
return this._getGroupModel(options.groupId).getTabGroupForPanel(options.panelId);
}
moveTabGroup(options) {
this._getGroupModel(options.groupId).moveTabGroup(options.tabGroupId, options.index);
}
/**
* Release resources and teardown component. Do not call when using framework versions of dockview.
*/
dispose() {
this.component.dispose();
}
};
var DragAndDropObserver = class extends CompositeDisposable {
constructor(element, callbacks) {
super();
this.element = element;
this.callbacks = callbacks;
this.target = null;
this.registerListeners();
}
onDragEnter(e) {
this.target = e.target;
this.callbacks.onDragEnter(e);
}
onDragOver(e) {
e.preventDefault();
if (this.callbacks.onDragOver) this.callbacks.onDragOver(e);
}
onDragLeave(e) {
if (this.target === e.target) {
this.target = null;
this.callbacks.onDragLeave(e);
}
}
onDragEnd(e) {
this.target = null;
this.callbacks.onDragEnd(e);
}
onDrop(e) {
this.callbacks.onDrop(e);
}
registerListeners() {
this.addDisposables(addDisposableListener(this.element, "dragenter", (e) => {
this.onDragEnter(e);
}, true));
this.addDisposables(addDisposableListener(this.element, "dragover", (e) => {
this.onDragOver(e);
}, true));
this.addDisposables(addDisposableListener(this.element, "dragleave", (e) => {
this.onDragLeave(e);
}));
this.addDisposables(addDisposableListener(this.element, "dragend", (e) => {
this.onDragEnd(e);
}));
this.addDisposables(addDisposableListener(this.element, "drop", (e) => {
this.onDrop(e);
}));
}
};
const DEFAULT_SIZE = {
value: 50,
type: "percentage"
};
const SMALL_WIDTH_BOUNDARY = 100;
const SMALL_HEIGHT_BOUNDARY = 100;
function createOverlayElements() {
const dropzone = document.createElement("div");
dropzone.className = "dv-drop-target-dropzone";
const selection = document.createElement("div");
selection.className = "dv-drop-target-selection";
dropzone.appendChild(selection);
return {
dropzone,
selection
};
}
function computeOverlayShape(quadrant, width, height, overlayModel) {
var _overlayModel$smallWi, _overlayModel$smallHe, _overlayModel$size;
const smallWidthBoundary = (_overlayModel$smallWi = overlayModel === null || overlayModel === void 0 ? void 0 : overlayModel.smallWidthBoundary) !== null && _overlayModel$smallWi !== void 0 ? _overlayModel$smallWi : SMALL_WIDTH_BOUNDARY;
const smallHeightBoundary = (_overlayModel$smallHe = overlayModel === null || overlayModel === void 0 ? void 0 : overlayModel.smallHeightBoundary) !== null && _overlayModel$smallHe !== void 0 ? _overlayModel$smallHe : SMALL_HEIGHT_BOUNDARY;
const isSmallX = width < smallWidthBoundary;
const isSmallY = height < smallHeightBoundary;
const isLeft = quadrant === "left";
const isRight = quadrant === "right";
const isTop = quadrant === "top";
const isBottom = quadrant === "bottom";
const rightClass = !isSmallX && isRight;
const leftClass = !isSmallX && isLeft;
const topClass = !isSmallY && isTop;
const bottomClass = !isSmallY && isBottom;
let size = 1;
const sizeOptions = (_overlayModel$size = overlayModel === null || overlayModel === void 0 ? void 0 : overlayModel.size) !== null && _overlayModel$size !== void 0 ? _overlayModel$size : DEFAULT_SIZE;
if (sizeOptions.type === "percentage") size = clamp(sizeOptions.value, 0, 100) / 100;
else {
if (rightClass || leftClass) size = clamp(0, sizeOptions.value, width) / width;
if (topClass || bottomClass) size = clamp(0, sizeOptions.value, height) / height;
}
return {
isSmallX,
isSmallY,
isLeft,
isRight,
isTop,
isBottom,
rightClass,
leftClass,
topClass,
bottomClass,
size
};
}
function renderInPlaceOverlay(overlay, quadrant, width, height, overlayModel) {
const shape = computeOverlayShape(quadrant, width, height, overlayModel);
const { rightClass, leftClass, topClass, bottomClass, size } = shape;
const box = {
top: "0px",
left: "0px",
width: "100%",
height: "100%"
};
if (rightClass) {
box.left = `${100 * (1 - size)}%`;
box.width = `${100 * size}%`;
} else if (leftClass) box.width = `${100 * size}%`;
else if (topClass) box.height = `${100 * size}%`;
else if (bottomClass) {
box.top = `${100 * (1 - size)}%`;
box.height = `${100 * size}%`;
}
if (shape.isSmallX && shape.isLeft) box.width = "4px";
if (shape.isSmallX && shape.isRight) {
box.left = `${width - 4}px`;
box.width = "4px";
}
if (shape.isSmallY && shape.isTop) box.height = "4px";
if (shape.isSmallY && shape.isBottom) {
box.top = `${height - 4}px`;
box.height = "4px";
}
overlay.style.top = box.top;
overlay.style.left = box.left;
overlay.style.width = box.width;
overlay.style.height = box.height;
overlay.style.visibility = "visible";
if (!overlay.style.transform || overlay.style.transform === "") overlay.style.transform = "translate3d(0, 0, 0)";
const isLine = shape.isSmallX && (shape.isLeft || shape.isRight) || shape.isSmallY && (shape.isTop || shape.isBottom);
toggleClass(overlay, "dv-drop-target-small-vertical", shape.isSmallY);
toggleClass(overlay, "dv-drop-target-small-horizontal", shape.isSmallX);
toggleClass(overlay, "dv-drop-target-selection-line", isLine);
toggleClass(overlay, "dv-drop-target-left", shape.isLeft);
toggleClass(overlay, "dv-drop-target-right", shape.isRight);
toggleClass(overlay, "dv-drop-target-top", shape.isTop);
toggleClass(overlay, "dv-drop-target-bottom", shape.isBottom);
toggleClass(overlay, "dv-drop-target-center", quadrant === "center");
}
function checkAnchoredBoundsChanged(overlay, bounds) {
const topPx = `${Math.round(bounds.top)}px`;
const leftPx = `${Math.round(bounds.left)}px`;
const widthPx = `${Math.round(bounds.width)}px`;
const heightPx = `${Math.round(bounds.height)}px`;
return overlay.style.top !== topPx || overlay.style.left !== leftPx || overlay.style.width !== widthPx || overlay.style.height !== heightPx;
}
function applyAnchoredBounds(overlay, bounds) {
overlay.style.top = `${Math.round(bounds.top)}px`;
overlay.style.left = `${Math.round(bounds.left)}px`;
overlay.style.width = `${Math.round(bounds.width)}px`;
overlay.style.height = `${Math.round(bounds.height)}px`;
overlay.style.visibility = "visible";
if (!overlay.style.transform || overlay.style.transform === "") overlay.style.transform = "translate3d(0, 0, 0)";
}
/** `boundsChanged: false` lets callers skip redundant work on tight drag loops. */
function renderAnchoredOverlay(args) {
const shape = computeOverlayShape(args.quadrant, args.width, args.height, args.overlayModel);
const { rightClass, leftClass, topClass, bottomClass, size } = shape;
const elBox = args.outlineElement.getBoundingClientRect();
const ta = args.targetModel.getElements(void 0, args.outlineElement);
const el = ta.root;
const overlay = ta.overlay;
const bigbox = el.getBoundingClientRect();
const rootTop = elBox.top - bigbox.top;
const rootLeft = elBox.left - bigbox.left;
const box = {
top: rootTop,
left: rootLeft,
width: args.width,
height: args.height
};
if (rightClass) {
box.left = rootLeft + args.width * (1 - size);
box.width = args.width * size;
} else if (leftClass) box.width = args.width * size;
else if (topClass) box.height = args.height * size;
else if (bottomClass) {
box.top = rootTop + args.height * (1 - size);
box.height = args.height * size;
}
if (shape.isSmallX && shape.isLeft) box.width = 4;
if (shape.isSmallX && shape.isRight) {
box.left = rootLeft + args.width - 4;
box.width = 4;
}
if (shape.isSmallY && shape.isTop) box.height = 4;
if (shape.isSmallY && shape.isBottom) {
box.top = rootTop + args.height - 4;
box.height = 4;
}
if (!checkAnchoredBoundsChanged(overlay, box)) return {
boundsChanged: false,
targetChanged: ta.changed
};
applyAnchoredBounds(overlay, box);
overlay.className = `dv-drop-target-anchor${args.className ? ` ${args.className}` : ""}`;
toggleClass(overlay, "dv-drop-target-left", shape.isLeft);
toggleClass(overlay, "dv-drop-target-right", shape.isRight);
toggleClass(overlay, "dv-drop-target-top", shape.isTop);
toggleClass(overlay, "dv-drop-target-bottom", shape.isBottom);
toggleClass(overlay, "dv-drop-target-anchor-line", shape.isSmallX && (shape.isLeft || shape.isRight) || shape.isSmallY && (shape.isTop || shape.isBottom));
toggleClass(overlay, "dv-drop-target-center", args.quadrant === "center");
if (ta.changed) {
toggleClass(overlay, "dv-drop-target-anchor-container-changed", true);
setTimeout(() => {
toggleClass(overlay, "dv-drop-target-anchor-container-changed", false);
}, 10);
}
return {
boundsChanged: true,
targetChanged: ta.changed
};
}
var WillShowOverlayEvent = class extends DockviewEvent {
get nativeEvent() {
return this.options.nativeEvent;
}
get position() {
return this.options.position;
}
/** See {@link DroptargetEvent.edge}. */
get edge() {
return !!this.options.edge;
}
/** See {@link PositionResolverResult.edgeGroup}. A display hint; a consumer
* (e.g. the DnD compass) can bow out of this cell. */
get edgeGroup() {
return !!this.options.edgeGroup;
}
constructor(options) {
super();
this.options = options;
}
};
function directionToPosition(direction) {
switch (direction) {
case "above": return "top";
case "below": return "bottom";
case "left": return "left";
case "right": return "right";
case "within": return "center";
default: throw new Error(`invalid direction '${direction}'`);
}
}
function positionToDirection(position) {
switch (position) {
case "top": return "above";
case "bottom": return "below";
case "left": return "left";
case "right": return "right";
case "center": return "within";
default: throw new Error(`invalid position '${position}'`);
}
}
const DEFAULT_ACTIVATION_SIZE$1 = {
value: 20,
type: "percentage"
};
var Droptarget = class Droptarget extends CompositeDisposable {
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = value;
}
get state() {
return this._state;
}
constructor(element, options) {
super();
this.element = element;
this.options = options;
this._edge = false;
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._disabled = false;
this._acceptedTargetZonesSet = new Set(this.options.acceptedTargetZones);
this.dnd = new DragAndDropObserver(this.element, {
onDragEnter: () => {
var _this$options$getOver, _this$options;
(_this$options$getOver = (_this$options = this.options).getOverrideTarget) === null || _this$options$getOver === void 0 || (_this$options$getOver = _this$options$getOver.call(_this$options)) === null || _this$options$getOver === void 0 || _this$options$getOver.getElements();
},
onDragOver: (e) => {
var _this$options$getOver2, _this$options2, _this$options$getOver3, _this$options$getOver4, _this$options3, _e$clientX, _e$clientY;
if (this._disabled) {
this.clearOwnOverlay();
return;
}
Droptarget.ACTUAL_TARGET = this;
const overrideTarget = (_this$options$getOver2 = (_this$options2 = this.options).getOverrideTarget) === null || _this$options$getOver2 === void 0 ? void 0 : _this$options$getOver2.call(_this$options2);
if (this._acceptedTargetZonesSet.size === 0) {
this.clearOwnOverlay();
return;
}
const target = (_this$options$getOver3 = (_this$options$getOver4 = (_this$options3 = this.options).getOverlayOutline) === null || _this$options$getOver4 === void 0 ? void 0 : _this$options$getOver4.call(_this$options3)) !== null && _this$options$getOver3 !== void 0 ? _this$options$getOver3 : this.element;
const width = target.offsetWidth;
const height = target.offsetHeight;
if (width === 0 || height === 0) return;
const rect = target.getBoundingClientRect();
const x = ((_e$clientX = e.clientX) !== null && _e$clientX !== void 0 ? _e$clientX : 0) - rect.left;
const y = ((_e$clientY = e.clientY) !== null && _e$clientY !== void 0 ? _e$clientY : 0) - rect.top;
const resolved = this.resolvePosition(x, y, width, height, e);
/**
* If the event has already been used by another DropTarget instance
* then don't show a second drop target, only one target should be
* active at any one time. The anchored overlay belongs to whichever
* target claimed the event, so leave it alone here.
*/
if (this.isAlreadyUsed(e)) {
this.removeDropTarget();
return;
}
if (resolved === null) {
this.clearOwnOverlay();
return;
}
const quadrant = resolved.position;
if (!this.options.canDisplayOverlay(e, quadrant)) {
this.clearOwnOverlay();
return;
}
const willShowOverlayEvent = new WillShowOverlayEvent({
nativeEvent: e,
position: quadrant,
edge: resolved.edge,
edgeGroup: resolved.edgeGroup
});
/**
* Provide an opportunity to prevent the overlay appearing and in turn
* any dnd behaviours
*/
this._onWillShowOverlay.fire(willShowOverlayEvent);
if (willShowOverlayEvent.defaultPrevented) {
this.clearOwnOverlay();
return;
}
this.markAsUsed(e);
if (resolved.edge) {
this.clearOwnOverlay();
this._state = quadrant;
this._edge = true;
return;
}
this._edge = false;
if (overrideTarget) {} else if (!this.targetElement) {
const els = createOverlayElements();
this.targetElement = els.dropzone;
this.overlayElement = els.selection;
this._state = "center";
target.classList.add("dv-drop-target");
target.append(this.targetElement);
}
this.toggleClasses(quadrant, width, height);
this._state = quadrant;
},
onDragLeave: () => {
var _this$options$getOver5, _this$options4;
const target = (_this$options$getOver5 = (_this$options4 = this.options).getOverrideTarget) === null || _this$options$getOver5 === void 0 ? void 0 : _this$options$getOver5.call(_this$options4);
if (target) {
var _target$scheduleClear;
this._state = void 0;
this._edge = false;
(_target$scheduleClear = target.scheduleClear) === null || _target$scheduleClear === void 0 || _target$scheduleClear.call(target);
return;
}
this.removeDropTarget();
},
onDragEnd: (e) => {
var _this$options$getOver6, _this$options5;
const target = (_this$options$getOver6 = (_this$options5 = this.options).getOverrideTarget) === null || _this$options$getOver6 === void 0 ? void 0 : _this$options$getOver6.call(_this$options5);
if (target && Droptarget.ACTUAL_TARGET === this) {
if (this._state) {
e.stopPropagation();
this._onDrop.fire({
position: this._state,
nativeEvent: e,
edge: this._edge
});
}
}
this.removeDropTarget();
target === null || target === void 0 || target.clear();
},
onDrop: (e) => {
var _this$options$getOver7, _this$options6;
e.preventDefault();
const state = this._state;
const edge = this._edge;
this.removeDropTarget();
(_this$options$getOver7 = (_this$options6 = this.options).getOverrideTarget) === null || _this$options$getOver7 === void 0 || (_this$options$getOver7 = _this$options$getOver7.call(_this$options6)) === null || _this$options$getOver7 === void 0 || _this$options$getOver7.clear();
if (state) {
e.stopPropagation();
this._onDrop.fire({
position: state,
nativeEvent: e,
edge
});
}
}
});
this.addDisposables(this._onDrop, this._onWillShowOverlay, this.dnd);
}
setTargetZones(acceptedTargetZones) {
this._acceptedTargetZonesSet = new Set(acceptedTargetZones);
}
setOverlayModel(model) {
this.options.overlayModel = model;
}
dispose() {
this.removeDropTarget();
super.dispose();
}
/**
* Add a property to the event object for other potential listeners to check
*/
markAsUsed(event) {
event[Droptarget.USED_EVENT_ID] = true;
}
/**
* Check is the event has already been used by another instance of DropTarget
*/
isAlreadyUsed(event) {
const value = event[Droptarget.USED_EVENT_ID];
return typeof value === "boolean" && value;
}
toggleClasses(quadrant, width, height) {
var _this$options$getOver8, _this$options7;
const target = (_this$options$getOver8 = (_this$options7 = this.options).getOverrideTarget) === null || _this$options$getOver8 === void 0 ? void 0 : _this$options$getOver8.call(_this$options7);
if (target) {
var _this$options$getOver9, _this$options$getOver10, _this$options8;
renderAnchoredOverlay({
outlineElement: (_this$options$getOver9 = (_this$options$getOver10 = (_this$options8 = this.options).getOverlayOutline) === null || _this$options$getOver10 === void 0 ? void 0 : _this$options$getOver10.call(_this$options8)) !== null && _this$options$getOver9 !== void 0 ? _this$options$getOver9 : this.element,
targetModel: target,
quadrant,
width,
height,
overlayModel: this.options.overlayModel,
className: this.options.className
});
return;
}
if (!this.overlayElement) return;
renderInPlaceOverlay(this.overlayElement, quadrant, width, height, this.options.overlayModel);
}
/**
* Resolve the drop {@link Position} for a pointer location: defer to an
* injected {@link PositionResolver} when present, otherwise the built-in
* cursor-quadrant logic (unchanged).
*/
resolvePosition(x, y, width, height, event) {
var _this$options$getPosi, _this$options9;
const resolver = (_this$options$getPosi = (_this$options9 = this.options).getPositionResolver) === null || _this$options$getPosi === void 0 ? void 0 : _this$options$getPosi.call(_this$options9);
if (resolver) {
const result = resolver.resolve({
x,
y,
width,
height,
zones: this._acceptedTargetZonesSet,
event
});
return result ? {
position: result.position,
edge: !!result.edge,
edgeGroup: !!result.edgeGroup
} : null;
}
const position = this.calculateQuadrant(this._acceptedTargetZonesSet, x, y, width, height);
return position ? {
position,
edge: false,
edgeGroup: false
} : null;
}
calculateQuadrant(overlayType, x, y, width, height) {
var _this$options$overlay, _this$options$overlay2;
const activationSizeOptions = (_this$options$overlay = (_this$options$overlay2 = this.options.overlayModel) === null || _this$options$overlay2 === void 0 ? void 0 : _this$options$overlay2.activationSize) !== null && _this$options$overlay !== void 0 ? _this$options$overlay : DEFAULT_ACTIVATION_SIZE$1;
if (activationSizeOptions.type === "percentage") return calculateQuadrantAsPercentage(overlayType, x, y, width, height, activationSizeOptions.value);
return calculateQuadrantAsPixels(overlayType, x, y, width, height, activationSizeOptions.value);
}
/**
* Tear down whatever this target is showing, on any frame it resolves to no
* overlay. Two things make this more than `removeDropTarget`:
*
* - With `dndOverlayMounting: 'absolute'` the overlay lives in an anchor
* container shared by every drop target in the component, and
* `removeDropTarget` only owns the in-place dropzone — so the container
* needs clearing explicitly or the last frame's highlight lingers.
* - It must only clear the container when this target actually put
* something there. The root edge target sits over every group and
* declines `center` on each frame in the middle of the layout purely so
* the event falls through; clearing from there would destroy and rebuild
* the group's overlay every frame, killing its move transition.
*
* Resetting `_state` unconditionally is the other half: a target that
* resolves to nothing must not stay latched, or a later drop commits a
* position the cursor left long ago.
*/
clearOwnOverlay() {
const owned = this._state !== void 0;
this.removeDropTarget();
if (owned) {
var _this$options$getOver11, _this$options10;
(_this$options$getOver11 = (_this$options10 = this.options).getOverrideTarget) === null || _this$options$getOver11 === void 0 || (_this$options$getOver11 = _this$options$getOver11.call(_this$options10)) === null || _this$options$getOver11 === void 0 || _this$options$getOver11.clear();
}
}
removeDropTarget() {
this._state = void 0;
this._edge = false;
if (this.targetElement) {
var _this$targetElement$p;
(_this$targetElement$p = this.targetElement.parentElement) === null || _this$targetElement$p === void 0 || _this$targetElement$p.classList.remove("dv-drop-target");
this.targetElement.remove();
this.targetElement = void 0;
this.overlayElement = void 0;
}
}
/**
* Render the drop overlay at `position` without a live drag, so keyboard
* docking shows the exact same preview as a mouse drag. Mirrors the
* `onDragOver` render path (in-place or anchored). Pair with `clearOverlay`.
*/
showOverlay(position) {
var _this$options$getOver12, _this$options11, _this$options$getOver13, _this$options$getOver14, _this$options12;
const overrideTarget = (_this$options$getOver12 = (_this$options11 = this.options).getOverrideTarget) === null || _this$options$getOver12 === void 0 ? void 0 : _this$options$getOver12.call(_this$options11);
const target = (_this$options$getOver13 = (_this$options$getOver14 = (_this$options12 = this.options).getOverlayOutline) === null || _this$options$getOver14 === void 0 ? void 0 : _this$options$getOver14.call(_this$options12)) !== null && _this$options$getOver13 !== void 0 ? _this$options$getOver13 : this.element;
const width = target.offsetWidth;
const height = target.offsetHeight;
if (!overrideTarget && !this.targetElement) {
const els = createOverlayElements();
this.targetElement = els.dropzone;
this.overlayElement = els.selection;
target.classList.add("dv-drop-target");
target.append(this.targetElement);
}
this.toggleClasses(position, width, height);
this._state = position;
}
/** Clear an overlay shown via {@link showOverlay} (in-place or anchored). */
clearOverlay() {
var _this$options$getOver15, _this$options13;
this.removeDropTarget();
(_this$options$getOver15 = (_this$options13 = this.options).getOverrideTarget) === null || _this$options$getOver15 === void 0 || (_this$options$getOver15 = _this$options$getOver15.call(_this$options13)) === null || _this$options$getOver15 === void 0 || _this$options$getOver15.clear();
this._state = void 0;
}
};
Droptarget.USED_EVENT_ID = "__dockview_droptarget_event_is_used__";
function calculateQuadrantAsPercentage(overlayType, x, y, width, height, threshold) {
const xp = 100 * x / width;
const yp = 100 * y / height;
if (overlayType.has("left") && xp < threshold) return "left";
if (overlayType.has("right") && xp > 100 - threshold) return "right";
if (overlayType.has("top") && yp < threshold) return "top";
if (overlayType.has("bottom") && yp > 100 - threshold) return "bottom";
if (!overlayType.has("center")) return null;
return "center";
}
function calculateQuadrantAsPixels(overlayType, x, y, width, height, threshold) {
if (overlayType.has("left") && x < threshold) return "left";
if (overlayType.has("right") && x > width - threshold) return "right";
if (overlayType.has("top") && y < threshold) return "top";
if (overlayType.has("bottom") && y > height - threshold) return "bottom";
if (!overlayType.has("center")) return null;
return "center";
}
function addGhostImage(dataTransfer, ghostElement, options) {
var _options$ownerDocumen, _options$x, _options$y;
addClasses(ghostElement, "dv-dragged");
ghostElement.style.top = "-9999px";
((_options$ownerDocumen = options === null || options === void 0 ? void 0 : options.ownerDocument) !== null && _options$ownerDocumen !== void 0 ? _options$ownerDocumen : document).body.appendChild(ghostElement);
dataTransfer.setDragImage(ghostElement, (_options$x = options === null || options === void 0 ? void 0 : options.x) !== null && _options$x !== void 0 ? _options$x : 0, (_options$y = options === null || options === void 0 ? void 0 : options.y) !== null && _options$y !== void 0 ? _options$y : 0);
setTimeout(() => {
removeClasses(ghostElement, "dv-dragged");
ghostElement.remove();
}, 0);
}
/**
* Only one pointer-driven drag can be active at a time.
*
* State is shared across every Dockview instance on the page. Targets
* from instance B receive hit-tests from drags originating in instance A;
* that's intentional for cross-instance drops since `LocalSelectionTransfer`
* is also process-wide. The corollary is that every Tabs subscriber to
* `onDragMove` fires for every pointer drag globally; each subscriber
* hit-tests against its own DOM, so this is O(N) per pointermove where N
* is the number of registered listeners across all instances.
*/
var PointerDragController = class PointerDragController extends CompositeDisposable {
static getInstance() {
var _PointerDragControlle;
(_PointerDragControlle = PointerDragController._instance) !== null && _PointerDragControlle !== void 0 || (PointerDragController._instance = new PointerDragController());
return PointerDragController._instance;
}
constructor() {
super();
this._targets = /* @__PURE__ */ new Set();
this._targetByElement = /* @__PURE__ */ new Map();
this._onDragStart = new Emitter();
this.onDragStart = this._onDragStart.event;
this._onDragMove = new Emitter();
this.onDragMove = this._onDragMove.event;
this._onDragEnd = new Emitter();
this.onDragEnd = this._onDragEnd.event;
this.addDisposables(this._onDragStart, this._onDragMove, this._onDragEnd);
}
get active() {
return this._active;
}
registerTarget(target) {
this._targets.add(target);
this._targetByElement.set(target.element, target);
return { dispose: () => {
this._targets.delete(target);
if (this._targetByElement.get(target.element) === target) this._targetByElement.delete(target.element);
if (this._currentTarget === target) this._currentTarget = void 0;
} };
}
beginDrag(args) {
var _source$ownerDocument, _source$ownerDocument2, _source$ownerDocument3;
if (this._active) this.cancel();
const { pointerEvent, source } = args;
const dataDisposable = args.getData();
this._active = {
pointerId: pointerEvent.pointerId,
startX: pointerEvent.clientX,
startY: pointerEvent.clientY,
source
};
this._lastPointerEvent = pointerEvent;
this._onDragMoveCallback = args.onDragMove;
this._onDragEndCallback = args.onDragEnd;
this._dataDisposable = dataDisposable;
this._ghost = args.ghost;
this._iframeShield = disableIframePointEvents((_source$ownerDocument = source.ownerDocument) !== null && _source$ownerDocument !== void 0 ? _source$ownerDocument : document);
const startEvent = {
clientX: pointerEvent.clientX,
clientY: pointerEvent.clientY,
pointerEvent
};
this._onDragStart.fire(startEvent);
const targetWindow = (_source$ownerDocument2 = (_source$ownerDocument3 = source.ownerDocument) === null || _source$ownerDocument3 === void 0 ? void 0 : _source$ownerDocument3.defaultView) !== null && _source$ownerDocument2 !== void 0 ? _source$ownerDocument2 : globalThis.window;
this._moveListener = addDisposableListener(targetWindow, "pointermove", (e) => {
var _this$_active;
if (e.pointerId !== ((_this$_active = this._active) === null || _this$_active === void 0 ? void 0 : _this$_active.pointerId)) return;
this._handleMove(e);
});
this._upListener = addDisposableListener(targetWindow, "pointerup", (e) => {
var _this$_active2;
if (e.pointerId !== ((_this$_active2 = this._active) === null || _this$_active2 === void 0 ? void 0 : _this$_active2.pointerId)) return;
this._handleEnd(e, true);
});
this._cancelListener = addDisposableListener(targetWindow, "pointercancel", (e) => {
var _this$_active3;
if (e.pointerId !== ((_this$_active3 = this._active) === null || _this$_active3 === void 0 ? void 0 : _this$_active3.pointerId)) return;
this._handleEnd(e, false);
});
}
cancel() {
var _this$_currentTarget, _this$_dataDisposable;
if (!this._active) return;
const onEnd = this._onDragEndCallback;
const lastEvent = this._lastPointerEvent;
(_this$_currentTarget = this._currentTarget) === null || _this$_currentTarget === void 0 || _this$_currentTarget.handleDragLeave();
this._teardown();
(_this$_dataDisposable = this._dataDisposable) === null || _this$_dataDisposable === void 0 || _this$_dataDisposable.dispose();
this._dataDisposable = void 0;
if (lastEvent) {
const dragEvent = {
clientX: lastEvent.clientX,
clientY: lastEvent.clientY,
pointerEvent: lastEvent
};
onEnd === null || onEnd === void 0 || onEnd(dragEvent, false);
this._onDragEnd.fire(dragEvent);
}
}
_findTargetUnder(x, y) {
var _this$_active$source$, _this$_active4;
const elements = ((_this$_active$source$ = (_this$_active4 = this._active) === null || _this$_active4 === void 0 ? void 0 : _this$_active4.source.ownerDocument) !== null && _this$_active$source$ !== void 0 ? _this$_active$source$ : document).elementsFromPoint(x, y);
for (const el of elements) {
let current = el;
while (current) {
const target = this._targetByElement.get(current);
if (target) return target;
current = current.parentElement;
}
}
}
_handleMove(e) {
var _this$_ghost, _this$_onDragMoveCall;
this._lastPointerEvent = e;
(_this$_ghost = this._ghost) === null || _this$_ghost === void 0 || _this$_ghost.update(e.clientX, e.clientY);
const dragEvent = {
clientX: e.clientX,
clientY: e.clientY,
pointerEvent: e
};
const newTarget = this._findTargetUnder(e.clientX, e.clientY);
if (newTarget !== this._currentTarget) {
var _this$_currentTarget2;
(_this$_currentTarget2 = this._currentTarget) === null || _this$_currentTarget2 === void 0 || _this$_currentTarget2.handleDragLeave();
this._currentTarget = newTarget;
}
if (newTarget) newTarget.handleDragOver(dragEvent);
(_this$_onDragMoveCall = this._onDragMoveCallback) === null || _this$_onDragMoveCall === void 0 || _this$_onDragMoveCall.call(this, dragEvent);
this._onDragMove.fire(dragEvent);
}
_handleEnd(e, dropped) {
const dragEvent = {
clientX: e.clientX,
clientY: e.clientY,
pointerEvent: e
};
if (dropped && this._currentTarget) this._currentTarget.handleDrop(dragEvent);
else {
var _this$_currentTarget3;
(_this$_currentTarget3 = this._currentTarget) === null || _this$_currentTarget3 === void 0 || _this$_currentTarget3.handleDragLeave();
}
const onEnd = this._onDragEndCallback;
const dataDisposable = this._dataDisposable;
this._teardown();
this._dataDisposable = void 0;
setTimeout(() => dataDisposable === null || dataDisposable === void 0 ? void 0 : dataDisposable.dispose(), 0);
onEnd === null || onEnd === void 0 || onEnd(dragEvent, dropped);
this._onDragEnd.fire(dragEvent);
}
_teardown() {
var _this$_ghost2, _this$_iframeShield, _this$_moveListener, _this$_upListener, _this$_cancelListener;
this._currentTarget = void 0;
this._active = void 0;
this._lastPointerEvent = void 0;
this._onDragMoveCallback = void 0;
this._onDragEndCallback = void 0;
(_this$_ghost2 = this._ghost) === null || _this$_ghost2 === void 0 || _this$_ghost2.dispose();
this._ghost = void 0;
(_this$_iframeShield = this._iframeShield) === null || _this$_iframeShield === void 0 || _this$_iframeShield.release();
this._iframeShield = void 0;
(_this$_moveListener = this._moveListener) === null || _this$_moveListener === void 0 || _this$_moveListener.dispose();
(_this$_upListener = this._upListener) === null || _this$_upListener === void 0 || _this$_upListener.dispose();
(_this$_cancelListener = this._cancelListener) === null || _this$_cancelListener === void 0 || _this$_cancelListener.dispose();
this._moveListener = void 0;
this._upListener = void 0;
this._cancelListener = void 0;
}
};
const DEFAULT_ACTIVATION_SIZE = {
value: 20,
type: "percentage"
};
/** Pointer-driven counterpart to `Droptarget` with identical visual output. */
var PointerDropTarget = class extends CompositeDisposable {
get disabled() {
return this._disabled;
}
set disabled(value) {
this._disabled = value;
if (value) this._removeOverlay();
}
get state() {
return this._state;
}
constructor(element, options) {
super();
this.element = element;
this.options = options;
this._edge = false;
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._disabled = false;
this._acceptedTargetZonesSet = new Set(options.acceptedTargetZones);
const handle = {
element: this.element,
handleDragOver: (e) => this._onDragOver(e),
handleDragLeave: () => this._onDragLeave(),
handleDrop: (e) => this._onDropEvent(e)
};
this.addDisposables(this._onDrop, this._onWillShowOverlay, PointerDragController.getInstance().registerTarget(handle));
}
setTargetZones(zones) {
this._acceptedTargetZonesSet = new Set(zones);
}
setOverlayModel(model) {
this.options.overlayModel = model;
}
dispose() {
this._removeOverlay();
super.dispose();
}
_onDragOver(event) {
var _this$options$getOver, _this$options, _this$options$getOver2, _this$options$getOver3, _this$options2;
if (this._disabled) {
this._clearOwnOverlay();
return;
}
const overrideTarget = (_this$options$getOver = (_this$options = this.options).getOverrideTarget) === null || _this$options$getOver === void 0 ? void 0 : _this$options$getOver.call(_this$options);
if (this._acceptedTargetZonesSet.size === 0) {
this._clearOwnOverlay();
return;
}
const outlineEl = (_this$options$getOver2 = (_this$options$getOver3 = (_this$options2 = this.options).getOverlayOutline) === null || _this$options$getOver3 === void 0 ? void 0 : _this$options$getOver3.call(_this$options2)) !== null && _this$options$getOver2 !== void 0 ? _this$options$getOver2 : this.element;
const width = outlineEl.offsetWidth;
const height = outlineEl.offsetHeight;
if (width === 0 || height === 0) return;
const rect = outlineEl.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const resolved = this._resolvePosition(x, y, width, height, event.pointerEvent);
if (resolved === null) {
this._clearOwnOverlay();
return;
}
const quadrant = resolved.position;
if (!this.options.canDisplayOverlay(event.pointerEvent, quadrant)) {
this._clearOwnOverlay();
return;
}
const willShow = new WillShowOverlayEvent({
nativeEvent: event.pointerEvent,
position: quadrant,
edge: resolved.edge
});
this._onWillShowOverlay.fire(willShow);
if (willShow.defaultPrevented) {
this._clearOwnOverlay();
return;
}
if (resolved.edge) {
this._clearOwnOverlay();
this._state = quadrant;
this._edge = true;
return;
}
this._edge = false;
if (overrideTarget) {
renderAnchoredOverlay({
outlineElement: outlineEl,
targetModel: overrideTarget,
quadrant,
width,
height,
overlayModel: this.options.overlayModel,
className: this.options.className
});
this._state = quadrant;
return;
}
if (!this._targetElement) {
const els = createOverlayElements();
this._targetElement = els.dropzone;
this._overlayElement = els.selection;
this._state = "center";
this.element.classList.add("dv-drop-target");
this.element.append(this._targetElement);
}
if (this._overlayElement) renderInPlaceOverlay(this._overlayElement, quadrant, width, height, this.options.overlayModel);
this._state = quadrant;
}
_onDragLeave() {
var _this$options$getOver4, _this$options3;
const overrideTarget = (_this$options$getOver4 = (_this$options3 = this.options).getOverrideTarget) === null || _this$options$getOver4 === void 0 ? void 0 : _this$options$getOver4.call(_this$options3);
if (overrideTarget) {
this._state = void 0;
overrideTarget.clear();
return;
}
this._removeOverlay();
}
_onDropEvent(event) {
var _this$options$getOver5, _this$options4;
const state = this._state;
const edge = this._edge;
const overrideTarget = (_this$options$getOver5 = (_this$options4 = this.options).getOverrideTarget) === null || _this$options$getOver5 === void 0 ? void 0 : _this$options$getOver5.call(_this$options4);
this._removeOverlay();
overrideTarget === null || overrideTarget === void 0 || overrideTarget.clear();
if (state) this._onDrop.fire({
position: state,
nativeEvent: event.pointerEvent,
edge
});
}
/**
* Resolve the drop {@link Position}: defer to an injected
* {@link PositionResolver} when present, otherwise the built-in
* cursor-quadrant logic (unchanged). Shares the resolver with the HTML5
* backend.
*/
_resolvePosition(x, y, width, height, event) {
var _this$options$getPosi, _this$options5;
const resolver = (_this$options$getPosi = (_this$options5 = this.options).getPositionResolver) === null || _this$options$getPosi === void 0 ? void 0 : _this$options$getPosi.call(_this$options5);
if (resolver) {
const result = resolver.resolve({
x,
y,
width,
height,
zones: this._acceptedTargetZonesSet,
event
});
return result ? {
position: result.position,
edge: !!result.edge
} : null;
}
const position = this._calculateQuadrant(x, y, width, height);
return position ? {
position,
edge: false
} : null;
}
_calculateQuadrant(x, y, width, height) {
var _this$options$overlay, _this$options$overlay2;
const activation = (_this$options$overlay = (_this$options$overlay2 = this.options.overlayModel) === null || _this$options$overlay2 === void 0 ? void 0 : _this$options$overlay2.activationSize) !== null && _this$options$overlay !== void 0 ? _this$options$overlay : DEFAULT_ACTIVATION_SIZE;
if (activation.type === "percentage") return calculateQuadrantAsPercentage(this._acceptedTargetZonesSet, x, y, width, height, activation.value);
return calculateQuadrantAsPixels(this._acceptedTargetZonesSet, x, y, width, height, activation.value);
}
/**
* Tear down whatever this target is showing, on any frame it resolves to no
* overlay. Two things make this more than `_removeOverlay`:
*
* - With `dndOverlayMounting: 'absolute'` the overlay lives in an anchor
* container shared by every drop target in the component, and
* `_removeOverlay` only owns the in-place dropzone — so the container
* needs clearing explicitly or the last frame's highlight lingers.
* - It must only clear the container when this target actually put
* something there. The root edge target sits over every group and
* declines `center` on each frame in the middle of the layout purely so
* the event falls through; clearing from there would destroy and rebuild
* the group's overlay every frame, killing its move transition.
*/
_clearOwnOverlay() {
const owned = this._state !== void 0;
this._removeOverlay();
if (owned) {
var _this$options$getOver6, _this$options6;
(_this$options$getOver6 = (_this$options6 = this.options).getOverrideTarget) === null || _this$options$getOver6 === void 0 || (_this$options$getOver6 = _this$options$getOver6.call(_this$options6)) === null || _this$options$getOver6 === void 0 || _this$options$getOver6.clear();
}
}
_removeOverlay() {
this._edge = false;
if (this._targetElement) {
var _this$_targetElement$;
this._state = void 0;
(_this$_targetElement$ = this._targetElement.parentElement) === null || _this$_targetElement$ === void 0 || _this$_targetElement$.classList.remove("dv-drop-target");
this._targetElement.remove();
this._targetElement = void 0;
this._overlayElement = void 0;
} else this._state = void 0;
}
};
const DEFAULT_THRESHOLD = 5;
const DEFAULT_TOUCH_INITIATION_DELAY = 250;
const DEFAULT_PRESS_TOLERANCE = 8;
/**
* Pointer-event drag source. Waits for movement past `threshold` (and
* touch-only `touchInitiationDelay`) before promoting to a drag so taps
* pass through unaffected.
*/
var PointerDragSource = class extends CompositeDisposable {
constructor(element, options) {
var _options$touchOnly;
super();
this.element = element;
this.options = options;
this._disabled = false;
this._armed = false;
this._startX = 0;
this._startY = 0;
this._touchOnly = (_options$touchOnly = options.touchOnly) !== null && _options$touchOnly !== void 0 ? _options$touchOnly : true;
this.addDisposables(addDisposableListener(this.element, "pointerdown", (e) => {
this._onPointerDown(e);
}));
}
setDisabled(value) {
this._disabled = value;
if (value) this._cancelPending();
}
/**
* `false` lets the pointer source also handle mouse pointers; used when
* `dndStrategy: 'pointer'` to drive every input type through this path.
*/
setTouchOnly(value) {
if (this._touchOnly === value) return;
this._touchOnly = value;
if (value) this._cancelPending();
}
_shouldHandle(event) {
var _this$options$isCance, _this$options;
if (this._disabled) return false;
if (this._touchOnly && event.pointerType !== "touch" && event.pointerType !== "pen") return false;
if ((_this$options$isCance = (_this$options = this.options).isCancelled) === null || _this$options$isCance === void 0 ? void 0 : _this$options$isCance.call(_this$options, event)) return false;
return true;
}
_onPointerDown(event) {
var _ref, _this$options$thresho, _ref2, _this$element$ownerDo, _this$element$ownerDo2;
if (!this._shouldHandle(event)) return;
this._cancelPending();
this._pendingPointerId = event.pointerId;
this._startX = event.clientX;
this._startY = event.clientY;
this._startEvent = event;
const isTouch = event.pointerType === "touch" || event.pointerType === "pen";
const initiationDelayOpt = this.options.touchInitiationDelay;
const initiationDelay = (_ref = typeof initiationDelayOpt === "function" ? initiationDelayOpt() : initiationDelayOpt) !== null && _ref !== void 0 ? _ref : DEFAULT_TOUCH_INITIATION_DELAY;
this._armed = !isTouch || initiationDelay <= 0;
if (isTouch && initiationDelay > 0 && Number.isFinite(initiationDelay)) this._armTimer = setTimeout(() => {
this._armTimer = void 0;
this._armed = true;
}, initiationDelay);
const threshold = (_this$options$thresho = this.options.threshold) !== null && _this$options$thresho !== void 0 ? _this$options$thresho : DEFAULT_THRESHOLD;
const pressToleranceOpt = this.options.pressTolerance;
const pressTolerance = (_ref2 = typeof pressToleranceOpt === "function" ? pressToleranceOpt() : pressToleranceOpt) !== null && _ref2 !== void 0 ? _ref2 : DEFAULT_PRESS_TOLERANCE;
const targetWindow = (_this$element$ownerDo = (_this$element$ownerDo2 = this.element.ownerDocument) === null || _this$element$ownerDo2 === void 0 ? void 0 : _this$element$ownerDo2.defaultView) !== null && _this$element$ownerDo !== void 0 ? _this$element$ownerDo : globalThis.window;
this._pendingMoveListener = addDisposableListener(targetWindow, "pointermove", (moveEvent) => {
if (moveEvent.pointerId !== this._pendingPointerId) return;
const dx = moveEvent.clientX - this._startX;
const dy = moveEvent.clientY - this._startY;
const distance = Math.hypot(dx, dy);
if (this._armed) {
if (distance >= threshold) this._beginDrag(moveEvent);
return;
}
if (distance > pressTolerance) this._beginDrag(moveEvent);
});
this._pendingUpListener = addDisposableListener(targetWindow, "pointerup", (upEvent) => {
if (upEvent.pointerId !== this._pendingPointerId) return;
this._cancelPending();
});
this._pendingCancelListener = addDisposableListener(targetWindow, "pointercancel", (cancelEvent) => {
if (cancelEvent.pointerId !== this._pendingPointerId) return;
this._cancelPending();
});
}
/** For sibling gesture detectors (e.g. LongPressDetector) to dismiss a pending drag. */
cancelPending() {
this._cancelPending();
}
_cancelPending() {
var _this$_pendingMoveLis, _this$_pendingUpListe, _this$_pendingCancelL;
this._pendingPointerId = void 0;
if (this._armTimer !== void 0) {
clearTimeout(this._armTimer);
this._armTimer = void 0;
}
this._armed = false;
(_this$_pendingMoveLis = this._pendingMoveListener) === null || _this$_pendingMoveLis === void 0 || _this$_pendingMoveLis.dispose();
(_this$_pendingUpListe = this._pendingUpListener) === null || _this$_pendingUpListe === void 0 || _this$_pendingUpListe.dispose();
(_this$_pendingCancelL = this._pendingCancelListener) === null || _this$_pendingCancelL === void 0 || _this$_pendingCancelL.dispose();
this._pendingMoveListener = void 0;
this._pendingUpListener = void 0;
this._pendingCancelListener = void 0;
this._startEvent = void 0;
}
_beginDrag(triggerEvent) {
var _this$_startEvent, _this$options$onDragS, _this$options2, _this$options$createG, _this$options3;
const startEvent = (_this$_startEvent = this._startEvent) !== null && _this$_startEvent !== void 0 ? _this$_startEvent : triggerEvent;
this._cancelPending();
(_this$options$onDragS = (_this$options2 = this.options).onDragStart) === null || _this$options$onDragS === void 0 || _this$options$onDragS.call(_this$options2, startEvent);
const ghost = (_this$options$createG = (_this$options3 = this.options).createGhost) === null || _this$options$createG === void 0 ? void 0 : _this$options$createG.call(_this$options3, startEvent);
PointerDragController.getInstance().beginDrag({
pointerEvent: triggerEvent,
source: this.element,
getData: () => this.options.getData(startEvent),
ghost,
onDragMove: this.options.onDragMove,
onDragEnd: this.options.onDragEnd
});
}
dispose() {
this._cancelPending();
super.dispose();
}
};
/**
* Floating clone that follows the pointer; appended to the owning
* document's body with `pointer-events: none` so it doesn't intercept
* hit-testing.
*/
var PointerGhost = class {
constructor(opts) {
var _opts$offsetX, _opts$offsetY, _opts$opacity, _opts$owner$ownerDocu, _opts$owner;
this._disposed = false;
this.element = opts.element;
this.offsetX = (_opts$offsetX = opts.offsetX) !== null && _opts$offsetX !== void 0 ? _opts$offsetX : 0;
this.offsetY = (_opts$offsetY = opts.offsetY) !== null && _opts$offsetY !== void 0 ? _opts$offsetY : 0;
this.element.style.position = "fixed";
this.element.style.left = "0px";
this.element.style.top = "0px";
this.element.style.pointerEvents = "none";
this.element.style.zIndex = "99999";
this.element.style.opacity = String((_opts$opacity = opts.opacity) !== null && _opts$opacity !== void 0 ? _opts$opacity : .8);
this.element.style.willChange = "transform";
this.element.style.transform = `translate3d(${opts.initialX - this.offsetX}px, ${opts.initialY - this.offsetY}px, 0)`;
((_opts$owner$ownerDocu = (_opts$owner = opts.owner) === null || _opts$owner === void 0 ? void 0 : _opts$owner.ownerDocument) !== null && _opts$owner$ownerDocu !== void 0 ? _opts$owner$ownerDocu : document).body.appendChild(this.element);
}
update(clientX, clientY) {
if (this._disposed) return;
this.element.style.transform = `translate3d(${clientX - this.offsetX}px, ${clientY - this.offsetY}px, 0)`;
}
dispose() {
if (this._disposed) return;
this._disposed = true;
this.element.remove();
}
};
/**
* HTML5 drag source. Listens for the native `dragstart` event, calls
* `getData` to populate transfer, optionally renders the ghost via
* `setDragImage`, fires `onDragStart` / `onDragEnd`, and tears down the
* transfer disposer after `dragend`.
*/
var Html5DragSource = class extends CompositeDisposable {
constructor(el, opts) {
super();
this.el = el;
this.opts = opts;
this._dataDisposable = new MutableDisposable();
this._pointerEventsDisposable = new MutableDisposable();
this._disabled = !!opts.disabled;
this.addDisposables(this._dataDisposable, this._pointerEventsDisposable, addDisposableListener(this.el, "dragstart", (event) => {
var _this$opts$isCancelle, _this$opts, _this$el$ownerDocumen, _this$opts$createGhos, _this$opts2, _this$opts$onDragStar, _this$opts3;
if (event.defaultPrevented || this._disabled || ((_this$opts$isCancelle = (_this$opts = this.opts).isCancelled) === null || _this$opts$isCancelle === void 0 ? void 0 : _this$opts$isCancelle.call(_this$opts, event))) {
event.preventDefault();
return;
}
const iframes = disableIframePointEvents((_this$el$ownerDocumen = this.el.ownerDocument) !== null && _this$el$ownerDocumen !== void 0 ? _this$el$ownerDocumen : document);
this._pointerEventsDisposable.value = { dispose: () => iframes.release() };
this.el.classList.add("dv-dragged");
setTimeout(() => this.el.classList.remove("dv-dragged"), 0);
this._dataDisposable.value = this.opts.getData(event);
const ghost = (_this$opts$createGhos = (_this$opts2 = this.opts).createGhost) === null || _this$opts$createGhos === void 0 ? void 0 : _this$opts$createGhos.call(_this$opts2, event);
if (ghost && event.dataTransfer) {
var _ghost$offsetX, _ghost$offsetY, _this$el$ownerDocumen2;
addGhostImage(event.dataTransfer, ghost.element, {
x: (_ghost$offsetX = ghost.offsetX) !== null && _ghost$offsetX !== void 0 ? _ghost$offsetX : 0,
y: (_ghost$offsetY = ghost.offsetY) !== null && _ghost$offsetY !== void 0 ? _ghost$offsetY : 0,
ownerDocument: (_this$el$ownerDocumen2 = this.el.ownerDocument) !== null && _this$el$ownerDocumen2 !== void 0 ? _this$el$ownerDocumen2 : void 0
});
if (ghost.dispose) {
const disposeGhost = ghost.dispose;
setTimeout(() => disposeGhost(), 0);
}
}
if (event.dataTransfer) {
event.dataTransfer.effectAllowed = "move";
if (event.dataTransfer.items.length === 0) event.dataTransfer.setData("text/plain", "");
}
(_this$opts$onDragStar = (_this$opts3 = this.opts).onDragStart) === null || _this$opts$onDragStar === void 0 || _this$opts$onDragStar.call(_this$opts3, event);
}), addDisposableListener(this.el, "dragend", (event) => {
var _this$opts$onDragEnd, _this$opts4;
this._pointerEventsDisposable.dispose();
setTimeout(() => this._dataDisposable.dispose(), 0);
(_this$opts$onDragEnd = (_this$opts4 = this.opts).onDragEnd) === null || _this$opts$onDragEnd === void 0 || _this$opts$onDragEnd.call(_this$opts4, event);
}));
}
setDisabled(value) {
this._disabled = value;
}
setTouchOnly(_) {}
cancelPending() {}
};
var Html5DragBackend = class {
constructor() {
this.kind = "html5";
}
createDropTarget(element, options) {
return new Droptarget(element, options);
}
createDragSource(element, options) {
return new Html5DragSource(element, options);
}
};
var PointerDragBackend = class {
constructor() {
this.kind = "pointer";
}
createDropTarget(element, options) {
return new PointerDropTarget(element, options);
}
createDragSource(element, options) {
const pointerCreateGhost = options.createGhost ? (event) => {
const spec = options.createGhost(event);
if (!spec) return;
const ghost = new PointerGhost({
element: spec.element,
initialX: event.clientX,
initialY: event.clientY,
offsetX: spec.offsetX,
offsetY: spec.offsetY,
owner: element
});
if (spec.dispose) {
const baseDispose = ghost.dispose.bind(ghost);
const disposeSpec = spec.dispose;
ghost.dispose = () => {
baseDispose();
disposeSpec();
};
}
return ghost;
} : void 0;
const source = new PointerDragSource(element, {
getData: options.getData,
isCancelled: options.isCancelled,
onDragStart: options.onDragStart,
onDragEnd: options.onDragEnd ? (event) => options.onDragEnd(event.pointerEvent) : void 0,
createGhost: pointerCreateGhost,
touchOnly: options.touchOnly,
touchInitiationDelay: options.touchInitiationDelay,
pressTolerance: options.pressTolerance,
threshold: options.threshold
});
if (options.disabled) source.setDisabled(true);
return source;
}
};
const html5Backend = new Html5DragBackend();
const pointerBackend = new PointerDragBackend();
const PROPERTY_KEYS_PANEVIEW = (() => {
return Object.keys({
disableAutoResizing: void 0,
disableDnd: void 0,
className: void 0
});
})();
var PaneviewUnhandledDragOverEvent = class extends AcceptableEvent {
constructor(nativeEvent, position, getData, panel) {
super();
this.nativeEvent = nativeEvent;
this.position = position;
this.getData = getData;
this.panel = panel;
}
};
var WillFocusEvent = class extends DockviewEvent {
constructor() {
super();
}
};
/**
* A core api implementation that should be used across all panel-like objects
*/
var PanelApiImpl = class extends CompositeDisposable {
get isFocused() {
return this._isFocused;
}
get isActive() {
return this._isActive;
}
get isVisible() {
return this._isVisible;
}
get width() {
return this._width;
}
get height() {
return this._height;
}
constructor(id, component) {
super();
this.id = id;
this.component = component;
this._isFocused = false;
this._isActive = false;
this._isVisible = true;
this._width = 0;
this._height = 0;
this._parameters = {};
this.panelUpdatesDisposable = new MutableDisposable();
this._onDidDimensionChange = new Emitter();
this.onDidDimensionsChange = this._onDidDimensionChange.event;
this._onDidChangeFocus = new Emitter();
this.onDidFocusChange = this._onDidChangeFocus.event;
this._onWillFocus = new Emitter();
this.onWillFocus = this._onWillFocus.event;
this._onDidVisibilityChange = new Emitter();
this.onDidVisibilityChange = this._onDidVisibilityChange.event;
this._onWillVisibilityChange = new Emitter();
this.onWillVisibilityChange = this._onWillVisibilityChange.event;
this._onDidActiveChange = new Emitter();
this.onDidActiveChange = this._onDidActiveChange.event;
this._onActiveChange = new Emitter();
this.onActiveChange = this._onActiveChange.event;
this._onDidParametersChange = new Emitter();
this.onDidParametersChange = this._onDidParametersChange.event;
this.addDisposables(this.onDidFocusChange((event) => {
this._isFocused = event.isFocused;
}), this.onDidActiveChange((event) => {
this._isActive = event.isActive;
}), this.onDidVisibilityChange((event) => {
this._isVisible = event.isVisible;
}), this.onDidDimensionsChange((event) => {
this._width = event.width;
this._height = event.height;
}), this.panelUpdatesDisposable, this._onDidDimensionChange, this._onDidChangeFocus, this._onDidVisibilityChange, this._onDidActiveChange, this._onWillFocus, this._onActiveChange, this._onWillFocus, this._onWillVisibilityChange, this._onDidParametersChange);
}
getParameters() {
return this._parameters;
}
initialize(panel) {
this.panelUpdatesDisposable.value = this._onDidParametersChange.event((parameters) => {
this._parameters = parameters;
panel.update({ params: parameters });
});
}
setVisible(isVisible) {
this._onWillVisibilityChange.fire({ isVisible });
}
setActive() {
this._onActiveChange.fire();
}
updateParameters(parameters) {
this._onDidParametersChange.fire(parameters);
}
};
var SplitviewPanelApiImpl = class extends PanelApiImpl {
constructor(id, component) {
super(id, component);
this._onDidConstraintsChangeInternal = new Emitter();
this.onDidConstraintsChangeInternal = this._onDidConstraintsChangeInternal.event;
this._onDidConstraintsChange = new Emitter({ replay: true });
this.onDidConstraintsChange = this._onDidConstraintsChange.event;
this._onDidSizeChange = new Emitter();
this.onDidSizeChange = this._onDidSizeChange.event;
this.addDisposables(this._onDidConstraintsChangeInternal, this._onDidConstraintsChange, this._onDidSizeChange);
}
setConstraints(value) {
this._onDidConstraintsChangeInternal.fire(value);
}
setSize(event) {
this._onDidSizeChange.fire(event);
}
};
var PaneviewPanelApiImpl = class extends SplitviewPanelApiImpl {
set pane(pane) {
this._pane = pane;
}
constructor(id, component) {
super(id, component);
this._onDidExpansionChange = new Emitter({ replay: true });
this.onDidExpansionChange = this._onDidExpansionChange.event;
this._onMouseEnter = new Emitter({});
this.onMouseEnter = this._onMouseEnter.event;
this._onMouseLeave = new Emitter({});
this.onMouseLeave = this._onMouseLeave.event;
this.addDisposables(this._onDidExpansionChange, this._onMouseEnter, this._onMouseLeave);
}
setExpanded(isExpanded) {
var _this$_pane;
(_this$_pane = this._pane) === null || _this$_pane === void 0 || _this$_pane.setExpanded(isExpanded);
}
get isExpanded() {
var _this$_pane2;
return !!((_this$_pane2 = this._pane) === null || _this$_pane2 === void 0 ? void 0 : _this$_pane2.isExpanded());
}
};
var BasePanelView = class extends CompositeDisposable {
get element() {
return this._element;
}
get width() {
return this._width;
}
get height() {
return this._height;
}
get params() {
var _this$_params;
return (_this$_params = this._params) === null || _this$_params === void 0 ? void 0 : _this$_params.params;
}
constructor(id, component, api) {
super();
this.id = id;
this.component = component;
this.api = api;
this._height = 0;
this._width = 0;
this._element = document.createElement("div");
this._element.tabIndex = -1;
this._element.style.outline = "none";
this._element.style.height = "100%";
this._element.style.width = "100%";
this._element.style.overflow = "hidden";
const focusTracker = trackFocus(this._element);
this.addDisposables(this.api, focusTracker.onDidFocus(() => {
this.api._onDidChangeFocus.fire({ isFocused: true });
}), focusTracker.onDidBlur(() => {
this.api._onDidChangeFocus.fire({ isFocused: false });
}), focusTracker);
}
focus() {
const event = new WillFocusEvent();
this.api._onWillFocus.fire(event);
if (event.defaultPrevented) return;
this._element.focus();
}
layout(width, height) {
this._width = width;
this._height = height;
this.api._onDidDimensionChange.fire({
width,
height
});
if (this.part) {
if (this._params) this.part.update(this._params.params);
}
}
init(parameters) {
this._params = parameters;
this.part = this.getComponent();
}
update(event) {
var _this$_params2, _this$part;
this._params = _objectSpread2(_objectSpread2({}, this._params), {}, { params: _objectSpread2(_objectSpread2({}, (_this$_params2 = this._params) === null || _this$_params2 === void 0 ? void 0 : _this$_params2.params), event.params) });
/**
* delete new keys that have a value of undefined,
* allow values of null
*/
for (const key of Object.keys(event.params)) if (event.params[key] === void 0) delete this._params.params[key];
(_this$part = this.part) === null || _this$part === void 0 || _this$part.update({ params: this._params.params });
}
toJSON() {
var _this$_params$params, _this$_params3;
const params = (_this$_params$params = (_this$_params3 = this._params) === null || _this$_params3 === void 0 ? void 0 : _this$_params3.params) !== null && _this$_params$params !== void 0 ? _this$_params$params : {};
return {
id: this.id,
component: this.component,
params: Object.keys(params).length > 0 ? params : void 0
};
}
dispose() {
var _this$part2;
this.api.dispose();
(_this$part2 = this.part) === null || _this$part2 === void 0 || _this$part2.dispose();
super.dispose();
}
};
var PaneviewPanel = class extends BasePanelView {
set orientation(value) {
this._orientation = value;
}
get orientation() {
return this._orientation;
}
get minimumSize() {
return this.headerSize + (this.isExpanded() ? this._minimumBodySize : 0);
}
get maximumSize() {
return this.headerSize + (this.isExpanded() ? this._maximumBodySize : 0);
}
get size() {
return this._size;
}
get orthogonalSize() {
return this._orthogonalSize;
}
set orthogonalSize(size) {
this._orthogonalSize = size;
}
get minimumBodySize() {
return this._minimumBodySize;
}
set minimumBodySize(value) {
this._minimumBodySize = typeof value === "number" ? value : 0;
}
get maximumBodySize() {
return this._maximumBodySize;
}
set maximumBodySize(value) {
this._maximumBodySize = typeof value === "number" ? value : Number.POSITIVE_INFINITY;
}
get headerVisible() {
return this._headerVisible;
}
set headerVisible(value) {
this._headerVisible = value;
this.header.style.display = value ? "" : "none";
}
constructor(options) {
super(options.id, options.component, new PaneviewPanelApiImpl(options.id, options.component));
this._onDidChangeExpansionState = new Emitter({ replay: true });
this.onDidChangeExpansionState = this._onDidChangeExpansionState.event;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._orthogonalSize = 0;
this._size = 0;
this._isExpanded = false;
this.api.pane = this;
this.api.initialize(this);
this.headerSize = options.headerSize;
this.headerComponent = options.headerComponent;
this._minimumBodySize = options.minimumBodySize;
this._maximumBodySize = options.maximumBodySize;
this._isExpanded = options.isExpanded;
this._headerVisible = options.isHeaderVisible;
this._onDidChangeExpansionState.fire(this.isExpanded());
this._orientation = options.orientation;
this.element.classList.add("dv-pane");
this.addDisposables(this.api.onWillVisibilityChange((event) => {
const { isVisible } = event;
const { accessor } = this._params;
accessor.setVisible(this, isVisible);
}), this.api.onDidSizeChange((event) => {
this._onDidChange.fire({ size: event.size });
}), addDisposableListener(this.element, "mouseenter", (ev) => {
this.api._onMouseEnter.fire(ev);
}), addDisposableListener(this.element, "mouseleave", (ev) => {
this.api._onMouseLeave.fire(ev);
}));
this.addDisposables(this._onDidChangeExpansionState, this.onDidChangeExpansionState((isPanelExpanded) => {
this.api._onDidExpansionChange.fire({ isExpanded: isPanelExpanded });
}), this.api.onDidFocusChange((e) => {
if (!this.header) return;
if (e.isFocused) addClasses(this.header, "focused");
else removeClasses(this.header, "focused");
}));
this.renderOnce();
}
setVisible(isVisible) {
this.api._onDidVisibilityChange.fire({ isVisible });
}
setActive(isActive) {
this.api._onDidActiveChange.fire({ isActive });
}
isExpanded() {
return this._isExpanded;
}
setExpanded(expanded) {
var _this$_expandedSize;
if (this._isExpanded === expanded) return;
this._isExpanded = expanded;
if (expanded) {
if (this.animationTimer) clearTimeout(this.animationTimer);
if (this.body) this.element.appendChild(this.body);
} else {
this._expandedSize = this._size;
this.animationTimer = setTimeout(() => {
var _this$body;
(_this$body = this.body) === null || _this$body === void 0 || _this$body.remove();
}, 200);
}
this._onDidChange.fire(expanded ? { size: (_this$_expandedSize = this._expandedSize) !== null && _this$_expandedSize !== void 0 ? _this$_expandedSize : this.width } : {});
this._onDidChangeExpansionState.fire(expanded);
}
layout(size, orthogonalSize) {
this._size = size;
this._orthogonalSize = orthogonalSize;
const [width, height] = this.orientation === "HORIZONTAL" ? [size, orthogonalSize] : [orthogonalSize, size];
super.layout(width, height);
}
init(parameters) {
var _this$body2, _this$header;
super.init(parameters);
if (typeof parameters.minimumBodySize === "number") this.minimumBodySize = parameters.minimumBodySize;
if (typeof parameters.maximumBodySize === "number") this.maximumBodySize = parameters.maximumBodySize;
this.bodyPart = this.getBodyComponent();
this.headerPart = this.getHeaderComponent();
this.bodyPart.init(_objectSpread2(_objectSpread2({}, parameters), {}, { api: this.api }));
this.headerPart.init(_objectSpread2(_objectSpread2({}, parameters), {}, { api: this.api }));
(_this$body2 = this.body) === null || _this$body2 === void 0 || _this$body2.append(this.bodyPart.element);
(_this$header = this.header) === null || _this$header === void 0 || _this$header.append(this.headerPart.element);
if (typeof parameters.isExpanded === "boolean") this.setExpanded(parameters.isExpanded);
}
toJSON() {
const params = this._params;
return _objectSpread2(_objectSpread2({}, super.toJSON()), {}, {
headerComponent: this.headerComponent,
title: params.title
});
}
renderOnce() {
this.header = document.createElement("div");
this.header.tabIndex = 0;
this.header.className = "dv-pane-header";
this.header.style.height = `${this.headerSize}px`;
this.header.style.lineHeight = `${this.headerSize}px`;
this.header.style.minHeight = `${this.headerSize}px`;
this.header.style.maxHeight = `${this.headerSize}px`;
this.element.appendChild(this.header);
this.body = document.createElement("div");
this.body.className = "dv-pane-body";
this.element.appendChild(this.body);
}
getComponent() {
return {
update: (params) => {
var _this$bodyPart, _this$headerPart;
(_this$bodyPart = this.bodyPart) === null || _this$bodyPart === void 0 || _this$bodyPart.update({ params });
(_this$headerPart = this.headerPart) === null || _this$headerPart === void 0 || _this$headerPart.update({ params });
},
dispose: () => {
var _this$bodyPart2, _this$headerPart2;
(_this$bodyPart2 = this.bodyPart) === null || _this$bodyPart2 === void 0 || _this$bodyPart2.dispose();
(_this$headerPart2 = this.headerPart) === null || _this$headerPart2 === void 0 || _this$headerPart2.dispose();
}
};
}
};
var DraggablePaneviewPanel = class extends PaneviewPanel {
constructor(options) {
super({
id: options.id,
component: options.component,
headerComponent: options.headerComponent,
orientation: options.orientation,
isExpanded: options.isExpanded,
isHeaderVisible: true,
headerSize: options.headerSize,
minimumBodySize: options.minimumBodySize,
maximumBodySize: options.maximumBodySize
});
this._onDidDrop = new Emitter();
this.onDidDrop = this._onDidDrop.event;
this._onUnhandledDragOver = new Emitter();
this.onUnhandledDragOver = this._onUnhandledDragOver.event;
this.accessor = options.accessor;
this.addDisposables(this._onDidDrop, this._onUnhandledDragOver);
if (!options.disableDnd) this.initDragFeatures();
}
initDragFeatures() {
if (!this.header) return;
const id = this.id;
const accessorId = this.accessor.id;
this.header.draggable = true;
const sharedDragOptions = { getData: () => {
LocalSelectionTransfer.getInstance().setData([new PaneTransfer(accessorId, id)], PaneTransfer.prototype);
return { dispose: () => {
LocalSelectionTransfer.getInstance().clearData(PaneTransfer.prototype);
} };
} };
this.html5DragSource = html5Backend.createDragSource(this.header, sharedDragOptions);
this.pointerDragSource = pointerBackend.createDragSource(this.header, sharedDragOptions);
const canDisplayOverlay = (event, position) => {
const data = getPaneData();
if (data) {
if (data.paneId !== this.id && data.viewId === this.accessor.id) return true;
}
const firedEvent = new PaneviewUnhandledDragOverEvent(event, position, getPaneData, this);
this._onUnhandledDragOver.fire(firedEvent);
return firedEvent.isAccepted;
};
const dropTargetOptions = {
acceptedTargetZones: ["top", "bottom"],
overlayModel: { activationSize: {
type: "percentage",
value: 50
} },
canDisplayOverlay
};
this.target = html5Backend.createDropTarget(this.element, dropTargetOptions);
this.pointerTarget = pointerBackend.createDropTarget(this.element, dropTargetOptions);
this.addDisposables(this._onDidDrop, this.html5DragSource, this.pointerDragSource, this.target, this.pointerTarget, this.target.onDrop((event) => {
this.onDrop(event);
}), this.pointerTarget.onDrop((event) => {
this.onDrop(event);
}));
}
onDrop(event) {
const data = getPaneData();
if ((data === null || data === void 0 ? void 0 : data.viewId) !== this.accessor.id) {
this._onDidDrop.fire(_objectSpread2(_objectSpread2({}, event), {}, {
panel: this,
api: new PaneviewApi(this.accessor),
getData: getPaneData
}));
return;
}
const containerApi = this._params.containerApi;
const panelId = data.paneId;
const existingPanel = containerApi.getPanel(panelId);
if (!existingPanel) {
this._onDidDrop.fire(_objectSpread2(_objectSpread2({}, event), {}, {
panel: this,
getData: getPaneData,
api: new PaneviewApi(this.accessor)
}));
return;
}
const allPanels = containerApi.panels;
const fromIndex = allPanels.indexOf(existingPanel);
let toIndex = containerApi.panels.indexOf(this);
if (event.position === "left" || event.position === "top") toIndex = Math.max(0, toIndex - 1);
if (event.position === "right" || event.position === "bottom") {
if (fromIndex > toIndex) toIndex++;
toIndex = Math.min(allPanels.length - 1, toIndex);
}
containerApi.movePanel(fromIndex, toIndex);
}
};
let _contentId = 0;
/** Stable DOM id so each tab's `aria-controls` can reference its tabpanel. */
const nextContentId = () => `dv-tabpanel-${_contentId++}`;
var ContentContainer = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor(accessor, group) {
var _this$accessor$resolv, _this$accessor, _this$accessor$resolv2, _this$accessor2, _this$accessor$onDidO, _this$accessor$onDidO2, _this$accessor3;
super();
this.accessor = accessor;
this.group = group;
this.disposable = new MutableDisposable();
this._onDidFocus = new Emitter();
this.onDidFocus = this._onDidFocus.event;
this._onDidBlur = new Emitter();
this.onDidBlur = this._onDidBlur.event;
this._element = document.createElement("div");
this._element.className = "dv-content-container";
this._element.tabIndex = -1;
this._element.id = nextContentId();
this._element.setAttribute("role", "tabpanel");
this.addDisposables(this._onDidFocus, this._onDidBlur);
const getOverrideTarget = () => {
var _group$dropTargetCont;
return (_group$dropTargetCont = group.dropTargetContainer) === null || _group$dropTargetCont === void 0 ? void 0 : _group$dropTargetCont.model;
};
const canDisplayOverlay = (event, position) => this.group.canDisplayContentOverlay(event, position);
this.dropTarget = new Droptarget(this.element, {
getOverlayOutline: () => {
var _accessor$options$the;
return ((_accessor$options$the = accessor.options.theme) === null || _accessor$options$the === void 0 ? void 0 : _accessor$options$the.dndPanelOverlay) === "group" ? this.element.parentElement : null;
},
className: "dv-drop-target-content",
acceptedTargetZones: [
"top",
"bottom",
"left",
"right",
"center"
],
canDisplayOverlay,
getOverrideTarget,
overlayModel: (_this$accessor$resolv = (_this$accessor = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv === void 0 ? void 0 : _this$accessor$resolv.call(_this$accessor, "content"),
getPositionResolver: () => {
var _accessor$getDropPosi;
return (_accessor$getDropPosi = accessor.getDropPositionResolver) === null || _accessor$getDropPosi === void 0 ? void 0 : _accessor$getDropPosi.call(accessor);
}
});
this.pointerDropTarget = pointerBackend.createDropTarget(this.element, {
acceptedTargetZones: [
"top",
"bottom",
"left",
"right",
"center"
],
canDisplayOverlay,
getOverlayOutline: () => {
var _accessor$options$the2;
return ((_accessor$options$the2 = accessor.options.theme) === null || _accessor$options$the2 === void 0 ? void 0 : _accessor$options$the2.dndPanelOverlay) === "group" ? this.element.parentElement : null;
},
className: "dv-drop-target-content",
getOverrideTarget,
overlayModel: (_this$accessor$resolv2 = (_this$accessor2 = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv2 === void 0 ? void 0 : _this$accessor$resolv2.call(_this$accessor2, "content"),
getPositionResolver: () => {
var _accessor$getDropPosi2;
return (_accessor$getDropPosi2 = accessor.getDropPositionResolver) === null || _accessor$getDropPosi2 === void 0 ? void 0 : _accessor$getDropPosi2.call(accessor);
}
});
this.addDisposables(this.dropTarget, this.pointerDropTarget, (_this$accessor$onDidO = (_this$accessor$onDidO2 = (_this$accessor3 = this.accessor).onDidOptionsChange) === null || _this$accessor$onDidO2 === void 0 ? void 0 : _this$accessor$onDidO2.call(_this$accessor3, () => {
var _this$accessor$resolv3, _this$accessor$resolv4, _this$accessor4;
const model = (_this$accessor$resolv3 = (_this$accessor$resolv4 = (_this$accessor4 = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv4 === void 0 ? void 0 : _this$accessor$resolv4.call(_this$accessor4, "content")) !== null && _this$accessor$resolv3 !== void 0 ? _this$accessor$resolv3 : {};
this.dropTarget.setOverlayModel(model);
this.pointerDropTarget.setOverlayModel(model);
})) !== null && _this$accessor$onDidO !== void 0 ? _this$accessor$onDidO : Disposable.NONE);
}
show() {
this.element.style.display = "";
}
hide() {
this.element.style.display = "none";
}
setLabelledBy(tabElementId) {
if (tabElementId) this._element.setAttribute("aria-labelledby", tabElementId);
else this._element.removeAttribute("aria-labelledby");
}
renderPanel(panel, options) {
var _options$asActive, _this$panel;
const doRender = ((_options$asActive = options === null || options === void 0 ? void 0 : options.asActive) !== null && _options$asActive !== void 0 ? _options$asActive : true) || this.panel && this.group.isPanelActive(this.panel);
if (((_this$panel = this.panel) === null || _this$panel === void 0 ? void 0 : _this$panel.view.content.element.parentElement) === this._element) {
var _this$panel$view$cont, _this$panel$view$cont2;
this.panel.view.content.element.remove();
(_this$panel$view$cont = (_this$panel$view$cont2 = this.panel.view.content).onHide) === null || _this$panel$view$cont === void 0 || _this$panel$view$cont.call(_this$panel$view$cont2);
}
this.panel = panel;
let container;
switch (panel.api.renderer) {
case "onlyWhenVisible":
this.group.renderContainer.detatch(panel);
if (this.panel) {
if (doRender) {
var _this$panel$view$cont3, _this$panel$view$cont4;
this._element.appendChild(this.panel.view.content.element);
(_this$panel$view$cont3 = (_this$panel$view$cont4 = this.panel.view.content).onShow) === null || _this$panel$view$cont3 === void 0 || _this$panel$view$cont3.call(_this$panel$view$cont4);
}
}
container = this._element;
break;
case "always":
if (panel.view.content.element.parentElement === this._element) panel.view.content.element.remove();
container = this.group.renderContainer.attach({
panel,
referenceContainer: this
});
break;
default: throw new Error(`dockview: invalid renderer type '${panel.api.renderer}'`);
}
if (doRender) {
const focusTracker = trackFocus(container);
this.focusTracker = focusTracker;
const disposable = new CompositeDisposable();
disposable.addDisposables(focusTracker, focusTracker.onDidFocus(() => this._onDidFocus.fire()), focusTracker.onDidBlur(() => this._onDidBlur.fire()));
this.disposable.value = disposable;
}
}
openPanel(panel) {
if (this.panel === panel) return;
this.renderPanel(panel);
}
layout(_width, _height) {}
closePanel() {
if (this.panel) {
if (this.panel.api.renderer === "onlyWhenVisible") {
var _this$panel$view$cont5, _this$panel$view$cont6;
this.panel.view.content.element.remove();
(_this$panel$view$cont5 = (_this$panel$view$cont6 = this.panel.view.content).onHide) === null || _this$panel$view$cont5 === void 0 || _this$panel$view$cont5.call(_this$panel$view$cont6);
}
}
this.panel = void 0;
}
dispose() {
this.disposable.dispose();
super.dispose();
}
/**
* Refresh the focus tracker state to handle cases where focus state
* gets out of sync due to programmatic panel activation
*/
refreshFocusState() {
var _this$focusTracker;
if ((_this$focusTracker = this.focusTracker) === null || _this$focusTracker === void 0 ? void 0 : _this$focusTracker.refreshState) this.focusTracker.refreshState();
}
};
const createSvgElementFromPath = (params) => {
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttributeNS(null, "height", params.height);
svg.setAttributeNS(null, "width", params.width);
svg.setAttributeNS(null, "viewBox", params.viewbox);
svg.setAttributeNS(null, "aria-hidden", "false");
svg.setAttributeNS(null, "focusable", "false");
svg.classList.add("dv-svg");
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttributeNS(null, "d", params.path);
svg.appendChild(path);
return svg;
};
const createCloseButton = () => createSvgElementFromPath({
width: "11",
height: "11",
viewbox: "0 0 28 28",
path: "M2.1 27.3L0 25.2L11.55 13.65L0 2.1L2.1 0L13.65 11.55L25.2 0L27.3 2.1L15.75 13.65L27.3 25.2L25.2 27.3L13.65 15.75L2.1 27.3Z"
});
const createPinButton = () => createSvgElementFromPath({
width: "11",
height: "11",
viewbox: "0 0 24 24",
path: "M16 9V4h1c.55 0 1-.45 1-1s-.45-1-1-1H6c-.55 0-1 .45-1 1s.45 1 1 1h1v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z"
});
const createExpandMoreButton = () => createSvgElementFromPath({
width: "11",
height: "11",
viewbox: "0 0 24 15",
path: "M12 14.15L0 2.15L2.15 0L12 9.9L21.85 0.0499992L24 2.2L12 14.15Z"
});
const createChevronRightButton = () => createSvgElementFromPath({
width: "11",
height: "11",
viewbox: "0 0 15 25",
path: "M2.15 24.1L0 21.95L9.9 12.05L0 2.15L2.15 0L14.2 12.05L2.15 24.1Z"
});
const DEFAULT_DELAY = 500;
const DEFAULT_TOLERANCE = 8;
/**
* Doesn't consume the pointer; movement past `tolerance` cancels
* silently so a sibling `PointerDragSource` can take over.
*/
var LongPressDetector = class extends CompositeDisposable {
constructor(element, options) {
super();
this.element = element;
this.options = options;
this._startX = 0;
this._startY = 0;
this.addDisposables(addDisposableListener(this.element, "pointerdown", (e) => {
this._onPointerDown(e);
}));
}
_onPointerDown(event) {
var _this$options$touchOn, _this$options$delay, _this$options$toleran, _this$element$ownerDo, _this$element$ownerDo2;
if (((_this$options$touchOn = this.options.touchOnly) !== null && _this$options$touchOn !== void 0 ? _this$options$touchOn : true) && event.pointerType !== "touch" && event.pointerType !== "pen") return;
this._cancelPending();
this._pointerId = event.pointerId;
this._startX = event.clientX;
this._startY = event.clientY;
const delay = (_this$options$delay = this.options.delay) !== null && _this$options$delay !== void 0 ? _this$options$delay : DEFAULT_DELAY;
const tolerance = (_this$options$toleran = this.options.tolerance) !== null && _this$options$toleran !== void 0 ? _this$options$toleran : DEFAULT_TOLERANCE;
const targetWindow = (_this$element$ownerDo = (_this$element$ownerDo2 = this.element.ownerDocument) === null || _this$element$ownerDo2 === void 0 ? void 0 : _this$element$ownerDo2.defaultView) !== null && _this$element$ownerDo !== void 0 ? _this$element$ownerDo : globalThis.window;
this._timer = setTimeout(() => {
this._timer = void 0;
this._cancelPending();
this._installContextMenuGuard(targetWindow);
this._installClickGuard(targetWindow);
this.options.onLongPress(event);
}, delay);
this._moveListener = addDisposableListener(targetWindow, "pointermove", (moveEvent) => {
if (moveEvent.pointerId !== this._pointerId) return;
const dx = moveEvent.clientX - this._startX;
const dy = moveEvent.clientY - this._startY;
if (Math.hypot(dx, dy) > tolerance) this._cancelPending();
});
this._upListener = addDisposableListener(targetWindow, "pointerup", (upEvent) => {
if (upEvent.pointerId !== this._pointerId) return;
this._cancelPending();
});
this._cancelListener = addDisposableListener(targetWindow, "pointercancel", (cancelEvent) => {
if (cancelEvent.pointerId !== this._pointerId) return;
this._cancelPending();
});
}
_installContextMenuGuard(targetWindow) {
let guard;
const timeout = setTimeout(() => guard === null || guard === void 0 ? void 0 : guard.dispose(), 500);
guard = addDisposableListener(targetWindow, "contextmenu", (event) => {
event.preventDefault();
clearTimeout(timeout);
guard === null || guard === void 0 || guard.dispose();
}, { capture: true });
}
_installClickGuard(targetWindow) {
let guard;
const timeout = setTimeout(() => guard === null || guard === void 0 ? void 0 : guard.dispose(), 500);
guard = addDisposableListener(targetWindow, "click", (event) => {
const target = event.target;
if (target && this.element.contains(target)) {
event.preventDefault();
event.stopPropagation();
}
clearTimeout(timeout);
guard === null || guard === void 0 || guard.dispose();
}, { capture: true });
}
_cancelPending() {
var _this$_moveListener, _this$_upListener, _this$_cancelListener;
if (this._timer !== void 0) {
clearTimeout(this._timer);
this._timer = void 0;
}
this._pointerId = void 0;
(_this$_moveListener = this._moveListener) === null || _this$_moveListener === void 0 || _this$_moveListener.dispose();
(_this$_upListener = this._upListener) === null || _this$_upListener === void 0 || _this$_upListener.dispose();
(_this$_cancelListener = this._cancelListener) === null || _this$_cancelListener === void 0 || _this$_cancelListener.dispose();
this._moveListener = void 0;
this._upListener = void 0;
this._cancelListener = void 0;
}
dispose() {
this._cancelPending();
super.dispose();
}
};
function resolveDndCapabilities(options) {
if (options.disableDnd) return {
html5: false,
pointer: false,
pointerHandlesMouse: false
};
switch (options.dndStrategy) {
case "pointer": return {
html5: false,
pointer: true,
pointerHandlesMouse: true
};
case "html5": return {
html5: true,
pointer: false,
pointerHandlesMouse: false
};
case "auto":
case void 0:
default: return isCoarsePrimaryInput$1() ? {
html5: false,
pointer: true,
pointerHandlesMouse: true
} : {
html5: true,
pointer: true,
pointerHandlesMouse: false
};
}
}
function isCoarsePrimaryInput$1() {
if (globalThis.window === void 0 || !globalThis.matchMedia) return false;
const coarse = globalThis.matchMedia("(pointer: coarse)").matches;
const fine = globalThis.matchMedia("(pointer: fine)").matches;
return coarse && !fine;
}
let _tabId = 0;
/** Stable DOM id referenced by the tabpanel's `aria-labelledby`. */
const nextTabId = () => `dv-tab-${_tabId++}`;
var Tab = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor(panel, accessor, group) {
var _this$panel$title, _this$group, _this$panel$api$onDid, _this$panel$api, _this$panel$api$onDid2, _this$panel$api$onDid3, _this$panel$api2, _this$panel$api2$onDi;
super();
this.panel = panel;
this.accessor = accessor;
this.group = group;
this.content = void 0;
this.panelTransfer = LocalSelectionTransfer.getInstance();
this._direction = "horizontal";
this._pinIndicator = void 0;
this._onPointDown = new Emitter();
this.onPointerDown = this._onPointDown.event;
this._onTabClick = new Emitter();
this.onTabClick = this._onTabClick.event;
this._onDropped = new Emitter();
this.onDrop = this._onDropped.event;
this._onDragStart = new Emitter();
this.onDragStart = this._onDragStart.event;
this._onDragEnd = new Emitter();
this.onDragEnd = this._onDragEnd.event;
const caps = resolveDndCapabilities(this.accessor.options);
this._element = document.createElement("div");
this._element.className = "dv-tab";
this._element.tabIndex = -1;
this._element.draggable = caps.html5;
this._element.id = nextTabId();
this._element.setAttribute("role", "tab");
this._element.setAttribute("aria-selected", "false");
this._element.setAttribute("aria-label", (_this$panel$title = this.panel.title) !== null && _this$panel$title !== void 0 ? _this$panel$title : this.panel.id);
this._element.dataset.tabPanelId = this.panel.id;
const contentContainerId = (_this$group = this.group) === null || _this$group === void 0 || (_this$group = _this$group.model) === null || _this$group === void 0 ? void 0 : _this$group.contentContainerId;
if (contentContainerId) this._element.setAttribute("aria-controls", contentContainerId);
toggleClass(this.element, "dv-inactive-tab", true);
this._updatePinnedClasses();
const canDisplayOverlay = (event, position) => {
if (this.group.locked) return false;
const data = getPanelData();
if (this.accessor.id === (data === null || data === void 0 ? void 0 : data.viewId)) {
var _this$accessor$option;
if (((_this$accessor$option = this.accessor.options.theme) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.tabAnimation) === "smooth") return false;
return true;
}
return this.group.model.canDisplayOverlay(event, position, "tab");
};
this.dropTarget = html5Backend.createDropTarget(this._element, {
acceptedTargetZones: ["left", "right"],
overlayModel: this._buildOverlayModel(),
canDisplayOverlay,
getOverrideTarget: () => {
var _group$model$dropTarg;
return (_group$model$dropTarg = group.model.dropTargetContainer) === null || _group$model$dropTarg === void 0 ? void 0 : _group$model$dropTarg.model;
}
});
this.pointerDropTarget = pointerBackend.createDropTarget(this._element, {
acceptedTargetZones: ["left", "right"],
overlayModel: this._buildOverlayModel(),
canDisplayOverlay,
getOverrideTarget: () => {
var _group$model$dropTarg2;
return (_group$model$dropTarg2 = group.model.dropTargetContainer) === null || _group$model$dropTarg2 === void 0 ? void 0 : _group$model$dropTarg2.model;
}
});
const sharedDragOptions = {
getData: () => {
this.panelTransfer.setData([new PanelTransfer(this.accessor.id, this.group.id, this.panel.id)], PanelTransfer.prototype);
return { dispose: () => {
this.panelTransfer.clearData(PanelTransfer.prototype);
} };
},
createGhost: () => ({
element: this._buildGhostElement(),
offsetX: 30,
offsetY: -10
}),
onDragStart: (event) => {
var _this$accessor$option2;
this._onDragStart.fire(event);
if (!(event instanceof PointerEvent) && ((_this$accessor$option2 = this.accessor.options.theme) === null || _this$accessor$option2 === void 0 ? void 0 : _this$accessor$option2.tabAnimation) === "smooth") requestAnimationFrame(() => {
toggleClass(this.element, "dv-tab--dragging", true);
});
},
onDragEnd: (event) => {
this._onDragEnd.fire(event);
}
};
this.html5DragSource = html5Backend.createDragSource(this._element, _objectSpread2(_objectSpread2({}, sharedDragOptions), {}, { disabled: !caps.html5 }));
this.pointerDragSource = pointerBackend.createDragSource(this._element, _objectSpread2(_objectSpread2({}, sharedDragOptions), {}, {
disabled: !caps.pointer,
touchOnly: !caps.pointerHandlesMouse,
isCancelled: () => !resolveDndCapabilities(this.accessor.options).pointer
}));
this.onWillShowOverlay = Event.any(this.dropTarget.onWillShowOverlay, this.pointerDropTarget.onWillShowOverlay);
this.addDisposables(this._onPointDown, this._onTabClick, this._onDropped, this._onDragStart, this._onDragEnd, this.accessor.onDidOptionsChange(() => {
const model = this._buildOverlayModel();
this.dropTarget.setOverlayModel(model);
this.pointerDropTarget.setOverlayModel(model);
this._updatePinnedClasses();
}), (_this$panel$api$onDid = (_this$panel$api = this.panel.api) === null || _this$panel$api === void 0 || (_this$panel$api$onDid2 = _this$panel$api.onDidChangePinned) === null || _this$panel$api$onDid2 === void 0 ? void 0 : _this$panel$api$onDid2.call(_this$panel$api, () => {
this._updatePinnedClasses();
})) !== null && _this$panel$api$onDid !== void 0 ? _this$panel$api$onDid : { dispose: () => {} }, (_this$panel$api$onDid3 = (_this$panel$api2 = this.panel.api) === null || _this$panel$api2 === void 0 || (_this$panel$api2$onDi = _this$panel$api2.onDidTitleChange) === null || _this$panel$api2$onDi === void 0 ? void 0 : _this$panel$api2$onDi.call(_this$panel$api2, (event) => {
var _event$title;
this._element.setAttribute("aria-label", (_event$title = event.title) !== null && _event$title !== void 0 ? _event$title : this.panel.id);
})) !== null && _this$panel$api$onDid3 !== void 0 ? _this$panel$api$onDid3 : { dispose: () => {} }, addDisposableListener(this._element, "dragend", () => {
toggleClass(this.element, "dv-tab--dragging", false);
}), this.html5DragSource, addDisposableListener(this._element, "pointerdown", (event) => {
this._onPointDown.fire(event);
}), addDisposableListener(this._element, "click", (event) => {
this._onTabClick.fire(event);
}), addDisposableListener(this._element, "contextmenu", (event) => {
var _this$accessor$contex;
(_this$accessor$contex = this.accessor.contextMenuService) === null || _this$accessor$contex === void 0 || _this$accessor$contex.show(this.panel, this.group, event);
}), new LongPressDetector(this._element, { onLongPress: (event) => {
var _this$accessor$contex2;
this.pointerDragSource.cancelPending();
(_this$accessor$contex2 = this.accessor.contextMenuService) === null || _this$accessor$contex2 === void 0 || _this$accessor$contex2.show(this.panel, this.group, event);
} }), this.dropTarget.onDrop((event) => {
this._onDropped.fire(event);
}), this.pointerDropTarget.onDrop((event) => {
this._onDropped.fire(event);
}), this.dropTarget, this.pointerDropTarget, this.pointerDragSource);
}
_updatePinnedClasses() {
var _this$panel$api$isPin, _this$panel$api3, _this$accessor$option3, _this$panel$api4;
const pinned = (_this$panel$api$isPin = (_this$panel$api3 = this.panel.api) === null || _this$panel$api3 === void 0 ? void 0 : _this$panel$api3.isPinned) !== null && _this$panel$api$isPin !== void 0 ? _this$panel$api$isPin : false;
const compact = pinned && ((_this$accessor$option3 = this.accessor.options.pinnedTabs) === null || _this$accessor$option3 === void 0 ? void 0 : _this$accessor$option3.compact) === true;
toggleClass(this.element, "dv-tab--pinned", pinned);
toggleClass(this.element, "dv-tab--pinned-compact", compact);
const wantGlyph = pinned && !((_this$panel$api4 = this.panel.api) === null || _this$panel$api4 === void 0 ? void 0 : _this$panel$api4.tabComponent);
if (wantGlyph && !this._pinIndicator) {
const indicator = document.createElement("div");
indicator.className = "dv-tab-pin";
indicator.appendChild(createPinButton());
this._element.insertBefore(indicator, this._element.firstChild);
this._pinIndicator = indicator;
} else if (!wantGlyph && this._pinIndicator) {
this._pinIndicator.remove();
this._pinIndicator = void 0;
}
}
setActive(isActive) {
toggleClass(this.element, "dv-active-tab", isActive);
toggleClass(this.element, "dv-inactive-tab", !isActive);
this._element.setAttribute("aria-selected", isActive ? "true" : "false");
this._element.tabIndex = isActive ? 0 : -1;
}
setContent(part) {
if (this.content) this.content.element.remove();
this.content = part;
this._element.appendChild(this.content.element);
}
_buildOverlayModel() {
var _this$accessor$resolv, _this$accessor, _this$accessor$option4;
const custom = (_this$accessor$resolv = (_this$accessor = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv === void 0 ? void 0 : _this$accessor$resolv.call(_this$accessor, "tab", this.group);
if (custom) return custom;
const smallBoundary = ((_this$accessor$option4 = this.accessor.options.theme) === null || _this$accessor$option4 === void 0 ? void 0 : _this$accessor$option4.dndTabIndicator) === "line" ? Number.POSITIVE_INFINITY : 0;
return {
activationSize: {
value: 50,
type: "percentage"
},
smallWidthBoundary: smallBoundary,
smallHeightBoundary: smallBoundary
};
}
setDirection(direction) {
this._direction = direction;
const zones = direction === "vertical" ? ["top", "bottom"] : ["left", "right"];
this.dropTarget.setTargetZones(zones);
this.pointerDropTarget.setTargetZones(zones);
}
updateDragAndDropState() {
const caps = resolveDndCapabilities(this.accessor.options);
this._element.draggable = caps.html5;
this.html5DragSource.setDisabled(!caps.html5);
this.pointerDragSource.setDisabled(!caps.pointer);
this.pointerDragSource.setTouchOnly(!caps.pointerHandlesMouse);
}
/**
* Vertical tabs are flipped to horizontal so the ghost stays readable
* during the drag rather than appearing sideways-rotated.
*/
_buildGhostElement() {
const style = getComputedStyle(this.element);
const newNode = this.element.cloneNode(true);
const isVertical = this._direction === "vertical";
const verticalSkip = /* @__PURE__ */ new Set([
"writing-mode",
"inline-size",
"block-size",
"min-inline-size",
"min-block-size",
"max-inline-size",
"max-block-size",
"margin-inline",
"margin-inline-start",
"margin-inline-end",
"margin-block",
"margin-block-start",
"margin-block-end",
"padding-inline",
"padding-inline-start",
"padding-inline-end",
"padding-block",
"padding-block-start",
"padding-block-end"
]);
Array.from(style).forEach((key) => {
if (isVertical && verticalSkip.has(key)) return;
newNode.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key));
});
if (isVertical) {
newNode.style.setProperty("writing-mode", "horizontal-tb");
newNode.style.setProperty("width", style.height);
newNode.style.setProperty("height", style.width);
}
newNode.style.position = "absolute";
newNode.classList.add("dv-tab-ghost-drag");
return newNode;
}
};
var DockviewWillShowOverlayLocationEvent = class {
get kind() {
return this.options.kind;
}
/** Narrow with `instanceof DragEvent` before reading `dataTransfer`. */
get nativeEvent() {
return this.event.nativeEvent;
}
get position() {
return this.event.position;
}
/** The resolved cell was marked `edge` (an outer "dock to the whole layout"
* cell). See {@link DroptargetEvent.edge}. */
get edge() {
return this.event.edge;
}
/** The resolved cell docks as a dedicated edge group (display hint). See
* {@link PositionResolverResult.edgeGroup}. */
get edgeGroup() {
return this.event.edgeGroup;
}
get defaultPrevented() {
return this.event.defaultPrevented;
}
get panel() {
return this.options.panel;
}
get api() {
return this.options.api;
}
get group() {
return this.options.group;
}
preventDefault() {
this.event.preventDefault();
}
getData() {
return this.options.getData();
}
constructor(event, options) {
this.event = event;
this.options = options;
}
};
/**
* The drag-source half of a group drag handle: html5 + pointer drag sources
* that publish a group-level `PanelTransfer`, the "Multiple Panels (N)" ghost,
* and the floating-group disambiguation that keeps the redock gesture from
* firing alongside the overlay's move-the-float gesture.
*
* Shared by the tab-bar void container and the dedicated floating title bar so
* both grab handles redock identically.
*/
var GroupDragSource = class extends CompositeDisposable {
get group() {
return this.groupAccessor();
}
constructor(options) {
var _options$isFloatingMo, _this$group2;
super();
this.panelTransfer = LocalSelectionTransfer.getInstance();
this._onDragStart = new Emitter();
this.onDragStart = this._onDragStart.event;
this._element = options.element;
this.accessor = options.accessor;
const group = options.group;
this.groupAccessor = typeof group === "function" ? group : () => group;
this.isFloatingMoveHandle = (_options$isFloatingMo = options.isFloatingMoveHandle) !== null && _options$isFloatingMo !== void 0 ? _options$isFloatingMo : (() => true);
const caps = resolveDndCapabilities(this.accessor.options);
this._element.draggable = caps.html5;
toggleClass(this._element, "dv-draggable", caps.html5 || caps.pointer);
this.addDisposables(this._onDragStart);
const buildMultiPanelsGhost = () => {
const ghostEl = document.createElement("div");
const style = globalThis.getComputedStyle(this._element);
const bgColor = style.getPropertyValue("--dv-activegroup-visiblepanel-tab-background-color");
const color = style.getPropertyValue("--dv-activegroup-visiblepanel-tab-color");
ghostEl.style.backgroundColor = bgColor;
ghostEl.style.color = color;
ghostEl.style.padding = "2px 8px";
ghostEl.style.height = "24px";
ghostEl.style.fontSize = "11px";
ghostEl.style.lineHeight = "20px";
ghostEl.style.borderRadius = "12px";
ghostEl.style.whiteSpace = "nowrap";
ghostEl.style.boxSizing = "border-box";
ghostEl.style.display = "inline-block";
ghostEl.textContent = `Multiple Panels (${this.group.size})`;
return ghostEl;
};
const buildGhostSpec = () => {
var _this$accessor$buildG, _this$accessor;
const customGhost = (_this$accessor$buildG = (_this$accessor = this.accessor).buildGroupDragGhost) === null || _this$accessor$buildG === void 0 ? void 0 : _this$accessor$buildG.call(_this$accessor, this.group);
if (customGhost) return customGhost;
return {
element: buildMultiPanelsGhost(),
offsetX: 30,
offsetY: -10
};
};
const sharedDragOptions = {
getData: () => {
this.panelTransfer.setData([new PanelTransfer(this.accessor.id, this.group.id, null)], PanelTransfer.prototype);
return { dispose: () => {
this.panelTransfer.clearData(PanelTransfer.prototype);
} };
},
createGhost: buildGhostSpec,
onDragStart: (event) => {
this._onDragStart.fire(event);
}
};
this.html5DragSource = html5Backend.createDragSource(this._element, _objectSpread2(_objectSpread2({}, sharedDragOptions), {}, {
disabled: !caps.html5,
isCancelled: (event) => {
if (this.group.api.location.type === "floating" && this.isFloatingMoveHandle() && !event.shiftKey) return true;
if (this.group.api.location.type === "edge" && this.group.size === 0) return true;
return false;
}
}));
const isFloating = () => {
var _this$group;
return ((_this$group = this.group) === null || _this$group === void 0 || (_this$group = _this$group.api) === null || _this$group === void 0 || (_this$group = _this$group.location) === null || _this$group === void 0 ? void 0 : _this$group.type) === "floating" && this.isFloatingMoveHandle();
};
this.pointerDragSource = pointerBackend.createDragSource(this._element, _objectSpread2(_objectSpread2({}, sharedDragOptions), {}, {
disabled: !caps.pointer,
touchOnly: !caps.pointerHandlesMouse,
touchInitiationDelay: () => isFloating() ? 500 : 250,
pressTolerance: () => isFloating() ? Infinity : 8,
isCancelled: () => {
if (!resolveDndCapabilities(this.accessor.options).pointer) return true;
if (this.group.api.location.type === "edge" && this.group.size === 0) return true;
return false;
},
onDragStart: (event) => {
var _this$getFloatingOver;
(_this$getFloatingOver = this.getFloatingOverlay()) === null || _this$getFloatingOver === void 0 || _this$getFloatingOver.cancelPendingDrag();
this._onDragStart.fire(event);
}
}));
const overlayMoveSub = new MutableDisposable();
const refreshOverlayMoveSub = () => {
const overlay = this.getFloatingOverlay();
overlayMoveSub.value = overlay ? overlay.onDidStartMoving(() => {
this.pointerDragSource.cancelPending();
}) : Disposable.NONE;
};
refreshOverlayMoveSub();
this.addDisposables(overlayMoveSub);
const locationChange = (_this$group2 = this.group) === null || _this$group2 === void 0 || (_this$group2 = _this$group2.api) === null || _this$group2 === void 0 ? void 0 : _this$group2.onDidLocationChange;
if (locationChange) this.addDisposables(locationChange(refreshOverlayMoveSub));
this.addDisposables(this.html5DragSource, this.pointerDragSource);
}
updateDragAndDropState() {
const caps = resolveDndCapabilities(this.accessor.options);
this._element.draggable = caps.html5;
toggleClass(this._element, "dv-draggable", caps.html5 || caps.pointer);
this.html5DragSource.setDisabled(!caps.html5);
this.pointerDragSource.setDisabled(!caps.pointer);
this.pointerDragSource.setTouchOnly(!caps.pointerHandlesMouse);
}
getFloatingOverlay() {
var _this$accessor$floati;
if (!this.group) return;
return (_this$accessor$floati = this.accessor.floatingGroups) === null || _this$accessor$floati === void 0 || (_this$accessor$floati = _this$accessor$floati.find((fg) => fg.group === this.group)) === null || _this$accessor$floati === void 0 ? void 0 : _this$accessor$floati.overlay;
}
};
var VoidContainer = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor(accessor, group) {
var _this$accessor$resolv, _this$accessor, _this$accessor$resolv2, _this$accessor2, _this$accessor$onDidO, _this$accessor$onDidO2, _this$accessor3;
super();
this.accessor = accessor;
this.group = group;
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this._onDragStart = new Emitter();
this.onDragStart = this._onDragStart.event;
this._element = document.createElement("div");
this._element.className = "dv-void-container";
this.addDisposables(this._onDrop, this._onDragStart, addDisposableListener(this._element, "pointerdown", () => {
this.accessor.doSetGroupActive(this.group);
}), addDisposableListener(this._element, "pointerdown", (e) => {
if (e.shiftKey) quasiPreventDefault(e);
}, true));
this.dragSource = new GroupDragSource({
element: this._element,
accessor: this.accessor,
group: this.group,
isFloatingMoveHandle: () => !this._element.closest(".dv-resize-container-with-titlebar")
});
const canDisplayOverlay = (event, position) => {
if (this.group.api.locked) return false;
const data = getPanelData();
if (this.accessor.id === (data === null || data === void 0 ? void 0 : data.viewId)) return true;
return group.model.canDisplayOverlay(event, position, "header_space");
};
this.dropTarget = html5Backend.createDropTarget(this._element, {
acceptedTargetZones: ["center"],
canDisplayOverlay,
getOverrideTarget: () => {
var _group$model$dropTarg;
return (_group$model$dropTarg = group.model.dropTargetContainer) === null || _group$model$dropTarg === void 0 ? void 0 : _group$model$dropTarg.model;
},
overlayModel: (_this$accessor$resolv = (_this$accessor = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv === void 0 ? void 0 : _this$accessor$resolv.call(_this$accessor, "header_space", this.group)
});
this.pointerDropTarget = pointerBackend.createDropTarget(this._element, {
acceptedTargetZones: ["center"],
canDisplayOverlay,
getOverrideTarget: () => {
var _group$model$dropTarg2;
return (_group$model$dropTarg2 = group.model.dropTargetContainer) === null || _group$model$dropTarg2 === void 0 ? void 0 : _group$model$dropTarg2.model;
},
overlayModel: (_this$accessor$resolv2 = (_this$accessor2 = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv2 === void 0 ? void 0 : _this$accessor$resolv2.call(_this$accessor2, "header_space", this.group)
});
this.onWillShowOverlay = Event.any(this.dropTarget.onWillShowOverlay, this.pointerDropTarget.onWillShowOverlay);
this.addDisposables(this.dragSource, this.dragSource.onDragStart((event) => {
this._onDragStart.fire(event);
}), this.dropTarget.onDrop((event) => {
this._onDrop.fire(event);
}), this.pointerDropTarget.onDrop((event) => {
this._onDrop.fire(event);
}), this.dropTarget, this.pointerDropTarget, (_this$accessor$onDidO = (_this$accessor$onDidO2 = (_this$accessor3 = this.accessor).onDidOptionsChange) === null || _this$accessor$onDidO2 === void 0 ? void 0 : _this$accessor$onDidO2.call(_this$accessor3, () => {
var _this$accessor$resolv3, _this$accessor$resolv4, _this$accessor4;
const model = (_this$accessor$resolv3 = (_this$accessor$resolv4 = (_this$accessor4 = this.accessor).resolveDropOverlayModel) === null || _this$accessor$resolv4 === void 0 ? void 0 : _this$accessor$resolv4.call(_this$accessor4, "header_space", this.group)) !== null && _this$accessor$resolv3 !== void 0 ? _this$accessor$resolv3 : {};
this.dropTarget.setOverlayModel(model);
this.pointerDropTarget.setOverlayModel(model);
})) !== null && _this$accessor$onDidO !== void 0 ? _this$accessor$onDidO : Disposable.NONE);
}
updateDragAndDropState() {
this.dragSource.updateDragAndDropState();
}
};
var Scrollbar = class Scrollbar extends CompositeDisposable {
get element() {
return this._element;
}
get orientation() {
return this._orientation;
}
set orientation(value) {
if (this._orientation === value) return;
this._scrollOffset = 0;
this._orientation = value;
removeClasses(this._scrollbar, "dv-scrollbar-vertical", "dv-scrollbar-horizontal");
if (value === "vertical") addClasses(this._scrollbar, "dv-scrollbar-vertical");
else addClasses(this._scrollbar, "dv-scrollbar-horizontal");
}
constructor(scrollableElement) {
super();
this.scrollableElement = scrollableElement;
this._scrollOffset = 0;
this._orientation = "horizontal";
this._element = document.createElement("div");
this._element.className = "dv-scrollable";
this._scrollbar = document.createElement("div");
this._scrollbar.className = "dv-scrollbar dv-scrollbar-horizontal";
this.element.appendChild(scrollableElement);
this.element.appendChild(this._scrollbar);
this.addDisposables(addDisposableListener(this.element, "wheel", (event) => {
this._scrollOffset += event.deltaY * Scrollbar.MouseWheelSpeed;
this.scheduleStyleUpdate();
}), addDisposableListener(this._scrollbar, "pointerdown", (event) => {
var _this$_scrollbar$owne;
event.preventDefault();
const doc = (_this$_scrollbar$owne = this._scrollbar.ownerDocument) !== null && _this$_scrollbar$owne !== void 0 ? _this$_scrollbar$owne : document;
toggleClass(this.element, "dv-scrollable-scrolling", true);
const originalClient = this._orientation === "horizontal" ? event.clientX : event.clientY;
const originalScrollOffset = this._scrollOffset;
const onPointerMove = (event) => {
const delta = this._orientation === "horizontal" ? event.clientX - originalClient : event.clientY - originalClient;
const p = (this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight) / (this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight);
this._scrollOffset = originalScrollOffset + delta / p;
this.scheduleStyleUpdate();
};
const onEnd = () => {
toggleClass(this.element, "dv-scrollable-scrolling", false);
doc.removeEventListener("pointermove", onPointerMove);
doc.removeEventListener("pointerup", onEnd);
doc.removeEventListener("pointercancel", onEnd);
};
doc.addEventListener("pointermove", onPointerMove);
doc.addEventListener("pointerup", onEnd);
doc.addEventListener("pointercancel", onEnd);
}), addDisposableListener(this.element, "scroll", () => {
this.scheduleStyleUpdate();
}), addDisposableListener(this.scrollableElement, "scroll", () => {
this._scrollOffset = this._orientation === "horizontal" ? this.scrollableElement.scrollLeft : this.scrollableElement.scrollTop;
this.scheduleStyleUpdate();
}), watchElementResize(this.element, () => {
toggleClass(this.element, "dv-scrollable-resizing", true);
if (this._animationTimer) clearTimeout(this._animationTimer);
this._animationTimer = setTimeout(() => {
clearTimeout(this._animationTimer);
toggleClass(this.element, "dv-scrollable-resizing", false);
}, 500);
this.scheduleStyleUpdate();
}), Disposable.from(() => {
if (this._pendingStyleFrame !== void 0) {
cancelAnimationFrame(this._pendingStyleFrame);
this._pendingStyleFrame = void 0;
}
}));
}
/**
* Coalesce the scrollbar restyle to one pass per animation frame. The
* wheel / scroll / pointermove handlers can each fire many times per frame,
* and `calculateScrollbarStyles` both reads layout (`clientWidth`/
* `scrollWidth`) and writes styles + `scrollLeft`/`scrollTop`; running it
* synchronously per event caused repeated read→write→read reflow. Batching
* to a frame keeps a burst of events to a single measure+write.
*/
scheduleStyleUpdate() {
if (this._pendingStyleFrame !== void 0) return;
this._pendingStyleFrame = requestAnimationFrame(() => {
this._pendingStyleFrame = void 0;
this.calculateScrollbarStyles();
});
}
calculateScrollbarStyles() {
const clientSize = this._orientation === "horizontal" ? this.element.clientWidth : this.element.clientHeight;
const scrollSize = this._orientation === "horizontal" ? this.scrollableElement.scrollWidth : this.scrollableElement.scrollHeight;
if (scrollSize > clientSize) {
const px = clientSize * (clientSize / scrollSize);
if (this._orientation === "horizontal") {
this._scrollbar.style.width = `${px}px`;
this._scrollbar.style.height = "";
} else {
this._scrollbar.style.height = `${px}px`;
this._scrollbar.style.width = "";
}
this._scrollOffset = clamp(this._scrollOffset, 0, scrollSize - clientSize);
if (this._orientation === "horizontal") this.scrollableElement.scrollLeft = this._scrollOffset;
else this.scrollableElement.scrollTop = this._scrollOffset;
const percentageComplete = this._scrollOffset / (scrollSize - clientSize);
if (this._orientation === "horizontal") {
this._scrollbar.style.left = `${(clientSize - px) * percentageComplete}px`;
this._scrollbar.style.top = "";
} else {
this._scrollbar.style.top = `${(clientSize - px) * percentageComplete}px`;
this._scrollbar.style.left = "";
}
} else {
if (this._orientation === "horizontal") {
this._scrollbar.style.width = "0px";
this._scrollbar.style.left = "0px";
} else {
this._scrollbar.style.height = "0px";
this._scrollbar.style.top = "0px";
}
this._scrollOffset = 0;
}
}
};
Scrollbar.MouseWheelSpeed = 1;
/**
* The CSS class the `MultiRowTabsModule` toggles on a group's tab list
* (`.dv-tabs-container`) to switch it into wrap layout. Shared here so core
* (the reorder controller's wrap detection + the SCSS rules) and the module
* agree on the one string; renaming it in one place would otherwise silently
* break the seam across the package boundary.
*/
const OVERFLOW_WRAP_TABS_CLASS = "dv-tabs-container--wrap";
/**
* The CSS class the `MultiRowTabsModule` adds alongside
* {@link OVERFLOW_WRAP_TABS_CLASS} once `overflow.maxRows` is set. It clips the
* wrapped strip to `--dv-max-tab-rows` rows (the surplus rows spill to the
* dropdown) so the header stops growing at the cap. Shared here so core (SCSS)
* and the module agree on the one string.
*/
const OVERFLOW_WRAP_TABS_CAPPED_CLASS = "dv-tabs-container--wrap-capped";
/**
* The custom property the `MultiRowTabsModule` sets on a capped wrapped strip to
* carry the `overflow.maxRows` value into the SCSS `max-height` calc. Shared for
* the same reason as the class names above.
*/
const OVERFLOW_MAX_TAB_ROWS_VARIABLE = "--dv-max-tab-rows";
/**
* The custom property the `MultiRowTabsModule` sets on a wrapped vertical
* (edge-group) tab list to pin every wrapped tab to a single, uniform height:
* the tallest tab's natural height, measured live by the controller. A vertical
* tab's natural height tracks its (rotated) title length, so a column only fits
* a handful of tabs and tiny per-tab height differences flip how many fit,
* ragged-ing the wrapped grid into a staircase. Pinning one height makes every
* column hold the same count, so the columns line up as a clean grid (the mirror
* of the horizontal wrap, whose tabs share the fixed row height). Shared here so
* core (SCSS) and the module agree on the one string. Only meaningful in vertical
* wrap; a horizontal strip never sets it and the SCSS falls back to auto height.
*/
const OVERFLOW_WRAP_TABS_VERTICAL_TAB_HEIGHT_VARIABLE = "--dv-wrap-vertical-tab-height";
/** Whether `position` is enabled in a per-edge {@link EdgeGroupSet}. */
function isEdgeGroupEnabled(set, position) {
if (typeof set === "boolean") return set;
return !!(set === null || set === void 0 ? void 0 : set[position]);
}
/** Whether any edge is enabled in a per-edge {@link EdgeGroupSet}. */
function isAnyEdgeGroupEnabled(set) {
if (typeof set === "boolean") return set;
if (!set) return false;
return Object.values(set).some(Boolean);
}
var DockviewUnhandledDragOverEvent = class extends AcceptableEvent {
constructor(nativeEvent, target, position, getData, group) {
super();
this.nativeEvent = nativeEvent;
this.target = target;
this.position = position;
this.getData = getData;
this.group = group;
}
};
const PROPERTY_KEYS_DOCKVIEW = (() => {
return Object.keys({
disableAutoResizing: void 0,
hideBorders: void 0,
singleTabMode: void 0,
disableFloatingGroups: void 0,
floatingGroupBounds: void 0,
transformFloatingGroupDrag: void 0,
smartGuides: void 0,
floatingGroupDragHandle: void 0,
popoutUrl: void 0,
nonce: void 0,
defaultRenderer: void 0,
defaultHeaderPosition: void 0,
debug: void 0,
locked: void 0,
disableDnd: void 0,
dndStrategy: void 0,
className: void 0,
noPanelsOverlay: void 0,
dndEdges: void 0,
dropPositionResolver: void 0,
dndCompass: void 0,
theme: void 0,
disableTabsOverflowList: void 0,
overflow: void 0,
scrollbars: void 0,
getTabContextMenuItems: void 0,
getTabGroupChipContextMenuItems: void 0,
createTabGroupChipComponent: void 0,
createGroupDragGhostComponent: void 0,
dropOverlayModel: void 0,
announcements: void 0,
getAnnouncement: void 0,
announcer: void 0,
messages: void 0,
keyboardNavigation: void 0,
layoutHistory: void 0,
autoHideEdgeGroups: void 0,
dockToEdgeGroups: void 0,
edgeGroupPeek: void 0,
tabGroupColors: void 0,
tabGroupAccent: void 0,
pinnedTabs: void 0
});
})();
function isPanelOptionsWithPanel(data) {
if (data.referencePanel) return true;
return false;
}
function isPanelOptionsWithGroup(data) {
if (data.referenceGroup) return true;
return false;
}
function isGroupOptionsWithPanel(data) {
if (data.referencePanel) return true;
return false;
}
function isGroupOptionsWithGroup(data) {
if (data.referenceGroup) return true;
return false;
}
const DEFAULT_TAB_GROUP_COLORS = [
{
id: "grey",
value: "var(--dv-tab-group-color-grey)",
label: "Grey"
},
{
id: "blue",
value: "var(--dv-tab-group-color-blue)",
label: "Blue"
},
{
id: "red",
value: "var(--dv-tab-group-color-red)",
label: "Red"
},
{
id: "yellow",
value: "var(--dv-tab-group-color-yellow)",
label: "Yellow"
},
{
id: "green",
value: "var(--dv-tab-group-color-green)",
label: "Green"
},
{
id: "pink",
value: "var(--dv-tab-group-color-pink)",
label: "Pink"
},
{
id: "purple",
value: "var(--dv-tab-group-color-purple)",
label: "Purple"
},
{
id: "cyan",
value: "var(--dv-tab-group-color-cyan)",
label: "Cyan"
},
{
id: "orange",
value: "var(--dv-tab-group-color-orange)",
label: "Orange"
}
];
/**
* Runtime palette for tab-group color accents.
*
* Resolves a stored `color` string to a CSS color expression, with three
* fall-through modes:
* 1. `id` matches an entry → entry's `value`
* 2. `id` doesn't match → `id` itself (raw CSS literal pass-through)
* 3. `id` is empty or undefined → undefined (caller skips assignment)
*
* When `enabled` is false the palette returns undefined for everything; this
* is the `tabGroupAccent: 'off'` opt-out path.
*/
var TabGroupColorPalette = class {
constructor(entries, enabled = true) {
this._entries = entries.slice();
this._byId = new Map(entries.map((e) => [e.id, e]));
this._enabled = enabled;
}
get enabled() {
return this._enabled;
}
set enabled(value) {
this._enabled = value;
}
/**
* Replace the entry list in place. Used by `updateOptions` so that
* existing palette references (held by chips, indicators, etc.) see
* the new palette without needing to be re-wired.
*/
setEntries(entries) {
this._entries = entries.slice();
this._byId = new Map(entries.map((e) => [e.id, e]));
}
entries() {
return this._entries;
}
has(id) {
return this._byId.has(id);
}
get(id) {
return this._byId.get(id);
}
/** First entry's id; used as the default when a color is unset. */
defaultId() {
var _this$_entries$;
return (_this$_entries$ = this._entries[0]) === null || _this$_entries$ === void 0 ? void 0 : _this$_entries$.id;
}
/**
* Resolve a stored color to its CSS value, or undefined if no value
* should be written (palette disabled, or color empty/undefined).
*/
resolveValue(color) {
if (!this._enabled || !color) return;
const entry = this._byId.get(color);
return entry ? entry.value : color;
}
};
let _fallbackPalette;
/**
* Lazy-built palette used when the accessor isn't available (test mocks,
* isolated chip construction). Production code paths always pass a real
* palette through.
*/
function getFallbackPalette() {
var _fallbackPalette2;
(_fallbackPalette2 = _fallbackPalette) !== null && _fallbackPalette2 !== void 0 || (_fallbackPalette = new TabGroupColorPalette(DEFAULT_TAB_GROUP_COLORS, true));
return _fallbackPalette;
}
/**
* Set the `--dv-tab-group-color` custom property on `el` to the resolved
* accent value, or remove it when the palette is disabled / color is unset.
*/
function applyTabGroupAccent(el, color, palette) {
const value = (palette !== null && palette !== void 0 ? palette : getFallbackPalette()).resolveValue(color);
if (value === void 0) el.style.removeProperty("--dv-tab-group-color");
else el.style.setProperty("--dv-tab-group-color", value);
}
/**
* Return the resolved CSS color for a tab group, or undefined when the
* palette is disabled or no color is set. Use this when you need the raw
* value to assign to a non-custom-property style (e.g. SVG stroke,
* backgroundColor on the indicator underline).
*/
function resolveTabGroupAccent(color, palette) {
return (palette !== null && palette !== void 0 ? palette : getFallbackPalette()).resolveValue(color);
}
/**
* Visual chip for a tab group. Owns the DOM element, label, click /
* context-menu interactions, and exposes a long-press gesture as a
* second `onContextMenu` source. Drag-and-drop wiring lives in
* `TabGroupManager`, which constructs the drag sources on this
* chip's element so it can include tabs-list context (custom group
* drag image, tab-group transfer payload).
*/
var TabGroupChip = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor(_palette) {
super();
this._palette = _palette;
this._onClick = new Emitter();
this.onClick = this._onClick.event;
this._onContextMenu = new Emitter();
this.onContextMenu = this._onContextMenu.event;
this._element = document.createElement("div");
this._element.className = "dv-tab-group-chip";
this._element.tabIndex = 0;
this._label = document.createElement("span");
this._label.className = "dv-tab-group-chip-label";
this._element.appendChild(this._label);
this.addDisposables(this._onClick, this._onContextMenu, new LongPressDetector(this._element, { onLongPress: (event) => {
this._onContextMenu.fire(event);
} }), addDisposableListener(this._element, "click", (event) => {
this._onClick.fire(event);
}), addDisposableListener(this._element, "contextmenu", (event) => {
this._onContextMenu.fire(event);
}));
}
init(params) {
this._tabGroup = params.tabGroup;
this.updateColor(params.tabGroup.color);
this.updateLabel(params.tabGroup.label);
this.updateCollapsed(params.tabGroup.collapsed);
this.addDisposables(params.tabGroup.onDidChange(() => {
if (this._tabGroup) {
this.updateColor(this._tabGroup.color);
this.updateLabel(this._tabGroup.label);
}
}), params.tabGroup.onDidCollapseChange((collapsed) => {
this.updateCollapsed(collapsed);
}), this._onClick.event(() => {
var _this$_tabGroup;
(_this$_tabGroup = this._tabGroup) === null || _this$_tabGroup === void 0 || _this$_tabGroup.toggle();
}));
}
update(params) {
this._tabGroup = params.tabGroup;
this.updateColor(params.tabGroup.color);
this.updateLabel(params.tabGroup.label);
this.updateCollapsed(params.tabGroup.collapsed);
}
updateColor(color) {
var _this$_palette;
applyTabGroupAccent(this._element, color, this._palette);
toggleClass(this._element, "dv-tab-group-chip--accent-off", ((_this$_palette = this._palette) === null || _this$_palette === void 0 ? void 0 : _this$_palette.enabled) === false);
}
updateLabel(label) {
this._label.textContent = label;
toggleClass(this._label, "dv-tab-group-chip-label--empty", !label);
}
updateCollapsed(collapsed) {
toggleClass(this._element, "dv-tab-group-chip--collapsed", collapsed);
}
};
/**
* Class for the small accent pip drawn at the leading edge of each wrapped
* row a tab group spans beyond its first. Echoes the group chip so a
* multi-row group reads as one group on every row it occupies.
*/
const TAB_GROUP_CHIP_CONTINUATION_CLASS = "dv-tab-group-chip-continuation";
/** Diameter (px) of a continuation pip; mirrors the CSS size. */
const CONTINUATION_PIP_SIZE = 8;
/**
* Shared positioning logic for tab group indicators.
* Subclasses implement `applyShape` to control the visual output.
*/
var BaseTabGroupIndicator = class {
get underlines() {
return this._underlines;
}
constructor(_ctx) {
this._ctx = _ctx;
this._underlines = /* @__PURE__ */ new Map();
this._continuationMarkers = /* @__PURE__ */ new Map();
this._rafId = null;
}
positionUnderlines() {
requestAnimationFrame(() => {
this._positionUnderlinesSync();
});
}
/**
* Continuously reposition underlines every frame for the duration
* of a tab transition (~200ms), so the underline tracks tab sizes.
*/
trackUnderlines() {
if (this._rafId !== null) cancelAnimationFrame(this._rafId);
const start = performance.now();
const duration = 250;
const tick = () => {
this._positionUnderlinesSync();
if (performance.now() - start < duration) this._rafId = requestAnimationFrame(tick);
else this._rafId = null;
};
this._rafId = requestAnimationFrame(tick);
}
syncUnderlineElements(activeGroupIds) {
for (const groupId of activeGroupIds) if (!this._underlines.has(groupId)) {
const underline = document.createElement("div");
underline.className = "dv-tab-group-underline";
this._ctx.tabsList.appendChild(underline);
this._underlines.set(groupId, underline);
}
for (const [groupId, el] of this._underlines) if (!activeGroupIds.has(groupId)) {
el.remove();
this._underlines.delete(groupId);
this._clearContinuationMarkers(groupId);
}
}
getUnderline(groupId) {
return this._underlines.get(groupId);
}
dispose() {
if (this._rafId !== null) {
cancelAnimationFrame(this._rafId);
this._rafId = null;
}
for (const [, el] of this._underlines) el.remove();
this._underlines.clear();
this._clearContinuationMarkers();
}
/**
* Grow/shrink a group's continuation-marker pool to `count` pips,
* creating/removing DOM nodes as needed, and return the pool.
*/
_syncContinuationMarkers(groupId, count) {
let pool = this._continuationMarkers.get(groupId);
if (!pool) {
pool = [];
this._continuationMarkers.set(groupId, pool);
}
while (pool.length < count) {
const marker = document.createElement("div");
marker.className = TAB_GROUP_CHIP_CONTINUATION_CLASS;
this._ctx.tabsList.appendChild(marker);
pool.push(marker);
}
while (pool.length > count) {
const marker = pool.pop();
marker === null || marker === void 0 || marker.remove();
}
return pool;
}
/**
* Remove continuation markers for a single group, or (when `groupId`
* is omitted) for every group. Used when a group dissolves, stops
* wrapping, or the indicator is disposed.
*/
_clearContinuationMarkers(groupId) {
if (groupId === void 0) {
for (const [, pool] of this._continuationMarkers) for (const marker of pool) marker.remove();
this._continuationMarkers.clear();
return;
}
const pool = this._continuationMarkers.get(groupId);
if (pool) {
for (const marker of pool) marker.remove();
this._continuationMarkers.delete(groupId);
}
}
_positionUnderlinesSync() {
const containerRect = this._ctx.tabsList.getBoundingClientRect();
const tabGroups = this._ctx.getTabGroups();
const isVertical = this._ctx.getDirection() === "vertical";
const wrapped = this._ctx.tabsList.classList.contains(OVERFLOW_WRAP_TABS_CLASS);
if (!wrapped) this._clearContinuationMarkers();
const containerCrossSize = isVertical ? containerRect.width : containerRect.height;
const activePanelId = this._ctx.getActivePanelId();
const tabMap = this._ctx.getTabMap();
for (const tg of tabGroups) {
const underline = this._underlines.get(tg.id);
if (!underline) continue;
const panelIds = tg.panelIds;
if (panelIds.length === 0) {
underline.style.display = "none";
continue;
}
underline.style.display = "";
if (wrapped) {
this._positionWrappedUnderline(underline, tg, containerRect, tabMap, isVertical);
continue;
}
const chipEl = this._ctx.getChipElement(tg.id);
let startEdge;
if (chipEl) {
const chipRect = chipEl.getBoundingClientRect();
const chipStyle = getComputedStyle(chipEl);
const leadingMargin = isVertical ? Number.parseFloat(chipStyle.marginTop) || 0 : Number.parseFloat(chipStyle.marginLeft) || 0;
startEdge = isVertical ? chipRect.top - containerRect.top - leadingMargin : chipRect.left - containerRect.left - leadingMargin;
} else {
const firstPanelId = panelIds[0];
const firstTabEntry = tabMap.get(firstPanelId);
if (firstTabEntry) {
const firstRect = firstTabEntry.value.element.getBoundingClientRect();
startEdge = isVertical ? firstRect.top - containerRect.top : firstRect.left - containerRect.left;
} else startEdge = 0;
}
const lastPanelId = panelIds[panelIds.length - 1];
const lastTabEntry = tabMap.get(lastPanelId);
if (!lastTabEntry) {
if (isVertical) {
underline.style.top = `${startEdge}px`;
underline.style.height = "0px";
underline.style.left = "";
underline.style.width = "";
} else {
underline.style.left = `${startEdge}px`;
underline.style.width = "0px";
underline.style.top = "";
underline.style.height = "";
}
continue;
}
const lastTabRect = lastTabEntry.value.element.getBoundingClientRect();
let endEdge = isVertical ? lastTabRect.bottom - containerRect.top : lastTabRect.right - containerRect.left;
let span = endEdge - startEdge;
if ((tg.collapsed || tg.panelIds.some((pid) => {
const te = tabMap.get(pid);
return te === null || te === void 0 ? void 0 : te.value.element.classList.contains("dv-tab--group-expanding");
})) && chipEl) {
const chipRect = chipEl.getBoundingClientRect();
const chipCenter = isVertical ? chipRect.top + chipRect.height / 2 - containerRect.top : chipRect.left + chipRect.width / 2 - containerRect.left;
let currentTabSize = 0;
let fullTabSize = 0;
for (const pid of tg.panelIds) {
const te = tabMap.get(pid);
if (!te) continue;
const el = te.value.element;
if (isVertical) {
currentTabSize += el.getBoundingClientRect().height;
fullTabSize += el.scrollHeight;
} else {
currentTabSize += el.getBoundingClientRect().width;
fullTabSize += el.scrollWidth;
}
}
const progress = fullTabSize > 0 ? Math.min(1, currentTabSize / fullTabSize) : 0;
startEdge = chipCenter + (startEdge - chipCenter) * progress;
endEdge = chipCenter + (endEdge - chipCenter) * progress;
span = Math.max(0, endEdge - startEdge);
}
if (isVertical) {
underline.style.top = `${startEdge}px`;
underline.style.height = `${Math.max(0, span)}px`;
underline.style.left = "";
underline.style.width = "";
underline.style.bottom = "";
} else {
underline.style.left = `${startEdge}px`;
underline.style.width = `${Math.max(0, span)}px`;
underline.style.top = "";
underline.style.height = "";
underline.style.bottom = "";
}
this.applyShape(underline, tg, startEdge, span, containerCrossSize, activePanelId, containerRect, isVertical);
}
}
/**
* Position a group's underline across a multi-line (wrapped) tab strip. The
* single-bar model can't span lines, so the element is sized to cover the
* group's line span and an SVG draws one straight segment per line-run of
* the group's tabs: a horizontal segment per row (horizontal header) or a
* vertical segment per column (vertical header). Tabs are bucketed into
* runs by their cross-axis offset: `top` for rows, `left` for columns.
* (The active-tab wrap-around bump is omitted in wrap; the per-line lines
* still convey membership.)
*/
_positionWrappedUnderline(underline, tg, containerRect, tabMap, isVertical) {
const t = 2;
const color = resolveTabGroupAccent(tg.color, this._ctx.getColorPalette());
if (color === void 0) {
underline.style.display = "none";
this._clearContinuationMarkers(tg.id);
return;
}
const { runs, firstRun } = this._computeWrappedRuns(tg, containerRect, tabMap, isVertical);
if (runs.length === 0) {
underline.style.display = "none";
this._clearContinuationMarkers(tg.id);
return;
}
this._positionContinuationMarkers(tg.id, runs, firstRun, color, isVertical);
const minLeft = Math.min(...runs.map((r) => r.left));
const maxRight = Math.max(...runs.map((r) => r.right));
const minTop = Math.min(...runs.map((r) => r.top));
const maxBottom = Math.max(...runs.map((r) => r.bottom));
const width = isVertical ? Math.max(0, maxRight - minLeft) : containerRect.width;
const height = isVertical ? containerRect.height : Math.max(0, maxBottom - minTop);
underline.style.left = isVertical ? `${minLeft}px` : "0px";
underline.style.top = isVertical ? "0px" : `${minTop}px`;
underline.style.bottom = "auto";
underline.style.width = `${width}px`;
underline.style.height = `${height}px`;
underline.style.backgroundColor = "";
const { svg, path } = this.ensureSvgPath(underline);
svg.setAttribute("width", String(width));
svg.setAttribute("height", String(height));
path.setAttribute("stroke", color);
path.setAttribute("stroke-width", String(t));
const headerPosition = this._ctx.getHeaderPosition();
const inverted = isVertical ? headerPosition === "right" : headerPosition === "bottom";
path.setAttribute("d", this._wrappedPathData(runs, isVertical, inverted, minLeft, minTop, t));
}
/**
* Bucket a group's visible tabs into line-runs by their cross-axis offset
* (rows by `top`, columns by `left`), with a 2px sub-pixel tolerance.
* `firstRun` is the run holding the group's first tab (the chip's line),
* tracked by reference so it is correct regardless of axis or header side (a
* `vertical-rl` first column is right-most, not left-most).
*/
_computeWrappedRuns(tg, containerRect, tabMap, isVertical) {
const runs = [];
let firstRun;
for (const pid of tg.panelIds) {
var _firstRun;
const te = tabMap.get(pid);
if (!te) continue;
const r = te.value.element.getBoundingClientRect();
if (r.width === 0 && r.height === 0) continue;
const top = r.top - containerRect.top;
const bottom = r.bottom - containerRect.top;
const left = r.left - containerRect.left;
const right = r.right - containerRect.left;
const key = isVertical ? left : top;
let run = runs.find((x) => Math.abs((isVertical ? x.left : x.top) - key) <= 2);
if (run) {
run.top = Math.min(run.top, top);
run.bottom = Math.max(run.bottom, bottom);
run.left = Math.min(run.left, left);
run.right = Math.max(run.right, right);
} else {
run = {
top,
bottom,
left,
right
};
runs.push(run);
}
(_firstRun = firstRun) !== null && _firstRun !== void 0 || (firstRun = run);
}
return {
runs,
firstRun
};
}
/**
* Build the SVG `path` data for a group's per-line underline segments: a
* horizontal segment along each row's edge (`svg-y` offset by `minTop`) or a
* vertical segment down each column's leading edge (`svg-x` offset by
* `minLeft`). `inverted` moves the line to the header-facing edge.
*/
_wrappedPathData(runs, isVertical, inverted, minLeft, minTop, t) {
let d = "";
for (const run of runs) if (isVertical) {
const x = inverted ? run.right - minLeft - t / 2 : run.left - minLeft + t / 2;
d += `M ${x},${run.top} L ${x},${run.bottom} `;
} else {
const y = inverted ? run.top - minTop + t / 2 : run.bottom - minTop - t / 2;
d += `M ${run.left},${y} L ${run.right},${y} `;
}
return d.trim();
}
/**
* Draw a small accent pip at the leading edge of every wrapped line-run a
* group spans beyond its first. The group's real chip already marks the
* first run; these lightweight markers echo it on the continuation runs so
* a group that wraps onto rows/columns 2–3 reads as one group on each line
* instead of losing its colour after the first.
*
* `runs` are the group's line-runs (container-relative geometry) as
* bucketed by {@link _positionWrappedUnderline}; `firstRun` is the run that
* holds the chip and is skipped. The pip sits at each continuation run's
* main-axis start (top of a column, left of a row), centred on the cross
* axis.
*/
_positionContinuationMarkers(groupId, runs, firstRun, color, isVertical) {
const continuationRuns = runs.filter((run) => run !== firstRun);
const markers = this._syncContinuationMarkers(groupId, continuationRuns.length);
continuationRuns.forEach((run, i) => {
const marker = markers[i];
marker.style.backgroundColor = color;
if (isVertical) {
const center = (run.left + run.right) / 2;
marker.style.left = `${center - CONTINUATION_PIP_SIZE / 2}px`;
marker.style.top = `${run.top}px`;
} else {
const center = (run.top + run.bottom) / 2;
marker.style.left = `${run.left}px`;
marker.style.top = `${center - CONTINUATION_PIP_SIZE / 2}px`;
}
});
}
/**
* Ensure the underline element holds a single reusable `<svg><path/></svg>`
* (created once, reused across frames) and return them.
*/
ensureSvgPath(underline) {
const existing = underline.firstElementChild;
if ((existing === null || existing === void 0 ? void 0 : existing.tagName) === "svg") return {
svg: existing,
path: existing.firstElementChild
};
underline.replaceChildren();
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.style.display = "block";
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
path.setAttribute("fill", "none");
svg.appendChild(path);
underline.appendChild(svg);
return {
svg,
path
};
}
};
/**
* Chrome-style wrap-around indicator using SVG paths.
*/
var WrapTabGroupIndicator = class extends BaseTabGroupIndicator {
_applyStraightLine(svg, path, underline, mainSize, crossSize, near, isVertical) {
const svgW = isVertical ? crossSize : mainSize;
const svgH = isVertical ? mainSize : crossSize;
svg.setAttribute("width", String(svgW));
svg.setAttribute("height", String(svgH));
underline.style.width = `${svgW}px`;
underline.style.height = `${svgH}px`;
path.setAttribute("d", isVertical ? `M ${near},0 L ${near},${mainSize}` : `M 0,${near} L ${mainSize},${near}`);
}
/**
* Chrome-style wrap-around underline: a stroked SVG path that runs
* along the bottom (or left edge in vertical mode), curving up and
* over the active tab with rounded corners.
*
* The SVG and path elements are created once per underline and reused;
* only the `d`, `stroke`, and viewport attributes are updated each frame.
*/
applyShape(underline, tg, groupStart, groupSpan, containerCrossSize, activePanelId, containerRect, isVertical) {
const t = 2;
const crossSize = containerCrossSize;
const mainSize = groupSpan;
const color = resolveTabGroupAccent(tg.color, this._ctx.getColorPalette());
if (mainSize <= 0 || crossSize <= 0 || color === void 0) {
underline.style.display = "none";
return;
}
underline.style.display = "";
let activeTabEntry;
if (activePanelId && tg.panelIds.includes(activePanelId)) activeTabEntry = this._ctx.getTabMap().get(activePanelId);
const { svg, path } = this.ensureSvgPath(underline);
path.setAttribute("stroke", color);
path.setAttribute("stroke-width", String(t));
const inset = t;
const headerPosition = this._ctx.getHeaderPosition();
const isBottomHeader = !isVertical && headerPosition === "bottom";
const isRightHeader = isVertical && headerPosition === "right";
let near;
let far;
if (isVertical) {
near = isRightHeader ? crossSize - inset : inset;
far = isRightHeader ? inset : crossSize - inset;
} else {
near = isBottomHeader ? inset : crossSize - inset;
far = isBottomHeader ? crossSize - inset : inset;
}
if (!activeTabEntry) {
this._applyStraightLine(svg, path, underline, mainSize, crossSize, near, isVertical);
return;
}
const activeRect = activeTabEntry.value.element.getBoundingClientRect();
let aStart;
let aEnd;
if (isVertical) {
aStart = Math.max(0, activeRect.top - containerRect.top - groupStart);
aEnd = Math.min(mainSize, activeRect.bottom - containerRect.top - groupStart);
} else {
aStart = Math.max(0, activeRect.left - containerRect.left - groupStart);
aEnd = Math.min(mainSize, activeRect.right - containerRect.left - groupStart);
}
if (aEnd <= aStart) {
this._applyStraightLine(svg, path, underline, mainSize, crossSize, near, isVertical);
return;
}
const r = 6;
const cd = far > near ? 1 : -1;
if (isVertical) {
const svgW = crossSize;
const svgH = mainSize;
svg.setAttribute("width", String(svgW));
svg.setAttribute("height", String(svgH));
underline.style.width = `${svgW}px`;
underline.style.height = `${svgH}px`;
const d = [
`M ${near},0`,
`L ${near},${aStart - r}`,
`Q ${near},${aStart} ${near + cd * r},${aStart}`,
`L ${far - cd * r},${aStart}`,
`Q ${far},${aStart} ${far},${aStart + r}`,
`L ${far},${aEnd - r}`,
`Q ${far},${aEnd} ${far - cd * r},${aEnd}`,
`L ${near + cd * r},${aEnd}`,
`Q ${near},${aEnd} ${near},${aEnd + r}`,
`L ${near},${svgH}`
].join(" ");
path.setAttribute("d", d);
} else {
const svgW = mainSize;
const svgH = crossSize;
svg.setAttribute("width", String(svgW));
svg.setAttribute("height", String(svgH));
underline.style.width = `${svgW}px`;
underline.style.height = `${svgH}px`;
const d = [
`M 0,${near}`,
`L ${aStart - r},${near}`,
`Q ${aStart},${near} ${aStart},${near + cd * r}`,
`L ${aStart},${far - cd * r}`,
`Q ${aStart},${far} ${aStart + r},${far}`,
`L ${aEnd - r},${far}`,
`Q ${aEnd},${far} ${aEnd},${far - cd * r}`,
`L ${aEnd},${near + cd * r}`,
`Q ${aEnd},${near} ${aEnd + r},${near}`,
`L ${svgW},${near}`
].join(" ");
path.setAttribute("d", d);
}
}
};
/**
* Flat continuous bar indicator: a plain colored line spanning the full tab
* group width, with no wrap-around.
*/
var NoneTabGroupIndicator = class extends BaseTabGroupIndicator {
applyShape(underline, tg, _startEdge, span, _containerCrossSize, _activePanelId, _containerRect, isVertical) {
const t = 2;
const color = resolveTabGroupAccent(tg.color, this._ctx.getColorPalette());
if (span <= 0 || color === void 0) {
underline.style.display = "none";
return;
}
underline.style.display = "";
if (underline.firstElementChild) underline.replaceChildren();
underline.style.backgroundColor = color;
if (isVertical) {
underline.style.width = `${t}px`;
underline.style.height = `${span}px`;
} else {
underline.style.width = `${span}px`;
underline.style.height = `${t}px`;
}
}
};
const EMPTY_MAP = /* @__PURE__ */ new Map();
var TabGroupManager = class {
get chipRenderers() {
return this._chipRenderers;
}
get groupUnderlines() {
var _this$_indicator$unde, _this$_indicator;
return (_this$_indicator$unde = (_this$_indicator = this._indicator) === null || _this$_indicator === void 0 ? void 0 : _this$_indicator.underlines) !== null && _this$_indicator$unde !== void 0 ? _this$_indicator$unde : EMPTY_MAP;
}
get skipNextCollapseAnimation() {
return this._skipNextCollapseAnimation;
}
set skipNextCollapseAnimation(value) {
this._skipNextCollapseAnimation = value;
}
constructor(_ctx, _callbacks) {
this._ctx = _ctx;
this._callbacks = _callbacks;
this._chipRenderers = /* @__PURE__ */ new Map();
this._indicator = null;
this._skipNextCollapseAnimation = false;
this._pendingTransitionCleanups = /* @__PURE__ */ new Map();
}
/**
* Synchronize chip elements and CSS classes for all tab groups
* in the parent group model. Call after any tab group mutation.
*/
update() {
const tabGroups = this._ctx.group.model.getTabGroups();
const activeGroupIds = /* @__PURE__ */ new Set();
for (const tabGroup of tabGroups) {
activeGroupIds.add(tabGroup.id);
this._ensureChipForGroup(tabGroup);
this._positionChipForGroup(tabGroup);
}
for (const [groupId, entry] of this._chipRenderers) if (!activeGroupIds.has(groupId)) {
var _entry$dragSourcesDis;
entry.chip.element.remove();
entry.chip.dispose();
(_entry$dragSourcesDis = entry.dragSourcesDisposable) === null || _entry$dragSourcesDis === void 0 || _entry$dragSourcesDis.dispose();
entry.disposable.dispose();
this._chipRenderers.delete(groupId);
}
this._updateTabGroupClasses();
}
/**
* Re-read the active palette and re-apply colors to chips, tabs and
* the indicator. Called when `tabGroupColors` / `tabGroupAccent`
* options change at runtime.
*/
refreshAccents() {
for (const tabGroup of this._ctx.group.model.getTabGroups()) {
var _entry$chip, _entry$chip$update;
const entry = this._chipRenderers.get(tabGroup.id);
entry === null || entry === void 0 || (_entry$chip$update = (_entry$chip = entry.chip).update) === null || _entry$chip$update === void 0 || _entry$chip$update.call(_entry$chip, { tabGroup });
}
this._updateTabGroupClasses();
}
positionAllChips() {
if (this._chipRenderers.size === 0) return;
for (const tabGroup of this._ctx.group.model.getTabGroups()) this._positionChipForGroup(tabGroup);
}
updateDirection() {
const isVertical = this._ctx.getDirection() === "vertical";
for (const [, entry] of this._chipRenderers) entry.dropTarget.setTargetZones(isVertical ? ["top"] : ["left"]);
}
snapshotChipWidths() {
const widths = /* @__PURE__ */ new Map();
for (const [groupId, entry] of this._chipRenderers) widths.set(groupId, entry.chip.element.getBoundingClientRect().width);
return widths;
}
positionUnderlines() {
var _this$_indicator2;
(_this$_indicator2 = this._indicator) === null || _this$_indicator2 === void 0 || _this$_indicator2.positionUnderlines();
}
trackUnderlines() {
var _this$_indicator3;
(_this$_indicator3 = this._indicator) === null || _this$_indicator3 === void 0 || _this$_indicator3.trackUnderlines();
}
setGroupDragImage(event, tabGroup, chipEl) {
if (!event.dataTransfer) return;
const isVertical = this._ctx.getDirection() === "vertical";
const clone = this._ctx.tabsList.cloneNode(true);
if (isVertical) {
clone.classList.remove("dv-tabs-container-vertical", "dv-vertical");
clone.classList.add("dv-horizontal");
clone.style.writingMode = "horizontal-tb";
clone.style.height = `${this._ctx.tabsList.offsetWidth}px`;
} else clone.style.height = `${this._ctx.tabsList.offsetHeight}px`;
clone.style.width = "auto";
clone.style.overflow = "visible";
clone.style.pointerEvents = "none";
const children = Array.from(clone.children);
const realChildren = Array.from(this._ctx.tabsList.children);
for (let i = children.length - 1; i >= 0; i--) {
if (realChildren[i] === chipEl) continue;
children[i].remove();
}
const wrapper = document.createElement("div");
wrapper.className = "dv-groupview dv-active-group";
wrapper.style.position = "fixed";
wrapper.style.top = "-10000px";
wrapper.style.left = "0px";
wrapper.style.height = "auto";
wrapper.style.width = "auto";
wrapper.style.pointerEvents = "none";
const actionsWrapper = document.createElement("div");
actionsWrapper.className = "dv-tabs-and-actions-container";
actionsWrapper.style.height = "auto";
actionsWrapper.style.width = "auto";
wrapper.appendChild(actionsWrapper);
actionsWrapper.appendChild(clone);
this._ctx.accessor.element.appendChild(wrapper);
const clonedChip = clone.querySelector(".dv-tab-group-chip");
const chipRect = chipEl.getBoundingClientRect();
const cursorInChipX = event.clientX - chipRect.left;
const cursorInChipY = event.clientY - chipRect.top;
if (clonedChip) {
const clonedChipRect = clonedChip.getBoundingClientRect();
const wrapperRect = wrapper.getBoundingClientRect();
const offsetX = clonedChipRect.left - wrapperRect.left + cursorInChipX;
const offsetY = clonedChipRect.top - wrapperRect.top + cursorInChipY;
event.dataTransfer.setDragImage(wrapper, offsetX, offsetY);
} else event.dataTransfer.setDragImage(wrapper, cursorInChipX, cursorInChipY);
requestAnimationFrame(() => {
wrapper.remove();
});
}
cleanupTransition(panelId) {
var _this$_pendingTransit;
(_this$_pendingTransit = this._pendingTransitionCleanups.get(panelId)) === null || _this$_pendingTransit === void 0 || _this$_pendingTransit();
this._pendingTransitionCleanups.delete(panelId);
}
updateDragAndDropState() {
const caps = resolveDndCapabilities(this._ctx.accessor.options);
for (const entry of this._chipRenderers.values()) {
var _entry$html5DragSourc, _entry$pointerDragSou, _entry$pointerDragSou2;
entry.chip.element.draggable = caps.html5;
(_entry$html5DragSourc = entry.html5DragSource) === null || _entry$html5DragSourc === void 0 || _entry$html5DragSourc.setDisabled(!caps.html5);
(_entry$pointerDragSou = entry.pointerDragSource) === null || _entry$pointerDragSou === void 0 || _entry$pointerDragSou.setDisabled(!caps.pointer);
(_entry$pointerDragSou2 = entry.pointerDragSource) === null || _entry$pointerDragSou2 === void 0 || _entry$pointerDragSou2.setTouchOnly(!caps.pointerHandlesMouse);
}
}
/**
* Synchronously dispose the chip drag sources for an in-flight chip
* drag. Called from `_commitGroupMove` so the transfer payload +
* iframe shield are released before the cross-group move detaches
* the chip (chip dispose is scheduled on a microtask via
* `_scheduleTabGroupUpdate`, which is too late for callers that read
* `getPanelData()` synchronously after the move).
*
* A cross-group move dissolves the source chip entirely, but a
* within-group reorder keeps the same chip element — so the sources are
* cleared to `undefined` here and the subsequent `update()` re-arms them
* via `_ensureChipForGroup`. Without that re-arm the chip would fire a
* native `dragstart` (the element keeps `draggable=true`) but never set
* a transfer payload, leaving the group permanently undraggable after
* its first move. (#1410)
*/
disposeChipDrag(tabGroupId) {
var _entry$dragSourcesDis2;
const entry = this._chipRenderers.get(tabGroupId);
if (!entry) return;
(_entry$dragSourcesDis2 = entry.dragSourcesDisposable) === null || _entry$dragSourcesDis2 === void 0 || _entry$dragSourcesDis2.dispose();
entry.html5DragSource = void 0;
entry.pointerDragSource = void 0;
entry.dragSourcesDisposable = void 0;
}
/** Cloned chip rect used as the pointer follow-finger ghost. */
_buildChipGhostElement(chipEl) {
const style = getComputedStyle(chipEl);
const clone = chipEl.cloneNode(true);
Array.from(style).forEach((key) => {
clone.style.setProperty(key, style.getPropertyValue(key), style.getPropertyPriority(key));
});
clone.style.position = "absolute";
return clone;
}
disposeAll() {
var _this$_indicator4;
(_this$_indicator4 = this._indicator) === null || _this$_indicator4 === void 0 || _this$_indicator4.dispose();
this._indicator = null;
for (const [, cleanup] of this._pendingTransitionCleanups) cleanup();
this._pendingTransitionCleanups.clear();
for (const [, entry] of this._chipRenderers) {
var _entry$dragSourcesDis3;
entry.chip.element.remove();
entry.chip.dispose();
(_entry$dragSourcesDis3 = entry.dragSourcesDisposable) === null || _entry$dragSourcesDis3 === void 0 || _entry$dragSourcesDis3.dispose();
entry.disposable.dispose();
}
this._chipRenderers.clear();
}
_ensureIndicator() {
var _this$_ctx$accessor$o, _this$_ctx$accessor$o2, _this$_indicator5;
const Ctor = ((_this$_ctx$accessor$o = (_this$_ctx$accessor$o2 = this._ctx.accessor.options.theme) === null || _this$_ctx$accessor$o2 === void 0 ? void 0 : _this$_ctx$accessor$o2.tabGroupIndicator) !== null && _this$_ctx$accessor$o !== void 0 ? _this$_ctx$accessor$o : "wrap") === "none" ? NoneTabGroupIndicator : WrapTabGroupIndicator;
if (this._indicator && !(this._indicator instanceof Ctor)) {
this._indicator.dispose();
this._indicator = null;
}
(_this$_indicator5 = this._indicator) !== null && _this$_indicator5 !== void 0 || (this._indicator = new Ctor({
tabsList: this._ctx.tabsList,
getTabGroups: () => this._ctx.group.model.getTabGroups(),
getActivePanelId: () => {
var _this$_ctx$group$acti;
return (_this$_ctx$group$acti = this._ctx.group.activePanel) === null || _this$_ctx$group$acti === void 0 ? void 0 : _this$_ctx$group$acti.id;
},
getTabMap: () => this._ctx.getTabMap(),
getChipElement: (id) => {
var _this$_chipRenderers$;
return (_this$_chipRenderers$ = this._chipRenderers.get(id)) === null || _this$_chipRenderers$ === void 0 ? void 0 : _this$_chipRenderers$.chip.element;
},
getDirection: () => this._ctx.getDirection(),
getHeaderPosition: () => this._ctx.group.model.headerPosition,
getColorPalette: () => this._ctx.accessor.tabGroupColorPalette
}));
}
/**
* Create the HTML5 + pointer drag sources for a chip (and the direct
* dragend listener that clears the transfer synchronously). Extracted so
* both the initial chip build and the post-move re-arm in
* `_ensureChipForGroup` share one wiring path. (#1410)
*/
_createChipDragSources(chip, tabGroup) {
const caps = resolveDndCapabilities(this._ctx.accessor.options);
chip.element.draggable = caps.html5;
const panelTransfer = LocalSelectionTransfer.getInstance();
const getData = () => {
panelTransfer.setData([new PanelTransfer(this._ctx.accessor.id, this._ctx.group.id, null, tabGroup.id)], PanelTransfer.prototype);
return { dispose: () => {
panelTransfer.clearData(PanelTransfer.prototype);
} };
};
const html5DragSource = html5Backend.createDragSource(chip.element, {
getData,
disabled: !caps.html5,
isCancelled: () => !resolveDndCapabilities(this._ctx.accessor.options).html5,
onDragStart: (event) => {
if ("dataTransfer" in event && event.dataTransfer) this.setGroupDragImage(event, tabGroup, chip.element);
this._callbacks.onChipDragStart(tabGroup, chip, event);
},
onDragEnd: (event) => {
var _this$_callbacks$onCh, _this$_callbacks;
(_this$_callbacks$onCh = (_this$_callbacks = this._callbacks).onChipDragEnd) === null || _this$_callbacks$onCh === void 0 || _this$_callbacks$onCh.call(_this$_callbacks, tabGroup, chip, event);
}
});
const onDragEndClear = () => {
panelTransfer.clearData(PanelTransfer.prototype);
};
chip.element.addEventListener("dragend", onDragEndClear, { once: true });
const pointerDragSource = pointerBackend.createDragSource(chip.element, {
getData,
disabled: !caps.pointer,
touchOnly: !caps.pointerHandlesMouse,
isCancelled: () => !resolveDndCapabilities(this._ctx.accessor.options).pointer,
createGhost: () => ({
element: this._buildChipGhostElement(chip.element),
offsetX: 8,
offsetY: 8
}),
onDragStart: (event) => {
this._callbacks.onChipDragStart(tabGroup, chip, event);
}
});
return {
html5DragSource,
pointerDragSource,
disposable: { dispose: () => {
html5DragSource.dispose();
pointerDragSource.dispose();
chip.element.removeEventListener("dragend", onDragEndClear);
} }
};
}
_ensureChipForGroup(tabGroup) {
const existing = this._chipRenderers.get(tabGroup.id);
if (existing) {
if (!existing.html5DragSource) {
const sources = this._createChipDragSources(existing.chip, tabGroup);
existing.html5DragSource = sources.html5DragSource;
existing.pointerDragSource = sources.pointerDragSource;
existing.dragSourcesDisposable = sources.disposable;
}
return;
}
const createChip = this._ctx.accessor.options.createTabGroupChipComponent;
const chip = createChip ? createChip(tabGroup) : new TabGroupChip(this._ctx.accessor.tabGroupColorPalette);
chip.init({
tabGroup,
api: this._ctx.accessor.api
});
const dragSources = this._createChipDragSources(chip, tabGroup);
const disposables = [
tabGroup.onDidChange(() => {
var _chip$update;
(_chip$update = chip.update) === null || _chip$update === void 0 || _chip$update.call(chip, { tabGroup });
this._updateTabGroupClasses();
}),
tabGroup.onDidPanelChange(() => {
this._positionChipForGroup(tabGroup);
this._updateTabGroupClasses();
}),
tabGroup.onDidCollapseChange(() => {
this._updateTabGroupClasses();
})
];
const onContextMenu = (event) => {
var _this$_chipRenderers$2;
(_this$_chipRenderers$2 = this._chipRenderers.get(tabGroup.id)) === null || _this$_chipRenderers$2 === void 0 || (_this$_chipRenderers$2 = _this$_chipRenderers$2.pointerDragSource) === null || _this$_chipRenderers$2 === void 0 || _this$_chipRenderers$2.cancelPending();
this._callbacks.onChipContextMenu(tabGroup, event);
};
if (chip instanceof TabGroupChip) disposables.push(chip.onContextMenu(onContextMenu));
else disposables.push(new LongPressDetector(chip.element, { onLongPress: onContextMenu }), addDisposableListener(chip.element, "contextmenu", onContextMenu));
const isVertical = this._ctx.getDirection() === "vertical";
const dropTarget = new Droptarget(chip.element, {
acceptedTargetZones: isVertical ? ["top"] : ["left"],
overlayModel: { activationSize: {
value: 100,
type: "percentage"
} },
canDisplayOverlay: (event, position) => {
if (this._ctx.group.locked) return false;
if (this._ctx.accessor.options.disableDnd) return false;
const data = getPanelData();
if (this._ctx.accessor.id === (data === null || data === void 0 ? void 0 : data.viewId)) {
var _this$_ctx$accessor$o3;
if (((_this$_ctx$accessor$o3 = this._ctx.accessor.options.theme) === null || _this$_ctx$accessor$o3 === void 0 ? void 0 : _this$_ctx$accessor$o3.tabAnimation) === "smooth") return false;
return true;
}
return this._ctx.group.model.canDisplayOverlay(event, position, "tab");
}
});
disposables.push(dropTarget, dropTarget.onDrop((event) => {
this._callbacks.onChipDrop(tabGroup, event);
}));
const disposable = new CompositeDisposable(...disposables);
this._chipRenderers.set(tabGroup.id, {
chip,
html5DragSource: dragSources.html5DragSource,
pointerDragSource: dragSources.pointerDragSource,
dragSourcesDisposable: dragSources.disposable,
disposable,
dropTarget
});
if (tabGroup.collapsed) this._skipNextCollapseAnimation = true;
}
_positionChipForGroup(tabGroup) {
const entry = this._chipRenderers.get(tabGroup.id);
if (!entry) return;
const chipEl = entry.chip.element;
const panelIds = tabGroup.panelIds;
if (panelIds.length === 0) {
chipEl.remove();
return;
}
const firstPanelId = panelIds[0];
const firstTabEntry = this._ctx.getTabMap().get(firstPanelId);
if (!firstTabEntry) {
chipEl.remove();
return;
}
const firstTabEl = firstTabEntry.value.element;
if (chipEl.nextSibling !== firstTabEl) this._ctx.tabsList.insertBefore(chipEl, firstTabEl);
}
_updateTabGroupClasses() {
const tabGroups = this._ctx.group.model.getTabGroups();
const tabs = this._ctx.getTabs();
const tabMap = this._ctx.getTabMap();
let hasAnimation = false;
const panelGroupMap = /* @__PURE__ */ new Map();
for (const tg of tabGroups) for (const pid of tg.panelIds) panelGroupMap.set(pid, tg);
for (const tabEntry of tabs) {
const tab = tabEntry.value;
const panelId = tab.panel.id;
const tg = panelGroupMap.get(panelId);
const isGrouped = !!tg;
toggleClass(tab.element, "dv-tab--grouped", isGrouped);
if (tg) {
const ids = tg.panelIds;
const isFirst = ids[0] === panelId;
const isLast = ids[ids.length - 1] === panelId;
toggleClass(tab.element, "dv-tab--group-first", isFirst);
toggleClass(tab.element, "dv-tab--group-last", isLast);
applyTabGroupAccent(tab.element, tg.color, this._ctx.accessor.tabGroupColorPalette);
const isCollapsed = tab.element.classList.contains("dv-tab--group-collapsed");
if (!tg.collapsed && isCollapsed) {
var _this$_pendingTransit2;
hasAnimation = true;
tab.element.classList.remove("dv-tab--group-collapsed");
tab.element.classList.add("dv-tab--group-expanding");
(_this$_pendingTransit2 = this._pendingTransitionCleanups.get(panelId)) === null || _this$_pendingTransit2 === void 0 || _this$_pendingTransit2();
const onEnd = () => {
tab.element.classList.remove("dv-tab--group-expanding");
tab.element.style.removeProperty("width");
tab.element.removeEventListener("transitionend", onEnd);
clearTimeout(fallbackTimer);
this._pendingTransitionCleanups.delete(panelId);
};
const fallbackTimer = setTimeout(onEnd, 300);
this._pendingTransitionCleanups.set(panelId, onEnd);
tab.element.addEventListener("transitionend", onEnd);
}
} else {
toggleClass(tab.element, "dv-tab--group-first", false);
toggleClass(tab.element, "dv-tab--group-last", false);
tab.element.classList.remove("dv-tab--group-collapsed", "dv-tab--group-expanding");
tab.element.style.removeProperty("width");
tab.element.style.removeProperty("--dv-tab-group-color");
}
}
const activeGroupIds = /* @__PURE__ */ new Set();
for (const tg of tabGroups) {
activeGroupIds.add(tg.id);
if (tg.collapsed && tg.panelIds.some((pid) => {
const te = tabMap.get(pid);
return te && !te.value.element.classList.contains("dv-tab--group-collapsed");
})) if (this._skipNextCollapseAnimation) {
const affected = [];
for (const pid of tg.panelIds) {
const te = tabMap.get(pid);
if (te) {
te.value.element.style.transition = "none";
te.value.element.classList.add("dv-tab--group-collapsed");
affected.push(te.value.element);
}
}
if (affected.length > 0) {
affected[0].offsetHeight;
for (const el of affected) el.style.removeProperty("transition");
}
} else {
hasAnimation = true;
const isVert = this._ctx.getDirection() === "vertical";
for (const pid of tg.panelIds) {
const te = tabMap.get(pid);
if (te && !te.value.element.classList.contains("dv-tab--group-collapsed")) {
const rect = te.value.element.getBoundingClientRect();
if (isVert) te.value.element.style.height = `${rect.height}px`;
else te.value.element.style.width = `${rect.width}px`;
te.value.element.offsetHeight;
te.value.element.classList.add("dv-tab--group-collapsed");
}
}
}
}
this._skipNextCollapseAnimation = false;
this._ensureIndicator();
if (this._indicator) {
this._indicator.syncUnderlineElements(activeGroupIds);
if (hasAnimation) this._indicator.trackUnderlines();
else this._indicator.positionUnderlines();
}
}
};
/**
* Tab drag-reorder + FLIP animation subsystem extracted from `Tabs`. The DOM
* event wiring remains in `Tabs` and delegates the reorder/animation logic to
* this controller. All access back into `Tabs` goes through
* {@link ITabReorderHost}.
*/
var TabReorderController = class extends CompositeDisposable {
get _tabs() {
return this.host.tabItems;
}
get _tabMap() {
return this.host.tabMap;
}
get _tabsList() {
return this.host.tabsList;
}
get _direction() {
return this.host.direction;
}
get group() {
return this.host.groupPanel;
}
get accessor() {
return this.host.component;
}
get _tabGroupManager() {
return this.host.tabGroupManager;
}
/** Whether the tab strip is in multi-row wrap layout
* (`MultiRowTabsModule`, `overflow.mode: 'wrap'`). In wrap, the 1-D
* gap/FLIP animation is suppressed and reorder uses a 2-D hit-test. */
get _wrapMode() {
return this._tabsList.classList.contains(OVERFLOW_WRAP_TABS_CLASS);
}
get animState() {
return this._animState;
}
set animState(value) {
this._animState = value;
}
get pendingCollapse() {
return this._pendingCollapse;
}
set pendingCollapse(value) {
this._pendingCollapse = value;
}
set voidContainerElement(el) {
this._voidContainer = el;
}
constructor(host) {
super();
this.host = host;
this._animState = null;
this._pendingMarginCleanups = /* @__PURE__ */ new Map();
this._pendingCollapse = false;
this._flipTransitionCleanup = null;
this._voidContainer = null;
this._extendedDropZone = null;
this._pointerInsideTabsList = false;
this._wrapIndicatorEl = null;
this.addDisposables({ dispose: () => {
var _this$_flipTransition;
(_this$_flipTransition = this._flipTransitionCleanup) === null || _this$_flipTransition === void 0 || _this$_flipTransition.call(this);
} });
}
setExtendedDropZone(el) {
this._extendedDropZone = el;
}
/**
* Allows external elements (e.g. void container, left actions) to push an
* insertion index into the animation while the cursor is outside the tabs
* list itself. Pass `null` to clear the indicator.
*/
setExternalInsertionIndex(index) {
if (!this._animState) return;
if (index === this._animState.currentInsertionIndex) return;
this._animState.currentInsertionIndex = index;
this.applyDragOverTransforms();
}
/**
* Called when the drag cursor leaves the entire header area (not just the
* tabs list). Clears animation state for cross-group drags, which never
* receive a `dragend` event on this tab list.
*/
clearExternalAnimState() {
if (!this._animState) return;
this.resetTabTransforms();
if (this._animState.sourceIndex === -1) this._animState = null;
else this._animState.currentInsertionIndex = null;
}
snapshotTabPositions() {
const positions = /* @__PURE__ */ new Map();
for (const tab of this._tabs) positions.set(tab.value.panel.id, tab.value.element.getBoundingClientRect());
return positions;
}
getAverageTabWidth() {
if (this._tabs.length === 0) return 0;
const isVertical = this._direction === "vertical";
let total = 0;
for (const tab of this._tabs) {
const rect = tab.value.element.getBoundingClientRect();
total += isVertical ? rect.height : rect.width;
}
return total / this._tabs.length;
}
/**
* Pointer-event entry point. The HTML5 path enters via the per-element
* `dragover` listener; this one hit-tests the global pointer-drag
* position against the tabs list and routes through the same shared
* `processDragOver` / `processDragLeave` helpers.
*/
handlePointerDragMove(clientX, clientY) {
var _this$_tabsList$owner;
const elAtPoint = ((_this$_tabsList$owner = this._tabsList.ownerDocument) !== null && _this$_tabsList$owner !== void 0 ? _this$_tabsList$owner : document).elementFromPoint(clientX, clientY);
if (!(!!elAtPoint && (this._tabsList.contains(elAtPoint) || !!this._extendedDropZone && this._extendedDropZone.contains(elAtPoint)))) {
if (this._pointerInsideTabsList) {
this._pointerInsideTabsList = false;
this.processDragLeave(elAtPoint);
}
return;
}
this._pointerInsideTabsList = true;
this.processDragOver(clientX, clientY);
}
/**
* Pointer-side cleanup hook: when any pointer drag ends, reset the
* inside-strip flag and tear down any smooth-reorder anim state the
* dragover bridge may have installed.
*/
handlePointerDragEnd(e) {
if (e && this._animState && this._animState.sourceIndex !== -1 && !this._animState.sourceTabGroupId && this._animState.currentInsertionIndex !== null && this.isPointInsideTabsList(e.clientX, e.clientY)) this.commitPointerReorder(e.pointerEvent);
this._pointerInsideTabsList = false;
this.resetDragAnimation();
}
isPointInsideTabsList(clientX, clientY) {
var _this$_tabsList$owner2;
const el = ((_this$_tabsList$owner2 = this._tabsList.ownerDocument) !== null && _this$_tabsList$owner2 !== void 0 ? _this$_tabsList$owner2 : document).elementFromPoint(clientX, clientY);
return !!el && this._tabsList.contains(el);
}
/** Commit a smooth-mode intra-group reorder from the current insertion
* index (adjusted for the source's own position), then clear drag state.
* Used by the pointer backend for both single-row and wrap layouts. */
commitPointerReorder(event) {
const animState = this._animState;
if ((animState === null || animState === void 0 ? void 0 : animState.currentInsertionIndex) == null) return;
const insertionIndex = animState.currentInsertionIndex;
const sourceIndex = animState.sourceIndex;
const adjustedIndex = insertionIndex - (sourceIndex !== -1 && sourceIndex < insertionIndex ? 1 : 0);
this._animState = null;
this.clearWrapDropIndicator();
this.uncollapseSourceTab(animState.sourceTabId);
if (adjustedIndex === sourceIndex) return;
this.host.fireDrop({
event,
index: adjustedIndex,
targetTabGroupId: null
});
}
/**
* Shared body of the dragover entry point. Refreshes stale anim state
* for a changed drag identity, initializes anim state for incoming
* cross-group drags, and dispatches to the gap-following math in
* `handleDragOver`. Returns true when this tabs list has taken
* ownership of the drag; HTML5 callers use this to gate
* `event.preventDefault()`.
*/
processDragOver(clientX, clientY) {
if (this.accessor.options.disableDnd) return false;
this._dropStaleAnimState();
if (!this._animState && !this._initAnimStateForExternalDrag()) return false;
if (this._animState.sourceIndex !== -1) {
var _this$group$model$dro;
(_this$group$model$dro = this.group.model.dropTargetContainer) === null || _this$group$model$dro === void 0 || (_this$group$model$dro = _this$group$model$dro.model) === null || _this$group$model$dro === void 0 || _this$group$model$dro.clear();
}
this.handleDragOver({
clientX,
clientY
});
return true;
}
/**
* If a previous drag's anim state lingers but the current drag is a
* different cross-group chip identity, drop the stale state so the new drag
* starts clean.
*/
_dropStaleAnimState() {
if (!this._animState) return;
const data = getPanelData();
if ((data === null || data === void 0 ? void 0 : data.tabGroupId) && data.groupId !== this.group.id && this._animState.sourceTabGroupId !== data.tabGroupId) this._animState = null;
}
/**
* Create the anim state for an external (cross-group) tab or chip drag.
* Returns `true` when state was created, `false` when this drag should not
* be handled at the tabs-list level.
*/
_initAnimStateForExternalDrag() {
var _this$accessor$option;
const data = getPanelData();
if (((_this$accessor$option = this.accessor.options.theme) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.tabAnimation) === "default" && !(data === null || data === void 0 ? void 0 : data.tabGroupId)) return false;
if (!data || !(data.panelId || data.tabGroupId) || data.groupId === this.group.id) return false;
const avgWidth = this.getAverageTabWidth();
this._animState = data.tabGroupId ? this._makeGroupDragAnimState(data, avgWidth) : this._makeTabDragAnimState(data, avgWidth);
return true;
}
/** Anim state for an external group-chip drag (gap sized to the group). */
_makeGroupDragAnimState(data, avgWidth) {
var _sourceTg$panelIds$le;
const sourceGroup = this.accessor.getPanel(data.groupId);
const sourceTg = sourceGroup === null || sourceGroup === void 0 ? void 0 : sourceGroup.model.getTabGroups().find((tg) => tg.id === data.tabGroupId);
const groupGapWidth = avgWidth * ((_sourceTg$panelIds$le = sourceTg === null || sourceTg === void 0 ? void 0 : sourceTg.panelIds.length) !== null && _sourceTg$panelIds$le !== void 0 ? _sourceTg$panelIds$le : 1) + avgWidth;
return {
sourceTabId: "",
sourceIndex: -1,
tabPositions: this.snapshotTabPositions(),
chipPositions: this._tabGroupManager.snapshotChipWidths(),
currentInsertionIndex: null,
targetTabGroupId: null,
sourceTabGroupId: data.tabGroupId,
sourceGroupPanelIds: sourceTg ? new Set(sourceTg.panelIds) : /* @__PURE__ */ new Set(),
sourceChipWidth: avgWidth,
cursorOffsetFromDragLeft: groupGapWidth / 2,
sourceGapWidth: groupGapWidth,
containerLeft: this._tabsList.getBoundingClientRect().left
};
}
/** Anim state for an external single-tab drag. */
_makeTabDragAnimState(data, avgWidth) {
return {
sourceTabId: data.panelId,
sourceIndex: -1,
tabPositions: this.snapshotTabPositions(),
chipPositions: this._tabGroupManager.snapshotChipWidths(),
currentInsertionIndex: null,
targetTabGroupId: null,
sourceTabGroupId: null,
sourceGroupPanelIds: null,
sourceChipWidth: 0,
cursorOffsetFromDragLeft: avgWidth / 2,
sourceGapWidth: avgWidth,
containerLeft: this._tabsList.getBoundingClientRect().left
};
}
/**
* Shared body of the dragleave entry point. Preserves anim state when
* the drag moves between tabs-list children, into the extended drop
* zone, or into the void container; tears it down otherwise.
*/
processDragLeave(related) {
var _this$_extendedDropZo;
if (!this._animState) return;
if (related && this._tabsList.contains(related)) return;
if (related && ((_this$_extendedDropZo = this._extendedDropZone) === null || _this$_extendedDropZo === void 0 ? void 0 : _this$_extendedDropZo.contains(related))) {
this.resetTabTransforms();
this._animState.currentInsertionIndex = null;
return;
}
if (this._voidContainer && related && (related === this._voidContainer || this._voidContainer.contains(related))) return;
this.resetTabTransforms();
if (this._animState.sourceIndex === -1) {
var _this$group$model$dro2;
(_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();
this._animState = null;
} else this._animState.currentInsertionIndex = null;
}
handleDragOver(event) {
var _this$accessor$option2;
if (!this._animState) return;
if (this._wrapMode && !this._animState.sourceTabGroupId) {
var _event$clientY;
this.handleWrappedDragOver(event.clientX, (_event$clientY = event.clientY) !== null && _event$clientY !== void 0 ? _event$clientY : 0);
return;
}
const mouseX = event.clientX;
const firstPanelToGroup = this._buildFirstPanelToGroupMap();
let insertionIndex = this._computeDragOverInsertionIndex(mouseX, firstPanelToGroup);
let targetTabGroupId = null;
if (insertionIndex !== null && this._tabGroupManager.chipRenderers.size > 0) {
const resolved = this._resolveInsertionAgainstTabGroups(mouseX, insertionIndex, firstPanelToGroup);
insertionIndex = resolved.insertionIndex;
targetTabGroupId = resolved.targetTabGroupId;
}
if (insertionIndex === this._animState.currentInsertionIndex && targetTabGroupId === this._animState.targetTabGroupId) return;
this._animState.currentInsertionIndex = insertionIndex;
this._animState.targetTabGroupId = targetTabGroupId;
if (((_this$accessor$option2 = this.accessor.options.theme) === null || _this$accessor$option2 === void 0 ? void 0 : _this$accessor$option2.tabAnimation) === "smooth") this.applyDragOverTransforms();
}
/**
* Build a lookup from the first panel ID of each non-source group → group
* ID, so the accumulation walk can add chip widths when it reaches a
* group's first tab.
*/
_buildFirstPanelToGroupMap() {
const firstPanelToGroup = /* @__PURE__ */ new Map();
if (this._tabGroupManager.chipRenderers.size === 0) return firstPanelToGroup;
for (const tg of this.group.model.getTabGroups()) {
if (tg.id === this._animState.sourceTabGroupId) continue;
if (tg.panelIds.length > 0) firstPanelToGroup.set(tg.panelIds[0], tg.id);
}
return firstPanelToGroup;
}
/**
* Walk the tabs left-to-right by their original widths and return the
* insertion slot for the drag image's left edge (null if no slot fits).
*/
_computeDragOverInsertionIndex(mouseX, firstPanelToGroup) {
const animState = this._animState;
const sourceGroupPanelIds = animState.sourceGroupPanelIds;
const availableSpace = mouseX - animState.cursorOffsetFromDragLeft - animState.containerLeft;
let accWidth = 0;
let insertionIndex = null;
for (let i = 0; i < this._tabs.length; i++) {
const tab = this._tabs[i].value;
if (tab.panel.id === animState.sourceTabId) continue;
if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) continue;
const groupId = firstPanelToGroup.get(tab.panel.id);
if (groupId) {
var _animState$chipPositi;
const chipWidth = (_animState$chipPositi = animState.chipPositions.get(groupId)) !== null && _animState$chipPositi !== void 0 ? _animState$chipPositi : 0;
if (accWidth + chipWidth > availableSpace) {
var _insertionIndex;
(_insertionIndex = insertionIndex) !== null && _insertionIndex !== void 0 || (insertionIndex = i);
break;
}
accWidth += chipWidth;
}
const origRect = animState.tabPositions.get(tab.panel.id);
const tabWidth = origRect ? origRect.width : tab.element.getBoundingClientRect().width;
if (accWidth + tabWidth / 2 <= availableSpace) {
accWidth += tabWidth;
insertionIndex = i + 1;
} else {
var _insertionIndex2;
(_insertionIndex2 = insertionIndex) !== null && _insertionIndex2 !== void 0 || (insertionIndex = i);
break;
}
}
return insertionIndex;
}
/**
* Rebuild the accumulated original width up to `insertionIndex`, walking the
* tabs exactly the way the accumulation loop does, so callers know the
* original right edge of the chip (if any) that precedes the insertion slot.
*/
_accumulatedWidthUpTo(insertionIndex, firstPanelToGroup) {
const animState = this._animState;
const sourceGroupPanelIds = animState.sourceGroupPanelIds;
let accUpTo = 0;
for (let i = 0; i < this._tabs.length; i++) {
const tab = this._tabs[i].value;
if (tab.panel.id === animState.sourceTabId) continue;
if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) continue;
if (i >= insertionIndex) break;
const gid = firstPanelToGroup.get(tab.panel.id);
if (gid) {
var _animState$chipPositi2;
accUpTo += (_animState$chipPositi2 = animState.chipPositions.get(gid)) !== null && _animState$chipPositi2 !== void 0 ? _animState$chipPositi2 : 0;
}
const origRect = animState.tabPositions.get(tab.panel.id);
accUpTo += origRect ? origRect.width : tab.element.getBoundingClientRect().width;
}
return accUpTo;
}
/**
* Given a raw insertion index, snap it out of / into any tab group it falls
* within and resolve the target group (if the drop should merge into one).
*/
_resolveInsertionAgainstTabGroups(mouseX, insertionIndex, firstPanelToGroup) {
const isGroupDrag = !!this._animState.sourceTabGroupId;
const accUpTo = this._accumulatedWidthUpTo(insertionIndex, firstPanelToGroup);
for (const tg of this.group.model.getTabGroups()) {
const resolved = this._evaluateTabGroupForInsertion(tg, mouseX, insertionIndex, accUpTo, isGroupDrag);
if (resolved) return resolved;
}
return {
insertionIndex,
targetTabGroupId: null
};
}
/**
* Evaluate a single tab group against the proposed insertion index.
* Returns the resolved `{ insertionIndex, targetTabGroupId }` when this
* group governs the slot (the caller stops iterating), or `null` when the
* group is irrelevant and the caller should keep looking.
*/
_evaluateTabGroupForInsertion(tg, mouseX, insertionIndex, accUpTo, isGroupDrag) {
const range = this._resolveGroupRange(tg);
if (!range) return null;
const { firstIdx, lastIdx } = range;
const isInsideRange = insertionIndex >= firstIdx && insertionIndex <= lastIdx;
const isJustBeforeGroup = !isInsideRange && insertionIndex === firstIdx - 1;
if (!isInsideRange && !isJustBeforeGroup) return null;
if (isGroupDrag) return this._resolveGroupDragTarget(insertionIndex, firstIdx, lastIdx, isInsideRange);
return this._resolveTabDragTarget(tg, mouseX, insertionIndex, accUpTo, firstIdx, isJustBeforeGroup);
}
/**
* The index range this group occupies in the live tab list, excluding the
* source tab / source group tabs (so dragging a tab out of its own group
* doesn't inflate the range). Null when the group has no effective tabs or
* its bounds can't be located.
*/
_resolveGroupRange(tg) {
const animState = this._animState;
const sourceGroupPanelIds = animState.sourceGroupPanelIds;
const effectivePanelIds = tg.panelIds.filter((pid) => pid !== animState.sourceTabId && !(sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(pid)));
if (effectivePanelIds.length === 0) return null;
const firstIdx = this._tabs.findIndex((t) => t.value.panel.id === effectivePanelIds[0]);
const lastIdx = this._tabs.findIndex((t) => t.value.panel.id === effectivePanelIds[effectivePanelIds.length - 1]);
if (firstIdx === -1 || lastIdx === -1) return null;
return {
firstIdx,
lastIdx
};
}
/** Insertion target for a group-chip drag: a group can't be dropped inside
* another group, so snap out of the range; a just-before slot is kept. */
_resolveGroupDragTarget(insertionIndex, firstIdx, lastIdx, isInsideRange) {
if (isInsideRange) return {
insertionIndex: insertionIndex < (firstIdx + lastIdx + 1) / 2 ? firstIdx : lastIdx + 1,
targetTabGroupId: null
};
return {
insertionIndex,
targetTabGroupId: null
};
}
/** Insertion target for a single-tab drag against a group: decide whether
* the drop merges into the group based on the cursor vs the chip edge. */
_resolveTabDragTarget(tg, mouseX, insertionIndex, accUpTo, firstIdx, isJustBeforeGroup) {
var _animState$chipPositi3;
const animState = this._animState;
const chipWidth = (_animState$chipPositi3 = animState.chipPositions.get(tg.id)) !== null && _animState$chipPositi3 !== void 0 ? _animState$chipPositi3 : 0;
if (isJustBeforeGroup) {
if (!this._onlySourceBetween(insertionIndex, firstIdx)) return null;
if (mouseX >= (tg.collapsed ? animState.containerLeft + accUpTo + chipWidth / 2 : animState.containerLeft + accUpTo + chipWidth)) return {
insertionIndex: firstIdx,
targetTabGroupId: tg.id
};
return {
insertionIndex,
targetTabGroupId: null
};
}
const chipOriginalRight = animState.containerLeft + accUpTo + chipWidth;
if (insertionIndex === firstIdx) return {
insertionIndex,
targetTabGroupId: mouseX >= chipOriginalRight ? tg.id : null
};
return {
insertionIndex,
targetTabGroupId: tg.id
};
}
/**
* Whether every tab in the half-open range `[from, to)` is the source tab
* or one of the source group's tabs.
*/
_onlySourceBetween(from, to) {
const animState = this._animState;
for (let j = from; j < to; j++) {
var _animState$sourceGrou;
const pid = this._tabs[j].value.panel.id;
if (pid !== animState.sourceTabId && !((_animState$sourceGrou = animState.sourceGroupPanelIds) === null || _animState$sourceGrou === void 0 ? void 0 : _animState$sourceGrou.has(pid))) return false;
}
return true;
}
/**
* Multi-row wrap drag-over: resolve the 2-D insertion slot and show a
* discrete drop indicator. No gap/FLIP animation (that's 1-D and fights the
* flow layout, so `applyDragOverTransforms`/`runFlipAnimation` no-op in wrap).
*/
handleWrappedDragOver(clientX, clientY) {
if (!this._animState) return;
const index = this.computeWrappedInsertionIndex(clientX, clientY);
if (index === this._animState.currentInsertionIndex && this._animState.targetTabGroupId === null) return;
this._animState.currentInsertionIndex = index;
this._animState.targetTabGroupId = null;
this.updateWrapDropIndicator(index);
}
/**
* The insertion slot (index into the full tab list) for a pointer at
* `(clientX, clientY)` over a wrapped strip. Two axes:
* - horizontal header (rows): pick the row whose vertical span contains
* `clientY`, then the slot within it by x-midpoint;
* - vertical header (columns): pick the column whose horizontal span
* contains `clientX`, then the slot within it by y-midpoint.
* The pointer's cross-axis coordinate selects the line (nearest line if it
* falls in an inter-line gap); its main-axis coordinate selects the slot.
* Excludes the drag source so its own position doesn't bias the result; the
* drop path adjusts for the source offset.
*/
computeWrappedInsertionIndex(clientX, clientY) {
var _lines$find;
const isVertical = this._direction === "vertical";
const entries = this._wrappedDragEntries();
if (entries.length === 0) return this._tabs.length;
const crossPointer = isVertical ? clientX : clientY;
const mainPointer = isVertical ? clientY : clientX;
const crossStart = (r) => isVertical ? r.left : r.top;
const crossEnd = (r) => isVertical ? r.right : r.bottom;
const mainStart = (r) => isVertical ? r.top : r.left;
const mainSize = (r) => isVertical ? r.height : r.width;
const lines = this._bucketWrappedLines(entries, crossStart, crossEnd);
const items = [...((_lines$find = lines.find((l) => crossPointer >= l.start && crossPointer <= l.end)) !== null && _lines$find !== void 0 ? _lines$find : this._nearestWrappedLine(lines, crossPointer)).items].sort((a, b) => mainStart(a.rect) - mainStart(b.rect));
for (const item of items) if (mainPointer < mainStart(item.rect) + mainSize(item.rect) / 2) return item.index;
return items[items.length - 1].index + 1;
}
/**
* The wrapped drop candidates: each non-source tab's list index + live
* rect. The drag source (and any grouped tabs moving with it) are excluded
* so their own positions don't bias the hit-test.
*/
_wrappedDragEntries() {
var _this$_animState, _this$_animState2;
const sourceId = (_this$_animState = this._animState) === null || _this$_animState === void 0 ? void 0 : _this$_animState.sourceTabId;
const sourceGroupIds = (_this$_animState2 = this._animState) === null || _this$_animState2 === void 0 ? void 0 : _this$_animState2.sourceGroupPanelIds;
const entries = [];
for (let i = 0; i < this._tabs.length; i++) {
const tab = this._tabs[i].value;
if (tab.panel.id === sourceId) continue;
if (sourceGroupIds === null || sourceGroupIds === void 0 ? void 0 : sourceGroupIds.has(tab.panel.id)) continue;
entries.push({
index: i,
rect: tab.element.getBoundingClientRect()
});
}
return entries;
}
/**
* Bucket entries into lines by cross-axis start (2px tolerance for sub-pixel
* rounding), sorted along the cross axis.
*/
_bucketWrappedLines(entries, crossStart, crossEnd) {
const lines = [];
for (const entry of entries) {
const s = crossStart(entry.rect);
const line = lines.find((l) => Math.abs(l.start - s) <= 2);
if (line) {
line.items.push(entry);
line.end = Math.max(line.end, crossEnd(entry.rect));
} else lines.push({
start: s,
end: crossEnd(entry.rect),
items: [entry]
});
}
lines.sort((a, b) => a.start - b.start);
return lines;
}
/** The line nearest `crossPointer` by cross-axis distance (gap fallback). */
_nearestWrappedLine(lines, crossPointer) {
const distance = (l) => crossPointer < l.start ? l.start - crossPointer : crossPointer - l.end;
return lines.reduce((nearest, l) => distance(l) < distance(nearest) ? l : nearest, lines[0]);
}
updateWrapDropIndicator(index) {
var _this$_animState3;
this.clearWrapDropIndicator();
const count = this._tabs.length;
if (count === 0) return;
const sourceId = (_this$_animState3 = this._animState) === null || _this$_animState3 === void 0 ? void 0 : _this$_animState3.sourceTabId;
if (index < count) {
const el = this._tabs[index].value.element;
if (this._tabs[index].value.panel.id === sourceId) return;
el.classList.add("dv-tab--reorder-before");
this._wrapIndicatorEl = el;
} else {
const el = this._tabs[count - 1].value.element;
el.classList.add("dv-tab--reorder-after");
this._wrapIndicatorEl = el;
}
}
clearWrapDropIndicator() {
var _this$_wrapIndicatorE;
(_this$_wrapIndicatorE = this._wrapIndicatorEl) === null || _this$_wrapIndicatorE === void 0 || _this$_wrapIndicatorE.classList.remove("dv-tab--reorder-before", "dv-tab--reorder-after");
this._wrapIndicatorEl = null;
}
/**
* Batch-remove a CSS class from multiple elements instantly,
* forcing only a single reflow for the entire batch.
*/
_removeClassInstantlyBatch(elements, cls) {
const affected = [];
for (const el of elements) if (el.classList.contains(cls)) {
el.style.transition = "none";
toggleClass(el, cls, false);
affected.push(el);
}
if (affected.length > 0) {
affected[0].getBoundingClientRect();
for (const el of affected) el.style.removeProperty("transition");
}
}
/**
* Remove `dv-tab--dragging` from the source tab instantly so it
* regains its real width before FLIP snapshots.
*/
uncollapseSourceTab(sourceTabId) {
const entry = this._tabMap.get(sourceTabId);
if (entry) this._removeClassInstantlyBatch([entry.value.element], "dv-tab--dragging");
}
applyDragOverTransforms(skipTransition = false) {
var _this$_animState4;
if (this._wrapMode) return;
if (((_this$_animState4 = this._animState) === null || _this$_animState4 === void 0 ? void 0 : _this$_animState4.currentInsertionIndex) == null) {
this.resetTabTransforms();
return;
}
if (this._pendingCollapse) return;
const insertionIndex = this._animState.currentInsertionIndex;
const gapWidth = this._computeGapWidth();
const chipToShift = this._computeChipToShift(insertionIndex);
this._applyGapMargins(insertionIndex, gapWidth, chipToShift, skipTransition);
}
/** Gap width for the current drag: the whole group's span for a group
* drag, otherwise the source tab's own width. */
_computeGapWidth() {
const animState = this._animState;
if (animState.sourceTabGroupId && animState.sourceGroupPanelIds) return animState.sourceGapWidth;
const sourceRect = animState.tabPositions.get(animState.sourceTabId);
return sourceRect ? sourceRect.width : this.getAverageTabWidth();
}
/**
* When the insertion lands at or before a group's first tab, the chip is
* shifted so the gap appears before the entire group. Returns that chip
* element, or null when no chip should shift.
*
* A chip shifts when dropping outside the group (targetTabGroupId null), or
* when dropping inside a collapsed group (whose tabs are invisible, so
* putting the gap on them has no visual effect).
*/
_computeChipToShift(insertionIndex) {
if (this._tabGroupManager.chipRenderers.size === 0) return null;
for (const tg of this.group.model.getTabGroups()) {
const result = this._chipToShiftForGroup(tg, insertionIndex);
if (result !== void 0) return result;
}
return null;
}
/**
* The chip to shift for a single group, or `undefined` to keep scanning the
* remaining groups. A defined result (the chip element, or null when the
* governing group has no shiftable chip) stops the scan.
*/
_chipToShiftForGroup(tg, insertionIndex) {
const animState = this._animState;
if (tg.id === animState.sourceTabGroupId) return void 0;
if (tg.panelIds.includes(animState.sourceTabId)) return void 0;
const effectivePids = tg.panelIds.filter((pid) => {
var _animState$sourceGrou2;
return pid !== animState.sourceTabId && !((_animState$sourceGrou2 = animState.sourceGroupPanelIds) === null || _animState$sourceGrou2 === void 0 ? void 0 : _animState$sourceGrou2.has(pid));
});
if (effectivePids.length === 0) return void 0;
const firstIdx = this._tabs.findIndex((t) => t.value.panel.id === effectivePids[0]);
if (!(!animState.targetTabGroupId || animState.targetTabGroupId === tg.id && tg.collapsed)) return void 0;
if (firstIdx < insertionIndex) return void 0;
if (this._onlySourceBetween(insertionIndex, firstIdx)) {
const chipEntry = this._tabGroupManager.chipRenderers.get(tg.id);
if (chipEntry) return chipEntry.chip.element;
}
return null;
}
/** Reset non-source chip margins, then apply the gap margin to the chip or
* the first tab at/after the insertion index. */
_applyGapMargins(insertionIndex, gapWidth, chipToShift, skipTransition) {
const animState = this._animState;
const sourceGroupPanelIds = animState.sourceGroupPanelIds;
let gapApplied = false;
for (const [groupId, entry] of this._tabGroupManager.chipRenderers) {
if (groupId === animState.sourceTabGroupId) continue;
this._clearMargin(entry.chip.element, skipTransition);
}
if (chipToShift) {
this._setMargin(chipToShift, `${gapWidth}px`, skipTransition);
gapApplied = true;
}
for (let i = 0; i < this._tabs.length; i++) {
const tab = this._tabs[i].value;
if (tab.panel.id === animState.sourceTabId) continue;
if (sourceGroupPanelIds === null || sourceGroupPanelIds === void 0 ? void 0 : sourceGroupPanelIds.has(tab.panel.id)) continue;
if (!gapApplied && i >= insertionIndex) {
this._setMargin(tab.element, `${gapWidth}px`, skipTransition);
gapApplied = true;
} else this._clearMargin(tab.element, skipTransition);
}
this._tabGroupManager.trackUnderlines();
}
/** Pick the correct shifting class for tabs vs chips. */
_shiftingClass(el) {
return el.classList.contains("dv-tab-group-chip") ? "dv-tab-group-chip--shifting" : "dv-tab--shifting";
}
/** Apply a margin-left value to an element, optionally bypassing CSS
* transitions for instant positioning. */
_setMargin(el, value, skipTransition) {
if (skipTransition) {
el.style.transition = "none";
el.style.marginLeft = value;
el.getBoundingClientRect();
el.style.removeProperty("transition");
} else el.style.marginLeft = value;
toggleClass(el, this._shiftingClass(el), true);
}
/** Clear an element's gap margin, animating it back to zero unless
* transitions are skipped. */
_clearMargin(el, skipTransition) {
const cls = this._shiftingClass(el);
const prev = this._pendingMarginCleanups.get(el);
if (prev) prev();
if (skipTransition || !el.style.marginLeft) {
el.style.removeProperty("margin-left");
toggleClass(el, cls, false);
} else {
el.style.marginLeft = "0px";
toggleClass(el, cls, true);
const onEnd = () => {
el.style.removeProperty("margin-left");
toggleClass(el, cls, false);
el.removeEventListener("transitionend", onEnd);
clearTimeout(fallbackTimer);
this._pendingMarginCleanups.delete(el);
};
const fallbackTimer = setTimeout(onEnd, 300);
this._pendingMarginCleanups.set(el, onEnd);
el.addEventListener("transitionend", onEnd);
}
}
resetTabTransforms() {
this.clearWrapDropIndicator();
for (const [, cleanup] of this._pendingMarginCleanups) cleanup();
this._pendingMarginCleanups.clear();
for (const tab of this._tabs) {
tab.value.element.style.removeProperty("margin-left");
tab.value.element.style.removeProperty("margin-right");
tab.value.element.style.removeProperty("margin-top");
tab.value.element.style.removeProperty("margin-bottom");
tab.value.element.style.removeProperty("transform");
toggleClass(tab.value.element, "dv-tab--shifting", false);
}
for (const [, entry] of this._tabGroupManager.chipRenderers) {
entry.chip.element.style.removeProperty("margin-left");
toggleClass(entry.chip.element, "dv-tab-group-chip--shifting", false);
}
this._tabGroupManager.positionUnderlines();
}
/**
* Commit a group-drag drop: clear drag classes, move the group
* in the model, and run a FLIP animation.
*/
commitGroupMove(sourceTabGroupId, insertionIndex) {
const data = getPanelData();
this._tabGroupManager.disposeChipDrag(sourceTabGroupId);
if (this.group.model.getTabGroups().some((tg) => tg.id === sourceTabGroupId)) {
var _this$accessor$option3;
if (((_this$accessor$option3 = this.accessor.options.theme) === null || _this$accessor$option3 === void 0 ? void 0 : _this$accessor$option3.tabAnimation) === "smooth") {
this._clearGroupDragClasses(sourceTabGroupId);
const firstPositions = this.snapshotTabPositions();
this.resetTabTransforms();
this.group.model.moveTabGroup(sourceTabGroupId, insertionIndex);
this.runFlipAnimation(firstPositions, "", false);
} else {
this._tabGroupManager.skipNextCollapseAnimation = true;
this.group.model.moveTabGroup(sourceTabGroupId, insertionIndex);
}
} else if (data) {
var _data$tabGroupId;
this.resetTabTransforms();
this.accessor.moveGroupOrPanel({
from: {
groupId: data.groupId,
tabGroupId: (_data$tabGroupId = data.tabGroupId) !== null && _data$tabGroupId !== void 0 ? _data$tabGroupId : sourceTabGroupId
},
to: {
group: this.group,
position: "center",
index: insertionIndex
}
});
}
}
_clearGroupDragClasses(sourceTabGroupId) {
const chipEntry = this._tabGroupManager.chipRenderers.get(sourceTabGroupId);
if (chipEntry) this._removeClassInstantlyBatch([chipEntry.chip.element], "dv-tab-group-chip--dragging");
this._removeClassInstantlyBatch(this._tabs.map((t) => t.value.element), "dv-tab--dragging");
const underline = this._tabGroupManager.groupUnderlines.get(sourceTabGroupId);
if (underline) underline.style.removeProperty("display");
this._tabGroupManager.skipNextCollapseAnimation = true;
}
resetDragAnimation() {
this._pendingCollapse = false;
if (this._animState) {
this.resetTabTransforms();
if (this._animState.sourceTabGroupId) this._clearGroupDragClasses(this._animState.sourceTabGroupId);
else this._removeClassInstantlyBatch(this._tabs.map((t) => t.value.element), "dv-tab--dragging");
this._animState = null;
for (const [, el] of this._tabGroupManager.groupUnderlines) el.style.removeProperty("display");
}
}
runFlipAnimation(firstPositions, sourceTabId, isCrossGroup = false, animRange) {
if (this._wrapMode) return;
let hasAnimation = false;
for (let i = 0; i < this._tabs.length; i++) if (this._applyFlipTransform(this._tabs[i], i, sourceTabId, isCrossGroup, firstPositions, animRange)) hasAnimation = true;
if (!hasAnimation) return;
this._scheduleFlipReset();
}
/**
* Set a single tab's FLIP transform: the source tab slides in from the end
* on a cross-group insert; other in-range tabs slide from their first
* position to their last. Returns true if the tab will animate.
*/
_applyFlipTransform(tab, index, sourceTabId, isCrossGroup, firstPositions, animRange) {
const isVertical = this._direction === "vertical";
const panelId = tab.value.panel.id;
if (panelId === sourceTabId) {
if (!isCrossGroup) return false;
const rect = tab.value.element.getBoundingClientRect();
tab.value.element.style.transform = isVertical ? `translateY(${rect.height}px)` : `translateX(${rect.width}px)`;
toggleClass(tab.value.element, "dv-tab--shifting", true);
return true;
}
if (animRange !== void 0 && (index < animRange.from || index > animRange.to)) return false;
const firstRect = firstPositions.get(panelId);
if (!firstRect) return false;
const lastRect = tab.value.element.getBoundingClientRect();
const delta = isVertical ? firstRect.top - lastRect.top : firstRect.left - lastRect.left;
if (Math.abs(delta) < 1) return false;
tab.value.element.style.transform = isVertical ? `translateY(${delta}px)` : `translateX(${delta}px)`;
toggleClass(tab.value.element, "dv-tab--shifting", true);
return true;
}
/**
* Next frame, clear the transforms (triggering the CSS transition), track
* underlines through the slide, and drop the shifting classes once the
* transform transition ends.
*/
_scheduleFlipReset() {
requestAnimationFrame(() => {
var _this$_flipTransition2;
for (const tab of this._tabs) if (tab.value.element.style.transform) tab.value.element.style.transform = "";
this._tabGroupManager.trackUnderlines();
(_this$_flipTransition2 = this._flipTransitionCleanup) === null || _this$_flipTransition2 === void 0 || _this$_flipTransition2.call(this);
const onTransitionEnd = (event) => {
if (event.propertyName === "transform") {
cleanup();
for (const tab of this._tabs) toggleClass(tab.value.element, "dv-tab--shifting", false);
this._tabGroupManager.positionUnderlines();
}
};
const cleanup = () => {
this._tabsList.removeEventListener("transitionend", onTransitionEnd);
this._flipTransitionCleanup = null;
};
this._flipTransitionCleanup = cleanup;
this._tabsList.addEventListener("transitionend", onTransitionEnd);
});
}
};
var Tabs = class extends CompositeDisposable {
get _animState() {
return this._reorder.animState;
}
set _animState(value) {
this._reorder.animState = value;
}
get _pendingCollapse() {
return this._reorder.pendingCollapse;
}
set _pendingCollapse(value) {
this._reorder.pendingCollapse = value;
}
get tabItems() {
return this._tabs;
}
get tabMap() {
return this._tabMap;
}
get tabsList() {
return this._tabsList;
}
get groupPanel() {
return this.group;
}
get component() {
return this.accessor;
}
get tabGroupManager() {
return this._tabGroupManager;
}
fireDrop(event) {
this._onDrop.fire(event);
}
/**
* Register a predicate excluding panels from the overflow dropdown (pinned
* tabs). Re-evaluates the dropdown immediately if overflow is being
* observed. Passing `() => false` restores default behaviour.
*/
setOverflowExclude(fn) {
this._overflowExclude = fn;
this.refreshOverflow();
}
/**
* Register a predicate that forces matching panels into the overflow
* dropdown regardless of horizontal fit (the MultiRowTabs surplus set, the
* rows beyond `overflow.maxRows`). Re-evaluates the dropdown immediately.
* Passing `() => false` restores default behaviour.
*/
setForcedOverflow(fn) {
this._forcedOverflow = fn;
this.refreshOverflow();
}
/** Re-evaluate the overflow dropdown now (e.g. after the exclusion set
* changed) instead of waiting for the next resize/scroll observer fire. */
refreshOverflow() {
if (this._showTabsOverflowControl) this.toggleDropdown({ reset: false });
this._applyPinnedSticky();
}
/**
* Enable/disable sticky-on-scroll for pinned tabs (inline mode), wired by
* the PinnedTabs module. When on, pinned (overflow-excluded) tabs are frozen
* to the left edge as the strip scrolls, each at the cumulative width of the
* pinned tabs before it; when off, the sticky styling is cleared. The
* offsets are scroll-invariant, so they are recomputed only on resize,
* horizontal scroll (a cheap self-heal after a reorder), and pinned-set
* changes, not continuously.
*/
setPinnedSticky(enabled) {
if (this._pinnedSticky === enabled) return;
this._pinnedSticky = enabled;
this._applyPinnedSticky();
}
/**
* Freeze the pinned (overflow-excluded) tabs to the left edge: give each the
* `dv-tab--pinned-sticky` class and a `--dv-pinned-sticky-left` offset so the
* CSS `position: sticky` rule holds it in place as the strip scrolls. Clears
* the styling from any tab that is no longer pinned, and from all tabs when
* the feature is off.
*/
_applyPinnedSticky() {
const pinned = this._pinnedSticky ? this._tabs.filter((tab) => this._overflowExclude(tab.value.panel.id)) : [];
if (pinned.length === 0 && !this._hasPinnedStickyStyling) return;
for (const tab of this._tabs) {
const el = tab.value.element;
if (el.classList.contains("dv-tab--pinned-sticky")) {
el.classList.remove("dv-tab--pinned-sticky");
el.style.removeProperty("--dv-pinned-sticky-left");
}
}
if (pinned.length === 0) {
this._hasPinnedStickyStyling = false;
return;
}
const lefts = pinned.map((tab) => tab.value.element.offsetLeft);
pinned.forEach((tab, index) => {
const el = tab.value.element;
el.classList.add("dv-tab--pinned-sticky");
el.style.setProperty("--dv-pinned-sticky-left", `${lefts[index]}px`);
});
this._hasPinnedStickyStyling = true;
}
get showTabsOverflowControl() {
return this._showTabsOverflowControl;
}
set showTabsOverflowControl(value) {
if (this._showTabsOverflowControl == value) return;
this._showTabsOverflowControl = value;
if (value) {
const observer = new OverflowObserver(this._tabsList);
this._observerDisposable.value = new CompositeDisposable(observer, observer.onDidChange((event) => {
const hasOverflow = event.hasScrollX || event.hasScrollY;
this.toggleDropdown({ reset: !hasOverflow });
this._applyPinnedSticky();
if (this._tabGroupManager.groupUnderlines.size > 0) this._tabGroupManager.positionUnderlines();
}), addDisposableListener(this._tabsList, "scroll", () => {
this.toggleDropdown({ reset: false });
this._applyPinnedSticky();
if (this._tabGroupManager.groupUnderlines.size > 0) this._tabGroupManager.positionUnderlines();
}));
} else this._observerDisposable.value = Disposable.NONE;
}
get element() {
return this._element;
}
/**
* The scrollable tab list (`.dv-tabs-container`) holding the tab elements,
* exposed (read-only) so the multi-row wrap controller can measure the
* wrapped row count and toggle the wrap class. Not the outer header element.
*/
get tabsListElement() {
return this._tabsList;
}
set voidContainer(el) {
var _this$_voidContainerL;
(_this$_voidContainerL = this._voidContainerListeners) === null || _this$_voidContainerL === void 0 || _this$_voidContainerL.dispose();
this._voidContainerListeners = null;
this._reorder.voidContainerElement = el;
if (el) this._voidContainerListeners = new CompositeDisposable(addDisposableListener(el, "dragover", (event) => {
if (this._animState) event.preventDefault();
}), addDisposableListener(el, "drop", (event) => {
var _this$_animState;
if (((_this$_animState = this._animState) === null || _this$_animState === void 0 ? void 0 : _this$_animState.sourceTabGroupId) && this._animState.currentInsertionIndex !== null) {
event.preventDefault();
event.stopPropagation();
this.handleVoidDrop();
}
}));
}
/**
* Handle a drop that occurred on the void container (empty header
* space to the right of the tabs). Returns `true` if the drop was
* consumed by an active group drag, `false` otherwise.
*/
handleVoidDrop() {
var _this$_animState2, _this$_animState$curr;
if (!((_this$_animState2 = this._animState) === null || _this$_animState2 === void 0 ? void 0 : _this$_animState2.sourceTabGroupId)) return false;
const sourceTabGroupId = this._animState.sourceTabGroupId;
const insertionIndex = (_this$_animState$curr = this._animState.currentInsertionIndex) !== null && _this$_animState$curr !== void 0 ? _this$_animState$curr : this._tabs.length;
this._animState = null;
this._commitGroupMove(sourceTabGroupId, insertionIndex);
return true;
}
get panels() {
return this._tabs.map((_) => _.value.panel.id);
}
get size() {
return this._tabs.length;
}
get tabs() {
return this._tabs.map((_) => _.value);
}
get direction() {
return this._direction;
}
set direction(value) {
if (this._direction === value) return;
this._direction = value;
this._tabsList.setAttribute("aria-orientation", value === "vertical" ? "vertical" : "horizontal");
if (this._scrollbar) this._scrollbar.orientation = value;
removeClasses(this._tabsList, "dv-horizontal", "dv-vertical");
if (value === "vertical") addClasses(this._tabsList, "dv-tabs-container-vertical", "dv-vertical");
else {
removeClasses(this._tabsList, "dv-tabs-container-vertical");
addClasses(this._tabsList, "dv-horizontal");
}
for (const tab of this._tabs) tab.value.setDirection(value);
this._tabGroupManager.updateDirection();
}
constructor(group, accessor, options) {
super();
this.group = group;
this.accessor = accessor;
this._observerDisposable = new MutableDisposable();
this._pointerActivation = new MutableDisposable();
this._scrollbar = null;
this._tabs = [];
this._tabMap = /* @__PURE__ */ new Map();
this.selectedIndex = -1;
this._showTabsOverflowControl = false;
this._overflowExclude = () => false;
this._forcedOverflow = () => false;
this._pinnedSticky = false;
this._hasPinnedStickyStyling = false;
this._direction = "horizontal";
this._voidContainerListeners = null;
this._onTabDragStart = new Emitter();
this.onTabDragStart = this._onTabDragStart.event;
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._onOverflowTabsChange = new Emitter();
this.onOverflowTabsChange = this._onOverflowTabsChange.event;
this._tabsList = document.createElement("div");
this._tabsList.className = "dv-tabs-container";
this._tabsList.setAttribute("role", "tablist");
this._tabsList.setAttribute("aria-orientation", this._direction === "vertical" ? "vertical" : "horizontal");
this.showTabsOverflowControl = options.showTabsOverflowControl;
if (accessor.options.scrollbars === "native") this._element = this._tabsList;
else {
this._scrollbar = new Scrollbar(this._tabsList);
this._scrollbar.orientation = this.direction;
this._element = this._scrollbar.element;
this.addDisposables(this._scrollbar);
}
this._tabGroupManager = new TabGroupManager({
group: this.group,
accessor: this.accessor,
tabsList: this._tabsList,
getTabs: () => this._tabs,
getTabMap: () => this._tabMap,
getDirection: () => this._direction
}, {
onChipContextMenu: (tabGroup, event) => {
var _this$accessor$contex;
(_this$accessor$contex = this.accessor.contextMenuService) === null || _this$accessor$contex === void 0 || _this$accessor$contex.showForChip(tabGroup, this.group, event);
},
onChipDragStart: (tabGroup, chip, event) => {
this._handleChipDragStart(tabGroup, chip, event);
},
onChipDragEnd: () => {
this._reorder.resetDragAnimation();
},
onChipDrop: (tabGroup, event) => {
this._handleChipDrop(tabGroup, event);
}
});
this._reorder = new TabReorderController(this);
this.addDisposables(this._onOverflowTabsChange, this._observerDisposable, this._pointerActivation, this._onWillShowOverlay, this._onDrop, this._onTabDragStart, this._reorder, PointerDragController.getInstance().onDragEnd((e) => {
this._reorder.handlePointerDragEnd(e);
}), PointerDragController.getInstance().onDragMove((e) => {
this._reorder.handlePointerDragMove(e.clientX, e.clientY);
}), addDisposableListener(this.element, "pointerdown", (event) => {
if (event.defaultPrevented) return;
if (event.button === 0) this.accessor.doSetGroupActive(this.group);
}), addDisposableListener(this._tabsList, "wheel", (event) => {
const isVertical = this._direction === "vertical";
const primary = isVertical ? event.deltaY || event.deltaX : event.deltaX || event.deltaY;
if (primary === 0) return;
const max = isVertical ? this._tabsList.scrollHeight - this._tabsList.clientHeight : this._tabsList.scrollWidth - this._tabsList.clientWidth;
if (max <= 0) return;
const current = isVertical ? this._tabsList.scrollTop : this._tabsList.scrollLeft;
if (primary < 0 && current <= 0 || primary > 0 && current >= max) return;
event.preventDefault();
event.stopPropagation();
if (isVertical) this._tabsList.scrollTop = current + primary;
else this._tabsList.scrollLeft = current + primary;
}, { passive: false }), addDisposableListener(this._tabsList, "keydown", (event) => {
this._onKeyDown(event);
}), addDisposableListener(this._tabsList, "dragover", (event) => {
if (this._reorder.processDragOver(event.clientX, event.clientY)) event.preventDefault();
}, true), addDisposableListener(this._tabsList, "dragleave", (event) => {
this._reorder.processDragLeave(event.relatedTarget);
}, true), addDisposableListener(this._tabsList, "dragend", () => {
this._reorder.resetDragAnimation();
}), addDisposableListener(this._tabsList, "drop", (event) => {
var _this$_animState3, _this$accessor$option, _this$group$model$dro;
if (((_this$_animState3 = this._animState) === null || _this$_animState3 === void 0 ? void 0 : _this$_animState3.currentInsertionIndex) == null) return;
if (((_this$accessor$option = this.accessor.options.theme) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.tabAnimation) !== "smooth" && !this._animState.sourceTabGroupId) return;
event.stopPropagation();
event.preventDefault();
(_this$group$model$dro = this.group.model.dropTargetContainer) === null || _this$group$model$dro === void 0 || (_this$group$model$dro = _this$group$model$dro.model) === null || _this$group$model$dro === void 0 || _this$group$model$dro.clear();
const animState = this._animState;
this._animState = null;
this._pendingCollapse = false;
if (animState.sourceTabGroupId) {
this._commitGroupMove(animState.sourceTabGroupId, animState.currentInsertionIndex);
return;
}
const insertionIndex = animState.currentInsertionIndex;
const sourceIndex = animState.sourceIndex;
const adjustedIndex = insertionIndex - (sourceIndex !== -1 && sourceIndex < insertionIndex ? 1 : 0);
const sourceCurrentGroup = this.group.model.getTabGroupForPanel(animState.sourceTabId);
if (adjustedIndex === sourceIndex && !animState.targetTabGroupId && !sourceCurrentGroup) {
this._reorder.uncollapseSourceTab(animState.sourceTabId);
this.resetTabTransforms();
return;
}
this._reorder.uncollapseSourceTab(animState.sourceTabId);
const firstPositions = this.snapshotTabPositions();
this.resetTabTransforms();
this._onDrop.fire({
event,
index: adjustedIndex,
targetTabGroupId: animState.targetTabGroupId
});
this.runFlipAnimation(firstPositions, animState.sourceTabId, animState.sourceIndex === -1, {
from: Math.min(sourceIndex, adjustedIndex),
to: Math.max(sourceIndex, adjustedIndex)
});
}, true), Disposable.from(() => {
var _this$_voidContainerL2;
(_this$_voidContainerL2 = this._voidContainerListeners) === null || _this$_voidContainerL2 === void 0 || _this$_voidContainerL2.dispose();
this._reorder.resetDragAnimation();
this._tabGroupManager.disposeAll();
for (const { value, disposable } of this._tabs) {
disposable.dispose();
value.dispose();
}
this._tabs = [];
this._tabMap.clear();
}));
}
indexOf(id) {
return this._tabs.findIndex((tab) => tab.value.panel.id === id);
}
/** DOM id of the tab element for a panel, used for the tabpanel's `aria-labelledby`. */
getTabId(panelId) {
var _this$_tabMap$get;
return (_this$_tabMap$get = this._tabMap.get(panelId)) === null || _this$_tabMap$get === void 0 ? void 0 : _this$_tabMap$get.value.element.id;
}
/** Inverse of {@link getTabId}: the panel whose tab owns `element` (the tab
* itself or any descendant of it), or `undefined` for non-tab targets. */
getPanelForTab(element) {
for (const { value } of this._tabs) if (value.element === element || value.element.contains(element)) return value.panel;
}
isActive(tab) {
return this.selectedIndex > -1 && this._tabs[this.selectedIndex].value === tab;
}
_onKeyDown(event) {
const index = this._tabs.findIndex((tab) => tab.value.element === event.target);
if (index === -1) return;
const isVertical = this._direction === "vertical";
const nextKey = isVertical ? "ArrowDown" : "ArrowRight";
const prevKey = isVertical ? "ArrowUp" : "ArrowLeft";
const last = this._tabs.length - 1;
switch (event.key) {
case nextKey:
event.preventDefault();
this._focusTab(Math.min(index + 1, last));
break;
case prevKey:
event.preventDefault();
this._focusTab(Math.max(index - 1, 0));
break;
case "Home":
event.preventDefault();
this._focusTab(0);
break;
case "End":
event.preventDefault();
this._focusTab(last);
break;
case "Enter":
case " ":
event.preventDefault();
this.accessor.withOrigin("user", () => this._tabs[index].value.panel.api.setActive());
break;
case "Delete":
case "Backspace":
event.preventDefault();
this._closeTab(index);
break;
}
}
/**
* Close the tab at `index` and move roving focus to a neighbouring tab so
* keyboard focus stays in the tablist.
*/
_closeTab(index) {
var _this$_tabs$index, _ref, _this$_tabs$value, _this$_tabs, _this$_tabs2;
const tab = (_this$_tabs$index = this._tabs[index]) === null || _this$_tabs$index === void 0 ? void 0 : _this$_tabs$index.value;
if (!tab) return;
const neighbourId = (_ref = (_this$_tabs$value = (_this$_tabs = this._tabs[index + 1]) === null || _this$_tabs === void 0 ? void 0 : _this$_tabs.value) !== null && _this$_tabs$value !== void 0 ? _this$_tabs$value : (_this$_tabs2 = this._tabs[index - 1]) === null || _this$_tabs2 === void 0 ? void 0 : _this$_tabs2.value) === null || _ref === void 0 ? void 0 : _ref.panel.id;
tab.panel.api.close();
if (neighbourId !== void 0) {
const nextIndex = this._tabs.findIndex((t) => t.value.panel.id === neighbourId);
if (nextIndex > -1) this._focusTab(nextIndex);
}
}
/** Move the roving focus to the tab at `index` (updates tabindex + DOM focus). */
_focusTab(index) {
for (let i = 0; i < this._tabs.length; i++) this._tabs[i].value.element.tabIndex = i === index ? 0 : -1;
this._tabs[index].value.element.focus();
}
/** Move DOM focus to the active tab, the entry point into the tablist. */
focusActiveTab() {
var _this$group$activePan;
if (this._tabs.length === 0) return;
const activeId = (_this$group$activePan = this.group.activePanel) === null || _this$group$activePan === void 0 ? void 0 : _this$group$activePan.id;
const activeIndex = activeId ? this._tabs.findIndex((t) => t.value.panel.id === activeId) : -1;
this._focusTab(activeIndex > -1 ? activeIndex : 0);
}
setActivePanel(panel) {
const isVertical = this._direction === "vertical";
for (const tab of this._tabs) {
const isActivePanel = panel.id === tab.value.panel.id;
tab.value.setActive(isActivePanel);
if (isActivePanel) this._scrollTabIntoView(tab.value.element, isVertical);
}
if (this._tabGroupManager.groupUnderlines.size > 0) this._tabGroupManager.positionUnderlines();
}
/**
* Scroll the active tab into view within its scroll container. Uses the
* element's own offset (relative to the container) rather than accumulating
* tab client sizes, which omits tab margins and any in-flow group chips
* between tabs and so undershoots the active tab's real position.
*/
_scrollTabIntoView(element, isVertical) {
const parentElement = element.parentElement;
if (!parentElement) return;
const start = isVertical ? element.offsetTop : element.offsetLeft;
const size = isVertical ? element.offsetHeight : element.offsetWidth;
const scrollStart = isVertical ? parentElement.scrollTop : parentElement.scrollLeft;
const clientSize = isVertical ? parentElement.clientHeight : parentElement.clientWidth;
if (start < scrollStart || start + size > scrollStart + clientSize) if (isVertical) parentElement.scrollTop = start;
else parentElement.scrollLeft = start;
}
/**
* Activate a tab in response to a plain (non-shift) left pointerdown.
*
* With the HTML5 drag backend the tab is natively `draggable`, and a native
* drag arms from the same pointerdown+move gesture. Firefox aborts that
* pending drag if the pointerdown also moves focus / relayouts the tab strip
* synchronously — which `openPanel` does — so a drag started on an inactive
* tab never begins (issue #932). Defer the activation past the frame so
* `dragstart` arms first; a plain click (no drag) still activates on the
* next frame, which is imperceptible. The pointer backend has no native drag
* to pre-empt, so it activates synchronously as before.
*/
_activateOnPointerDown(panel) {
if (!resolveDndCapabilities(this.accessor.options).html5) {
this.group.model.openPanel(panel);
return;
}
const handle = requestAnimationFrame(() => {
if (this._tabMap.has(panel.id)) this.group.model.openPanel(panel);
});
this._pointerActivation.value = { dispose: () => cancelAnimationFrame(handle) };
}
openPanel(panel, index = this._tabs.length) {
if (this._tabMap.has(panel.id)) return;
const tab = new Tab(panel, this.accessor, this.group);
tab.setContent(panel.view.tab);
if (this._direction !== "horizontal") tab.setDirection(this._direction);
const value = {
value: tab,
disposable: new CompositeDisposable(tab.onDragStart((event) => {
var _this$accessor$option2;
this._onTabDragStart.fire({
nativeEvent: event,
panel
});
if (((_this$accessor$option2 = this.accessor.options.theme) === null || _this$accessor$option2 === void 0 ? void 0 : _this$accessor$option2.tabAnimation) === "smooth") {
const tabWidth = tab.element.getBoundingClientRect().width;
const sourceIndex = this._tabs.findIndex((x) => x.value === tab);
this._animState = {
sourceTabId: panel.id,
sourceIndex,
tabPositions: this.snapshotTabPositions(),
chipPositions: this._tabGroupManager.snapshotChipWidths(),
currentInsertionIndex: null,
targetTabGroupId: null,
sourceTabGroupId: null,
sourceGroupPanelIds: null,
sourceChipWidth: 0,
cursorOffsetFromDragLeft: tabWidth / 2,
sourceGapWidth: tabWidth,
containerLeft: this._tabsList.getBoundingClientRect().left
};
this._pendingCollapse = true;
requestAnimationFrame(() => {
var _this$_animState4, _this$_animState4$cur;
this._pendingCollapse = false;
if (!this._animState) return;
if (!this._tabsList.classList.contains("dv-tabs-container--wrap")) {
tab.element.style.transition = "none";
toggleClass(tab.element, "dv-tab--dragging", true);
tab.element.offsetHeight;
}
(_this$_animState4$cur = (_this$_animState4 = this._animState).currentInsertionIndex) !== null && _this$_animState4$cur !== void 0 || (_this$_animState4.currentInsertionIndex = sourceIndex);
this.applyDragOverTransforms(true);
tab.element.style.removeProperty("transition");
});
}
}), tab.onTabClick((event) => {
if (event.defaultPrevented) return;
if (this.group.api.location.type !== "edge") return;
if (this.group.activePanel === panel) if (this.group.api.isCollapsed()) this.group.api.expand();
else this.group.api.collapse();
else {
this.group.model.openPanel(panel);
if (this.group.api.isCollapsed()) this.group.api.expand();
}
}), tab.onPointerDown((event) => {
if (event.defaultPrevented) return;
const isFloatingGroupsEnabled = !this.accessor.options.disableFloatingGroups;
const isFloatingWithOnePanel = this.group.api.location.type === "floating" && this.size === 1;
if (isFloatingGroupsEnabled && !isFloatingWithOnePanel && event.shiftKey) {
event.preventDefault();
const panel = this.accessor.getGroupPanel(tab.panel.id);
const { top, left } = tab.element.getBoundingClientRect();
const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
this.accessor.addFloatingGroup(panel, {
x: left - rootLeft,
y: top - rootTop,
inDragMode: true
});
return;
}
if (event.button === 0 && this.group.api.location.type !== "edge" && this.group.activePanel !== panel) this._activateOnPointerDown(panel);
}), tab.onDrop((event) => {
const animState = this._animState;
this._animState = null;
this._pendingCollapse = false;
const tabIndex = this._tabs.findIndex((x) => x.value === tab);
if (animState) {
var _this$accessor$option3;
const dropIndex = event.position === "right" ? tabIndex + 1 : tabIndex;
if (animState.sourceTabGroupId) {
var _animState$currentIns;
this._commitGroupMove(animState.sourceTabGroupId, (_animState$currentIns = animState.currentInsertionIndex) !== null && _animState$currentIns !== void 0 ? _animState$currentIns : dropIndex);
return;
}
this._reorder.uncollapseSourceTab(animState.sourceTabId);
const firstPositions = this.snapshotTabPositions();
this.resetTabTransforms();
this._onDrop.fire({
event: event.nativeEvent,
index: dropIndex,
targetTabGroupId: animState.targetTabGroupId
});
if (((_this$accessor$option3 = this.accessor.options.theme) === null || _this$accessor$option3 === void 0 ? void 0 : _this$accessor$option3.tabAnimation) === "smooth") this.runFlipAnimation(firstPositions, animState.sourceTabId, animState.sourceIndex === -1, animState.sourceIndex === -1 ? void 0 : {
from: Math.min(animState.sourceIndex, dropIndex),
to: Math.max(animState.sourceIndex, dropIndex)
});
} else {
var _this$group$model$get, _this$group$model$get2;
const afterPosition = this._direction === "vertical" ? "bottom" : "right";
const insertionIndex = event.position === afterPosition ? tabIndex + 1 : tabIndex;
const data = getPanelData();
const sourceIndex = data ? this._tabs.findIndex((x) => x.value.panel.id === data.panelId) : -1;
const adjustedIndex = insertionIndex - (sourceIndex !== -1 && sourceIndex < insertionIndex ? 1 : 0);
const targetTabGroupId = (_this$group$model$get = (_this$group$model$get2 = this.group.model.getTabGroupForPanel(tab.panel.id)) === null || _this$group$model$get2 === void 0 ? void 0 : _this$group$model$get2.id) !== null && _this$group$model$get !== void 0 ? _this$group$model$get : null;
this._onDrop.fire({
event: event.nativeEvent,
index: adjustedIndex,
targetTabGroupId
});
}
}), tab.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(new DockviewWillShowOverlayLocationEvent(event, {
kind: "tab",
panel: this.group.activePanel,
api: this.accessor.api,
group: this.group,
getData: getPanelData
}));
}))
};
this.addTab(value, index);
this._tabGroupManager.positionAllChips();
if (this._animState) {
this._animState.tabPositions = this.snapshotTabPositions();
this._animState.chipPositions = this._tabGroupManager.snapshotChipWidths();
this.applyDragOverTransforms();
}
}
delete(id) {
var _this$_animState5;
if (((_this$_animState5 = this._animState) === null || _this$_animState5 === void 0 ? void 0 : _this$_animState5.sourceTabId) === id) {
this.resetTabTransforms();
this._animState = null;
}
this._tabGroupManager.cleanupTransition(id);
const index = this.indexOf(id);
const tabToRemove = this._tabs.splice(index, 1)[0];
this._tabMap.delete(id);
if (tabToRemove) {
const { value, disposable } = tabToRemove;
disposable.dispose();
value.dispose();
value.element.remove();
}
if (this._animState) {
this._animState.tabPositions = this.snapshotTabPositions();
this._animState.chipPositions = this._tabGroupManager.snapshotChipWidths();
this.applyDragOverTransforms();
}
}
addTab(tab, index = this._tabs.length) {
if (index < 0 || index > this._tabs.length) throw new Error("invalid location");
const refNode = index < this._tabs.length ? this._tabs[index].value.element : null;
this._tabsList.insertBefore(tab.value.element, refNode);
this._tabs = [
...this._tabs.slice(0, index),
tab,
...this._tabs.slice(index)
];
this._tabMap.set(tab.value.panel.id, tab);
if (this.selectedIndex < 0) this.selectedIndex = index;
}
toggleDropdown(options) {
const hasForced = this._tabs.some((tab) => !this._overflowExclude(tab.value.panel.id) && this._forcedOverflow(tab.value.panel.id));
if (options.reset && !hasForced) {
this._onOverflowTabsChange.fire({
tabs: [],
tabGroups: [],
pinnedTabs: [],
reset: true
});
return;
}
const tabs = this._tabs.filter((tab) => !this._overflowExclude(tab.value.panel.id) && (this._forcedOverflow(tab.value.panel.id) || !options.reset && !isChildEntirelyVisibleWithinParent(tab.value.element, this._tabsList))).map((x) => x.value.panel.id);
const pinnedTabs = options.reset ? [] : this._tabs.filter((tab) => this._overflowExclude(tab.value.panel.id) && tab.value.element.getBoundingClientRect().width > 0 && !isChildEntirelyVisibleWithinParent(tab.value.element, this._tabsList)).map((x) => x.value.panel.id);
const overflowTabSet = new Set(tabs);
const tabGroups = [];
for (const tg of this.group.model.getTabGroups()) {
const chipEntry = this._tabGroupManager.chipRenderers.get(tg.id);
const chipClipped = chipEntry && !isChildEntirelyVisibleWithinParent(chipEntry.chip.element, this._tabsList);
const allTabsOverflow = tg.panelIds.length > 0 && tg.panelIds.every((pid) => overflowTabSet.has(pid));
if (chipClipped || allTabsOverflow) {
tabGroups.push(tg.id);
if (tg.collapsed) {
for (const pid of tg.panelIds) if (!overflowTabSet.has(pid)) {
overflowTabSet.add(pid);
tabs.push(pid);
}
}
}
}
this._onOverflowTabsChange.fire({
tabs,
tabGroups,
pinnedTabs,
reset: false
});
}
updateDragAndDropState() {
for (const tab of this._tabs) tab.value.updateDragAndDropState();
this._tabGroupManager.updateDragAndDropState();
}
/**
* Synchronize chip elements and CSS classes for all tab groups
* in the parent group model. Call after any tab group mutation.
*/
updateTabGroups() {
this._tabGroupManager.update();
}
refreshTabGroupAccent() {
this._tabGroupManager.refreshAccents();
}
/**
* Tabs-list-specific side effects of a chip drag start. The chip's
* drag sources (constructed by `TabGroupManager`) own the transfer
* payload, iframe shielding, dataTransfer setup, and the HTML5 drag
* image. This method just sets up the smooth-reorder anim state and
* collapses the source-group tabs in the tabs list.
*/
_handleChipDragStart(tabGroup, chip, event) {
var _this$accessor$option4;
const firstPanelId = tabGroup.panelIds[0];
const firstIdx = firstPanelId ? this._tabs.findIndex((t) => t.value.panel.id === firstPanelId) : -1;
const chipRect = chip.element.getBoundingClientRect();
let groupGapWidth = chipRect.width;
for (const pid of tabGroup.panelIds) {
const tabEntry = this._tabMap.get(pid);
if (tabEntry) groupGapWidth += tabEntry.value.element.getBoundingClientRect().width;
}
this._animState = {
sourceTabId: "",
sourceIndex: firstIdx,
tabPositions: this.snapshotTabPositions(),
chipPositions: this._tabGroupManager.snapshotChipWidths(),
currentInsertionIndex: null,
targetTabGroupId: null,
sourceTabGroupId: tabGroup.id,
sourceGroupPanelIds: new Set(tabGroup.panelIds),
sourceChipWidth: chipRect.width,
cursorOffsetFromDragLeft: event.clientX - chipRect.left,
sourceGapWidth: groupGapWidth,
containerLeft: this._tabsList.getBoundingClientRect().left
};
if (((_this$accessor$option4 = this.accessor.options.theme) === null || _this$accessor$option4 === void 0 ? void 0 : _this$accessor$option4.tabAnimation) !== "smooth") return;
const groupPanelIds = new Set(tabGroup.panelIds);
this._pendingCollapse = true;
requestAnimationFrame(() => {
var _this$_animState6, _this$_animState6$cur;
this._pendingCollapse = false;
if (!this._animState) return;
for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) {
t.value.element.style.transition = "none";
toggleClass(t.value.element, "dv-tab--dragging", true);
}
const chipEntry = this._tabGroupManager.chipRenderers.get(tabGroup.id);
if (chipEntry) {
chipEntry.chip.element.style.transition = "none";
toggleClass(chipEntry.chip.element, "dv-tab-group-chip--dragging", true);
}
this._tabsList.offsetHeight;
const underline = this._tabGroupManager.groupUnderlines.get(tabGroup.id);
if (underline) underline.style.display = "none";
(_this$_animState6$cur = (_this$_animState6 = this._animState).currentInsertionIndex) !== null && _this$_animState6$cur !== void 0 || (_this$_animState6.currentInsertionIndex = firstIdx);
this.applyDragOverTransforms(true);
for (const t of this._tabs) if (groupPanelIds.has(t.value.panel.id)) t.value.element.style.removeProperty("transition");
if (chipEntry) chipEntry.chip.element.style.removeProperty("transition");
});
}
/**
* A drop on a tab group chip means "insert before this group". Resolve to
* the index of the group's first tab, adjusting for same-group removal
* (when the source tab is currently to the left of the target slot, its
* removal shifts the insertion index down by one). Always clears
* `targetTabGroupId` so the dropped tab lands outside the group.
*/
_handleChipDrop(tabGroup, event) {
const firstPanelId = tabGroup.panelIds[0];
if (!firstPanelId) return;
const insertionIndex = this._tabs.findIndex((x) => x.value.panel.id === firstPanelId);
if (insertionIndex === -1) return;
const data = getPanelData();
const sourceIndex = (data === null || data === void 0 ? void 0 : data.groupId) === this.group.id && (data === null || data === void 0 ? void 0 : data.panelId) ? this._tabs.findIndex((x) => x.value.panel.id === data.panelId) : -1;
const adjustedIndex = insertionIndex - (sourceIndex !== -1 && sourceIndex < insertionIndex ? 1 : 0);
this._onDrop.fire({
event: event.nativeEvent,
index: adjustedIndex,
targetTabGroupId: null
});
}
/**
* Sets the broader container that is part of the same logical drop surface
* as this tab list (e.g. the full header element). When a dragleave from
* the tabs list lands inside this container, `_animState` is preserved so
* that external dragover listeners can continue the animation.
*/
setExtendedDropZone(el) {
this._reorder.setExtendedDropZone(el);
}
/**
* Allows external elements (e.g. void container, left actions) to push an
* insertion index into the animation while the cursor is outside the tabs
* list itself. Pass `null` to clear the indicator.
*/
setExternalInsertionIndex(index) {
this._reorder.setExternalInsertionIndex(index);
}
/**
* Called when the drag cursor leaves the entire header area (not just the
* tabs list). Clears animation state for cross-group drags, which never
* receive a `dragend` event on this tab list.
*/
clearExternalAnimState() {
this._reorder.clearExternalAnimState();
}
snapshotTabPositions() {
return this._reorder.snapshotTabPositions();
}
handleDragOver(event) {
this._reorder.handleDragOver(event);
}
applyDragOverTransforms(skipTransition = false) {
this._reorder.applyDragOverTransforms(skipTransition);
}
resetTabTransforms() {
this._reorder.resetTabTransforms();
}
_commitGroupMove(sourceTabGroupId, insertionIndex) {
this._reorder.commitGroupMove(sourceTabGroupId, insertionIndex);
}
runFlipAnimation(firstPositions, sourceTabId, isCrossGroup = false, animRange) {
this._reorder.runFlipAnimation(firstPositions, sourceTabId, isCrossGroup, animRange);
}
};
function createDropdownElementHandle() {
const el = document.createElement("div");
el.className = "dv-tabs-overflow-dropdown-default";
const text = document.createElement("span");
text.textContent = ``;
const icon = createChevronRightButton();
el.appendChild(icon);
el.appendChild(text);
return {
element: el,
update: (params) => {
text.textContent = `${params.tabs}`;
}
};
}
var TabsContainer = class extends CompositeDisposable {
get onTabDragStart() {
return this.tabs.onTabDragStart;
}
get panels() {
return this.tabs.panels;
}
get size() {
return this.tabs.size;
}
get hidden() {
return this._hidden;
}
set hidden(value) {
this._hidden = value;
this.element.style.display = value ? "none" : "";
}
get direction() {
return this._direction;
}
set direction(value) {
this._direction = value;
if (value === "vertical") {
addClasses(this._element, "dv-groupview-header-vertical");
addClasses(this.rightActionsContainer, "dv-right-actions-container-vertical");
this.tabs.direction = value;
} else {
removeClasses(this._element, "dv-groupview-header-vertical");
removeClasses(this.rightActionsContainer, "dv-right-actions-container-vertical");
this.tabs.direction = value;
}
}
get element() {
return this._element;
}
get tabsListElement() {
return this.tabs.tabsListElement;
}
constructor(accessor, group) {
super();
this.accessor = accessor;
this.group = group;
this._hidden = false;
this._direction = "horizontal";
this._dropIndexResolver = (_panelId, index) => index;
this._pinnedRow = void 0;
this.dropdownPart = null;
this._overflowTabs = [];
this._overflowTabGroups = [];
this._overflowPinnedTabs = [];
this._dropdownDisposable = new MutableDisposable();
this._onDrop = new Emitter();
this.onDrop = this._onDrop.event;
this._onGroupDragStart = new Emitter();
this.onGroupDragStart = this._onGroupDragStart.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._element = document.createElement("div");
this._element.className = "dv-tabs-and-actions-container";
toggleClass(this._element, "dv-full-width-single-tab", this.accessor.options.singleTabMode === "fullwidth");
this.rightActionsContainer = document.createElement("div");
this.rightActionsContainer.className = "dv-right-actions-container";
this.leftActionsContainer = document.createElement("div");
this.leftActionsContainer.className = "dv-left-actions-container";
this.preActionsContainer = document.createElement("div");
this.preActionsContainer.className = "dv-pre-actions-container";
this.tabs = new Tabs(group, accessor, { showTabsOverflowControl: !accessor.options.disableTabsOverflowList });
this.voidContainer = new VoidContainer(this.accessor, this.group);
this.tabs.voidContainer = this.voidContainer.element;
this._element.appendChild(this.preActionsContainer);
this._element.appendChild(this.tabs.element);
this._element.appendChild(this.leftActionsContainer);
this._element.appendChild(this.voidContainer.element);
this._element.appendChild(this.rightActionsContainer);
this.tabs.setExtendedDropZone(this._element);
this.addDisposables(this.tabs.onDrop((e) => this._onDrop.fire(e)), this.tabs.onWillShowOverlay((e) => this._onWillShowOverlay.fire(e)), accessor.onDidOptionsChange(() => {
this.tabs.showTabsOverflowControl = !accessor.options.disableTabsOverflowList;
}), this.tabs.onOverflowTabsChange((event) => {
this.toggleDropdown(event);
}), this.tabs, this._onWillShowOverlay, this._onDrop, this._onGroupDragStart, this.voidContainer, this.voidContainer.onDragStart((event) => {
this._onGroupDragStart.fire({
nativeEvent: event,
group: this.group
});
}), this.voidContainer.onDrop((event) => {
if (this.tabs.handleVoidDrop()) return;
this._onDrop.fire({
event: event.nativeEvent,
index: this.tabs.size
});
}), this.voidContainer.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(new DockviewWillShowOverlayLocationEvent(event, {
kind: "header_space",
panel: this.group.activePanel,
api: this.accessor.api,
group: this.group,
getData: getPanelData
}));
}), addDisposableListener(this.leftActionsContainer, "dragleave", (event) => {
const related = event.relatedTarget;
if (!this.leftActionsContainer.contains(related) && !this._element.contains(related)) this.tabs.clearExternalAnimState();
}), addDisposableListener(this.voidContainer.element, "dragleave", (event) => {
const related = event.relatedTarget;
if (!this.voidContainer.element.contains(related)) if (this._element.contains(related)) this.tabs.setExternalInsertionIndex(null);
else this.tabs.clearExternalAnimState();
}), addDisposableListener(this.voidContainer.element, "pointerdown", (event) => {
if (event.defaultPrevented) return;
if (!this.accessor.options.disableFloatingGroups && event.shiftKey && this.group.api.location.type !== "floating" && this.group.api.location.type !== "edge") {
event.preventDefault();
const { top, left } = this.element.getBoundingClientRect();
const { top: rootTop, left: rootLeft } = this.accessor.element.getBoundingClientRect();
this.accessor.addFloatingGroup(this.group, {
x: left - rootLeft + 20,
y: top - rootTop + 20,
inDragMode: true
});
}
}));
}
show() {
if (!this.hidden) this.element.style.display = "";
}
hide() {
this._element.style.display = "none";
}
setRightActionsElement(element) {
if (this.rightActions === element) return;
if (this.rightActions) {
this.rightActions.remove();
this.rightActions = void 0;
}
if (element) {
this.rightActionsContainer.appendChild(element);
this.rightActions = element;
}
}
setLeftActionsElement(element) {
if (this.leftActions === element) return;
if (this.leftActions) {
this.leftActions.remove();
this.leftActions = void 0;
}
if (element) {
this.leftActionsContainer.appendChild(element);
this.leftActions = element;
}
}
setPrefixActionsElement(element) {
if (this.preActions === element) return;
if (this.preActions) {
this.preActions.remove();
this.preActions = void 0;
}
if (element) {
this.preActionsContainer.appendChild(element);
this.preActions = element;
}
}
isActive(tab) {
return this.tabs.isActive(tab);
}
indexOf(id) {
return this.tabs.indexOf(id);
}
getTabId(panelId) {
return this.tabs.getTabId(panelId);
}
getPanelForTab(element) {
return this.tabs.getPanelForTab(element);
}
setActive(_isGroupActive) {}
delete(id) {
this.tabs.delete(id);
this.updateClassnames();
}
setActivePanel(panel) {
this.tabs.setActivePanel(panel);
}
focusActiveTab() {
this.tabs.focusActiveTab();
}
openPanel(panel, index = this.tabs.size) {
this.tabs.openPanel(panel, index);
this.updateClassnames();
}
closePanel(panel) {
this.delete(panel.id);
}
setOverflowExclude(fn) {
this.tabs.setOverflowExclude(fn);
}
setForcedOverflow(fn) {
this.tabs.setForcedOverflow(fn);
}
setPinnedSticky(enabled) {
this.tabs.setPinnedSticky(enabled);
}
refreshOverflow() {
this.tabs.refreshOverflow();
}
setPinnedRow(el) {
if (this._pinnedRow === el) return;
if (this._pinnedRow) this._pinnedRow.remove();
this._pinnedRow = el;
if (el) {
el.classList.add("dv-pinned-row");
this._element.insertBefore(el, this._element.firstChild);
}
toggleClass(this._element, "dv-tabs-and-actions-container--pinned-row", !!el);
}
setDropIndexResolver(fn) {
this._dropIndexResolver = fn;
}
resolveDropIndex(panelId, index) {
return this._dropIndexResolver(panelId, index);
}
updateClassnames() {
toggleClass(this._element, "dv-single-tab", this.size === 1);
}
toggleDropdown(options) {
const tabs = options.reset ? [] : options.tabs;
const tabGroups = options.reset ? [] : options.tabGroups;
const pinnedTabs = options.reset ? [] : options.pinnedTabs;
this._overflowTabs = tabs;
this._overflowTabGroups = tabGroups;
this._overflowPinnedTabs = pinnedTabs;
const totalCount = this._overflowTabs.length + this._overflowPinnedTabs.length;
if (totalCount > 0 && this.dropdownPart) {
this.dropdownPart.update({ tabs: totalCount });
return;
}
if (totalCount === 0) {
this._dropdownDisposable.dispose();
return;
}
const root = document.createElement("div");
root.className = "dv-tabs-overflow-dropdown-root";
const part = createDropdownElementHandle();
part.update({ tabs: totalCount });
this.dropdownPart = part;
root.appendChild(part.element);
this.rightActionsContainer.prepend(root);
this._dropdownDisposable.value = new CompositeDisposable(Disposable.from(() => {
var _this$dropdownPart, _this$dropdownPart$di;
root.remove();
(_this$dropdownPart = this.dropdownPart) === null || _this$dropdownPart === void 0 || (_this$dropdownPart$di = _this$dropdownPart.dispose) === null || _this$dropdownPart$di === void 0 || _this$dropdownPart$di.call(_this$dropdownPart);
this.dropdownPart = null;
}), addDisposableListener(root, "pointerdown", (event) => {
event.preventDefault();
}, { capture: true }), addDisposableListener(root, "click", (event) => {
const relativeParent = findRelativeZIndexParent(root);
const anchor = {
x: event.clientX,
y: event.clientY,
zIndex: (relativeParent === null || relativeParent === void 0 ? void 0 : relativeParent.style.zIndex) ? `calc(${relativeParent.style.zIndex} * 2)` : void 0
};
const context = this.createOverflowRenderContext(root, anchor);
const advancedOverflow = this.accessor.advancedOverflowService;
if (advancedOverflow) advancedOverflow.renderOverflow({
group: this.group,
overflowTabs: [...this._overflowTabs],
overflowTabGroups: [...this._overflowTabGroups],
pinnedOverflowTabs: [...this._overflowPinnedTabs],
context
});
else context.open(this.renderFreeOverflowList(context));
}));
}
/**
* Build the core row/header builders + popover control shared by the free
* overflow list and the advanced overflow module. Everything the module
* needs to rebuild the dropdown body in a custom order lives here, so the
* row DOM, group-header DOM, click-to-activate, and (critically) the
* window-bound popover open/close stay in core, so the module never captures
* the wrong `window` for a popped-out group.
*/
createOverflowRenderContext(root, anchor) {
const overflowGroupSet = new Set(this._overflowTabGroups);
const allTabGroups = this.group.model.getTabGroups();
const panelToGroup = /* @__PURE__ */ new Map();
const groupById = /* @__PURE__ */ new Map();
for (const tg of allTabGroups) {
groupById.set(tg.id, tg);
if (overflowGroupSet.has(tg.id)) for (const pid of tg.panelIds) panelToGroup.set(pid, tg);
}
const popup = () => this.accessor.getPopupServiceForGroup(this.group);
const buildGroupHeader = (tg) => {
const groupHeader = document.createElement("div");
groupHeader.className = "dv-tabs-overflow-group-header";
const colorDot = document.createElement("span");
colorDot.className = "dv-tabs-overflow-group-color";
applyTabGroupAccent(colorDot, tg.color, this.accessor.tabGroupColorPalette);
groupHeader.appendChild(colorDot);
const labelSpan = document.createElement("span");
labelSpan.className = "dv-tabs-overflow-group-label";
labelSpan.textContent = tg.label || tg.id;
groupHeader.appendChild(labelSpan);
if (tg.collapsed) {
const badge = document.createElement("span");
badge.className = "dv-tabs-overflow-group-collapsed-badge";
badge.textContent = `${tg.panelIds.length}`;
groupHeader.appendChild(badge);
}
groupHeader.addEventListener("click", () => {
popup().close();
if (tg.collapsed) tg.expand();
const firstPanelId = tg.panelIds[0];
if (firstPanelId) {
const panel = this.group.panels.find((p) => p.id === firstPanelId);
this.accessor.withOrigin("user", () => panel === null || panel === void 0 ? void 0 : panel.api.setActive());
}
});
return groupHeader;
};
return {
overflowGroupIdForPanel: (panelId) => {
var _panelToGroup$get;
return (_panelToGroup$get = panelToGroup.get(panelId)) === null || _panelToGroup$get === void 0 ? void 0 : _panelToGroup$get.id;
},
buildGroupHeader: (tabGroupId) => {
const tg = groupById.get(tabGroupId);
if (!tg || !overflowGroupSet.has(tg.id)) return;
return buildGroupHeader(tg);
},
buildPinnedHeader: () => {
const header = document.createElement("div");
header.className = "dv-tabs-overflow-group-header dv-tabs-overflow-pinned-header";
const glyph = createPinButton();
glyph.classList.add("dv-tabs-overflow-pinned-icon");
header.appendChild(glyph);
const labelSpan = document.createElement("span");
labelSpan.className = "dv-tabs-overflow-group-label";
labelSpan.textContent = "Pinned";
header.appendChild(labelSpan);
return header;
},
buildRow: (panelId) => {
const panel = this.group.panels.find((p) => p.id === panelId);
if (!panel) return;
const tab = this.tabs.tabs.find((t) => t.panel.id === panelId);
const tg = panelToGroup.get(panelId);
const child = panel.view.createTabRenderer("headerOverflow").element;
const wrapper = document.createElement("div");
toggleClass(wrapper, "dv-tab", true);
toggleClass(wrapper, "dv-active-tab", panel.api.isActive);
toggleClass(wrapper, "dv-inactive-tab", !panel.api.isActive);
if (tg) toggleClass(wrapper, "dv-tab--grouped", true);
const doActivate = () => {
if (tg === null || tg === void 0 ? void 0 : tg.collapsed) tg.expand();
tab === null || tab === void 0 || tab.element.scrollIntoView({
block: "nearest",
inline: "nearest"
});
this.accessor.withOrigin("user", () => panel.api.setActive());
};
wrapper.addEventListener("click", (event) => {
popup().close();
if (event.defaultPrevented) return;
doActivate();
});
wrapper.appendChild(child);
return {
element: wrapper,
panel,
activate: () => {
popup().close();
doActivate();
}
};
},
open: (body) => {
popup().openPopover(body, anchor);
},
close: () => popup().close(),
focusTrigger: () => {
root.tabIndex = -1;
root.focus();
}
};
}
/**
* The free (module-absent) overflow list: the flat `.dv-tabs-overflow-container`
* body with a group header before the first member tab of each overflow
* group, in tab (DOM) order. Reuses the shared row/header builders so it
* stays identical to the advanced path's per-row DOM.
*/
renderFreeOverflowList(context) {
const el = document.createElement("div");
el.style.overflow = "auto";
el.className = "dv-tabs-overflow-container";
const pinnedRows = this._overflowPinnedTabs.map((panelId) => context.buildRow(panelId)).filter((row) => row != null);
if (pinnedRows.length > 0) {
el.appendChild(context.buildPinnedHeader());
for (const row of pinnedRows) el.appendChild(row.element);
}
const renderedGroups = /* @__PURE__ */ new Set();
for (const tab of this.tabs.tabs.filter((tab) => this._overflowTabs.includes(tab.panel.id))) {
const tgId = context.overflowGroupIdForPanel(tab.panel.id);
if (tgId && !renderedGroups.has(tgId)) {
renderedGroups.add(tgId);
const header = context.buildGroupHeader(tgId);
if (header) el.appendChild(header);
}
const row = context.buildRow(tab.panel.id);
if (row) el.appendChild(row.element);
}
return el;
}
updateDragAndDropState() {
this.tabs.updateDragAndDropState();
this.voidContainer.updateDragAndDropState();
}
updateTabGroups() {
this.tabs.updateTabGroups();
}
refreshTabGroupAccent() {
this.tabs.refreshTabGroupAccent();
}
};
var TabGroup = class extends CompositeDisposable {
get label() {
return this._label;
}
get color() {
return this._color;
}
get componentParams() {
return this._componentParams;
}
setLabel(value) {
if (this.isDisposed || this._label === value) return;
this._label = value;
this._onDidChange.fire();
}
setColor(value) {
if (this.isDisposed) return;
const next = value === "" ? void 0 : value;
if (this._color === next) return;
this._color = next;
this._onDidChange.fire();
}
setComponentParams(value) {
if (this.isDisposed) return;
this._componentParams = value;
this._onDidChange.fire();
}
get collapsed() {
return this._collapsed;
}
get panelIds() {
return this._panelIds;
}
get size() {
return this._panelIds.length;
}
get isEmpty() {
return this._panelIds.length === 0;
}
constructor(id, options) {
var _options$label, _options$collapsed;
super();
this.id = id;
this._collapsed = false;
this._panelIds = [];
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._onDidPanelChange = new Emitter();
this.onDidPanelChange = this._onDidPanelChange.event;
this._onDidCollapseChange = new Emitter();
this.onDidCollapseChange = this._onDidCollapseChange.event;
this._onDidDestroy = new Emitter();
this.onDidDestroy = this._onDidDestroy.event;
this._label = (_options$label = options === null || options === void 0 ? void 0 : options.label) !== null && _options$label !== void 0 ? _options$label : "";
this._color = (options === null || options === void 0 ? void 0 : options.color) === "" ? void 0 : options === null || options === void 0 ? void 0 : options.color;
this._collapsed = (_options$collapsed = options === null || options === void 0 ? void 0 : options.collapsed) !== null && _options$collapsed !== void 0 ? _options$collapsed : false;
this._componentParams = options === null || options === void 0 ? void 0 : options.componentParams;
this.addDisposables(this._onDidChange, this._onDidPanelChange, this._onDidCollapseChange, this._onDidDestroy);
}
addPanel(panelId, index) {
if (this.isDisposed) return;
if (this._panelIds.includes(panelId)) return;
const insertIndex = index === void 0 ? this._panelIds.length : Math.max(0, Math.min(index, this._panelIds.length));
this._panelIds.splice(insertIndex, 0, panelId);
this._onDidPanelChange.fire({
panelId,
type: "add"
});
}
removePanel(panelId) {
if (this.isDisposed) return false;
const index = this._panelIds.indexOf(panelId);
if (index === -1) return false;
this._panelIds.splice(index, 1);
this._onDidPanelChange.fire({
panelId,
type: "remove"
});
return true;
}
indexOfPanel(panelId) {
return this._panelIds.indexOf(panelId);
}
containsPanel(panelId) {
return this._panelIds.includes(panelId);
}
collapse() {
if (this.isDisposed || this._collapsed) return;
this._collapsed = true;
this._onDidCollapseChange.fire(true);
}
expand() {
if (this.isDisposed || !this._collapsed) return;
this._collapsed = false;
this._onDidCollapseChange.fire(false);
}
toggle() {
if (this._collapsed) this.expand();
else this.collapse();
}
toJSON() {
const result = {
id: this.id,
collapsed: this._collapsed,
panelIds: [...this._panelIds]
};
if (this._label) result.label = this._label;
if (this._color !== void 0) result.color = this._color;
if (this._componentParams !== void 0) result.componentParams = this._componentParams;
return result;
}
dispose() {
this._onDidDestroy.fire();
super.dispose();
}
};
var DockviewDidDropEvent = class extends DockviewEvent {
/**
* `PointerEvent` for touch drags has no `dataTransfer`; use
* `getData()` for the dockview payload regardless of input method.
*/
get nativeEvent() {
return this.options.nativeEvent;
}
get position() {
return this.options.position;
}
get panel() {
return this.options.panel;
}
get group() {
return this.options.group;
}
get api() {
return this.options.api;
}
constructor(options) {
super();
this.options = options;
}
getData() {
return this.options.getData();
}
};
var DockviewWillDropEvent = class extends DockviewDidDropEvent {
get kind() {
return this._kind;
}
constructor(options) {
super(options);
this._kind = options.kind;
}
};
var DockviewGroupPanelModel = class extends CompositeDisposable {
get tabGroups() {
return this._tabGroups;
}
get element() {
throw new Error("dockview: not supported");
}
get activePanel() {
return this._activePanel;
}
/** DOM id of the content container (the group's tabpanel), referenced by each tab's `aria-controls`. */
get contentContainerId() {
return this.contentContainer.element.id;
}
/** The group's content drop target; lets keyboard docking preview a drop here. */
get contentDropTarget() {
return this.contentContainer.dropTarget;
}
get locked() {
return this._locked;
}
set locked(value) {
this._locked = value;
toggleClass(this.container, "dv-locked-groupview", value === "no-drop-target" || value);
}
get isActive() {
return this._isGroupActive;
}
get panels() {
return this._panels;
}
get size() {
return this._panels.length;
}
get isEmpty() {
return this._panels.length === 0;
}
get hasWatermark() {
return !!(this.watermark && this.container.contains(this.watermark.element));
}
get header() {
return this.tabsContainer;
}
/** The scrollable tab list element (`.dv-tabs-container`), exposed for the
* multi-row wrap controller to measure rows / toggle the wrap class. */
get tabsListElement() {
return this.tabsContainer.tabsListElement;
}
/** The panel whose tab owns `element` (the tab itself or a descendant of
* it), or `undefined` when the target isn't a tab. The robust inverse of a
* tab→panel lookup, with no positional/DOM-order assumptions. */
getPanelForTab(element) {
return this.tabsContainer.getPanelForTab(element);
}
get isContentFocused() {
if (!document.activeElement) return false;
return isAncestor(document.activeElement, this.contentContainer.element);
}
get headerPosition() {
var _this$_headerPosition;
return (_this$_headerPosition = this._headerPosition) !== null && _this$_headerPosition !== void 0 ? _this$_headerPosition : "top";
}
set headerPosition(value) {
var _this$_activePanel;
this._headerPosition = value;
this.invalidateHeaderSize();
removeClasses(this.container, "dv-groupview-header-top", "dv-groupview-header-bottom", "dv-groupview-header-left", "dv-groupview-header-right");
addClasses(this.container, `dv-groupview-header-${value}`);
const direction = value === "top" || value === "bottom" ? "horizontal" : "vertical";
const previousDirection = this._headerDirection;
this._headerDirection = direction;
this.tabsContainer.direction = direction;
this.header.direction = direction;
if ((_this$_activePanel = this._activePanel) === null || _this$_activePanel === void 0 ? void 0 : _this$_activePanel.layout) {
const { width, height } = this.contentDimensions();
this._activePanel.layout(width, height);
}
this.updateHeaderActions();
if (previousDirection !== void 0 && previousDirection !== direction) {
var _this$groupPanel;
(_this$groupPanel = this.groupPanel) === null || _this$groupPanel === void 0 || _this$groupPanel.api._onDidHeaderDirectionChange.fire({
direction,
position: value
});
}
}
get location() {
return this._location;
}
set location(value) {
this._location = value;
toggleClass(this.container, "dv-groupview-floating", false);
toggleClass(this.container, "dv-groupview-popout", false);
toggleClass(this.container, "dv-groupview-edge", false);
const applyZones = (zones) => {
this.contentContainer.dropTarget.setTargetZones(zones);
this.contentContainer.pointerDropTarget.setTargetZones(zones);
};
switch (value.type) {
case "grid":
applyZones([
"top",
"bottom",
"left",
"right",
"center"
]);
break;
case "floating":
applyZones([
"top",
"bottom",
"left",
"right",
"center"
]);
toggleClass(this.container, "dv-groupview-floating", true);
break;
case "popout":
applyZones([
"top",
"bottom",
"left",
"right",
"center"
]);
toggleClass(this.container, "dv-groupview-popout", true);
break;
case "edge":
applyZones(["center"]);
toggleClass(this.container, "dv-groupview-edge", true);
break;
}
this.groupPanel.api._onDidLocationChange.fire({ location: this.location });
}
constructor(container, accessor, id, options, groupPanel) {
var _options$locked, _options$headerPositi;
super();
this.container = container;
this.accessor = accessor;
this.id = id;
this.options = options;
this.groupPanel = groupPanel;
this._isGroupActive = false;
this._locked = false;
this._location = { type: "grid" };
this.mostRecentlyUsed = [];
this._overwriteRenderContainer = null;
this._overwriteDropTargetContainer = null;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._width = 0;
this._height = 0;
this._panels = [];
this._panelDisposables = /* @__PURE__ */ new Map();
this._tabGroupDisposables = /* @__PURE__ */ new Map();
this._pendingMicrotaskDisposables = /* @__PURE__ */ new Set();
this._onMove = new Emitter();
this.onMove = this._onMove.event;
this._onDidDrop = new Emitter();
this.onDidDrop = this._onDidDrop.event;
this._onWillDrop = new Emitter();
this.onWillDrop = this._onWillDrop.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._onTabDragStart = new Emitter();
this.onTabDragStart = this._onTabDragStart.event;
this._onGroupDragStart = new Emitter();
this.onGroupDragStart = this._onGroupDragStart.event;
this._onDidAddPanel = new Emitter();
this.onDidAddPanel = this._onDidAddPanel.event;
this._onDidPanelTitleChange = new Emitter();
this.onDidPanelTitleChange = this._onDidPanelTitleChange.event;
this._onDidPanelParametersChange = new Emitter();
this.onDidPanelParametersChange = this._onDidPanelParametersChange.event;
this._onDidRemovePanel = new Emitter();
this.onDidRemovePanel = this._onDidRemovePanel.event;
this._onDidActivePanelChange = new Emitter();
this.onDidActivePanelChange = this._onDidActivePanelChange.event;
this._onUnhandledDragOver = new Emitter();
this.onUnhandledDragOver = this._onUnhandledDragOver.event;
this._tabGroups = [];
this._tabGroupMap = /* @__PURE__ */ new Map();
this._panelToTabGroup = /* @__PURE__ */ new Map();
this._tabGroupIdCounter = 0;
this._pendingTabGroupUpdate = false;
this._onDidCreateTabGroup = new Emitter();
this.onDidCreateTabGroup = this._onDidCreateTabGroup.event;
this._onDidDestroyTabGroup = new Emitter();
this.onDidDestroyTabGroup = this._onDidDestroyTabGroup.event;
this._onDidAddPanelToTabGroup = new Emitter();
this.onDidAddPanelToTabGroup = this._onDidAddPanelToTabGroup.event;
this._onDidRemovePanelFromTabGroup = new Emitter();
this.onDidRemovePanelFromTabGroup = this._onDidRemovePanelFromTabGroup.event;
this._onDidTabGroupChange = new Emitter();
this.onDidTabGroupChange = this._onDidTabGroupChange.event;
this._onDidTabGroupCollapsedChange = new Emitter();
this.onDidTabGroupCollapsedChange = this._onDidTabGroupCollapsedChange.event;
toggleClass(this.container, "dv-groupview", true);
this.container.setAttribute("role", "region");
this._api = new DockviewApi(this.accessor);
this.tabsContainer = new TabsContainer(this.accessor, this.groupPanel);
this.contentContainer = new ContentContainer(this.accessor, this);
container.append(this.tabsContainer.element, this.contentContainer.element);
this.header.hidden = !!options.hideHeader;
this.locked = (_options$locked = options.locked) !== null && _options$locked !== void 0 ? _options$locked : false;
this.headerPosition = (_options$headerPositi = options.headerPosition) !== null && _options$headerPositi !== void 0 ? _options$headerPositi : accessor.defaultHeaderPosition;
this.addDisposables(watchElementResize(this.tabsContainer.element, () => {
if (this.isDisposed) return;
const size = this.measureHeaderSize();
if (size !== this._cachedHeaderSize) {
this._cachedHeaderSize = size;
this.layout(this._width, this._height);
}
}), this._onDidPanelTitleChange.event(() => this.updateAccessibleLabel()), this._onTabDragStart, this._onGroupDragStart, this._onWillShowOverlay, this.tabsContainer.onTabDragStart((event) => {
this._onTabDragStart.fire(event);
}), this.tabsContainer.onGroupDragStart((event) => {
this._onGroupDragStart.fire(event);
}), this.tabsContainer.onDrop((event) => {
var _dragData$panelId;
const dragData = getPanelData();
const draggedPanelId = (_dragData$panelId = dragData === null || dragData === void 0 ? void 0 : dragData.panelId) !== null && _dragData$panelId !== void 0 ? _dragData$panelId : null;
const resolvedIndex = draggedPanelId ? this.tabsContainer.resolveDropIndex(draggedPanelId, event.index) : event.index;
this.handleDropEvent("header", event.event, "center", resolvedIndex);
if (draggedPanelId && event.targetTabGroupId) {
const tabGroup = this._tabGroupMap.get(event.targetTabGroupId);
let localIndex;
if (tabGroup) {
const globalIdx = this._panels.findIndex((p) => p.id === draggedPanelId);
if (globalIdx !== -1) {
localIndex = 0;
for (const pid of tabGroup.panelIds) if (this._panels.findIndex((p) => p.id === pid) < globalIdx) localIndex++;
}
}
this.addPanelToTabGroup(event.targetTabGroupId, draggedPanelId, localIndex);
} else if (draggedPanelId && event.targetTabGroupId === null) this.removePanelFromTabGroup(draggedPanelId);
}), this.contentContainer.onDidFocus(() => {
this.accessor.doSetGroupActive(this.groupPanel);
}), this.contentContainer.onDidBlur(() => {}), this.contentContainer.dropTarget.onDrop((event) => {
this.handleDropEvent("content", event.nativeEvent, event.position, void 0, event.edge);
}), this.contentContainer.pointerDropTarget.onDrop((event) => {
this.handleDropEvent("content", event.nativeEvent, event.position, void 0, event.edge);
}), this.tabsContainer.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(event);
}), this.contentContainer.dropTarget.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(new DockviewWillShowOverlayLocationEvent(event, {
kind: "content",
panel: this.activePanel,
api: this._api,
group: this.groupPanel,
getData: getPanelData
}));
}), this.contentContainer.pointerDropTarget.onWillShowOverlay((event) => {
this._onWillShowOverlay.fire(new DockviewWillShowOverlayLocationEvent(event, {
kind: "content",
panel: this.activePanel,
api: this._api,
group: this.groupPanel,
getData: getPanelData
}));
}), this._onMove, this._onDidChange, this._onDidDrop, this._onWillDrop, this._onDidAddPanel, this._onDidRemovePanel, this._onDidActivePanelChange, this._onUnhandledDragOver, this._onDidPanelTitleChange, this._onDidPanelParametersChange, this._onDidCreateTabGroup, this._onDidDestroyTabGroup, this._onDidAddPanelToTabGroup, this._onDidRemovePanelFromTabGroup, this._onDidTabGroupChange, this._onDidTabGroupCollapsedChange, this._onDidCreateTabGroup.event(() => {
this._scheduleTabGroupUpdate();
}), this._onDidDestroyTabGroup.event(() => {
this._scheduleTabGroupUpdate();
}), this._onDidAddPanelToTabGroup.event(() => {
this._scheduleTabGroupUpdate();
}), this._onDidRemovePanelFromTabGroup.event(() => {
this._scheduleTabGroupUpdate();
}), this._onDidTabGroupChange.event(() => {
this._scheduleTabGroupUpdate();
}), this._onDidTabGroupCollapsedChange.event(() => {
this._scheduleTabGroupUpdate();
}));
}
_scheduleTabGroupUpdate() {
if (this._pendingTabGroupUpdate) return;
this._pendingTabGroupUpdate = true;
queueMicrotask(() => {
this._pendingTabGroupUpdate = false;
if (!this.isDisposed) this.tabsContainer.updateTabGroups();
});
}
/**
* Bracket a tab-group mutation as a layout transaction. `accessor` is
* always a full {@link DockviewComponent} in production; the optional-call
* fallback keeps partial test doubles (which omit `mutation`) working.
* When nested inside a larger operation (a drag-driven move, fromJSON
* restore) the component's depth counter folds it into the outer one.
*/
_bracketTabGroupMutation(func) {
return this.accessor.mutation ? this.accessor.mutation("tab-group", func) : func();
}
createTabGroup(options) {
return this._bracketTabGroupMutation(() => this._doCreateTabGroup(options));
}
_doCreateTabGroup(options) {
var _options$id;
const id = (_options$id = options === null || options === void 0 ? void 0 : options.id) !== null && _options$id !== void 0 ? _options$id : `tg-${this.id}-${this._tabGroupIdCounter++}`;
const tabGroup = new TabGroup(id, {
label: options === null || options === void 0 ? void 0 : options.label,
color: options === null || options === void 0 ? void 0 : options.color,
collapsed: options === null || options === void 0 ? void 0 : options.collapsed,
componentParams: options === null || options === void 0 ? void 0 : options.componentParams
});
this._tabGroups.push(tabGroup);
this._tabGroupMap.set(id, tabGroup);
this._tabGroupDisposables.set(id, new CompositeDisposable(tabGroup.onDidChange(() => {
this._onDidTabGroupChange.fire({ tabGroup });
}), tabGroup.onDidCollapseChange((isCollapsed) => {
if (isCollapsed) this._handleGroupCollapse(tabGroup);
else this._handleGroupExpand(tabGroup);
this._onDidTabGroupCollapsedChange.fire({ tabGroup });
}), tabGroup.onDidDestroy(() => {
this._removeTabGroupInternal(tabGroup);
})));
this._onDidCreateTabGroup.fire({ tabGroup });
return tabGroup;
}
dissolveTabGroup(tabGroupId) {
const tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup) return;
this._bracketTabGroupMutation(() => {
const panelIds = [...tabGroup.panelIds];
for (const panelId of panelIds) {
tabGroup.removePanel(panelId);
this._panelToTabGroup.delete(panelId);
this._onDidRemovePanelFromTabGroup.fire({
tabGroup,
panelId
});
}
tabGroup.dispose();
});
}
addPanelToTabGroup(tabGroupId, panelId, index) {
const tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup) return;
if (!this._panels.some((p) => p.id === panelId)) return;
const existingGroup = this.getTabGroupForPanel(panelId);
if ((existingGroup === null || existingGroup === void 0 ? void 0 : existingGroup.id) === tabGroupId) return;
this._bracketTabGroupMutation(() => {
if (existingGroup) this.removePanelFromTabGroup(panelId);
tabGroup.addPanel(panelId, index);
this._panelToTabGroup.set(panelId, tabGroup);
this._enforceContiguity(tabGroup, panelId);
this._onDidAddPanelToTabGroup.fire({
tabGroup,
panelId
});
});
}
/**
* Move a panel to a new index within its tab group.
* Updates both the group's panelIds order and the flat _panels array.
*/
movePanelWithinGroup(tabGroupId, panelId, newIndex) {
const tabGroup = this._tabGroupMap.get(tabGroupId);
if (!(tabGroup === null || tabGroup === void 0 ? void 0 : tabGroup.containsPanel(panelId))) return;
tabGroup.removePanel(panelId);
tabGroup.addPanel(panelId, newIndex);
this._enforceContiguity(tabGroup, panelId);
this.tabsContainer.updateTabGroups();
}
/**
* Move a panel from one tab group to another.
*/
movePanelBetweenGroups(sourcePanelId, targetTabGroupId, targetIndex) {
const sourceGroup = this._findTabGroupForPanel(sourcePanelId);
const targetGroup = this._tabGroupMap.get(targetTabGroupId);
if (!targetGroup) return;
if (sourceGroup) {
sourceGroup.removePanel(sourcePanelId);
this._panelToTabGroup.delete(sourcePanelId);
this._onDidRemovePanelFromTabGroup.fire({
tabGroup: sourceGroup,
panelId: sourcePanelId
});
if (sourceGroup.isEmpty) sourceGroup.dispose();
}
targetGroup.addPanel(sourcePanelId, targetIndex);
this._panelToTabGroup.set(sourcePanelId, targetGroup);
this._enforceContiguity(targetGroup, sourcePanelId);
this._onDidAddPanelToTabGroup.fire({
tabGroup: targetGroup,
panelId: sourcePanelId
});
}
/**
* Move an entire tab group to a new position in the tab bar.
* The group's internal panel order is preserved.
*/
moveTabGroup(tabGroupId, targetIndex) {
const tabGroup = this._tabGroupMap.get(tabGroupId);
if (!tabGroup || tabGroup.panelIds.length === 0) return;
const groupPanelIds = new Set(tabGroup.panelIds);
const groupPanels = tabGroup.panelIds.map((pid) => this._panels.find((p) => p.id === pid)).filter((p) => p !== void 0);
if (groupPanels.length === 0) return;
let groupPanelsBefore = 0;
for (let i = 0; i < Math.min(targetIndex, this._panels.length); i++) if (groupPanelIds.has(this._panels[i].id)) groupPanelsBefore++;
for (const panel of groupPanels) {
const idx = this._panels.indexOf(panel);
if (idx !== -1) this._panels.splice(idx, 1);
}
const adjustedIndex = targetIndex - groupPanelsBefore;
const insertAt = Math.max(0, Math.min(adjustedIndex, this._panels.length));
this._panels.splice(insertAt, 0, ...groupPanels);
for (const panel of this._panels) this.tabsContainer.delete(panel.id);
for (let i = 0; i < this._panels.length; i++) this.tabsContainer.openPanel(this._panels[i], i);
this.tabsContainer.updateTabGroups();
}
/**
* Ensure a panel is at the correct global index in _panels
* to maintain contiguity of its tab group members.
*/
_enforceContiguity(tabGroup, panelId) {
const panel = this._panels.find((p) => p.id === panelId);
if (!panel) return;
const localIndex = tabGroup.indexOfPanel(panelId);
const globalIndex = this._computeGlobalIndex(tabGroup, localIndex);
const currentIndex = this._panels.indexOf(panel);
if (currentIndex === globalIndex) return;
this._panels.splice(currentIndex, 1);
const adjustedIndex = globalIndex > currentIndex ? globalIndex - 1 : globalIndex;
this._panels.splice(adjustedIndex, 0, panel);
this.tabsContainer.delete(panelId);
this.tabsContainer.openPanel(panel, adjustedIndex);
}
/**
* Compute the global index in _panels for a group-local index.
* Finds where the group's panels start in the flat array and offsets.
*/
_computeGlobalIndex(tabGroup, localIndex) {
const groupPanelIds = tabGroup.panelIds;
if (groupPanelIds.length <= 1) {
const panel = this._panels.find((p) => p.id === groupPanelIds[0]);
return panel ? this._panels.indexOf(panel) : this._panels.length;
}
for (let i = 0; i < groupPanelIds.length; i++) {
if (i === localIndex) continue;
const existingPanel = this._panels.find((p) => p.id === groupPanelIds[i]);
if (existingPanel) {
const existingGlobalIndex = this._panels.indexOf(existingPanel);
return Math.max(0, existingGlobalIndex + (localIndex - i));
}
}
return this._panels.length;
}
removePanelFromTabGroup(panelId) {
const tabGroup = this._findTabGroupForPanel(panelId);
if (!tabGroup) return;
this._bracketTabGroupMutation(() => {
tabGroup.removePanel(panelId);
this._panelToTabGroup.delete(panelId);
this._onDidRemovePanelFromTabGroup.fire({
tabGroup,
panelId
});
if (tabGroup.isEmpty) tabGroup.dispose();
});
}
getTabGroups() {
return this._tabGroups;
}
updateTabGroups() {
this.tabsContainer.updateTabGroups();
}
refreshTabGroupAccent() {
this.tabsContainer.refreshTabGroupAccent();
}
refreshWatermark() {
if (this.watermark) {
var _this$watermark$dispo, _this$watermark;
this.watermark.element.remove();
(_this$watermark$dispo = (_this$watermark = this.watermark).dispose) === null || _this$watermark$dispo === void 0 || _this$watermark$dispo.call(_this$watermark);
this.watermark = void 0;
}
this.updateContainer();
}
getTabGroupForPanel(panelId) {
return this._findTabGroupForPanel(panelId);
}
_findTabGroupForPanel(panelId) {
return this._panelToTabGroup.get(panelId);
}
_removeTabGroupInternal(tabGroup) {
const index = this._tabGroups.indexOf(tabGroup);
if (index !== -1) {
this._tabGroups.splice(index, 1);
this._tabGroupMap.delete(tabGroup.id);
for (const panelId of tabGroup.panelIds) this._panelToTabGroup.delete(panelId);
this._onDidDestroyTabGroup.fire({ tabGroup });
const tabGroupDisposable = this._tabGroupDisposables.get(tabGroup.id);
this._tabGroupDisposables.delete(tabGroup.id);
if (tabGroupDisposable) {
this._pendingMicrotaskDisposables.add(tabGroupDisposable);
queueMicrotask(() => {
this._pendingMicrotaskDisposables.delete(tabGroupDisposable);
tabGroupDisposable.dispose();
});
}
}
}
_handleGroupCollapse(tabGroup) {
if (!this._activePanel) return;
if (!tabGroup.containsPanel(this._activePanel.id)) return;
const activePanelIndex = this._panels.indexOf(this._activePanel);
for (let i = activePanelIndex + 1; i < this._panels.length; i++) {
const candidate = this._panels[i];
const candidateGroup = this._findTabGroupForPanel(candidate.id);
if (!(candidateGroup === null || candidateGroup === void 0 ? void 0 : candidateGroup.collapsed)) {
this.doSetActivePanel(candidate);
this.updateContainer();
return;
}
}
for (let i = activePanelIndex - 1; i >= 0; i--) {
const candidate = this._panels[i];
const candidateGroup = this._findTabGroupForPanel(candidate.id);
if (!(candidateGroup === null || candidateGroup === void 0 ? void 0 : candidateGroup.collapsed)) {
this.doSetActivePanel(candidate);
this.updateContainer();
return;
}
}
this.contentContainer.closePanel();
this.doSetActivePanel(void 0);
this.updateContainer();
}
_handleGroupExpand(tabGroup) {
if (this._activePanel) return;
const firstPanelId = tabGroup.panelIds[0];
if (firstPanelId) {
const panel = this._panels.find((p) => p.id === firstPanelId);
if (panel) {
this.doSetActivePanel(panel);
this.updateContainer();
}
}
}
/** Restore tab groups from serialized data (used by fromJSON) */
restoreTabGroups(serializedGroups) {
for (const data of serializedGroups) {
const match = /-(\d+)$/.exec(data.id);
if (match) {
const num = Number.parseInt(match[1], 10) + 1;
if (num > this._tabGroupIdCounter) this._tabGroupIdCounter = num;
}
}
for (const data of serializedGroups) {
const tabGroup = this.createTabGroup({
id: data.id,
label: data.label,
color: data.color,
componentParams: data.componentParams
});
const concreteGroup = this._tabGroupMap.get(tabGroup.id);
for (const panelId of data.panelIds) if (this._panels.some((p) => p.id === panelId)) {
tabGroup.addPanel(panelId);
this._panelToTabGroup.set(panelId, concreteGroup);
this._enforceContiguity(concreteGroup, panelId);
}
if (data.collapsed) tabGroup.collapse();
if (tabGroup.isEmpty) tabGroup.dispose();
}
}
focusContent() {
this.contentContainer.element.focus();
}
focusActiveTab() {
this.tabsContainer.focusActiveTab();
}
set renderContainer(value) {
this.panels.forEach((panel) => {
this.renderContainer.detatch(panel);
});
this._overwriteRenderContainer = value;
this.panels.forEach((panel) => {
this.rerender(panel);
});
if (this._activePanel) {
this.contentContainer.renderPanel(this._activePanel, { asActive: true });
const { width, height } = this.contentDimensions();
this._activePanel.layout(width, height);
}
}
get renderContainer() {
var _this$_overwriteRende;
return (_this$_overwriteRende = this._overwriteRenderContainer) !== null && _this$_overwriteRende !== void 0 ? _this$_overwriteRende : this.accessor.overlayRenderContainer;
}
set dropTargetContainer(value) {
this._overwriteDropTargetContainer = value;
}
get dropTargetContainer() {
var _this$_overwriteDropT;
if (this._location.type === "floating" && this.accessor.rootDropTargetContainer.disabled) return this.accessor.floatingDropTargetContainer;
return (_this$_overwriteDropT = this._overwriteDropTargetContainer) !== null && _this$_overwriteDropT !== void 0 ? _this$_overwriteDropT : this.accessor.rootDropTargetContainer;
}
initialize() {
if (this.options.panels) this.options.panels.forEach((panel) => {
this.doAddPanel(panel);
});
if (this.options.activePanel) this.openPanel(this.options.activePanel);
this.setActive(this.isActive, true);
this.updateContainer();
this.updateHeaderActions();
}
updateHeaderActions() {
var _this$accessor$header;
(_this$accessor$header = this.accessor.headerActionsService) === null || _this$accessor$header === void 0 || _this$accessor$header.refresh(this.groupPanel);
}
attachHeaderAction(slot, element) {
switch (slot) {
case "left":
this.tabsContainer.setLeftActionsElement(element);
break;
case "right":
this.tabsContainer.setRightActionsElement(element);
break;
case "prefix":
this.tabsContainer.setPrefixActionsElement(element);
break;
}
}
rerender(panel) {
this.contentContainer.renderPanel(panel, { asActive: false });
}
indexOf(panel) {
return this.tabsContainer.indexOf(panel.id);
}
toJSON() {
var _this$_activePanel2;
const result = {
views: this.tabsContainer.panels,
activeView: (_this$_activePanel2 = this._activePanel) === null || _this$_activePanel2 === void 0 ? void 0 : _this$_activePanel2.id,
id: this.id
};
if (this.locked !== false) result.locked = this.locked;
if (this.header.hidden) result.hideHeader = true;
if (this.headerPosition !== "top") result.headerPosition = this.headerPosition;
if (this._tabGroups.length > 0) result.tabGroups = this._tabGroups.map((tg) => tg.toJSON());
return result;
}
moveToNext(options) {
var _options, _options2, _options2$panel;
(_options = options) !== null && _options !== void 0 || (options = {});
(_options2$panel = (_options2 = options).panel) !== null && _options2$panel !== void 0 || (_options2.panel = this.activePanel);
const index = options.panel ? this.panels.indexOf(options.panel) : -1;
let normalizedIndex;
if (index < this.panels.length - 1) normalizedIndex = index + 1;
else if (options.suppressRoll) return;
else normalizedIndex = 0;
this.openPanel(this.panels[normalizedIndex]);
}
moveToPrevious(options) {
var _options3, _options4, _options4$panel;
(_options3 = options) !== null && _options3 !== void 0 || (options = {});
(_options4$panel = (_options4 = options).panel) !== null && _options4$panel !== void 0 || (_options4.panel = this.activePanel);
if (!options.panel) return;
const index = this.panels.indexOf(options.panel);
let normalizedIndex;
if (index > 0) normalizedIndex = index - 1;
else if (options.suppressRoll) return;
else normalizedIndex = this.panels.length - 1;
this.openPanel(this.panels[normalizedIndex]);
}
containsPanel(panel) {
return this.panels.includes(panel);
}
init(_params) {}
update(_params) {}
focus() {
var _this$_activePanel3;
(_this$_activePanel3 = this._activePanel) === null || _this$_activePanel3 === void 0 || _this$_activePanel3.focus();
}
openPanel(panel, options = {}) {
if (typeof options.index !== "number" || options.index > this.panels.length) options.index = this.panels.length;
const skipSetActive = !!options.skipSetActive;
panel.updateParentGroup(this.groupPanel, { skipSetActive: options.skipSetActive });
this.doAddPanel(panel, options.index, { skipSetActive });
this.invalidateHeaderSize();
if (this._activePanel === panel) {
this.contentContainer.renderPanel(panel, { asActive: true });
return;
}
if (!skipSetActive) this.doSetActivePanel(panel);
if (!options.skipSetGroupActive) this.accessor.doSetGroupActive(this.groupPanel);
if (!options.skipSetActive) this.updateContainer();
}
removePanel(groupItemOrId, options) {
const id = typeof groupItemOrId === "string" ? groupItemOrId : groupItemOrId.id;
const panelToRemove = this._panels.find((panel) => panel.id === id);
if (!panelToRemove) throw new Error("invalid operation");
return this._removePanel(panelToRemove, options);
}
closeAllPanels() {
if (this.panels.length > 0) {
const arrPanelCpy = [...this.panels];
for (const panel of arrPanelCpy) this.doClose(panel);
} else this.accessor.removeGroup(this.groupPanel);
}
closePanel(panel) {
this.doClose(panel);
}
doClose(panel) {
const isLast = this.panels.length === 1 && this.accessor.groups.length === 1;
this.accessor.removePanel(panel, isLast && this.accessor.options.noPanelsOverlay === "emptyGroup" ? { removeEmptyGroup: false } : void 0);
}
isPanelActive(panel) {
return this._activePanel === panel;
}
updateActions(element) {
this.tabsContainer.setRightActionsElement(element);
}
setActive(isGroupActive, force = false) {
if (!force && this.isActive === isGroupActive) return;
this._isGroupActive = isGroupActive;
toggleClass(this.container, "dv-active-group", isGroupActive);
toggleClass(this.container, "dv-inactive-group", !isGroupActive);
this.tabsContainer.setActive(this.isActive);
if (!this._activePanel && this.panels.length > 0) {
const candidate = this._panels.find((p) => {
const tg = this._findTabGroupForPanel(p.id);
return !(tg === null || tg === void 0 ? void 0 : tg.collapsed);
});
if (candidate) this.doSetActivePanel(candidate);
}
this.updateContainer();
}
layout(width, height) {
var _this$_activePanel4;
this._width = width;
this._height = height;
const { width: contentWidth, height: contentHeight } = this.contentDimensions();
this.contentContainer.layout(contentWidth, contentHeight);
if ((_this$_activePanel4 = this._activePanel) === null || _this$_activePanel4 === void 0 ? void 0 : _this$_activePanel4.layout) this._activePanel.layout(contentWidth, contentHeight);
}
/**
* Re-run the group's layout with its current dimensions. Used to propagate a
* header-size change (e.g. a header that grew/shrank without the group box
* changing) down to the content + active panel.
*/
relayout() {
this.invalidateHeaderSize();
this.layout(this._width, this._height);
}
/**
* The dimensions available to the content/panel: the group box minus the
* header along its axis. The header (`dv-tabs-and-actions-container`) is
* laid out top/bottom (subtract its height) or left/right (subtract its
* width); a hidden header has `display:none` so its `offset*` is 0 and
* nothing is subtracted.
*/
/**
* Measure the header's extent along its occupied axis. This is the single
* forced-reflow read; it now runs only on a cache miss (first layout after
* a header change) and inside the header's ResizeObserver — not on every
* layout frame.
*/
measureHeaderSize() {
const headerEl = this.tabsContainer.element;
return this.headerPosition === "top" || this.headerPosition === "bottom" ? headerEl.offsetHeight : headerEl.offsetWidth;
}
invalidateHeaderSize() {
this._cachedHeaderSize = void 0;
}
contentDimensions() {
var _this$_cachedHeaderSi;
const horizontal = this.headerPosition === "top" || this.headerPosition === "bottom";
const headerSize = (_this$_cachedHeaderSi = this._cachedHeaderSize) !== null && _this$_cachedHeaderSi !== void 0 ? _this$_cachedHeaderSi : this._cachedHeaderSize = this.measureHeaderSize();
return {
width: horizontal ? this._width : Math.max(0, this._width - headerSize),
height: horizontal ? Math.max(0, this._height - headerSize) : this._height
};
}
_removePanel(panel, options) {
const isActivePanel = this._activePanel === panel;
this.doRemovePanel(panel);
this.invalidateHeaderSize();
if (isActivePanel && this.panels.length > 0) {
const nextPanel = this.mostRecentlyUsed[0];
this.openPanel(nextPanel, {
skipSetActive: options === null || options === void 0 ? void 0 : options.skipSetActive,
skipSetGroupActive: options === null || options === void 0 ? void 0 : options.skipSetActiveGroup
});
}
if (this._activePanel && this.panels.length === 0) this.doSetActivePanel(void 0);
if (!(options === null || options === void 0 ? void 0 : options.skipSetActive)) this.updateContainer();
return panel;
}
doRemovePanel(panel) {
const index = this.panels.indexOf(panel);
if (this._activePanel === panel) this.contentContainer.closePanel();
this.tabsContainer.delete(panel.id);
this._panels.splice(index, 1);
if (this.mostRecentlyUsed.includes(panel)) {
const index = this.mostRecentlyUsed.indexOf(panel);
this.mostRecentlyUsed.splice(index, 1);
}
const disposable = this._panelDisposables.get(panel.id);
if (disposable) {
disposable.dispose();
this._panelDisposables.delete(panel.id);
}
this.removePanelFromTabGroup(panel.id);
this._onDidRemovePanel.fire({ panel });
}
doAddPanel(panel, index = this.panels.length, options) {
const hasExistingPanel = this._panels.indexOf(panel) > -1;
this.tabsContainer.show();
this.contentContainer.show();
this.tabsContainer.openPanel(panel, index);
if (!(options === null || options === void 0 ? void 0 : options.skipSetActive)) this.contentContainer.openPanel(panel);
else if (panel.api.renderer === "always") this.contentContainer.renderPanel(panel, { asActive: false });
if (hasExistingPanel) return;
this.updateMru(panel);
this.panels.splice(index, 0, panel);
this._panelDisposables.set(panel.id, new CompositeDisposable(panel.api.onDidTitleChange((event) => this._onDidPanelTitleChange.fire(event)), panel.api.onDidParametersChange((event) => this._onDidPanelParametersChange.fire(event))));
this._onDidAddPanel.fire({ panel });
}
doSetActivePanel(panel) {
if (this._activePanel === panel) return;
this._activePanel = panel;
if (panel) {
var _this$accessor$curren, _this$accessor$curren2, _this$accessor;
this.tabsContainer.setActivePanel(panel);
this.contentContainer.setLabelledBy(this.tabsContainer.getTabId(panel.id));
this.contentContainer.openPanel(panel);
const { width, height } = this.contentDimensions();
panel.layout(width, height);
this.updateMru(panel);
this.contentContainer.refreshFocusState();
this._onDidActivePanelChange.fire({
panel,
origin: (_this$accessor$curren = (_this$accessor$curren2 = (_this$accessor = this.accessor).currentOrigin) === null || _this$accessor$curren2 === void 0 ? void 0 : _this$accessor$curren2.call(_this$accessor)) !== null && _this$accessor$curren !== void 0 ? _this$accessor$curren : "user"
});
}
this.updateAccessibleLabel();
}
/** Label the group region with its active panel's title (the WAI-ARIA region name). */
updateAccessibleLabel() {
var _this$_activePanel5;
const title = (_this$_activePanel5 = this._activePanel) === null || _this$_activePanel5 === void 0 ? void 0 : _this$_activePanel5.title;
if (title) this.container.setAttribute("aria-label", title);
else this.container.removeAttribute("aria-label");
}
updateMru(panel) {
if (this.mostRecentlyUsed.includes(panel)) this.mostRecentlyUsed.splice(this.mostRecentlyUsed.indexOf(panel), 1);
this.mostRecentlyUsed = [panel, ...this.mostRecentlyUsed];
}
updateContainer() {
this.panels.forEach((panel) => panel.runEvents());
const shouldShowWatermark = this.isEmpty || !this._activePanel;
if (shouldShowWatermark && !this.watermark) {
const watermark = this.accessor.createWatermarkComponent();
watermark.init({
containerApi: this._api,
group: this.groupPanel
});
this.watermark = watermark;
addDisposableListener(this.watermark.element, "pointerdown", () => {
if (!this.isActive) this.accessor.doSetGroupActive(this.groupPanel);
});
this.contentContainer.element.appendChild(this.watermark.element);
}
if (!shouldShowWatermark && this.watermark) {
var _this$watermark$dispo2, _this$watermark2;
this.watermark.element.remove();
(_this$watermark$dispo2 = (_this$watermark2 = this.watermark).dispose) === null || _this$watermark$dispo2 === void 0 || _this$watermark$dispo2.call(_this$watermark2);
this.watermark = void 0;
}
}
canDisplayOverlay(event, position, target) {
const firedEvent = new DockviewUnhandledDragOverEvent(event, target, position, getPanelData, this.accessor.getPanel(this.id));
this._onUnhandledDragOver.fire(firedEvent);
return firedEvent.isAccepted;
}
/**
* Whether a content drop at `position` is allowed: the locked rules, the
* shift-to-not-drop gesture, the same-component shortcut, then the
* `canDisplayOverlay` veto. The single source of truth for the content drop
* target (`content.ts`) and the compass cell gating (`canDropOnGroup`).
*/
canDisplayContentOverlay(event, position) {
if (this.locked === "no-drop-target" || this.locked && position === "center") return false;
const data = getPanelData();
if (!data && event.shiftKey && this.location.type !== "floating") return false;
if ((data === null || data === void 0 ? void 0 : data.viewId) === this.accessor.id) return true;
return this.canDisplayOverlay(event, position, "content");
}
handleDropEvent(type, event, position, index, edge) {
if (this.locked === "no-drop-target") return;
if (type === "content" && edge) {
this.accessor.dockToLayoutEdge(event, position);
return;
}
function getKind() {
switch (type) {
case "header": return typeof index === "number" ? "tab" : "header_space";
case "content": return "content";
}
}
const panel = typeof index === "number" ? this.panels[index] : void 0;
const willDropEvent = new DockviewWillDropEvent({
nativeEvent: event,
position,
panel,
getData: () => getPanelData(),
kind: getKind(),
group: this.groupPanel,
api: this._api
});
this._onWillDrop.fire(willDropEvent);
if (willDropEvent.defaultPrevented) return;
const data = getPanelData();
if ((data === null || data === void 0 ? void 0 : data.viewId) === this.accessor.id) {
if (type === "content") {
if (data.groupId === this.id) {
if (position === "center") return;
if (data.panelId === null && !data.tabGroupId) return;
}
}
if (type === "header") {
if (data.groupId === this.id) {
if (data.panelId === null && !data.tabGroupId) return;
}
}
if (data.panelId === null) {
const { groupId } = data;
this._onMove.fire({
target: position,
groupId,
index,
tabGroupId: data.tabGroupId
});
return;
}
if (this.tabsContainer.indexOf(data.panelId) !== -1 && this.tabsContainer.size === 1) return;
const { groupId, panelId } = data;
if (this.id === groupId && !position) {
if (this.tabsContainer.indexOf(panelId) === index) return;
}
this._onMove.fire({
target: position,
groupId: data.groupId,
itemId: data.panelId,
index
});
} else this._onDidDrop.fire(new DockviewDidDropEvent({
nativeEvent: event,
position,
panel,
getData: () => getPanelData(),
group: this.groupPanel,
api: this._api
}));
}
updateDragAndDropState() {
this.tabsContainer.updateDragAndDropState();
}
dispose() {
var _this$watermark3, _this$watermark4, _this$watermark4$disp;
super.dispose();
(_this$watermark3 = this.watermark) === null || _this$watermark3 === void 0 || _this$watermark3.element.remove();
(_this$watermark4 = this.watermark) === null || _this$watermark4 === void 0 || (_this$watermark4$disp = _this$watermark4.dispose) === null || _this$watermark4$disp === void 0 || _this$watermark4$disp.call(_this$watermark4);
this.watermark = void 0;
for (const tabGroup of [...this._tabGroups]) tabGroup.dispose();
for (const disposable of this._tabGroupDisposables.values()) disposable.dispose();
this._tabGroupDisposables.clear();
for (const disposable of this._pendingMicrotaskDisposables) disposable.dispose();
this._pendingMicrotaskDisposables.clear();
for (const panel of this.panels) panel.dispose();
this.tabsContainer.dispose();
this.contentContainer.dispose();
}
};
var GridviewPanelApiImpl = class extends PanelApiImpl {
constructor(id, component, panel) {
super(id, component);
this._onDidConstraintsChangeInternal = new Emitter();
this.onDidConstraintsChangeInternal = this._onDidConstraintsChangeInternal.event;
this._onDidConstraintsChange = new Emitter();
this.onDidConstraintsChange = this._onDidConstraintsChange.event;
this._onDidSizeChange = new Emitter();
this.onDidSizeChange = this._onDidSizeChange.event;
this.addDisposables(this._onDidConstraintsChangeInternal, this._onDidConstraintsChange, this._onDidSizeChange);
if (panel) this.initialize(panel);
}
setConstraints(value) {
this._onDidConstraintsChangeInternal.fire(value);
}
setSize(event) {
this._onDidSizeChange.fire(event);
}
};
var GridviewPanel = class extends BasePanelView {
get priority() {
return this._priority;
}
get snap() {
return this._snap;
}
get minimumWidth() {
/**
* defer to protected function to allow subclasses to override easily.
* see https://github.com/microsoft/TypeScript/issues/338
*/
return this.__minimumWidth();
}
get minimumHeight() {
/**
* defer to protected function to allow subclasses to override easily.
* see https://github.com/microsoft/TypeScript/issues/338
*/
return this.__minimumHeight();
}
get maximumHeight() {
/**
* defer to protected function to allow subclasses to override easily.
* see https://github.com/microsoft/TypeScript/issues/338
*/
return this.__maximumHeight();
}
get maximumWidth() {
/**
* defer to protected function to allow subclasses to override easily.
* see https://github.com/microsoft/TypeScript/issues/338
*/
return this.__maximumWidth();
}
__minimumWidth() {
const width = typeof this._minimumWidth === "function" ? this._minimumWidth() : this._minimumWidth;
if (width !== this._evaluatedMinimumWidth) {
this._evaluatedMinimumWidth = width;
this.updateConstraints();
}
return width;
}
__maximumWidth() {
const width = typeof this._maximumWidth === "function" ? this._maximumWidth() : this._maximumWidth;
if (width !== this._evaluatedMaximumWidth) {
this._evaluatedMaximumWidth = width;
this.updateConstraints();
}
return width;
}
__minimumHeight() {
const height = typeof this._minimumHeight === "function" ? this._minimumHeight() : this._minimumHeight;
if (height !== this._evaluatedMinimumHeight) {
this._evaluatedMinimumHeight = height;
this.updateConstraints();
}
return height;
}
__maximumHeight() {
const height = typeof this._maximumHeight === "function" ? this._maximumHeight() : this._maximumHeight;
if (height !== this._evaluatedMaximumHeight) {
this._evaluatedMaximumHeight = height;
this.updateConstraints();
}
return height;
}
get isActive() {
return this.api.isActive;
}
get isVisible() {
return this.api.isVisible;
}
constructor(id, component, options, api) {
super(id, component, api !== null && api !== void 0 ? api : new GridviewPanelApiImpl(id, component));
this._evaluatedMinimumWidth = 0;
this._evaluatedMaximumWidth = Number.MAX_SAFE_INTEGER;
this._evaluatedMinimumHeight = 0;
this._evaluatedMaximumHeight = Number.MAX_SAFE_INTEGER;
this._minimumWidth = 0;
this._minimumHeight = 0;
this._maximumWidth = Number.MAX_SAFE_INTEGER;
this._maximumHeight = Number.MAX_SAFE_INTEGER;
this._snap = false;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
if (typeof (options === null || options === void 0 ? void 0 : options.minimumWidth) === "number") this._minimumWidth = options.minimumWidth;
if (typeof (options === null || options === void 0 ? void 0 : options.maximumWidth) === "number") this._maximumWidth = options.maximumWidth;
if (typeof (options === null || options === void 0 ? void 0 : options.minimumHeight) === "number") this._minimumHeight = options.minimumHeight;
if (typeof (options === null || options === void 0 ? void 0 : options.maximumHeight) === "number") this._maximumHeight = options.maximumHeight;
this.api.initialize(this);
this.addDisposables(this.api.onWillVisibilityChange((event) => {
const { isVisible } = event;
const { accessor } = this._params;
accessor.setVisible(this, isVisible);
}), this.api.onActiveChange(() => {
const { accessor } = this._params;
accessor.doSetGroupActive(this);
}), this.api.onDidConstraintsChangeInternal((event) => {
if (typeof event.minimumWidth === "number" || typeof event.minimumWidth === "function") this._minimumWidth = event.minimumWidth;
if (typeof event.minimumHeight === "number" || typeof event.minimumHeight === "function") this._minimumHeight = event.minimumHeight;
if (typeof event.maximumWidth === "number" || typeof event.maximumWidth === "function") this._maximumWidth = event.maximumWidth;
if (typeof event.maximumHeight === "number" || typeof event.maximumHeight === "function") this._maximumHeight = event.maximumHeight;
this._onDidChange.fire(void 0);
}), this.api.onDidSizeChange((event) => {
this._onDidChange.fire({
height: event.height,
width: event.width
});
}), this._onDidChange);
}
setVisible(isVisible) {
this.api._onDidVisibilityChange.fire({ isVisible });
}
setActive(isActive) {
this.api._onDidActiveChange.fire({ isActive });
}
init(parameters) {
if (parameters.maximumHeight) this._maximumHeight = parameters.maximumHeight;
if (parameters.minimumHeight) this._minimumHeight = parameters.minimumHeight;
if (parameters.maximumWidth) this._maximumWidth = parameters.maximumWidth;
if (parameters.minimumWidth) this._minimumWidth = parameters.minimumWidth;
this._priority = parameters.priority;
this._snap = !!parameters.snap;
super.init(parameters);
if (typeof parameters.isVisible === "boolean") this.setVisible(parameters.isVisible);
}
updateConstraints() {
this.api._onDidConstraintsChange.fire({
minimumWidth: this._evaluatedMinimumWidth,
maximumWidth: this._evaluatedMaximumWidth,
minimumHeight: this._evaluatedMinimumHeight,
maximumHeight: this._evaluatedMaximumHeight
});
}
toJSON() {
const state = super.toJSON();
const maximum = (value) => value === Number.MAX_SAFE_INTEGER ? void 0 : value;
const minimum = (value) => value <= 0 ? void 0 : value;
return _objectSpread2(_objectSpread2({}, state), {}, {
minimumHeight: minimum(this.minimumHeight),
maximumHeight: maximum(this.maximumHeight),
minimumWidth: minimum(this.minimumWidth),
maximumWidth: maximum(this.maximumWidth),
snap: this.snap,
priority: this.priority
});
}
};
const NOT_INITIALIZED_MESSAGE = "dockview: DockviewGroupPanelApiImpl not initialized";
var DockviewGroupPanelApiImpl = class extends GridviewPanelApiImpl {
get location() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
return this._group.model.location;
}
/**
* The group's bounding box relative to the top-left of the dockview root,
* in pixels. Covers grid and floating groups; returns `undefined` for a
* popout group (it lives in a separate window). Reflects the live rendered
* geometry, so it is only meaningful once the layout has been sized.
*/
get boundingBox() {
if (!this._group || this._group.model.location.type === "popout") return;
const root = this.accessor.element.getBoundingClientRect();
const rect = this._group.element.getBoundingClientRect();
return {
left: rect.left - root.left,
top: rect.top - root.top,
width: rect.width,
height: rect.height
};
}
get locked() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
return this._group.locked;
}
set locked(value) {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
this._group.locked = value;
}
constructor(id, accessor) {
super(id, "__dockviewgroup__");
this.accessor = accessor;
this._onDidLocationChange = new Emitter();
this.onDidLocationChange = this._onDidLocationChange.event;
this._onDidActivePanelChange = new Emitter();
this.onDidActivePanelChange = this._onDidActivePanelChange.event;
this._onDidCollapsedChange = new Emitter();
this.onDidCollapsedChange = this._onDidCollapsedChange.event;
this._onDidPeekChange = new Emitter();
this.onDidPeekChange = this._onDidPeekChange.event;
this._onDidHeaderDirectionChange = new Emitter();
this.onDidHeaderDirectionChange = this._onDidHeaderDirectionChange.event;
this.addDisposables(this._onDidLocationChange, this._onDidActivePanelChange, this._onDidCollapsedChange, this._onDidPeekChange, this._onDidHeaderDirectionChange, this._onDidVisibilityChange.event((event) => {
if (event.isVisible && this._pendingSize) {
super.setSize(this._pendingSize);
this._pendingSize = void 0;
}
}));
}
setSize(event) {
this._pendingSize = _objectSpread2({}, event);
super.setSize(event);
}
close() {
if (!this._group) return;
return this.accessor.removeGroup(this._group);
}
getWindow() {
return this.location.type === "popout" ? this.location.getWindow() : globalThis.window;
}
setHeaderPosition(position) {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
this._group.model.headerPosition = position;
}
getHeaderPosition() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
return this._group.model.headerPosition;
}
moveTo(options) {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
this.accessor.withOrigin("api", () => {
var _options$group, _options$position, _options$skipSetActiv, _options$position2;
const group = (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this.accessor.addGroup({
direction: positionToDirection((_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "right"),
skipSetActive: (_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false
});
this.accessor.moveGroupOrPanel({
from: { groupId: this._group.id },
to: {
group,
position: options.group ? (_options$position2 = options.position) !== null && _options$position2 !== void 0 ? _options$position2 : "center" : "center",
index: options.index
},
skipSetActive: options.skipSetActive
});
});
}
maximize() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
if (this.location.type !== "grid") return;
this.accessor.maximizeGroup(this._group);
}
isMaximized() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
return this.accessor.isMaximizedGroup(this._group);
}
exitMaximized() {
if (!this._group) throw new Error(NOT_INITIALIZED_MESSAGE);
if (this.isMaximized()) this.accessor.exitMaximizedGroup();
}
collapse() {
if (!this._group) return;
this.accessor.setEdgeGroupCollapsed(this._group, true);
}
expand() {
if (!this._group) return;
this.accessor.setEdgeGroupCollapsed(this._group, false);
}
isCollapsed() {
if (!this._group) return false;
return this.accessor.isEdgeGroupCollapsed(this._group);
}
isPeeking() {
if (!this._group) return false;
return this.accessor.isEdgeGroupPeeking(this._group);
}
setAutoHide(value) {
if (!this._group) return;
this.accessor.setEdgeGroupAutoHide(this._group, value);
}
isAutoHide() {
if (!this._group) return false;
return this.accessor.isEdgeGroupAutoHide(this._group);
}
initialize(group) {
this._group = group;
}
};
const MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH = 100;
const MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT = 100;
var DockviewGroupPanel = class extends GridviewPanel {
get minimumWidth() {
var _this$activePanel;
if (typeof this._explicitConstraints.minimumWidth === "number") return this._explicitConstraints.minimumWidth;
const activePanelMinimumWidth = (_this$activePanel = this.activePanel) === null || _this$activePanel === void 0 ? void 0 : _this$activePanel.minimumWidth;
if (typeof activePanelMinimumWidth === "number") return activePanelMinimumWidth;
return super.__minimumWidth();
}
get minimumHeight() {
var _this$activePanel2;
if (typeof this._explicitConstraints.minimumHeight === "number") return this._explicitConstraints.minimumHeight;
const activePanelMinimumHeight = (_this$activePanel2 = this.activePanel) === null || _this$activePanel2 === void 0 ? void 0 : _this$activePanel2.minimumHeight;
if (typeof activePanelMinimumHeight === "number") return activePanelMinimumHeight;
return super.__minimumHeight();
}
get maximumWidth() {
var _this$activePanel3;
if (typeof this._explicitConstraints.maximumWidth === "number") return this._explicitConstraints.maximumWidth;
const activePanelMaximumWidth = (_this$activePanel3 = this.activePanel) === null || _this$activePanel3 === void 0 ? void 0 : _this$activePanel3.maximumWidth;
if (typeof activePanelMaximumWidth === "number") return activePanelMaximumWidth;
return super.__maximumWidth();
}
get maximumHeight() {
var _this$activePanel4;
if (typeof this._explicitConstraints.maximumHeight === "number") return this._explicitConstraints.maximumHeight;
const activePanelMaximumHeight = (_this$activePanel4 = this.activePanel) === null || _this$activePanel4 === void 0 ? void 0 : _this$activePanel4.maximumHeight;
if (typeof activePanelMaximumHeight === "number") return activePanelMaximumHeight;
return super.__maximumHeight();
}
get panels() {
return this._model.panels;
}
get activePanel() {
return this._model.activePanel;
}
get size() {
return this._model.size;
}
get model() {
return this._model;
}
get locked() {
return this._model.locked;
}
set locked(value) {
this._model.locked = value;
}
get header() {
return this._model.header;
}
constructor(accessor, id, options) {
var _options$constraints$, _options$constraints, _options$constraints$2, _options$constraints2, _options$constraints3, _options$constraints4;
super(id, "groupview_default", {
minimumHeight: (_options$constraints$ = (_options$constraints = options.constraints) === null || _options$constraints === void 0 ? void 0 : _options$constraints.minimumHeight) !== null && _options$constraints$ !== void 0 ? _options$constraints$ : MINIMUM_DOCKVIEW_GROUP_PANEL_HEIGHT,
minimumWidth: (_options$constraints$2 = (_options$constraints2 = options.constraints) === null || _options$constraints2 === void 0 ? void 0 : _options$constraints2.minimumWidth) !== null && _options$constraints$2 !== void 0 ? _options$constraints$2 : MINIMUM_DOCKVIEW_GROUP_PANEL_WIDTH,
maximumHeight: (_options$constraints3 = options.constraints) === null || _options$constraints3 === void 0 ? void 0 : _options$constraints3.maximumHeight,
maximumWidth: (_options$constraints4 = options.constraints) === null || _options$constraints4 === void 0 ? void 0 : _options$constraints4.maximumWidth
}, new DockviewGroupPanelApiImpl(id, accessor));
this._explicitConstraints = {};
this.api.initialize(this);
this._model = new DockviewGroupPanelModel(this.element, accessor, id, options, this);
this.addDisposables(this.model.onDidActivePanelChange((event) => {
this.api._onDidActivePanelChange.fire(event);
}), this.api.onDidConstraintsChangeInternal((event) => {
if (event.minimumWidth !== void 0) this._explicitConstraints.minimumWidth = typeof event.minimumWidth === "function" ? event.minimumWidth() : event.minimumWidth;
if (event.minimumHeight !== void 0) this._explicitConstraints.minimumHeight = typeof event.minimumHeight === "function" ? event.minimumHeight() : event.minimumHeight;
if (event.maximumWidth !== void 0) this._explicitConstraints.maximumWidth = typeof event.maximumWidth === "function" ? event.maximumWidth() : event.maximumWidth;
if (event.maximumHeight !== void 0) this._explicitConstraints.maximumHeight = typeof event.maximumHeight === "function" ? event.maximumHeight() : event.maximumHeight;
}));
}
focus() {
if (!this.api.isActive) this.api.setActive();
super.focus();
}
initialize() {
this._model.initialize();
}
setActive(isActive) {
super.setActive(isActive);
this.model.setActive(isActive);
}
layout(width, height) {
super.layout(width, height);
this.model.layout(width, height);
}
/**
* Re-run the group's layout with its current dimensions, propagating a
* header-size change down to the content + active panel.
*/
relayout() {
this.model.relayout();
}
getComponent() {
return this._model;
}
toJSON() {
return this.model.toJSON();
}
};
const themeDark = {
name: "dark",
className: "dockview-theme-dark",
colorScheme: "dark"
};
const themeLight = {
name: "light",
className: "dockview-theme-light",
colorScheme: "light"
};
const themeVisualStudio = {
name: "visualStudio",
className: "dockview-theme-vs",
colorScheme: "dark",
edgeGroupCollapsedSize: 22
};
const themeAbyss = {
name: "abyss",
className: "dockview-theme-abyss",
colorScheme: "dark",
tabGroupIndicator: "none"
};
const themeDracula = {
name: "dracula",
className: "dockview-theme-dracula",
colorScheme: "dark"
};
const themeAbyssSpaced = {
name: "abyssSpaced",
className: "dockview-theme-abyss-spaced",
colorScheme: "dark",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeLightSpaced = {
name: "lightSpaced",
className: "dockview-theme-light-spaced",
colorScheme: "light",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeNord = {
name: "nord",
className: "dockview-theme-nord",
colorScheme: "dark"
};
const themeNordSpaced = {
name: "nordSpaced",
className: "dockview-theme-nord-spaced",
colorScheme: "dark",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeCatppuccinMocha = {
name: "catppuccinMocha",
className: "dockview-theme-catppuccin-mocha",
colorScheme: "dark"
};
const themeCatppuccinMochaSpaced = {
name: "catppuccinMochaSpaced",
className: "dockview-theme-catppuccin-mocha-spaced",
colorScheme: "dark",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeMonokai = {
name: "monokai",
className: "dockview-theme-monokai",
colorScheme: "dark"
};
const themeSolarizedLight = {
name: "solarizedLight",
className: "dockview-theme-solarized-light",
colorScheme: "light"
};
const themeSolarizedLightSpaced = {
name: "solarizedLightSpaced",
className: "dockview-theme-solarized-light-spaced",
colorScheme: "light",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeGithubDark = {
name: "githubDark",
className: "dockview-theme-github-dark",
colorScheme: "dark"
};
const themeGithubDarkSpaced = {
name: "githubDarkSpaced",
className: "dockview-theme-github-dark-spaced",
colorScheme: "dark",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
const themeGithubLight = {
name: "githubLight",
className: "dockview-theme-github-light",
colorScheme: "light"
};
const themeGithubLightSpaced = {
name: "githubLightSpaced",
className: "dockview-theme-github-light-spaced",
colorScheme: "light",
gap: 10,
edgeGroupCollapsedSize: 44,
dndOverlayMounting: "absolute",
dndPanelOverlay: "group",
dndTabIndicator: "line",
dndOverlayBorder: "2px solid var(--dv-active-sash-color)"
};
var DockviewPanelApiImpl = class extends GridviewPanelApiImpl {
get location() {
return this.group.api.location;
}
get title() {
return this.panel.title;
}
get isPinned() {
return this.panel.isPinned;
}
get isGroupActive() {
return this.group.isActive;
}
get renderer() {
return this.panel.renderer;
}
set group(value) {
const oldGroup = this._group;
if (this._group !== value) {
this._group = value;
this._onDidGroupChange.fire({});
this.setupGroupEventListeners(oldGroup);
this.fireLocationChange();
}
}
get group() {
return this._group;
}
get tabComponent() {
return this._tabComponent;
}
constructor(panel, group, accessor, component, tabComponent) {
super(panel.id, component);
this.panel = panel;
this.accessor = accessor;
this._onDidTitleChange = new Emitter();
this.onDidTitleChange = this._onDidTitleChange.event;
this._onDidChangePinned = new Emitter();
this.onDidChangePinned = this._onDidChangePinned.event;
this._onDidActiveGroupChange = new Emitter();
this.onDidActiveGroupChange = this._onDidActiveGroupChange.event;
this._onDidGroupChange = new Emitter();
this.onDidGroupChange = this._onDidGroupChange.event;
this._onDidRendererChange = new Emitter();
this.onDidRendererChange = this._onDidRendererChange.event;
this._onDidLocationChange = new Emitter();
this.onDidLocationChange = this._onDidLocationChange.event;
this.groupEventsDisposable = new MutableDisposable();
this._tabComponent = tabComponent;
this.initialize(panel);
this._group = group;
this.setupGroupEventListeners();
this.addDisposables(this.groupEventsDisposable, this._onDidRendererChange, this._onDidTitleChange, this._onDidChangePinned, this._onDidGroupChange, this._onDidActiveGroupChange, this._onDidLocationChange);
}
getWindow() {
return this.group.api.getWindow();
}
setActive() {
this.accessor.withOrigin("api", () => super.setActive());
}
moveTo(options) {
this.accessor.withOrigin("api", () => {
var _options$group, _options$position;
return this.accessor.moveGroupOrPanel({
from: {
groupId: this._group.id,
panelId: this.panel.id
},
to: {
group: (_options$group = options.group) !== null && _options$group !== void 0 ? _options$group : this._group,
position: options.group ? (_options$position = options.position) !== null && _options$position !== void 0 ? _options$position : "center" : "center",
index: options.index
},
skipSetActive: options.skipSetActive
});
});
}
setTitle(title) {
this.panel.setTitle(title);
}
setPinned(pinned) {
this.accessor.setPanelPinned(this.panel, pinned);
}
setRenderer(renderer) {
this.panel.setRenderer(renderer);
}
close() {
this.group.model.closePanel(this.panel);
}
maximize() {
this.group.api.maximize();
}
isMaximized() {
return this.group.api.isMaximized();
}
exitMaximized() {
this.group.api.exitMaximized();
}
/**
* Report that this panel's location may have moved.
*
* A relocation touches the location more than once - the panel is
* reparented into the destination group, then that group is tagged with
* the location it ends up at - so the event is coalesced to the end of the
* enclosing layout mutation and reports where the panel actually settled.
* Firing each signal as it happened surfaced an intermediate `grid`
* location the panel was never in, at a point where it was in neither
* group's panel list. Outside a mutation this fires straight away.
*/
fireLocationChange() {
this.accessor.deferLocationChange(this, () => {
if (this.isDisposed) return;
this._onDidLocationChange.fire({ location: this.location });
});
}
setupGroupEventListeners(previousGroup) {
var _previousGroup$isActi;
let _trackGroupActive = (_previousGroup$isActi = previousGroup === null || previousGroup === void 0 ? void 0 : previousGroup.isActive) !== null && _previousGroup$isActi !== void 0 ? _previousGroup$isActi : false;
this.groupEventsDisposable.value = new CompositeDisposable(this.group.api.onDidVisibilityChange((event) => {
const hasBecomeHidden = !event.isVisible && this.isVisible;
const hasBecomeVisible = event.isVisible && !this.isVisible;
const isActivePanel = this.group.model.isPanelActive(this.panel);
if (hasBecomeHidden || hasBecomeVisible && isActivePanel) this._onDidVisibilityChange.fire(event);
}), this.group.api.onDidLocationChange(() => {
if (this.group !== this.panel.group) return;
this.fireLocationChange();
}), this.group.api.onDidActiveChange(() => {
if (this.group !== this.panel.group) return;
if (_trackGroupActive !== this.isGroupActive) {
_trackGroupActive = this.isGroupActive;
this._onDidActiveGroupChange.fire({ isActive: this.isGroupActive });
}
}));
}
};
var DockviewPanel = class extends CompositeDisposable {
get params() {
return this._params;
}
get title() {
return this._title;
}
get isPinned() {
return this._pinned;
}
get group() {
return this._group;
}
get renderer() {
var _this$_renderer;
return (_this$_renderer = this._renderer) !== null && _this$_renderer !== void 0 ? _this$_renderer : this.accessor.renderer;
}
get minimumWidth() {
return this._minimumWidth;
}
get minimumHeight() {
return this._minimumHeight;
}
get maximumWidth() {
return this._maximumWidth;
}
get maximumHeight() {
return this._maximumHeight;
}
constructor(id, component, tabComponent, accessor, containerApi, group, view, options) {
super();
this.id = id;
this.accessor = accessor;
this.containerApi = containerApi;
this.view = view;
this._pinned = false;
this._renderer = options.renderer;
this._group = group;
this._minimumWidth = options.minimumWidth;
this._minimumHeight = options.minimumHeight;
this._maximumWidth = options.maximumWidth;
this._maximumHeight = options.maximumHeight;
this.api = new DockviewPanelApiImpl(this, this._group, accessor, component, tabComponent);
this.addDisposables(this.api.onActiveChange(() => {
accessor.setActivePanel(this);
}), this.api.onDidSizeChange((event) => {
this.group.api.setSize(event);
}), this.api.onDidRendererChange(() => {
this.group.model.rerender(this);
}));
}
init(params) {
this._params = params.params;
this.view.init(_objectSpread2(_objectSpread2({}, params), {}, {
api: this.api,
containerApi: this.containerApi
}));
this.setTitle(params.title);
}
focus() {
const event = new WillFocusEvent();
this.api._onWillFocus.fire(event);
if (event.defaultPrevented) return;
if (!this.api.isActive) this.api.setActive();
}
toJSON() {
return {
id: this.id,
contentComponent: this.view.contentComponent,
tabComponent: this.view.tabComponent,
params: Object.keys(this._params || {}).length > 0 ? this._params : void 0,
title: this.title,
renderer: this._renderer,
minimumHeight: this._minimumHeight,
maximumHeight: this._maximumHeight,
minimumWidth: this._minimumWidth,
maximumWidth: this._maximumWidth,
pinned: this._pinned ? true : void 0
};
}
setTitle(title) {
if (title !== this.title) {
this._title = title;
this.view.setTitle(title);
this.api._onDidTitleChange.fire({ title });
}
}
/**
* Low-level pinned-state mutation. Sets the flag and fires the panel-api
* event. The public, module-gated entry point is
* `panel.api.setPinned` → `DockviewComponent.setPanelPinned`, which decides
* whether pinning is active before reaching here; this method itself does
* not gate, so the ordering module and serialization can drive it directly.
*/
setPinned(pinned) {
if (this._pinned === pinned) return;
this._pinned = pinned;
this.api._onDidChangePinned.fire({ isPinned: pinned });
}
setRenderer(renderer) {
if (renderer !== this.renderer) {
this._renderer = renderer;
this.api._onDidRendererChange.fire({ renderer });
}
}
update(event) {
this._params = _objectSpread2(_objectSpread2({}, this._params), event.params);
/**
* delete new keys that have a value of undefined,
* allow values of null
*/
for (const key of Object.keys(event.params)) if (event.params[key] === void 0) delete this._params[key];
this.view.update({ params: this._params });
}
updateFromStateModel(state) {
var _state$params, _state$title, _state$renderer, _state$pinned, _this$accessor$option;
this._maximumHeight = state.maximumHeight;
this._minimumHeight = state.minimumHeight;
this._maximumWidth = state.maximumWidth;
this._minimumWidth = state.minimumWidth;
this._params = (_state$params = state.params) !== null && _state$params !== void 0 ? _state$params : {};
this.view.update({ params: this._params });
this.setTitle((_state$title = state.title) !== null && _state$title !== void 0 ? _state$title : this.id);
this.setRenderer((_state$renderer = state.renderer) !== null && _state$renderer !== void 0 ? _state$renderer : this.accessor.renderer);
this.setPinned(((_state$pinned = state.pinned) !== null && _state$pinned !== void 0 ? _state$pinned : false) && !!((_this$accessor$option = this.accessor.options.pinnedTabs) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.enabled));
}
updateParentGroup(group, options) {
this._group = group;
this.api.group = this._group;
const isPanelVisible = this._group.model.isPanelActive(this);
const isActive = this.group.api.isActive && isPanelVisible;
if (!(options === null || options === void 0 ? void 0 : options.skipSetActive)) {
if (this.api.isActive !== isActive) this.api._onDidActiveChange.fire({ isActive: this.group.api.isActive && isPanelVisible });
}
if (this.api.isVisible !== isPanelVisible) this.api._onDidVisibilityChange.fire({ isVisible: isPanelVisible });
}
runEvents() {
const isPanelVisible = this._group.model.isPanelActive(this);
const isActive = this.group.api.isActive && isPanelVisible;
if (this.api.isActive !== isActive) this.api._onDidActiveChange.fire({ isActive: this.group.api.isActive && isPanelVisible });
if (this.api.isVisible !== isPanelVisible) this.api._onDidVisibilityChange.fire({ isVisible: isPanelVisible });
}
layout(width, height) {
this.api._onDidDimensionChange.fire({
width,
height
});
this.view.layout(width, height);
}
dispose() {
this.api.dispose();
this.view.dispose();
}
};
var DefaultTab = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor() {
super();
this._messages = DEFAULT_MESSAGES;
this._element = document.createElement("div");
this._element.className = "dv-default-tab";
this._content = document.createElement("div");
this._content.className = "dv-default-tab-content";
this.action = document.createElement("div");
this.action.className = "dv-default-tab-action";
this.action.setAttribute("role", "button");
this.action.setAttribute("tabindex", "-1");
const closeIcon = createCloseButton();
closeIcon.setAttribute("aria-hidden", "true");
this.action.appendChild(closeIcon);
this._element.appendChild(this._content);
this._element.appendChild(this.action);
this.render();
}
init(params) {
var _params$containerApi$;
this._title = params.title;
this._messages = (_params$containerApi$ = params.containerApi.messages) !== null && _params$containerApi$ !== void 0 ? _params$containerApi$ : DEFAULT_MESSAGES;
this.addDisposables(params.api.onDidTitleChange((event) => {
this._title = event.title;
this.render();
}), addDisposableListener(this.action, "pointerdown", (ev) => {
ev.preventDefault();
}), addDisposableListener(this.action, "click", (ev) => {
if (ev.defaultPrevented) return;
ev.preventDefault();
params.api.close();
}));
this.render();
}
render() {
if (this._content.textContent !== this._title) {
var _this$_title;
this._content.textContent = (_this$_title = this._title) !== null && _this$_title !== void 0 ? _this$_title : "";
}
this.action.setAttribute("aria-label", this._title ? this._messages.closeTab(this._title) : this._messages.closeTabPlain());
}
};
var DockviewPanelModel = class {
get content() {
return this._content;
}
get tab() {
return this._tab;
}
constructor(accessor, id, contentComponent, tabComponent) {
this.accessor = accessor;
this.id = id;
this.contentComponent = contentComponent;
this.tabComponent = tabComponent;
this._content = this.createContentComponent(this.id, contentComponent);
this._tab = this.createTabComponent(this.id, tabComponent);
}
createTabRenderer(tabLocation) {
const cmp = this.createTabComponent(this.id, this.tabComponent);
if (this._params) cmp.init(_objectSpread2(_objectSpread2({}, this._params), {}, { tabLocation }));
if (this._updateEvent) {
var _cmp$update;
(_cmp$update = cmp.update) === null || _cmp$update === void 0 || _cmp$update.call(cmp, this._updateEvent);
}
return cmp;
}
init(params) {
this._params = params;
this.content.init(params);
this.tab.init(_objectSpread2(_objectSpread2({}, params), {}, { tabLocation: "header" }));
}
setTitle(title) {
if (this._params) this._params.title = title;
}
layout(width, height) {
var _this$content$layout, _this$content;
(_this$content$layout = (_this$content = this.content).layout) === null || _this$content$layout === void 0 || _this$content$layout.call(_this$content, width, height);
}
update(event) {
var _this$content$update, _this$content2, _this$tab$update, _this$tab;
this._updateEvent = event;
(_this$content$update = (_this$content2 = this.content).update) === null || _this$content$update === void 0 || _this$content$update.call(_this$content2, event);
(_this$tab$update = (_this$tab = this.tab).update) === null || _this$tab$update === void 0 || _this$tab$update.call(_this$tab, event);
}
dispose() {
var _this$content$dispose, _this$content3, _this$tab$dispose, _this$tab2;
(_this$content$dispose = (_this$content3 = this.content).dispose) === null || _this$content$dispose === void 0 || _this$content$dispose.call(_this$content3);
(_this$tab$dispose = (_this$tab2 = this.tab).dispose) === null || _this$tab$dispose === void 0 || _this$tab$dispose.call(_this$tab2);
}
createContentComponent(id, componentName) {
return this.accessor.options.createComponent({
id,
name: componentName
});
}
createTabComponent(id, componentName) {
const name = componentName !== null && componentName !== void 0 ? componentName : this.accessor.options.defaultTabComponent;
if (name) {
if (this.accessor.options.createTabComponent) {
const component = this.accessor.options.createTabComponent({
id,
name
});
if (component) return component;
else return new DefaultTab();
}
console.warn(`dockview: tabComponent '${componentName}' was not found. falling back to the default tab.`);
}
return new DefaultTab();
}
};
var DefaultDockviewDeserialzier = class {
constructor(accessor) {
this.accessor = accessor;
}
fromJSON(panelData, group) {
var _panelData$contentCom, _viewData$tab, _this$accessor$option;
const panelId = panelData.id;
const params = panelData.params;
const title = panelData.title;
const viewData = panelData.view;
const contentComponent = viewData ? viewData.content.id : (_panelData$contentCom = panelData.contentComponent) !== null && _panelData$contentCom !== void 0 ? _panelData$contentCom : "unknown";
const tabComponent = viewData ? (_viewData$tab = viewData.tab) === null || _viewData$tab === void 0 ? void 0 : _viewData$tab.id : panelData.tabComponent;
const view = new DockviewPanelModel(this.accessor, panelId, contentComponent, tabComponent);
const panel = new DockviewPanel(panelId, contentComponent, tabComponent, this.accessor, new DockviewApi(this.accessor), group, view, {
renderer: panelData.renderer,
minimumWidth: panelData.minimumWidth,
minimumHeight: panelData.minimumHeight,
maximumWidth: panelData.maximumWidth,
maximumHeight: panelData.maximumHeight
});
panel.init({
title: title !== null && title !== void 0 ? title : panelId,
params: params !== null && params !== void 0 ? params : {}
});
if (panelData.pinned && ((_this$accessor$option = this.accessor.options.pinnedTabs) === null || _this$accessor$option === void 0 ? void 0 : _this$accessor$option.enabled)) panel.setPinned(true);
return panel;
}
};
var Watermark = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor() {
super();
this._element = document.createElement("div");
this._element.className = "dv-watermark";
}
init(_params) {}
};
var AriaLevelTracker = class {
constructor() {
this._orderedList = [];
}
push(element) {
this._orderedList = [...this._orderedList.filter((item) => item !== element), element];
this.update();
}
destroy(element) {
this._orderedList = this._orderedList.filter((item) => item !== element);
this.update();
}
update() {
for (let i = 0; i < this._orderedList.length; i++) {
this._orderedList[i].setAttribute("aria-level", `${i}`);
this._orderedList[i].style.zIndex = `calc(var(--dv-overlay-z-index, 999) + ${i * 2})`;
}
}
};
const arialLevelTracker = new AriaLevelTracker();
var Overlay = class Overlay extends CompositeDisposable {
set minimumInViewportWidth(value) {
this.options.minimumInViewportWidth = value;
}
set minimumInViewportHeight(value) {
this.options.minimumInViewportHeight = value;
}
get element() {
return this._element;
}
get isVisible() {
return this._isVisible;
}
/**
* Height of the optional drag-handle header, or 0 when none is present.
* Used to translate between the overlay's outer box and the content area
* available to the group beneath the header.
*/
get headerHeight() {
var _this$options$header$, _this$options$header;
return (_this$options$header$ = (_this$options$header = this.options.header) === null || _this$options$header === void 0 ? void 0 : _this$options$header.offsetHeight) !== null && _this$options$header$ !== void 0 ? _this$options$header$ : 0;
}
constructor(options) {
super();
this.options = options;
this._element = document.createElement("div");
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this._onDidChangeEnd = new Emitter();
this.onDidChangeEnd = this._onDidChangeEnd.event;
this._onDidStartMoving = new Emitter();
this.onDidStartMoving = this._onDidStartMoving.event;
this._dragMove = new MutableDisposable();
this._dragCancelled = false;
this.addDisposables(this._onDidChange, this._onDidChangeEnd, this._onDidStartMoving, this._dragMove);
this._element.className = "dv-resize-container";
this._element.setAttribute("role", "dialog");
this._element.setAttribute("aria-modal", "false");
this._isVisible = true;
this.setupResize("top");
this.setupResize("bottom");
this.setupResize("left");
this.setupResize("right");
this.setupResize("topleft");
this.setupResize("topright");
this.setupResize("bottomleft");
this.setupResize("bottomright");
if (this.options.header) {
toggleClass(this._element, "dv-resize-container-with-titlebar", true);
this._element.appendChild(this.options.header);
}
this._element.appendChild(this.options.content);
this.options.container.appendChild(this._element);
this.setBounds(_objectSpread2(_objectSpread2(_objectSpread2(_objectSpread2({
height: this.options.height,
width: this.options.width
}, "top" in this.options && { top: this.options.top }), "bottom" in this.options && { bottom: this.options.bottom }), "left" in this.options && { left: this.options.left }), "right" in this.options && { right: this.options.right }));
arialLevelTracker.push(this._element);
}
setVisible(isVisible) {
if (isVisible === this.isVisible) return;
this._isVisible = isVisible;
toggleClass(this.element, "dv-hidden", !this.isVisible);
}
bringToFront() {
arialLevelTracker.push(this._element);
}
setBounds(bounds = {}) {
if (typeof bounds.height === "number") this._element.style.height = `${bounds.height}px`;
if (typeof bounds.width === "number") this._element.style.width = `${bounds.width}px`;
if ("top" in bounds && typeof bounds.top === "number") {
this._element.style.top = `${bounds.top}px`;
this._element.style.bottom = "auto";
this.verticalAlignment = "top";
}
if ("bottom" in bounds && typeof bounds.bottom === "number") {
this._element.style.bottom = `${bounds.bottom}px`;
this._element.style.top = "auto";
this.verticalAlignment = "bottom";
}
if ("left" in bounds && typeof bounds.left === "number") {
this._element.style.left = `${bounds.left}px`;
this._element.style.right = "auto";
this.horiziontalAlignment = "left";
}
if ("right" in bounds && typeof bounds.right === "number") {
this._element.style.right = `${bounds.right}px`;
this._element.style.left = "auto";
this.horiziontalAlignment = "right";
}
const containerRect = this.options.container.getBoundingClientRect();
const overlayRect = this._element.getBoundingClientRect();
const xOffset = Math.max(0, this.getMinimumWidth(overlayRect.width));
const yOffset = Math.max(0, this.getMinimumHeight(overlayRect.height));
if (this.verticalAlignment === "top") {
const top = clamp(overlayRect.top - containerRect.top, -yOffset, Math.max(0, containerRect.height - overlayRect.height + yOffset));
this._element.style.top = `${top}px`;
this._element.style.bottom = "auto";
}
if (this.verticalAlignment === "bottom") {
const bottom = clamp(containerRect.bottom - overlayRect.bottom, -yOffset, Math.max(0, containerRect.height - overlayRect.height + yOffset));
this._element.style.bottom = `${bottom}px`;
this._element.style.top = "auto";
}
if (this.horiziontalAlignment === "left") {
const left = clamp(overlayRect.left - containerRect.left, -xOffset, Math.max(0, containerRect.width - overlayRect.width + xOffset));
this._element.style.left = `${left}px`;
this._element.style.right = "auto";
}
if (this.horiziontalAlignment === "right") {
const right = clamp(containerRect.right - overlayRect.right, -xOffset, Math.max(0, containerRect.width - overlayRect.width + xOffset));
this._element.style.right = `${right}px`;
this._element.style.left = "auto";
}
this._onDidChange.fire();
}
toJSON() {
const container = this.options.container.getBoundingClientRect();
const element = this._element.getBoundingClientRect();
const result = {};
if (this.verticalAlignment === "top") result.top = Number.parseFloat(this._element.style.top);
else if (this.verticalAlignment === "bottom") result.bottom = Number.parseFloat(this._element.style.bottom);
else result.top = element.top - container.top;
if (this.horiziontalAlignment === "left") result.left = Number.parseFloat(this._element.style.left);
else if (this.horiziontalAlignment === "right") result.right = Number.parseFloat(this._element.style.right);
else result.left = element.left - container.left;
result.width = element.width;
result.height = element.height;
return result;
}
/**
* Abort an in-flight move-the-float drag. Used by the void container
* when a redock long-press fires after the move started, so the ghost
* gesture wins without the float continuing to follow the finger.
* Does not emit `onDidChangeEnd` because no change is being committed.
*/
cancelPendingDrag() {
if (!this._dragMove.value) return;
this._dragCancelled = true;
toggleClass(this._element, "dv-resize-container-dragging", false);
this._dragMove.value = Disposable.NONE;
}
setupDrag(dragTarget, options) {
const track = (captureTarget, pointerId) => {
var _this$options$getSibl, _this$options$getSibl2, _this$options;
let offset = null;
let hasMoved = false;
this._dragCancelled = false;
const siblingBoxes = this.options.transformDragPosition ? (_this$options$getSibl = (_this$options$getSibl2 = (_this$options = this.options).getSiblingBoxes) === null || _this$options$getSibl2 === void 0 ? void 0 : _this$options$getSibl2.call(_this$options)) !== null && _this$options$getSibl !== void 0 ? _this$options$getSibl : [] : [];
const iframes = disableIframePointEvents();
if (captureTarget && typeof pointerId === "number" && typeof captureTarget.setPointerCapture === "function") try {
captureTarget.setPointerCapture(pointerId);
} catch (_unused) {}
const end = () => {
toggleClass(this._element, "dv-resize-container-dragging", false);
this._dragMove.value = Disposable.NONE;
this._onDidChangeEnd.fire();
};
this._dragMove.value = new CompositeDisposable({ dispose: () => {
iframes.release();
if (captureTarget && typeof pointerId === "number" && typeof captureTarget.releasePointerCapture === "function") try {
captureTarget.releasePointerCapture(pointerId);
} catch (_unused2) {}
} }, addDisposableListener(globalThis.window, "pointermove", (e) => {
var _offset;
if (this._dragCancelled) return;
const containerRect = this.options.container.getBoundingClientRect();
const x = e.clientX - containerRect.left;
const y = e.clientY - containerRect.top;
toggleClass(this._element, "dv-resize-container-dragging", true);
const overlayRect = this._element.getBoundingClientRect();
(_offset = offset) !== null && _offset !== void 0 || (offset = {
x: e.clientX - overlayRect.left,
y: e.clientY - overlayRect.top
});
const xOffset = Math.max(0, this.getMinimumWidth(overlayRect.width));
const yOffset = Math.max(0, this.getMinimumHeight(overlayRect.height));
let proposedTop = y - offset.y;
let proposedLeft = x - offset.x;
if (this.options.transformDragPosition) {
const adjusted = this.options.transformDragPosition({
proposed: {
top: proposedTop,
left: proposedLeft,
width: overlayRect.width,
height: overlayRect.height
},
container: {
width: containerRect.width,
height: containerRect.height
},
others: siblingBoxes,
modifiers: {
altKey: e.altKey,
ctrlKey: e.ctrlKey,
metaKey: e.metaKey,
shiftKey: e.shiftKey
}
});
if (adjusted) {
proposedTop = adjusted.top;
proposedLeft = adjusted.left;
}
}
const maxTop = Math.max(0, containerRect.height - overlayRect.height + yOffset);
const maxLeft = Math.max(0, containerRect.width - overlayRect.width + xOffset);
const top = clamp(proposedTop, -yOffset, maxTop);
const bottom = clamp(containerRect.height - overlayRect.height - proposedTop, -yOffset, maxTop);
const left = clamp(proposedLeft, -xOffset, maxLeft);
const right = clamp(containerRect.width - overlayRect.width - proposedLeft, -xOffset, maxLeft);
const bounds = {};
if (top <= bottom) bounds.top = top;
else bounds.bottom = bottom;
if (left <= right) bounds.left = left;
else bounds.right = right;
this.setBounds(bounds);
if (!hasMoved) {
hasMoved = true;
this._onDidStartMoving.fire();
}
}), addDisposableListener(globalThis.window, "pointerup", end), addDisposableListener(globalThis.window, "pointercancel", end));
};
this.addDisposables(addDisposableListener(dragTarget, "pointerdown", (event) => {
if (event.defaultPrevented) {
event.preventDefault();
return;
}
if (quasiDefaultPrevented(event)) return;
track(dragTarget, event.pointerId);
}), addDisposableListener(this.options.content, "pointerdown", (event) => {
if (event.defaultPrevented) return;
if (quasiDefaultPrevented(event)) return;
if (event.shiftKey) track(this.options.content, event.pointerId);
}), addDisposableListener(this.options.content, "pointerdown", () => {
arialLevelTracker.push(this._element);
}, true));
if (options === null || options === void 0 ? void 0 : options.inDragMode) track();
}
setupResize(direction) {
const resizeHandleElement = document.createElement("div");
resizeHandleElement.className = `dv-resize-handle-${direction}`;
this._element.appendChild(resizeHandleElement);
const move = new MutableDisposable();
this.addDisposables(move, addDisposableListener(resizeHandleElement, "pointerdown", (e) => {
e.preventDefault();
let startPosition = null;
const iframes = disableIframePointEvents();
const pointerId = e.pointerId;
if (typeof resizeHandleElement.setPointerCapture === "function") try {
resizeHandleElement.setPointerCapture(pointerId);
} catch (_unused3) {}
const end = () => {
move.dispose();
this._onDidChangeEnd.fire();
};
move.value = new CompositeDisposable(addDisposableListener(globalThis.window, "pointermove", (e) => {
var _startPosition;
const containerRect = this.options.container.getBoundingClientRect();
const overlayRect = this._element.getBoundingClientRect();
const y = e.clientY - containerRect.top;
const x = e.clientX - containerRect.left;
(_startPosition = startPosition) !== null && _startPosition !== void 0 || (startPosition = {
originalY: y,
originalHeight: overlayRect.height,
originalX: x,
originalWidth: overlayRect.width
});
let top = void 0;
let bottom = void 0;
let height = void 0;
let left = void 0;
let right = void 0;
let width = void 0;
const moveTop = () => {
const maxTop = startPosition.originalY + startPosition.originalHeight > containerRect.height ? Math.max(0, containerRect.height - Overlay.MINIMUM_HEIGHT) : Math.max(0, startPosition.originalY + startPosition.originalHeight - Overlay.MINIMUM_HEIGHT);
top = clamp(y, 0, maxTop);
height = startPosition.originalY + startPosition.originalHeight - top;
bottom = containerRect.height - top - height;
};
const moveBottom = () => {
top = startPosition.originalY - startPosition.originalHeight;
const minHeight = top < 0 && typeof this.options.minimumInViewportHeight === "number" ? -top + this.options.minimumInViewportHeight : Overlay.MINIMUM_HEIGHT;
const maxHeight = containerRect.height - Math.max(0, top);
height = clamp(y - top, minHeight, maxHeight);
bottom = containerRect.height - top - height;
};
const moveLeft = () => {
const maxLeft = startPosition.originalX + startPosition.originalWidth > containerRect.width ? Math.max(0, containerRect.width - Overlay.MINIMUM_WIDTH) : Math.max(0, startPosition.originalX + startPosition.originalWidth - Overlay.MINIMUM_WIDTH);
left = clamp(x, 0, maxLeft);
width = startPosition.originalX + startPosition.originalWidth - left;
right = containerRect.width - left - width;
};
const moveRight = () => {
left = startPosition.originalX - startPosition.originalWidth;
const minWidth = left < 0 && typeof this.options.minimumInViewportWidth === "number" ? -left + this.options.minimumInViewportWidth : Overlay.MINIMUM_WIDTH;
const maxWidth = containerRect.width - Math.max(0, left);
width = clamp(x - left, minWidth, maxWidth);
right = containerRect.width - left - width;
};
switch (direction) {
case "top":
moveTop();
break;
case "bottom":
moveBottom();
break;
case "left":
moveLeft();
break;
case "right":
moveRight();
break;
case "topleft":
moveTop();
moveLeft();
break;
case "topright":
moveTop();
moveRight();
break;
case "bottomleft":
moveBottom();
moveLeft();
break;
case "bottomright":
moveBottom();
moveRight();
break;
}
const bounds = {};
if (top <= bottom) bounds.top = top;
else bounds.bottom = bottom;
if (left <= right) bounds.left = left;
else bounds.right = right;
bounds.height = height;
bounds.width = width;
this.setBounds(bounds);
}), { dispose: () => {
iframes.release();
if (typeof resizeHandleElement.releasePointerCapture === "function") try {
resizeHandleElement.releasePointerCapture(pointerId);
} catch (_unused4) {}
} }, addDisposableListener(globalThis.window, "pointerup", end), addDisposableListener(globalThis.window, "pointercancel", end));
}));
}
getMinimumWidth(width) {
if (typeof this.options.minimumInViewportWidth === "number") return width - this.options.minimumInViewportWidth;
return 0;
}
getMinimumHeight(height) {
if (typeof this.options.minimumInViewportHeight === "number") return height - this.options.minimumInViewportHeight;
return 0;
}
dispose() {
arialLevelTracker.destroy(this._element);
this._element.remove();
super.dispose();
}
};
Overlay.MINIMUM_HEIGHT = 20;
Overlay.MINIMUM_WIDTH = 20;
/**
* A dedicated, blank drag handle rendered above a floating group's tab bar.
*
* It plays the same dual role the tab-bar void container plays today: a plain
* pointer-drag moves the floating window (wired by the overlay via
* `setupDrag`), while a shift+drag (mouse) or long-press (touch) detaches the
* group to redock it into the grid. The redock half is provided by the shared
* {@link GroupDragSource}; the move half is owned by the overlay.
*
* The bar is intentionally contentless; styling is driven entirely through
* the `--dv-floating-titlebar-*` theme variables.
*/
var FloatingTitleBar = class extends CompositeDisposable {
get element() {
return this._element;
}
/** The window's current anchor group, the one this bar drags/activates. */
get group() {
return this._group;
}
/**
* Retarget the bar at a new anchor group. Called when the original anchor
* leaves a multi-group floating window and another member is promoted, so
* the bar keeps activating/redocking a group that actually lives here.
*/
setGroup(group) {
this._group = group;
}
constructor(accessor, group) {
super();
this.accessor = accessor;
this._onDragStart = new Emitter();
this.onDragStart = this._onDragStart.event;
this._group = group;
this._element = document.createElement("div");
this._element.className = "dv-floating-titlebar";
this.addDisposables(this._onDragStart, addDisposableListener(this._element, "pointerdown", () => {
this.accessor.doSetGroupActive(this._group);
}), addDisposableListener(this._element, "pointerdown", (e) => {
if (e.shiftKey) quasiPreventDefault(e);
}, true));
this.dragSource = new GroupDragSource({
element: this._element,
accessor: this.accessor,
group: () => this._group
});
this.addDisposables(this.dragSource, this.dragSource.onDragStart((event) => {
this._onDragStart.fire(event);
}));
}
updateDragAndDropState() {
this.dragSource.updateDragAndDropState();
}
};
/**
* Typed helper for defining a module. Enforces that the factory's return
* type matches the slot in ServiceCollection at compile time, replacing
* the manual cast each module file would otherwise need.
*/
function defineModule(config) {
return {
moduleName: config.name,
options: config.options,
services: { [config.serviceKey]: config.create },
init: config.init ? (host, services) => config.init(host, services[config.serviceKey]) : void 0,
dependsOn: config.dependsOn
};
}
/**
* The modules that ship in `dockview-enterprise` rather than the free
* packages. Core never imports them; it holds their names only so a
* missing-module message can name the package that provides the fix.
*
* Keep in sync with the `Modules` list exported by `dockview-enterprise`.
*/
const ENTERPRISE_MODULE_NAMES = /* @__PURE__ */ new Set([
"AdvancedOverflow",
"AutoEdgeGroup",
"AutoHideEdgeGroup",
"ContextMenu",
"DndCompass",
"KeyboardDocking",
"KeyboardNavigation",
"LayoutHistory",
"License",
"MultiRowTabs",
"PinnedTabs",
"SmartGuides"
]);
const _warnedMissingModule = /* @__PURE__ */ new Set();
/**
* Builds the text for a module that a caller needed but which isn't
* registered. `reason` describes what the user did to need it (an option they
* set, an api method they called) and is quoted verbatim. Pass several when one
* module is needed for several reasons at once: a single message listing them
* beats one message per reason each repeating the same install instructions.
*
* Exported so the rare caller that must throw (see `assertModule`) can raise
* the same message rather than a thinner second one.
*
* Enterprise modules get the install/import fix inline, because importing
* `dockview-enterprise` self-registers every enterprise module, so there is no
* separate `registerModules` step for the user to get wrong.
*/
function missingModuleMessage(moduleName, reason) {
const reasons = reason === void 0 ? [] : [].concat(reason);
const quoted = reasons.map((r) => `\`${r}\``).join(", ");
const requires = reasons.length > 1 ? "require" : "requires";
const needed = reasons.length ? `${quoted} ${requires} the "${moduleName}" module` : `The "${moduleName}" module is required`;
if (ENTERPRISE_MODULE_NAMES.has(moduleName)) return `dockview: ${needed}, which ships in dockview-enterprise.\n\n npm install dockview-enterprise\n import 'dockview-enterprise'; // self-registers every enterprise module\n`;
return `dockview: ${needed}, but it is not registered.`;
}
/**
* Logs a deduplicated "module not registered" error naming what was needed and
* how to get it. Deduped per module+reasons for the lifetime of the page, so
* repeated calls (and re-validation on every `updateOptions`) log once.
*/
function logMissingModule(moduleName, reason) {
const key = `${moduleName}|${(reason === void 0 ? [] : [].concat(reason)).join("|")}`;
if (_warnedMissingModule.has(key)) return;
_warnedMissingModule.add(key);
console.error(missingModuleMessage(moduleName, reason));
}
/**
* Returns the service if its module is registered, otherwise logs a
* deduplicated console error and returns `undefined`. This function never
* throws: the affected feature degrades to a no-op so consuming applications
* don't crash in production.
*
* The one exception is an entry point that must return a value it cannot
* synthesise: `addEdgeGroup(): DockviewGroupPanelApi` has no group to hand
* back and no `undefined` in its return type, so it throws
* `missingModuleMessage(...)` directly instead of calling this. Don't route
* such a caller through here: it would log *and* throw, reporting twice.
*
* Use at public-API entry points where the caller wants to surface which
* module is missing. For internal/lifecycle paths, plain `?.` chaining on
* the service slot is preferred: no log, a silent no-op.
*
* Two rules decide whether an entry point guards:
*
* 1. **Commands, not queries.** `api.undo()` asked for something to happen, so
* silence is a bug report waiting to happen; `canUndo` asked a question, and
* `false` is a truthful answer that shouldn't log. Same for event getters
* falling back to a never-firing event, and for idempotent cleanup
* (`clearHistory()` on an absent history has genuinely nothing to do).
*
* 2. **Only if the option rule can't already have fired.** A command reachable
* without its gating option (`api.undo()` works on a component that never
* set `layoutHistory`) needs this, because no rule will have run. A command
* that can't be reached until the option is set doesn't: `setPanelPinned`
* returns early unless `pinnedTabs.enabled`, so by the time it could warn,
* the option rule has already named the same module. Guarding it too would
* report one mistake twice.
*
* Rule 2 tolerates one overlap: setting `layoutHistory.enabled` *and* calling
* `api.undo()` without the module reports both, since the reasons differ and
* dedup is per module+reason. That's the price of covering the far more likely
* case: calling `undo()` having never set the option at all.
*
* Interaction handlers are queries in this sense too: a right-click reaching an
* absent ContextMenu module means the app never asked for one, so it stays
* silent (`?.`) and the browser's own menu shows. Options are where intent is
* declared; see `optionsModules.ts`.
*/
function assertModule(service, moduleName, context) {
if (service !== void 0) return service;
logMissingModule(moduleName, context);
}
var ModuleRegistry = class {
constructor() {
this._modules = /* @__PURE__ */ new Map();
this._services = {};
this._initDisposables = [];
}
get services() {
return this._services;
}
register(module) {
if (this._modules.has(module.moduleName)) return;
if (module.dependsOn) for (const dep of module.dependsOn) this.register(dep);
this._modules.set(module.moduleName, module);
}
initialize(host) {
for (const module of this._modules.values()) {
if (!module.services) continue;
for (const [name, factory] of Object.entries(module.services)) this._services[name] = factory(host);
}
}
postConstruct(host) {
for (const module of this._modules.values()) if (module.init) this._initDisposables.push(module.init(host, this._services));
}
has(moduleName) {
return this._modules.has(moduleName);
}
dispose() {
for (const disposable of this._initDisposables) disposable.dispose();
this._initDisposables.length = 0;
for (const service of Object.values(this._services)) if (service !== void 0 && typeof service.dispose === "function") service.dispose();
}
};
/**
* Process-global list of modules registered via {@link registerModules}.
* `DockviewComponent` appends these to its built-in set at construction, so
* importing a package that calls `registerModules(...)` (e.g. `dockview`)
* makes those modules available to every component in the process.
*/
const _globalModules = [];
/**
* Register modules globally. Idempotent per `moduleName`: registering the
* same module twice is a no-op. Intended to be called once at import time by
* the package that bundles a given set of modules.
*/
function registerModules(modules) {
for (const module of modules) {
if (_globalModules.some((m) => m.moduleName === module.moduleName)) continue;
_globalModules.push(module);
}
}
/**
* Returns the globally-registered modules (a copy). `DockviewComponent` reads
* this to extend its built-in module set.
*/
function getRegisteredModules() {
return [..._globalModules];
}
/**
* For tests: clears the global module registry.
*/
function clearRegisteredModules() {
_globalModules.length = 0;
}
/**
* Maps dockview options to the modules that implement them, so setting an
* option for an absent module reports which module is missing instead of
* silently doing nothing.
*
* This is the "declared intent" diagnostic. It fires when the user asks for a
* feature via options, at construction and on every `updateOptions`. It
* deliberately does *not* fire on user interaction: right-clicking without the
* ContextMenu module stays silent (the browser's own menu shows), because the
* `?.` service-slot call sites can't tell a missing module from a feature the
* app never wanted.
*
* Rules are a flat list rather than a `Record<keyof DockviewOptions, ...>`
* because dockview's options are nested and value-gated: `overflow.mode:
* 'wrap'` needs MultiRowTabs while `overflow.search` needs AdvancedOverflow,
* so one top-level key maps to several modules depending on its contents. A
* rule per gated thing also lets `reason` name the exact path the user set,
* which is the whole point of the message.
*
* ## Adding a rule
*
* A rule is just "this option needs that module". Whether that makes it paid is
* not a judgement call: a module is enterprise iff it ships in
* `dockview-enterprise`'s `Modules` list. {@link ENTERPRISE_MODULE_NAMES}
* mirrors that list, and a test in dockview-enterprise fails if the two drift,
* so naming the right module is the only thing you have to get right here.
*
* The one trap is picking the module. Don't infer it from what core does at
* runtime: a core fallback that hands a paid feature to free builds would read
* as "the option is free" and turn one bug into two. Pick the module that
* *implements the documented feature*; if core also does it, that is a leak to
* fix in core, not a reason to drop the rule.
*/
const OPTION_MODULE_RULES = [
{
optionKey: "smartGuides",
reason: "smartGuides",
moduleName: "SmartGuides",
when: (o) => !!o.smartGuides && o.smartGuides.enabled !== false
},
{
optionKey: "layoutHistory",
reason: "layoutHistory.enabled: true",
moduleName: "LayoutHistory",
when: (o) => {
var _o$layoutHistory;
return ((_o$layoutHistory = o.layoutHistory) === null || _o$layoutHistory === void 0 ? void 0 : _o$layoutHistory.enabled) === true;
}
},
{
optionKey: "pinnedTabs",
reason: "pinnedTabs.enabled: true",
moduleName: "PinnedTabs",
when: (o) => {
var _o$pinnedTabs;
return ((_o$pinnedTabs = o.pinnedTabs) === null || _o$pinnedTabs === void 0 ? void 0 : _o$pinnedTabs.enabled) === true;
}
},
{
optionKey: "overflow",
reason: "overflow.mode: 'wrap'",
moduleName: "MultiRowTabs",
when: (o) => {
var _o$overflow;
return ((_o$overflow = o.overflow) === null || _o$overflow === void 0 ? void 0 : _o$overflow.mode) === "wrap";
}
},
{
optionKey: "overflow",
reason: "overflow.maxRows",
moduleName: "MultiRowTabs",
when: (o) => {
var _o$overflow2;
return ((_o$overflow2 = o.overflow) === null || _o$overflow2 === void 0 ? void 0 : _o$overflow2.maxRows) != null;
}
},
{
optionKey: "overflow",
reason: "overflow.search",
moduleName: "AdvancedOverflow",
when: (o) => {
var _o$overflow3;
return !!((_o$overflow3 = o.overflow) === null || _o$overflow3 === void 0 ? void 0 : _o$overflow3.search);
}
},
{
optionKey: "overflow",
reason: "overflow.mru",
moduleName: "AdvancedOverflow",
when: (o) => {
var _o$overflow4;
return !!((_o$overflow4 = o.overflow) === null || _o$overflow4 === void 0 ? void 0 : _o$overflow4.mru);
}
},
{
optionKey: "autoHideEdgeGroups",
reason: "autoHideEdgeGroups",
moduleName: "AutoHideEdgeGroup",
when: (o) => isAnyEdgeGroupEnabled(o.autoHideEdgeGroups)
},
{
optionKey: "dockToEdgeGroups",
reason: "dockToEdgeGroups",
moduleName: "AutoEdgeGroup",
when: (o) => isAnyEdgeGroupEnabled(o.dockToEdgeGroups)
},
{
optionKey: "getTabContextMenuItems",
reason: "getTabContextMenuItems",
moduleName: "ContextMenu",
when: (o) => o.getTabContextMenuItems != null
},
{
optionKey: "getTabGroupChipContextMenuItems",
reason: "getTabGroupChipContextMenuItems",
moduleName: "ContextMenu",
when: (o) => o.getTabGroupChipContextMenuItems != null
},
{
optionKey: "dndCompass",
reason: "dndCompass",
moduleName: "DndCompass",
when: (o) => !!o.dndCompass
},
{
optionKey: "keyboardNavigation",
reason: "keyboardNavigation",
moduleName: "KeyboardNavigation",
when: (o) => !!o.keyboardNavigation
}
];
/**
* Reports any option in `options` whose module isn't registered. Never throws.
*
* Reasons are grouped so each missing module produces exactly one message, however
* many of its options were set: `overflow: { mode: 'wrap', maxRows: 3 }` names
* both paths in a single MultiRowTabs error rather than repeating the install
* instructions twice. Logging is deduplicated per module+reasons (see
* `logMissingModule`), so re-validating on every `updateOptions` stays quiet.
*
* Pass the options the caller supplied, not the merged set; see
* {@link OptionModuleRule.when}.
*/
function validateOptionModules(options, isModuleRegistered) {
const reasonsByModule = /* @__PURE__ */ new Map();
for (const rule of OPTION_MODULE_RULES) {
if (!rule.when(options) || isModuleRegistered(rule.moduleName)) continue;
const reasons = reasonsByModule.get(rule.moduleName);
if (reasons) reasons.push(rule.reason);
else reasonsByModule.set(rule.moduleName, [rule.reason]);
}
for (const [moduleName, reasons] of reasonsByModule) logMissingModule(moduleName, reasons);
}
const DEFAULT_FLOATING_GROUP_POSITION = {
left: 100,
top: 100,
width: 300,
height: 300
};
var DockviewFloatingGroupPanel = class extends CompositeDisposable {
/**
* The window's representative/anchor group. A floating window can host a
* nested layout of several groups; the anchor is used for back-compat
* single-group APIs and is reassigned if it leaves the window.
*/
get group() {
return this._group;
}
/**
* Register the dedicated title bar (if any) so anchor reassignment keeps
* its drag handle pointed at a group that still lives in this window.
*/
setTitleBar(titleBar) {
this._titleBar = titleBar;
}
setAnchorGroup(group) {
var _this$_titleBar;
this._group = group;
(_this$_titleBar = this._titleBar) === null || _this$_titleBar === void 0 || _this$_titleBar.setGroup(group);
}
constructor(group, overlay, gridview) {
super();
this.overlay = overlay;
this.gridview = gridview;
this._group = group;
this.addDisposables(overlay, { dispose: () => this.gridview.dispose() });
}
position(bounds) {
this.overlay.setBounds(bounds);
}
};
var FloatingGroupService = class {
get floatingGroups() {
return this._floatingGroups;
}
constructor(host) {
this._floatingGroups = [];
this._host = host;
}
add(group, overlay, gridview) {
const floatingGroupPanel = new DockviewFloatingGroupPanel(group, overlay, gridview);
const disposable = new CompositeDisposable(group.api.onDidActiveChange((event) => {
if (event.isActive) overlay.bringToFront();
}), (() => {
let lastWidth = -1;
let lastHeight = -1;
return watchElementResize(gridview.element, (entry) => {
const width = Math.round(entry.contentRect.width);
const height = Math.round(entry.contentRect.height);
if (width === lastWidth && height === lastHeight) return;
lastWidth = width;
lastHeight = height;
gridview.layout(width, height);
});
})());
const updateDialogLabel = () => {
var _group$activePanel;
const title = (_group$activePanel = group.activePanel) === null || _group$activePanel === void 0 ? void 0 : _group$activePanel.title;
if (title) overlay.element.setAttribute("aria-label", title);
else overlay.element.removeAttribute("aria-label");
};
updateDialogLabel();
floatingGroupPanel.addDisposables(group.api.onDidActivePanelChange(() => updateDialogLabel()), overlay.onDidChange(() => {
gridview.layout(gridview.width, gridview.height);
}), overlay.onDidChangeEnd(() => {
this._host.fireLayoutChange();
}), group.onDidChange((event) => {
overlay.setBounds({
height: typeof (event === null || event === void 0 ? void 0 : event.height) === "number" ? event.height + overlay.headerHeight : event === null || event === void 0 ? void 0 : event.height,
width: event === null || event === void 0 ? void 0 : event.width
});
}), { dispose: () => {
disposable.dispose();
remove(this._floatingGroups, floatingGroupPanel);
group.model.location = { type: "grid" };
} });
this._floatingGroups.push(floatingGroupPanel);
return floatingGroupPanel;
}
findByGroup(group) {
return this._floatingGroups.find((floating) => floating.group === group || floating.gridview.element.contains(group.element));
}
serialize() {
return this._floatingGroups.map((floating) => {
const grid = floating.gridview.serialize();
const position = floating.overlay.toJSON();
const root = grid.root;
if (root.type === "branch" && root.data.length === 1 && root.data[0].type === "leaf") return {
data: root.data[0].data,
position
};
return {
grid,
position
};
});
}
constrainBounds() {
for (const floating of this._floatingGroups) floating.overlay.setBounds();
}
updateBounds(options) {
if (!("floatingGroupBounds" in options)) return;
for (const group of this._floatingGroups) {
switch (options.floatingGroupBounds) {
case "boundedWithinViewport":
group.overlay.minimumInViewportHeight = void 0;
group.overlay.minimumInViewportWidth = void 0;
break;
case void 0:
group.overlay.minimumInViewportHeight = 100;
group.overlay.minimumInViewportWidth = 100;
break;
default:
var _options$floatingGrou, _options$floatingGrou2;
group.overlay.minimumInViewportHeight = (_options$floatingGrou = options.floatingGroupBounds) === null || _options$floatingGrou === void 0 ? void 0 : _options$floatingGrou.minimumHeightWithinViewport;
group.overlay.minimumInViewportWidth = (_options$floatingGrou2 = options.floatingGroupBounds) === null || _options$floatingGrou2 === void 0 ? void 0 : _options$floatingGrou2.minimumWidthWithinViewport;
}
group.overlay.setBounds();
}
}
disposeAll() {
for (const floating of [...this._floatingGroups]) floating.dispose();
}
dispose() {
this.disposeAll();
}
};
const FloatingGroupModule = defineModule({
name: "FloatingGroup",
serviceKey: "floatingGroupService",
create: (host) => new FloatingGroupService(host)
});
var PopoutWindowService = class {
constructor(host) {
this._entries = [];
this._restorationCleanups = /* @__PURE__ */ new Set();
this._restorationPromise = Promise.resolve();
this._onDidRemove = new Emitter();
this.onDidRemove = this._onDidRemove.event;
this._host = host;
}
get entries() {
return this._entries;
}
get restorationPromise() {
return this._restorationPromise;
}
add(entry) {
this._entries.push(entry);
}
remove(entry) {
if (remove(this._entries, entry) && !this._host.isDisposed) this._onDidRemove.fire(entry);
}
findByGroup(group) {
return this._entries.find((entry) => entry.popoutGroup === group || entry.gridview.element.contains(group.element));
}
findReferenceGroupId(group) {
var _this$_entries$find;
return (_this$_entries$find = this._entries.find((entry) => entry.popoutGroup === group)) === null || _this$_entries$find === void 0 ? void 0 : _this$_entries$find.referenceGroup;
}
/**
* The popout window's innerWidth/innerHeight are often 0/stale until it has
* painted, and the nested gridview lays its children out to the size passed
* to layout() (a plain group fills via CSS instead). To stop content
* rendering into a zero box until a manual resize, and to avoid the race a
* fixed number of animation frames had, observe the gridview element with
* a ResizeObserver created in the popout window's own realm. A parent-realm
* observer fires unreliably across the window boundary; a same-realm one
* fires reliably, including the initial observation once the window is
* sized.
*
* @returns a disposable that disconnects the observer, or `undefined` when
* the popout realm has no ResizeObserver (e.g. jsdom).
*/
observeGridviewSize(popoutWindow, gridview, overlayRenderContainer) {
var _popoutWindow$window;
const PopoutResizeObserver = (_popoutWindow$window = popoutWindow.window) === null || _popoutWindow$window === void 0 ? void 0 : _popoutWindow$window.ResizeObserver;
if (!PopoutResizeObserver) return;
let lastWidth = -1;
let lastHeight = -1;
const relayout = () => {
const win = popoutWindow.window;
if (this._host.isDisposed || !win || win.closed) return;
const width = Math.round(gridview.element.clientWidth);
const height = Math.round(gridview.element.clientHeight);
if (width === lastWidth && height === lastHeight) return;
lastWidth = width;
lastHeight = height;
if (width > 0 && height > 0) gridview.layout(width, height);
overlayRenderContainer.updateAllPositions();
};
const observer = new PopoutResizeObserver(() => {
var _popoutWindow$window2;
const raf = (_popoutWindow$window2 = popoutWindow.window) === null || _popoutWindow$window2 === void 0 ? void 0 : _popoutWindow$window2.requestAnimationFrame;
if (raf) raf.call(popoutWindow.window, relayout);
else relayout();
});
observer.observe(gridview.element);
return { dispose: () => observer.disconnect() };
}
scheduleRestoration(delayMs, work, onCancel) {
return new Promise((resolve) => {
const cleanup = () => {
this._restorationCleanups.delete(cleanup);
clearTimeout(handle);
onCancel === null || onCancel === void 0 || onCancel();
resolve();
};
const handle = setTimeout(() => {
this._restorationCleanups.delete(cleanup);
if (this._host.isDisposed) {
resolve();
return;
}
work();
resolve();
}, delayMs);
this._restorationCleanups.add(cleanup);
});
}
finishRestoration(promises) {
this._restorationPromise = Promise.all(promises).then(() => void 0);
}
cancelPendingRestorations() {
for (const cleanup of [...this._restorationCleanups]) cleanup();
this._restorationCleanups.clear();
}
serialize() {
return this._entries.map((entry) => {
const grid = entry.gridview.serialize();
const root = grid.root;
const url = entry.popoutGroup.api.location.type === "popout" ? entry.popoutGroup.api.location.popoutUrl : void 0;
const base = {
gridReferenceGroup: entry.referenceGroup,
position: entry.window.dimensions(),
url
};
if (root.type === "branch" && root.data.length === 1 && root.data[0].type === "leaf") return _objectSpread2(_objectSpread2({}, base), {}, { data: root.data[0].data });
return _objectSpread2(_objectSpread2({}, base), {}, { grid });
});
}
disposeAll() {
for (const entry of [...this._entries]) entry.disposable.dispose();
}
dispose() {
this.cancelPendingRestorations();
this.disposeAll();
this._onDidRemove.dispose();
}
};
const PopoutWindowModule = defineModule({
name: "PopoutWindow",
serviceKey: "popoutWindowService",
create: (host) => new PopoutWindowService(host)
});
var WatermarkService = class {
constructor(host) {
this._watermark = null;
this._host = host;
}
update() {
if (this._host.hasVisibleGridGroup()) {
this._unmount();
return;
}
if (this._watermark) return;
this._watermark = this._host.createWatermarkComponent();
this._watermark.init({ containerApi: this._host.api });
const container = document.createElement("div");
container.className = "dv-watermark-container";
addTestId(container, "watermark-component");
container.appendChild(this._watermark.element);
this._host.mountElement.appendChild(container);
}
refresh() {
this._unmount();
this.update();
}
_unmount() {
var _this$_watermark$disp, _this$_watermark;
if (!this._watermark) return;
this._watermark.element.parentElement.remove();
(_this$_watermark$disp = (_this$_watermark = this._watermark).dispose) === null || _this$_watermark$disp === void 0 || _this$_watermark$disp.call(_this$_watermark);
this._watermark = null;
}
dispose() {
this._unmount();
}
};
const WatermarkModule = defineModule({
name: "Watermark",
serviceKey: "watermarkService",
create: (host) => new WatermarkService(host),
init: (host, service) => {
service.update();
return new CompositeDisposable(Event.any(host.onDidAdd, host.onDidRemove)(() => {
service.update();
}), host.onDidViewVisibilityChangeMicroTaskQueue(() => {
service.update();
}));
}
});
var EdgeGroupService = class {
constructor() {
this._edgeGroups = /* @__PURE__ */ new Map();
this._edgeGroupDisposables = /* @__PURE__ */ new Map();
this._autoHide = /* @__PURE__ */ new WeakMap();
this._autoReveal = /* @__PURE__ */ new WeakMap();
}
add(position, group, autoCollapseDisposable) {
this._edgeGroups.set(position, group);
this._edgeGroupDisposables.set(position, autoCollapseDisposable);
}
remove(position) {
var _this$_edgeGroupDispo;
(_this$_edgeGroupDispo = this._edgeGroupDisposables.get(position)) === null || _this$_edgeGroupDispo === void 0 || _this$_edgeGroupDispo.dispose();
this._edgeGroupDisposables.delete(position);
this._edgeGroups.delete(position);
}
get(position) {
return this._edgeGroups.get(position);
}
has(position) {
return this._edgeGroups.has(position);
}
hasAny() {
return this._edgeGroups.size > 0;
}
entries() {
return this._edgeGroups.entries();
}
includes(group) {
for (const edgeGroup of this._edgeGroups.values()) if (edgeGroup === group) return true;
return false;
}
findPositionOf(group) {
for (const [position, edgeGroup] of this._edgeGroups) if (edgeGroup === group) return position;
}
setAutoHide(group, value) {
if (value === void 0) this._autoHide.delete(group);
else this._autoHide.set(group, value);
}
isAutoHide(group) {
return this._autoHide.get(group);
}
setAutoReveal(group, value) {
this._autoReveal.set(group, value);
}
isAutoReveal(group) {
var _this$_autoReveal$get;
return (_this$_autoReveal$get = this._autoReveal.get(group)) !== null && _this$_autoReveal$get !== void 0 ? _this$_autoReveal$get : false;
}
disposeAll() {
for (const disposable of this._edgeGroupDisposables.values()) disposable.dispose();
this._edgeGroupDisposables.clear();
this._edgeGroups.clear();
}
dispose() {
this.disposeAll();
}
};
const EdgeGroupModule = defineModule({
name: "EdgeGroup",
serviceKey: "edgeGroupService",
create: () => new EdgeGroupService()
});
const DEFAULT_ROOT_OVERLAY_MODEL = {
activationSize: {
type: "pixels",
value: 10
},
size: {
type: "pixels",
value: 20
}
};
const AUTO_EDGE_ROOT_OVERLAY_MODEL = {
activationSize: {
type: "pixels",
value: 32
},
size: {
type: "pixels",
value: 20
}
};
/**
* `hasEdgeDragReveal` gates the widened band, *not* the `dockToEdgeGroups`
* option alone: without the affordance nothing consumes the outer sub-band, so
* widening would only enlarge the plain grid-split trigger, a 3.2x bigger
* target for the same behaviour the default band already gives.
*/
function resolveRootOverlayModel(options, hasEdgeDragReveal) {
if (typeof options.dndEdges === "object" && options.dndEdges !== null) return options.dndEdges;
return hasEdgeDragReveal && isAnyEdgeGroupEnabled(options.dockToEdgeGroups) ? AUTO_EDGE_ROOT_OVERLAY_MODEL : DEFAULT_ROOT_OVERLAY_MODEL;
}
var RootDropTargetService = class {
constructor(host) {
this._host = host;
const canDisplayOverlay = (event, position) => {
const data = getPanelData();
if (data) {
if (data.viewId !== host.id) return false;
if (position === "center") return host.isGridEmpty();
return true;
}
if (position === "center" && !host.isGridEmpty()) return false;
return host.dispatchUnhandledDragOver(event, position);
};
const overlayModel = resolveRootOverlayModel(host.options, false);
this._html5Target = html5Backend.createDropTarget(host.element, {
className: "dv-drop-target-edge",
canDisplayOverlay,
acceptedTargetZones: [
"top",
"bottom",
"left",
"right",
"center"
],
overlayModel,
getOverrideTarget: () => host.rootDropTargetOverrideTarget(),
getPositionResolver: () => host.options.dropPositionResolver
});
this._pointerTarget = pointerBackend.createDropTarget(host.element, {
className: "dv-drop-target-edge",
canDisplayOverlay,
acceptedTargetZones: [
"top",
"bottom",
"left",
"right",
"center"
],
overlayModel,
getOverrideTarget: () => host.rootDropTargetOverrideTarget(),
getPositionResolver: () => host.options.dropPositionResolver
});
this.onWillShowOverlay = Event.any(this._html5Target.onWillShowOverlay, this._pointerTarget.onWillShowOverlay);
this.onDrop = Event.any(this._html5Target.onDrop, this._pointerTarget.onDrop);
this.setOptions(host.options);
}
setOptions(options) {
if ("dndEdges" in options) {
const disabled = typeof options.dndEdges === "boolean" && options.dndEdges === false;
this._html5Target.disabled = disabled;
this._pointerTarget.disabled = disabled;
}
if ("dndEdges" in options || "dockToEdgeGroups" in options) {
const model = resolveRootOverlayModel({
dndEdges: "dndEdges" in options ? options.dndEdges : this._host.options.dndEdges,
dockToEdgeGroups: "dockToEdgeGroups" in options ? options.dockToEdgeGroups : this._host.options.dockToEdgeGroups
}, this._host.hasEdgeDragReveal);
this._html5Target.setOverlayModel(model);
this._pointerTarget.setOverlayModel(model);
}
}
dispose() {
this._html5Target.dispose();
this._pointerTarget.dispose();
}
};
const RootDropTargetModule = defineModule({
name: "RootDropTarget",
serviceKey: "rootDropTargetService",
create: (host) => new RootDropTargetService(host),
init: (host, service) => {
service.setOptions(host.options);
return Disposable.NONE;
}
});
const SLOT_OPTION_KEY = {
left: "createLeftHeaderActionComponent",
right: "createRightHeaderActionComponent",
prefix: "createPrefixHeaderActionComponent"
};
var HeaderActionsService = class {
constructor(host) {
this._perGroup = /* @__PURE__ */ new Map();
this._host = host;
}
refresh(group) {
if (!(group === null || group === void 0 ? void 0 : group.model)) return;
const state = this._ensureState(group);
this._refreshSlot("left", group, state.left);
this._refreshSlot("right", group, state.right);
this._refreshSlot("prefix", group, state.prefix);
}
refreshAll() {
for (const group of this._host.groups) this.refresh(group);
}
disposeGroup(group) {
const state = this._perGroup.get(group);
if (!state) return;
state.left.dispose();
state.right.dispose();
state.prefix.dispose();
this._perGroup.delete(group);
}
dispose() {
for (const group of [...this._perGroup.keys()]) this.disposeGroup(group);
}
_ensureState(group) {
let state = this._perGroup.get(group);
if (!state) {
state = {
left: new MutableDisposable(),
right: new MutableDisposable(),
prefix: new MutableDisposable()
};
this._perGroup.set(group, state);
}
return state;
}
_refreshSlot(slot, group, disposable) {
const factory = this._host.options[SLOT_OPTION_KEY[slot]];
if (factory) {
const renderer = factory(group);
disposable.value = renderer;
renderer.init({
containerApi: this._host.api,
api: group.api,
group
});
group.model.attachHeaderAction(slot, renderer.element);
} else {
disposable.dispose();
group.model.attachHeaderAction(slot, void 0);
}
}
};
const HeaderActionsModule = defineModule({
name: "HeaderActions",
serviceKey: "headerActionsService",
create: (host) => new HeaderActionsService(host),
init: (host, service) => {
return new CompositeDisposable(host.onDidRemoveGroup((group) => {
service.disposeGroup(group);
}));
}
});
/** Bulk transactions whose per-panel events should not each be announced. */
const isBulk = (kind) => kind === "load" || kind === "clear";
function createLiveRegion(doc, politeness) {
const el = doc.createElement("div");
el.className = politeness === "assertive" ? "dv-live-region-assertive" : "dv-live-region";
el.setAttribute("role", politeness === "assertive" ? "alert" : "status");
el.setAttribute("aria-live", politeness);
el.setAttribute("aria-atomic", "true");
Object.assign(el.style, {
position: "absolute",
width: "1px",
height: "1px",
margin: "-1px",
padding: "0",
overflow: "hidden",
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
whiteSpace: "nowrap",
border: "0"
});
return el;
}
/**
* Narrates layout state changes to screen readers via visually-hidden
* `aria-live` regions. Free / core (WCAG 4.1.3). Announces panel open/close +
* the shared `announce()` sink (the accessibility module narrates docking here too).
* Two regions: a **polite** one for routine status and an **assertive** one
* for errors/cancellations. The bulk load/clear burst is suppressed via the
* mutation-transaction events, and an app can take over delivery entirely with
* the `announcer` option.
*/
var LiveRegionService = class extends CompositeDisposable {
constructor(host) {
var _mainDoc$defaultView;
super();
this._regions = /* @__PURE__ */ new Map();
this._suppressDepth = 0;
this._locationSubs = /* @__PURE__ */ new Map();
this._host = host;
const mainDoc = host.element.ownerDocument;
this._mainWindow = (_mainDoc$defaultView = mainDoc.defaultView) !== null && _mainDoc$defaultView !== void 0 ? _mainDoc$defaultView : window;
const mainPair = {
polite: createLiveRegion(mainDoc, "polite"),
assertive: createLiveRegion(mainDoc, "assertive")
};
host.element.appendChild(mainPair.polite);
host.element.appendChild(mainPair.assertive);
this._regions.set(this._mainWindow, mainPair);
this._syncPopoutRegions();
this.addDisposables({ dispose: () => this._disposeRegions() }, host.onDidChangePopouts(() => this._syncPopoutRegions()), host.onDidAddPanel((panel) => this._announce(panel, "open")), host.onDidRemovePanel((panel) => this._announce(panel, "close")), host.onWillMutateLayout((e) => {
if (isBulk(e.kind)) this._suppressDepth++;
}), host.onDidMutateLayout((e) => {
if (isBulk(e.kind)) this._suppressDepth = Math.max(0, this._suppressDepth - 1);
}), host.onDidMaximizedGroupChange((e) => {
const panel = e.group.activePanel;
if (panel) this._announce(panel, e.isMaximized ? "maximize" : "restore");
}), host.onDidAddGroup((group) => this._trackLocation(group)), host.onDidRemoveGroup((group) => {
var _this$_locationSubs$g;
(_this$_locationSubs$g = this._locationSubs.get(group.id)) === null || _this$_locationSubs$g === void 0 || _this$_locationSubs$g.dispose();
this._locationSubs.delete(group.id);
}), { dispose: () => {
this._locationSubs.forEach((sub) => sub.dispose());
this._locationSubs.clear();
} });
}
/** Create a live region in every popout document that lacks one, and remove
* regions for popouts that have closed. The main window's pair is permanent. */
_syncPopoutRegions() {
const mainDoc = this._host.element.ownerDocument;
const desired = /* @__PURE__ */ new Set([this._mainWindow]);
for (const win of this._host.getPopoutWindows()) {
var _doc$body;
if (win.document === mainDoc) continue;
desired.add(win);
if (this._regions.has(win)) continue;
const doc = win.document;
const mount = (_doc$body = doc.body) !== null && _doc$body !== void 0 ? _doc$body : doc.documentElement;
const pair = {
polite: createLiveRegion(doc, "polite"),
assertive: createLiveRegion(doc, "assertive")
};
mount.appendChild(pair.polite);
mount.appendChild(pair.assertive);
this._regions.set(win, pair);
}
for (const [win, pair] of this._regions) if (!desired.has(win)) {
pair.polite.remove();
pair.assertive.remove();
this._regions.delete(win);
}
}
_disposeRegions() {
for (const pair of this._regions.values()) {
pair.polite.remove();
pair.assertive.remove();
}
this._regions.clear();
}
/** Route announcements to the live region of the window that currently has
* focus, so a screen-reader user in a popout hears them, falling back to
* the main window. */
_focusedRegions() {
for (const [win, pair] of this._regions) {
if (win === this._mainWindow) continue;
try {
if (win.document.hasFocus()) return pair;
} catch (_unused) {}
}
return this._regions.get(this._mainWindow);
}
_trackLocation(group) {
let prev = group.api.location.type;
const sub = group.api.onDidLocationChange((e) => {
const next = e.location.type;
if (next === prev) return;
prev = next;
const panel = group.activePanel;
if (!panel) return;
let kind;
switch (next) {
case "floating":
kind = "float";
break;
case "popout":
kind = "popout";
break;
default:
kind = "dock";
break;
}
this._announce(panel, kind);
});
this._locationSubs.set(group.id, sub);
}
announce(message, politeness = "polite") {
if (this._host.options.announcements === false || this._suppressDepth > 0 || !message) return;
const announcer = this._host.options.announcer;
if (announcer) {
announcer({
message,
politeness
});
return;
}
const pair = this._focusedRegions();
const region = politeness === "assertive" ? pair.assertive : pair.polite;
region.textContent = "";
region.textContent = message;
}
_announce(panel, kind) {
var _this$_host$options$g, _this$_host$options;
const custom = (_this$_host$options$g = (_this$_host$options = this._host.options).getAnnouncement) === null || _this$_host$options$g === void 0 ? void 0 : _this$_host$options$g.call(_this$_host$options, {
kind,
panel
});
if (custom === null || custom === "") return;
this.announce(custom !== null && custom !== void 0 ? custom : this._defaultMessage(panel, kind));
}
_defaultMessage(panel, kind) {
var _panel$title;
const m = resolveMessages(this._host.options.messages);
const name = (_panel$title = panel.title) !== null && _panel$title !== void 0 ? _panel$title : panel.id;
switch (kind) {
case "open": return m.panelOpened(name);
case "close": return m.panelClosed(name);
case "maximize": return m.groupMaximized(name);
case "restore": return m.groupRestored(name);
case "float": return m.groupFloated(name);
case "dock": return m.groupDocked(name);
case "popout": return m.groupPoppedOut(name);
}
}
};
const LiveRegionModule = defineModule({
name: "LiveRegion",
serviceKey: "liveRegionService",
create: (host) => new LiveRegionService(host)
});
/** Cursor offset of the group drag ghost. */
const GROUP_DRAG_GHOST_OFFSET_X = 30;
const GROUP_DRAG_GHOST_OFFSET_Y = -10;
/**
* Owns the dispatch of the advanced drag-and-drop hooks: `onWillDragPanel`,
* `onWillDragGroup`, `onWillDrop` and `onWillShowOverlay`, forwarding each to
* the host's emitters so the public event shape is unchanged whether or not
* this module is registered.
*
* The service holds no drag state of its own. The gesture is driven by the
* DnD backends, and the per-group subscriptions live on the component's group
* lifecycle (so groups created mid-move are not missed).
*/
var AdvancedDnDService = class {
constructor(host) {
this.host = host;
}
dispatchWillDragPanel(event) {
this.host.fireWillDragPanel(event);
}
dispatchWillDragGroup(event) {
this.host.fireWillDragGroup(event);
}
dispatchWillDrop(event) {
this.host.fireWillDrop(event);
}
dispatchWillShowOverlay(event) {
this.host.fireWillShowOverlay(event);
}
buildGroupDragGhost(group) {
const createGhost = this.host.options.createGroupDragGhostComponent;
if (!createGhost) return;
const renderer = createGhost(group);
renderer.init({
group,
api: this.host.api
});
return {
element: renderer.element,
offsetX: GROUP_DRAG_GHOST_OFFSET_X,
offsetY: GROUP_DRAG_GHOST_OFFSET_Y,
dispose: renderer.dispose ? () => {
var _renderer$dispose;
return (_renderer$dispose = renderer.dispose) === null || _renderer$dispose === void 0 ? void 0 : _renderer$dispose.call(renderer);
} : void 0
};
}
resolveOverlayModel(location, group) {
var _this$host$options$dr, _this$host$options;
return (_this$host$options$dr = (_this$host$options = this.host.options).dropOverlayModel) === null || _this$host$options$dr === void 0 ? void 0 : _this$host$options$dr.call(_this$host$options, {
location,
group
});
}
showPreviewOverlay(group, position) {
const target = group.model.contentDropTarget;
target.showOverlay(position);
return Disposable.from(() => target.clearOverlay());
}
dispose() {}
};
const AdvancedDnDModule = defineModule({
name: "AdvancedDnD",
serviceKey: "advancedDnDService",
create: (host) => new AdvancedDnDService(host)
});
var TabGroupChipsService = class {
constructor(host) {
this._host = host;
}
attachToGroup(group) {
return new CompositeDisposable(group.model.onDidCreateTabGroup((e) => {
this._host.fireDidCreateTabGroup(e);
}), group.model.onDidDestroyTabGroup((e) => {
this._host.fireDidDestroyTabGroup(e);
}), group.model.onDidAddPanelToTabGroup((e) => {
this._host.fireDidAddPanelToTabGroup(e);
}), group.model.onDidRemovePanelFromTabGroup((e) => {
this._host.fireDidRemovePanelFromTabGroup(e);
}), group.model.onDidTabGroupChange((e) => {
this._host.fireDidTabGroupChange(e);
}), group.model.onDidTabGroupCollapsedChange((e) => {
this._host.fireDidTabGroupCollapsedChange(e);
}));
}
dispose() {}
};
const TabGroupChipsModule = defineModule({
name: "TabGroupChips",
serviceKey: "tabGroupChipsService",
create: (host) => new TabGroupChipsService(host),
init: (host, service) => {
const perGroupDisposables = /* @__PURE__ */ new Map();
return new CompositeDisposable(host.onDidAddGroup((group) => {
perGroupDisposables.set(group, service.attachToGroup(group));
}), host.onDidRemoveGroup((group) => {
var _perGroupDisposables$;
(_perGroupDisposables$ = perGroupDisposables.get(group)) === null || _perGroupDisposables$ === void 0 || _perGroupDisposables$.dispose();
perGroupDisposables.delete(group);
}), { dispose: () => {
for (const d of perGroupDisposables.values()) d.dispose();
perGroupDisposables.clear();
} });
}
});
/**
* Internal list of the built-in modules that ship with the core. Registered
* automatically by DockviewComponent at construction time; not exported from
* the package. Additional modules contributed by sibling packages (via
* `registerModules(...)`) are appended at construction.
*/
const AllModules = [
FloatingGroupModule,
PopoutWindowModule,
WatermarkModule,
EdgeGroupModule,
RootDropTargetModule,
HeaderActionsModule,
LiveRegionModule,
AdvancedDnDModule,
TabGroupChipsModule
];
var PositionCache = class {
constructor() {
this.cache = /* @__PURE__ */ new WeakMap();
this.currentFrameId = 0;
this.rafId = null;
}
getPosition(element) {
const cached = this.cache.get(element);
if ((cached === null || cached === void 0 ? void 0 : cached.frameId) === this.currentFrameId) return cached.rect;
this.scheduleFrameUpdate();
const rect = getDomNodePagePosition(element);
this.cache.set(element, {
rect,
frameId: this.currentFrameId
});
return rect;
}
invalidate() {
this.currentFrameId++;
}
scheduleFrameUpdate() {
if (this.rafId) return;
this.rafId = requestAnimationFrame(() => {
this.currentFrameId++;
this.rafId = null;
});
}
};
/**
* Placeholder handle marking "a reposition frame has been requested but the
* real handle is not known yet". Never passed to `cancelAnimationFrame` in
* practice — it is replaced within the same synchronous block — and harmless
* if it were, since no frame carries this id.
*/
const SCHEDULED = -1;
function createFocusableElement() {
const element = document.createElement("div");
element.tabIndex = -1;
return element;
}
var OverlayRenderContainer = class extends CompositeDisposable {
constructor(element, accessor) {
super();
this.element = element;
this.accessor = accessor;
this.map = {};
this._disposed = false;
this._generation = 0;
this.positionCache = new PositionCache();
this.addDisposables(Disposable.from(() => {
for (const value of Object.values(this.map)) {
value.disposable.dispose();
value.destroy.dispose();
this.cancelPendingUpdate(value);
}
this._disposed = true;
}));
}
updateAllPositions() {
if (this._disposed) return;
this.positionCache.invalidate();
for (const entry of Object.values(this.map)) if (entry.panel.api.isVisible && entry.resize) entry.resize();
}
/**
* Reposition a single panel's overlay over its reference container,
* optionally forcing it visible even when the panel is not currently
* "visible" (e.g. its group is collapsed). Used by the auto-hide peek to
* slide an `always`-rendered panel out without reparenting it or mutating
* the panel's visibility state. No-op if the panel isn't overlay-rendered.
*/
repositionPanelOverlay(panelId, forceVisible = false, clip) {
var _entry$resize;
if (this._disposed) return;
const entry = this.map[panelId];
if (!entry) return;
entry.forceVisible = forceVisible;
entry.clip = clip;
this.positionCache.invalidate();
(_entry$resize = entry.resize) === null || _entry$resize === void 0 || _entry$resize.call(entry);
}
/**
* Drop the reposition frame queued for a panel, if any. A queued frame is
* bound to the reference container that was current when it was scheduled,
* so it must be discarded whenever that container stops being the one the
* overlay should track.
*/
cancelPendingUpdate(entry) {
if (entry.pendingUpdate !== void 0) {
cancelAnimationFrame(entry.pendingUpdate);
entry.pendingUpdate = void 0;
}
}
detatch(panel) {
if (this.map[panel.api.id]) {
const entry = this.map[panel.api.id];
entry.disposable.dispose();
entry.destroy.dispose();
this.cancelPendingUpdate(entry);
delete this.map[panel.api.id];
return true;
}
return false;
}
attach(options) {
const { panel, referenceContainer } = options;
if (!this.map[panel.api.id]) {
const element = createFocusableElement();
element.className = "dv-render-overlay";
element.style.visibility = "hidden";
this.map[panel.api.id] = {
panel,
disposable: Disposable.NONE,
destroy: Disposable.NONE,
element,
retainPreviousGeometry: false,
positioned: false,
generation: ++this._generation
};
}
const mapEntry = this.map[panel.api.id];
/**
* A *change* of reference container supersedes the previous `attach`:
* take a fresh generation so its `resize` closure can no longer run,
* and drop the frame it queued against the old container. During
* `fromJSON({ reuseExistingPanels: true })` that old container is a
* detached staging group measuring 0x0, so leaving its frame in flight
* both wastes the update and delays the reposition against the real one.
*
* Re-attaching over the *same* container (re-open, active panel change)
* deliberately leaves scheduled work alone: `repositionPanelOverlay`
* (the auto-hide peek) schedules a frame carrying the sticky
* `forceVisible`/`clip` state which `attach` does not re-apply, and a
* peeked panel's `api.isVisible` is false — so discarding that frame
* leaves `visibilityChanged` to hide the overlay and the peek renders
* nothing. A newly created entry already holds a fresh generation, so
* the stale closures a `detatch` left behind are fenced off either way.
*/
if (mapEntry.referenceContainer !== referenceContainer) {
mapEntry.generation = ++this._generation;
this.cancelPendingUpdate(mapEntry);
mapEntry.referenceContainer = referenceContainer;
/**
* Arm the retain flag only here: it means "a replacement reference
* container is awaiting layout", so the 0x0 early-return below
* holds the last good box rather than blanking the panel mid-move.
* Arming it on every re-attach instead would make a container that
* genuinely collapses to zero keep its stale geometry forever.
*
* Gated on `positioned` because an overlay that has never been
* positioned has no left/top/width/height, and `.dv-render-overlay`
* defaults to 100%/100% — retaining "previous" geometry there would
* pin the content over the whole container.
*/
mapEntry.retainPreviousGeometry = mapEntry.positioned;
}
const generation = mapEntry.generation;
const focusContainer = mapEntry.element;
const contentElement = panel.view.content.element;
if (contentElement.parentElement !== focusContainer) focusContainer.appendChild(contentElement);
if (focusContainer.parentElement !== this.element) this.element.appendChild(focusContainer);
const resize = () => {
const panelId = panel.api.id;
const scheduling = this.map[panelId];
if ((scheduling === null || scheduling === void 0 ? void 0 : scheduling.generation) !== generation) return;
if (scheduling.pendingUpdate !== void 0) return;
/**
* Claim the slot *before* scheduling. `requestAnimationFrame` is
* shimmed to run inline by some hosts (and by several suites here),
* in which case the callback below clears `pendingUpdate` while
* this call is still on the stack — assigning the handle afterwards
* would resurrect a slot that is already spent and block every
* later reposition. The sentinel is replaced by the real handle
* only if the frame has not already run.
*/
scheduling.pendingUpdate = SCHEDULED;
const handle = requestAnimationFrame(() => {
var _entry$forceVisible;
const entry = this.map[panelId];
if (entry) entry.pendingUpdate = void 0;
if (this.isDisposed || (entry === null || entry === void 0 ? void 0 : entry.generation) !== generation) return;
const forceVisible = (_entry$forceVisible = entry.forceVisible) !== null && _entry$forceVisible !== void 0 ? _entry$forceVisible : false;
const clip = entry.clip;
const box = this.positionCache.getPosition(referenceContainer.element);
const box2 = this.positionCache.getPosition(this.element);
const left = box.left - box2.left;
const top = box.top - box2.top;
const width = box.width;
const height = box.height;
if (entry.retainPreviousGeometry && (width === 0 || height === 0)) {
if (!panel.api.isVisible && !forceVisible) {
focusContainer.style.visibility = "hidden";
focusContainer.style.pointerEvents = "none";
}
return;
}
entry.retainPreviousGeometry = false;
entry.positioned = true;
focusContainer.style.left = `${left}px`;
focusContainer.style.top = `${top}px`;
focusContainer.style.width = `${width}px`;
focusContainer.style.height = `${height}px`;
if (panel.api.isVisible || forceVisible) {
focusContainer.style.visibility = "";
focusContainer.style.pointerEvents = "";
} else {
focusContainer.style.visibility = "hidden";
focusContainer.style.pointerEvents = "none";
}
if (forceVisible) focusContainer.style.zIndex = "1000";
else if (panel.api.location.type !== "floating") focusContainer.style.zIndex = "";
if (clip) {
var _view$scrollX, _view$scrollY;
const view = this.element.ownerDocument.defaultView;
const sx = (_view$scrollX = view === null || view === void 0 ? void 0 : view.scrollX) !== null && _view$scrollX !== void 0 ? _view$scrollX : 0;
const sy = (_view$scrollY = view === null || view === void 0 ? void 0 : view.scrollY) !== null && _view$scrollY !== void 0 ? _view$scrollY : 0;
const top = clip.top + sy;
const left = clip.left + sx;
const insetTop = Math.max(0, top - box.top);
const insetLeft = Math.max(0, left - box.left);
const insetRight = Math.max(0, box.left + width - (clip.right + sx));
const insetBottom = Math.max(0, box.top + height - (clip.bottom + sy));
focusContainer.style.clipPath = `inset(${insetTop}px ${insetRight}px ${insetBottom}px ${insetLeft}px)`;
} else focusContainer.style.clipPath = "";
toggleClass(focusContainer, "dv-render-overlay-float", panel.group.api.location.type === "floating");
});
if (scheduling.pendingUpdate === SCHEDULED) scheduling.pendingUpdate = handle;
};
const visibilityChanged = () => {
if (panel.api.isVisible) {
var _this$map$panel$api$i;
this.positionCache.invalidate();
resize();
/**
* Existing geometry is safe to show while its replacement lays
* out. `retainPreviousGeometry` is only set when geometry has
* actually been written (see `attach`), so this can never
* un-hide an overlay whose left/top/width/height are unset —
* `.dv-render-overlay` is 100%/100% by default and would
* otherwise cover the whole dock.
*
* The retained geometry is the panel's position in the previous
* layout, so restoring a layout that moves the panel does paint
* it at stale coordinates until the reposition lands. That is
* bounded to the next frame now that a superseded `attach` can
* no longer swallow the reposition (see `attach`); showing
* stale-but-real geometry for a frame is the deliberate
* trade-off against blanking the panel for the whole rebuild.
*/
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 = "";
focusContainer.style.pointerEvents = "";
} else {
focusContainer.style.visibility = "hidden";
focusContainer.style.pointerEvents = "none";
}
};
const observerDisposable = new MutableDisposable();
const correctLayerPosition = () => {
if (panel.api.location.type === "floating") queueMicrotask(() => {
const floatingGroup = this.accessor.getFloatingWindowForGroup(panel.api.group);
if (!floatingGroup) return;
const element = floatingGroup.overlay.element;
const update = () => {
const level = Number(element.getAttribute("aria-level"));
focusContainer.style.zIndex = `calc(var(--dv-overlay-z-index, 999) + ${level * 2 + 1})`;
};
const observer = new MutationObserver(() => {
update();
});
observerDisposable.value = Disposable.from(() => observer.disconnect());
observer.observe(element, {
attributeFilter: ["aria-level"],
attributes: true
});
update();
});
else focusContainer.style.zIndex = "";
};
const disposable = new CompositeDisposable(
observerDisposable,
/**
* since container is positioned absoutely we must explicitly forward
* the dnd events for the expect behaviours to continue to occur in terms of dnd
*
* the dnd observer does not need to be conditional on whether the panel is visible since
* non-visible panels have 'pointer-events: none' and in such case the dnd observer will not fire.
*/
new DragAndDropObserver(focusContainer, {
onDragEnd: (e) => {
referenceContainer.dropTarget.dnd.onDragEnd(e);
},
onDragEnter: (e) => {
referenceContainer.dropTarget.dnd.onDragEnter(e);
},
onDragLeave: (e) => {
referenceContainer.dropTarget.dnd.onDragLeave(e);
},
onDrop: (e) => {
referenceContainer.dropTarget.dnd.onDrop(e);
},
onDragOver: (e) => {
referenceContainer.dropTarget.dnd.onDragOver(e);
}
}),
panel.api.onDidVisibilityChange(() => {
/**
* Control the visibility of the content, however even when not visible (display: none)
* the content is still maintained within the DOM hence DOM specific attributes
* such as scroll position are maintained when next made visible.
*/
visibilityChanged();
}),
panel.api.onDidDimensionsChange(() => {
if (!panel.api.isVisible) return;
resize();
}),
panel.api.onDidLocationChange(() => {
correctLayerPosition();
})
);
this.map[panel.api.id].destroy = Disposable.from(() => {
if (contentElement.parentElement === focusContainer) contentElement.remove();
focusContainer.remove();
});
correctLayerPosition();
queueMicrotask(() => {
if (this.isDisposed) return;
/**
* wait until everything has finished in the current stack-frame call before
* calling the first resize as other size-altering events may still occur before
* the end of the stack-frame.
*/
visibilityChanged();
});
this.map[panel.api.id].disposable.dispose();
this.map[panel.api.id].disposable = disposable;
this.map[panel.api.id].resize = resize;
return focusContainer;
}
};
function asyncGeneratorStep(n, t, e, r, o, a, c) {
try {
var i = n[a](c), u = i.value;
} catch (n) {
e(n);
return;
}
i.done ? t(u) : Promise.resolve(u).then(r, o);
}
function _asyncToGenerator(n) {
return function() {
var t = this, e = arguments;
return new Promise(function(r, o) {
var a = n.apply(t, e);
function _next(n) {
asyncGeneratorStep(a, r, o, _next, _throw, "next", n);
}
function _throw(n) {
asyncGeneratorStep(a, r, o, _next, _throw, "throw", n);
}
_next(void 0);
});
};
}
/**
* Reject popout URLs that aren't same-origin http(s). Blocks `javascript:`,
* `data:`, `blob:`, `vbscript:`, and cross-origin URLs that would otherwise
* execute in a context the browser still associates with the opener via
* `window.opener`.
*/
function assertSameOriginPopoutUrl(url) {
let resolved;
try {
resolved = new URL(url, globalThis.location.href);
} catch (_unused) {
throw new Error(`dockview: invalid popout URL: ${url}`);
}
if (!(resolved.protocol === "http:" || resolved.protocol === "https:") || resolved.origin !== globalThis.location.origin) throw new Error(`dockview: popout URL must be same-origin http(s); got: ${url}`);
}
var PopoutWindow = class extends CompositeDisposable {
get window() {
var _this$_window$value, _this$_window;
return (_this$_window$value = (_this$_window = this._window) === null || _this$_window === void 0 ? void 0 : _this$_window.value) !== null && _this$_window$value !== void 0 ? _this$_window$value : null;
}
constructor(target, className, options) {
super();
this.target = target;
this.className = className;
this.options = options;
this._onWillClose = new Emitter();
this.onWillClose = this._onWillClose.event;
this._onDidClose = new Emitter();
this.onDidClose = this._onDidClose.event;
this._window = null;
this.addDisposables(this._onWillClose, this._onDidClose, { dispose: () => {
this.close();
} });
}
dimensions() {
if (!this._window) return null;
const left = this._window.value.screenX;
return {
top: this._window.value.screenY,
left,
width: this._window.value.innerWidth,
height: this._window.value.innerHeight
};
}
close() {
if (this._window) {
var _this$options$onWillC, _this$options;
this._onWillClose.fire();
(_this$options$onWillC = (_this$options = this.options).onWillClose) === null || _this$options$onWillC === void 0 || _this$options$onWillC.call(_this$options, {
id: this.target,
window: this._window.value
});
this._window.disposable.dispose();
this._window = null;
this._onDidClose.fire();
}
}
open() {
var _this = this;
return _asyncToGenerator(function* () {
var _this$options$onDidOp, _this$options2;
if (_this._window) throw new Error("instance of popout window is already open");
const url = `${_this.options.url}`;
assertSameOriginPopoutUrl(url);
const features = Object.entries({
top: _this.options.top,
left: _this.options.left,
width: _this.options.width,
height: _this.options.height
}).map(([key, value]) => `${key}=${value}`).join(",");
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/open
*/
const externalWindow = window.open(url, _this.target, features);
if (!externalWindow)
/**
* Popup blocked
*/
return null;
const disposable = new CompositeDisposable();
_this._window = {
value: externalWindow,
disposable
};
disposable.addDisposables(Disposable.from(() => {
externalWindow.close();
}), addDisposableListener(globalThis.window, "beforeunload", () => {
/**
* before the main window closes we should close this popup too
* to be good citizens
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
*/
_this.close();
}));
const container = _this.createPopoutWindowContainer();
if (_this.className) container.classList.add(_this.className);
(_this$options$onDidOp = (_this$options2 = _this.options).onDidOpen) === null || _this$options$onDidOp === void 0 || _this$options$onDidOp.call(_this$options2, {
id: _this.target,
window: externalWindow
});
return new Promise((resolve, reject) => {
externalWindow.addEventListener("unload", () => {});
/**
* `load` is the only event that resolves this promise with a
* container, so a window that goes away first would leave the
* caller awaiting a promise that never settles - and
* `addPopoutGroup` holds its layout transaction open until it does.
* Settle with `null` on close, the same signal a blocked popup
* gives and one the caller already handles. `resolve` after the
* fact is a no-op, so a `load` that arrived first still wins.
*/
disposable.addDisposables(_this.onWillClose(() => resolve(null)));
externalWindow.addEventListener("load", () => {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event
*/
try {
const externalDocument = externalWindow.document;
externalDocument.title = document.title;
externalDocument.body.appendChild(container);
addStyles(externalDocument, globalThis.document.styleSheets, { nonce: _this.options.nonce });
/**
* beforeunload must be registered after load for reasons I could not determine
* otherwise the beforeunload event will not fire when the window is closed
*/
addDisposableListener(externalWindow, "beforeunload", () => {
/**
* @see https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event
*/
_this.close();
});
resolve(container);
} catch (err) {
reject(err);
}
});
});
})();
}
createPopoutWindowContainer() {
const el = document.createElement("div");
el.classList.add("dv-popout-window");
el.id = "dv-popout-window";
el.style.position = "absolute";
el.style.width = "100%";
el.style.height = "100%";
el.style.top = "0px";
el.style.left = "0px";
return el;
}
};
var StrictEventsSequencing = class extends CompositeDisposable {
constructor(accessor) {
super();
this.accessor = accessor;
this.init();
}
init() {
const panels = /* @__PURE__ */ new Set();
const groups = /* @__PURE__ */ new Set();
this.addDisposables(this.accessor.onDidAddPanel((panel) => {
if (panels.has(panel.api.id)) throw new Error(`dockview: Invalid event sequence. [onDidAddPanel] called for panel ${panel.api.id} but panel already exists`);
else panels.add(panel.api.id);
}), this.accessor.onDidRemovePanel((panel) => {
if (panels.has(panel.api.id)) panels.delete(panel.api.id);
else throw new Error(`dockview: Invalid event sequence. [onDidRemovePanel] called for panel ${panel.api.id} but panel does not exists`);
}), this.accessor.onDidAddGroup((group) => {
if (groups.has(group.api.id)) throw new Error(`dockview: Invalid event sequence. [onDidAddGroup] called for group ${group.api.id} but group already exists`);
else groups.add(group.api.id);
}), this.accessor.onDidRemoveGroup((group) => {
if (groups.has(group.api.id)) groups.delete(group.api.id);
else throw new Error(`dockview: Invalid event sequence. [onDidRemoveGroup] called for group ${group.api.id} but group does not exists`);
}));
}
};
/** Touch-primary input (coarse pointer, no fine pointer). On these devices a
* window resize is usually an on-screen-keyboard pop / orientation change /
* address-bar collapse, none of which mean "dismiss". */
function isCoarsePrimaryInput(win) {
if (!win.matchMedia) return false;
const coarse = win.matchMedia("(pointer: coarse)").matches;
const fine = win.matchMedia("(pointer: fine)").matches;
return coarse && !fine;
}
/**
* The shared dismissal lifecycle behind transient surfaces (popovers, menus,
* peeks): while it lives it watches a configurable set of dismiss signals
* (Escape / extra keys, outside-pointerdown with an optional grace window,
* window resize, focus moving outside) and calls `onDismiss`. Inside/outside is
* decided by an `isInside` predicate (geometry) or a `contains` check against
* `elements`. Dispose to detach every listener.
*
* It owns only the *signals*, not the surface element, its position, or any
* hover/keep-open policy, so callers keep their own element lifecycle and
* layer this underneath.
*/
function createDismissableLayer(options) {
var _options$window, _options$capture, _options$escape, _options$keys, _options$outsidePoint, _options$pointerDownG, _options$now;
const win = (_options$window = options.window) !== null && _options$window !== void 0 ? _options$window : window;
const capture = (_options$capture = options.capture) !== null && _options$capture !== void 0 ? _options$capture : false;
const escape = (_options$escape = options.escape) !== null && _options$escape !== void 0 ? _options$escape : true;
const keys = (_options$keys = options.keys) !== null && _options$keys !== void 0 ? _options$keys : [];
const outside = (_options$outsidePoint = options.outsidePointerDown) !== null && _options$outsidePoint !== void 0 ? _options$outsidePoint : true;
const grace = (_options$pointerDownG = options.pointerDownGraceMs) !== null && _options$pointerDownG !== void 0 ? _options$pointerDownG : 0;
const now = (_options$now = options.now) !== null && _options$now !== void 0 ? _options$now : Date.now;
const openedAt = now();
const disposables = new CompositeDisposable();
const isInside = (event) => {
var _options$elements, _options$elements2;
if (options.isInside) return options.isInside(event);
const target = event.target;
if (!(target instanceof Node)) return false;
return ((_options$elements = (_options$elements2 = options.elements) === null || _options$elements2 === void 0 ? void 0 : _options$elements2.call(options)) !== null && _options$elements !== void 0 ? _options$elements : []).some((el) => el.contains(target));
};
if (escape || keys.length > 0) disposables.addDisposables(addDisposableListener(win, "keydown", (event) => {
if (escape && event.key === "Escape" || keys.includes(event.key)) options.onDismiss();
}, capture));
if (outside || options.onInsidePointerDown) disposables.addDisposables(addDisposableListener(win, "pointerdown", (event) => {
if (isInside(event)) {
var _options$onInsidePoin;
(_options$onInsidePoin = options.onInsidePointerDown) === null || _options$onInsidePoin === void 0 || _options$onInsidePoin.call(options, event);
return;
}
if (!outside || now() - openedAt < grace) return;
options.onDismiss();
}, capture));
if (options.resize) disposables.addDisposables(addDisposableListener(win, "resize", () => {
if (isCoarsePrimaryInput(win)) return;
options.onDismiss();
}));
if (options.focusOut) {
const isFocusInside = (focused) => {
var _options$elements3, _options$elements4;
if (options.isFocusInside) return options.isFocusInside(focused);
return ((_options$elements3 = (_options$elements4 = options.elements) === null || _options$elements4 === void 0 ? void 0 : _options$elements4.call(options)) !== null && _options$elements3 !== void 0 ? _options$elements3 : []).some((el) => el.contains(focused));
};
const onFocusIn = (event) => {
const target = event.target;
if (target instanceof Element && !isFocusInside(target)) options.onDismiss();
};
win.addEventListener("focusin", onFocusIn, capture);
disposables.addDisposables({ dispose: () => win.removeEventListener("focusin", onFocusIn, capture) });
}
return disposables;
}
var PopupService = class extends CompositeDisposable {
constructor(root, win = globalThis.window) {
super();
this._active = null;
this._activeDisposable = new MutableDisposable();
this._root = root;
this._window = win;
this._element = win.document.createElement("div");
this._element.className = "dv-popover-anchor";
this._element.style.position = "relative";
this._root.prepend(this._element);
this.addDisposables(Disposable.from(() => {
this.close();
}), this._activeDisposable);
}
/**
* Move the popup anchor into a new root element. Call this when a shell
* wraps the dockview component so that edge-group overflow dropdowns
* position correctly relative to the full layout area.
*/
updateRoot(newRoot) {
newRoot.prepend(this._element);
this._root = newRoot;
}
openPopover(element, position) {
var _position$zIndex;
this.close();
const wrapper = this._window.document.createElement("div");
wrapper.style.position = "absolute";
wrapper.style.zIndex = (_position$zIndex = position.zIndex) !== null && _position$zIndex !== void 0 ? _position$zIndex : "var(--dv-overlay-z-index)";
wrapper.appendChild(element);
const anchorBox = this._element.getBoundingClientRect();
const offsetX = anchorBox.left;
const offsetY = anchorBox.top;
wrapper.style.top = `${position.y - offsetY}px`;
wrapper.style.left = `${position.x - offsetX}px`;
this._element.appendChild(wrapper);
this._active = wrapper;
const POINTERDOWN_GRACE_MS = 200;
this._activeDisposable.value = createDismissableLayer({
window: this._window,
onDismiss: () => this.close(),
elements: () => this._active ? [this._active] : [],
keys: ["Enter"],
pointerDownGraceMs: POINTERDOWN_GRACE_MS,
resize: true
});
this._window.requestAnimationFrame(() => {
shiftAbsoluteElementIntoView(wrapper, this._root);
});
}
close() {
if (this._active) {
this._active.remove();
this._activeDisposable.dispose();
this._active = null;
}
}
};
var DropTargetAnchorContainer = class extends CompositeDisposable {
get disabled() {
return this._disabled;
}
set disabled(value) {
if (this.disabled === value) return;
this._disabled = value;
if (value) this._clearNow();
}
_cancelPendingClear() {
if (this._pendingClear !== void 0) {
clearTimeout(this._pendingClear);
this._pendingClear = void 0;
}
}
_clearNow() {
this._cancelPendingClear();
if (this._model) this._model.root.remove();
this._model = void 0;
}
get model() {
if (this.disabled) return;
return {
clear: () => {
this._clearNow();
},
scheduleClear: () => {
if (this._pendingClear !== void 0 || !this._model) return;
this._pendingClear = setTimeout(() => {
this._pendingClear = void 0;
this._clearNow();
}, 0);
},
exists: () => {
return !!this._model;
},
getElements: (event, outline) => {
this._cancelPendingClear();
const changed = this._outline !== outline;
this._outline = outline;
if (this._model) {
this._model.changed = changed;
return this._model;
}
const container = this.createContainer();
const anchor = this.createAnchor();
this._model = {
root: container,
overlay: anchor,
changed
};
container.appendChild(anchor);
this.element.appendChild(container);
if ((event === null || event === void 0 ? void 0 : event.target) instanceof HTMLElement) {
const targetBox = event.target.getBoundingClientRect();
const box = this.element.getBoundingClientRect();
anchor.style.left = `${targetBox.left - box.left}px`;
anchor.style.top = `${targetBox.top - box.top}px`;
}
return this._model;
}
};
}
constructor(element, options) {
super();
this.element = element;
this._disabled = false;
this._disabled = options.disabled;
this.addDisposables(Disposable.from(() => {
var _this$model;
(_this$model = this.model) === null || _this$model === void 0 || _this$model.clear();
}));
}
createContainer() {
const el = document.createElement("div");
el.className = "dv-drop-target-container";
return el;
}
createAnchor() {
const el = document.createElement("div");
el.className = "dv-drop-target-anchor";
el.style.visibility = "hidden";
return el;
}
};
var EdgeGroupView = class {
/**
* The effective (pre-gap) collapsed size: the measured tab-strip size when
* available, otherwise the configured/theme base.
*/
get _effectiveBaseCollapsed() {
var _this$_measuredTabSiz;
return (_this$_measuredTabSiz = this._measuredTabSize) !== null && _this$_measuredTabSiz !== void 0 ? _this$_measuredTabSiz : this._baseCollapsedSize;
}
get minimumSize() {
if (this._isCollapsed) return this.collapsedSize;
return this._baseMinimumSize !== void 0 ? this._baseMinimumSize + this._gapAdd : this._effectiveBaseCollapsed + 50 + this._gapAdd;
}
get maximumSize() {
return this._isCollapsed ? this.collapsedSize : this._expandedMaximumSize;
}
get element() {
return this._group.element;
}
get isCollapsed() {
return this._isCollapsed;
}
get lastExpandedSize() {
return this._lastExpandedSize;
}
get collapsedSize() {
return this._effectiveBaseCollapsed + this._gapAdd;
}
/** The user-configured (pre-gap) geometry constraints, for serialization.
* These are the raw values passed to `addEdgeGroup`, unlike the effective
* `minimumSize`/`maximumSize`/`collapsedSize` getters, which fold in the
* theme gap and collapse-locking. */
get configuredMinimumSize() {
return this._baseMinimumSize;
}
get configuredMaximumSize() {
return this._expandedMaximumSize;
}
get configuredCollapsedSize() {
return this._baseCollapsedSize;
}
constructor(options, group, orientation, gapAdd = 0) {
var _options$collapsedSiz, _options$maximumSize, _options$initialSize;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this.snap = false;
this.priority = "low";
this._isCollapsed = false;
this._tabSizeDisposables = new CompositeDisposable();
this._group = group;
this._orientation = orientation;
group.element.classList.add("dv-edge-group");
group.element.dataset.testid = `dv-edge-group-${options.id}`;
this._baseCollapsedSize = (_options$collapsedSiz = options.collapsedSize) !== null && _options$collapsedSiz !== void 0 ? _options$collapsedSiz : 35;
this._baseMinimumSize = options.minimumSize;
this._gapAdd = gapAdd;
this._expandedMaximumSize = (_options$maximumSize = options.maximumSize) !== null && _options$maximumSize !== void 0 ? _options$maximumSize : Number.POSITIVE_INFINITY;
this._lastExpandedSize = (_options$initialSize = options.initialSize) !== null && _options$initialSize !== void 0 ? _options$initialSize : 200;
if (options.collapsed) {
this._isCollapsed = true;
group.element.classList.add("dv-edge-collapsed");
}
this._observeTabStrip();
}
/**
* Watch the group's tab strip so the collapsed size follows the live tab
* height. The strip's cross-axis size (height for top/bottom, width for
* left/right) is exactly what a collapsed edge group should occupy; a
* ResizeObserver keeps them in sync when the tab height changes at runtime.
*/
_observeTabStrip() {
const strip = this._group.element.querySelector(".dv-tabs-and-actions-container");
if (!strip) return;
this._tabSizeDisposables.addDisposables(watchElementResize(strip, () => {
this._applyMeasuredTabSize(this._orientation === "vertical" ? strip.offsetHeight : strip.offsetWidth);
}));
}
/**
* Apply a freshly-measured tab-strip cross size. When the group is
* collapsed this resizes it in the parent splitview so the collapsed strip
* always shows the full tab height.
*/
_applyMeasuredTabSize(size) {
if (size <= 0 || size === this._measuredTabSize) return;
this._measuredTabSize = size;
if (this._isCollapsed) this._onDidChange.fire({ size: this.collapsedSize });
}
layout(size, orthogonalSize) {
if (!this._isCollapsed) this._lastExpandedSize = size;
if (this._orientation === "horizontal") this._group.layout(size, orthogonalSize);
else this._group.layout(orthogonalSize, size);
}
setCollapsed(collapsed) {
if (this._isCollapsed === collapsed) return;
this._isCollapsed = collapsed;
this._group.element.classList.toggle("dv-edge-collapsed", collapsed);
}
setVisible(_visible) {}
/**
* Restore the last-expanded size from serialized state without triggering
* a layout. Must be called before setCollapsed(true) during fromJSON so
* that expanding after deserialization restores the correct size.
*/
restoreExpandedSize(size) {
this._lastExpandedSize = size;
}
/**
* Apply a new base (pre-gap) collapsed size, base minimum size and gap
* contribution after a theme or gap change. Base values come from the
* original config; the ShellManager owns the gap arithmetic. A live
* measured tab size (if any) continues to win over the base collapsed size.
*/
updateSizing(baseCollapsedSize, baseMinimumSize, gapAdd) {
this._baseCollapsedSize = baseCollapsedSize;
this._baseMinimumSize = baseMinimumSize;
this._gapAdd = gapAdd;
}
dispose() {
this._tabSizeDisposables.dispose();
this._onDidChange.dispose();
}
};
var CenterView = class {
get element() {
return this._dockviewElement;
}
constructor(_dockviewElement, _layoutDockview) {
this._dockviewElement = _dockviewElement;
this._layoutDockview = _layoutDockview;
this.priority = "high";
this.minimumSize = 100;
this.maximumSize = Number.POSITIVE_INFINITY;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
}
layout(size, orthogonalSize) {
this._layoutDockview(orthogonalSize, size);
}
setVisible(_visible) {}
dispose() {
this._onDidChange.dispose();
}
};
/**
* The vertical centre column: top (optional) | center | bottom (optional).
* This view sits between the left and right edge panels in the outer
* horizontal splitview, so its primary axis is width (horizontal).
*/
var MiddleColumnView = class {
get element() {
return this._element;
}
constructor(centerView, gap = 0) {
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this.minimumSize = 100;
this.maximumSize = Number.POSITIVE_INFINITY;
this.priority = "high";
this._element = document.createElement("div");
this._element.className = "dv-shell-middle-column";
this._element.style.height = "100%";
this._element.style.width = "100%";
this._splitview = new Splitview(this._element, {
orientation: "VERTICAL",
proportionalLayout: false,
margin: gap
});
this._centerIndex = 0;
this._splitview.addView(centerView, { type: "distribute" }, 0);
}
addTopView(view, initialSize) {
this._splitview.addView(view, initialSize, 0);
this._topIndex = 0;
this._centerIndex += 1;
if (this._bottomIndex !== void 0) this._bottomIndex += 1;
}
addBottomView(view, initialSize) {
const newIndex = this._splitview.length;
this._splitview.addView(view, initialSize, newIndex);
this._bottomIndex = newIndex;
}
removeView(position) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index === void 0) return;
this._splitview.removeView(index);
if (position === "top") {
this._topIndex = void 0;
this._centerIndex -= 1;
if (this._bottomIndex !== void 0) this._bottomIndex -= 1;
} else this._bottomIndex = void 0;
}
layout(size, orthogonalSize) {
this._splitview.layout(orthogonalSize, size);
}
setVisible(_visible) {}
setViewVisible(position, visible) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index !== void 0) this._splitview.setViewVisible(index, visible);
}
isViewVisible(position) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index !== void 0) return this._splitview.isViewVisible(index);
return false;
}
getViewSize(position) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index !== void 0) return this._splitview.getViewSize(index);
return 0;
}
getViewCachedVisibleSize(position) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index !== void 0) return this._splitview.getViewCachedVisibleSize(index);
}
resizeView(position, size) {
const index = position === "top" ? this._topIndex : this._bottomIndex;
if (index !== void 0) this._splitview.resizeView(index, size);
}
updateMargin(gap) {
this._splitview.margin = gap;
}
dispose() {
this._onDidChange.dispose();
this._splitview.dispose();
}
};
var ShellManager = class {
constructor(container, dockviewElement, layoutGrid, gap = 0, defaultCollapsedSize = 35) {
this._disposables = new CompositeDisposable();
this._viewConfigs = /* @__PURE__ */ new Map();
this._currentWidth = 0;
this._currentHeight = 0;
this._gap = gap;
this._defaultCollapsedSize = defaultCollapsedSize;
this._shellElement = document.createElement("div");
this._shellElement.className = "dv-shell";
this._shellElement.style.height = "100%";
this._shellElement.style.width = "100%";
this._shellElement.style.position = "relative";
container.appendChild(this._shellElement);
const centerView = new CenterView(dockviewElement, layoutGrid);
this._middleColumn = new MiddleColumnView(centerView, gap);
this._outerSplitview = new Splitview(this._shellElement, {
orientation: "HORIZONTAL",
proportionalLayout: false,
margin: gap
});
this._middleIndex = 0;
this._outerSplitview.addView(this._middleColumn, { type: "distribute" }, 0);
this._disposables.addDisposables(watchElementResize(this._shellElement, (entry) => {
/**
* When the shell (or an ancestor) becomes hidden — e.g. a
* nested dockview whose `onlyWhenVisible` host panel is
* deactivated — the element collapses to (0, 0) or is detached
* from the DOM. Propagating that zero size would relayout the
* edge-group splitview at 0, clamping the low-priority edge
* groups down to their minimum size and destroying the user's
* sizing. Skip these cases so sizes are preserved while hidden;
* mirrors the guard in the `Resizable` base class. See #1495.
*
* offsetParent === null is equivalent to display: none on the
* element or one of its ancestors.
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent
*/
if (!this._shellElement.offsetParent) return;
if (!isInDocument(this._shellElement)) return;
const width = Math.round(entry.contentRect.width);
const height = Math.round(entry.contentRect.height);
if (width === this._currentWidth && height === this._currentHeight) return;
this._currentWidth = width;
this._currentHeight = height;
this.layout(width, height);
}), this._outerSplitview, this._middleColumn, centerView);
}
get element() {
return this._shellElement;
}
/**
* Add an edge group view at the given position. The view wraps the
* provided group element inside the shell's splitview layout.
* Throws if a group at this position is already registered.
*/
addEdgeView(position, options, group) {
if (this.hasEdgeGroup(position)) throw new Error(`dockview: edge group already registered at position '${position}'`);
this._viewConfigs.set(position, options);
const outerN = 1 + (this._viewConfigs.has("left") ? 1 : 0) + (this._viewConfigs.has("right") ? 1 : 0);
const innerN = 1 + (this._viewConfigs.has("top") ? 1 : 0) + (this._viewConfigs.has("bottom") ? 1 : 0);
const outerGapAdd = outerN > 1 ? this._gap * (outerN - 1) / outerN : 0;
const innerGapAdd = innerN > 1 ? this._gap * (innerN - 1) / innerN : 0;
const isHorizontal = position === "left" || position === "right";
const gapAdd = isHorizontal ? outerGapAdd : innerGapAdd;
const orientation = isHorizontal ? "horizontal" : "vertical";
const view = new EdgeGroupView(_objectSpread2({ collapsedSize: this._defaultCollapsedSize }, options), group, orientation, gapAdd);
const initialSize = view.isCollapsed ? view.collapsedSize : view.lastExpandedSize;
switch (position) {
case "left":
this._outerSplitview.addView(view, initialSize, 0);
this._leftIndex = 0;
this._middleIndex += 1;
if (this._rightIndex !== void 0) this._rightIndex += 1;
this._leftView = view;
break;
case "right":
{
const idx = this._outerSplitview.length;
this._outerSplitview.addView(view, initialSize, idx);
this._rightIndex = idx;
this._rightView = view;
}
break;
case "top":
this._middleColumn.addTopView(view, initialSize);
this._topView = view;
break;
case "bottom":
this._middleColumn.addBottomView(view, initialSize);
this._bottomView = view;
break;
}
this._disposables.addDisposables(view);
this.updateTheme(this._gap, this._defaultCollapsedSize);
return view;
}
layout(width, height) {
this._outerSplitview.layout(width, height);
}
/**
* Called when the active theme changes. Updates splitview margins and
* edge-group collapsed sizes so the layout matches the new theme's gap
* and tab-strip dimensions.
*/
updateTheme(gap, defaultCollapsedSize) {
var _this$_leftView, _this$_rightView, _this$_topView, _this$_bottomView;
this._gap = gap;
this._defaultCollapsedSize = defaultCollapsedSize;
const outerN = 1 + (this._viewConfigs.has("left") ? 1 : 0) + (this._viewConfigs.has("right") ? 1 : 0);
const innerN = 1 + (this._viewConfigs.has("top") ? 1 : 0) + (this._viewConfigs.has("bottom") ? 1 : 0);
const outerGapAdd = outerN > 1 ? gap * (outerN - 1) / outerN : 0;
const innerGapAdd = innerN > 1 ? gap * (innerN - 1) / innerN : 0;
this._outerSplitview.margin = gap;
this._middleColumn.updateMargin(gap);
const updateView = (view, baseCfg, gapAdd) => {
var _baseCfg$collapsedSiz;
const baseCS = (_baseCfg$collapsedSiz = baseCfg.collapsedSize) !== null && _baseCfg$collapsedSiz !== void 0 ? _baseCfg$collapsedSiz : defaultCollapsedSize;
view.updateSizing(baseCS, baseCfg.minimumSize, gapAdd);
};
const topCfg = this._viewConfigs.get("top");
if (this._topView && topCfg) updateView(this._topView, topCfg, innerGapAdd);
const bottomCfg = this._viewConfigs.get("bottom");
if (this._bottomView && bottomCfg) updateView(this._bottomView, bottomCfg, innerGapAdd);
const leftCfg = this._viewConfigs.get("left");
if (this._leftView && leftCfg) updateView(this._leftView, leftCfg, outerGapAdd);
const rightCfg = this._viewConfigs.get("right");
if (this._rightView && rightCfg) updateView(this._rightView, rightCfg, outerGapAdd);
if (((_this$_leftView = this._leftView) === null || _this$_leftView === void 0 ? void 0 : _this$_leftView.isCollapsed) && this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, this._leftView.collapsedSize);
if (((_this$_rightView = this._rightView) === null || _this$_rightView === void 0 ? void 0 : _this$_rightView.isCollapsed) && this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, this._rightView.collapsedSize);
if ((_this$_topView = this._topView) === null || _this$_topView === void 0 ? void 0 : _this$_topView.isCollapsed) this._middleColumn.resizeView("top", this._topView.collapsedSize);
if ((_this$_bottomView = this._bottomView) === null || _this$_bottomView === void 0 ? void 0 : _this$_bottomView.isCollapsed) this._middleColumn.resizeView("bottom", this._bottomView.collapsedSize);
if (this._currentWidth > 0 && this._currentHeight > 0) this.layout(this._currentWidth, this._currentHeight);
}
removeEdgeView(position) {
const view = this._getView(position);
if (!view) return;
switch (position) {
case "left":
this._outerSplitview.removeView(this._leftIndex);
this._leftIndex = void 0;
this._leftView = void 0;
this._middleIndex -= 1;
if (this._rightIndex !== void 0) this._rightIndex -= 1;
break;
case "right":
this._outerSplitview.removeView(this._rightIndex);
this._rightIndex = void 0;
this._rightView = void 0;
break;
case "top":
this._middleColumn.removeView("top");
this._topView = void 0;
break;
case "bottom":
this._middleColumn.removeView("bottom");
this._bottomView = void 0;
break;
}
this._disposables.removeDisposable(view);
view.dispose();
this._viewConfigs.delete(position);
this.updateTheme(this._gap, this._defaultCollapsedSize);
}
hasEdgeGroup(position) {
switch (position) {
case "top": return this._topView !== void 0;
case "bottom": return this._bottomView !== void 0;
case "left": return this._leftView !== void 0;
case "right": return this._rightView !== void 0;
}
}
setEdgeGroupVisible(position, visible) {
switch (position) {
case "left":
if (this._leftIndex !== void 0) this._outerSplitview.setViewVisible(this._leftIndex, visible);
break;
case "right":
if (this._rightIndex !== void 0) this._outerSplitview.setViewVisible(this._rightIndex, visible);
break;
case "top":
case "bottom":
this._middleColumn.setViewVisible(position, visible);
break;
}
}
isEdgeGroupVisible(position) {
switch (position) {
case "left":
if (this._leftIndex !== void 0) return this._outerSplitview.isViewVisible(this._leftIndex);
return false;
case "right":
if (this._rightIndex !== void 0) return this._outerSplitview.isViewVisible(this._rightIndex);
return false;
case "top":
case "bottom": return this._middleColumn.isViewVisible(position);
}
}
setEdgeGroupCollapsed(position, collapsed) {
const view = this._getView(position);
if (!view) return;
view.setCollapsed(collapsed);
const targetSize = collapsed ? view.collapsedSize : view.lastExpandedSize;
switch (position) {
case "left":
if (this._leftIndex !== void 0) this._outerSplitview.resizeView(this._leftIndex, targetSize);
break;
case "right":
if (this._rightIndex !== void 0) this._outerSplitview.resizeView(this._rightIndex, targetSize);
break;
case "top":
case "bottom":
this._middleColumn.resizeView(position, targetSize);
break;
}
}
isEdgeGroupCollapsed(position) {
var _this$_getView$isColl, _this$_getView;
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;
}
/** The size an edge group expands to (its pre-collapse size), used to size
* the auto-hide peek overlay. */
getEdgeGroupExpandedSize(position) {
var _this$_getView$lastEx, _this$_getView2;
return (_this$_getView$lastEx = (_this$_getView2 = this._getView(position)) === null || _this$_getView2 === void 0 ? void 0 : _this$_getView2.lastExpandedSize) !== null && _this$_getView$lastEx !== void 0 ? _this$_getView$lastEx : 0;
}
_getView(position) {
switch (position) {
case "top": return this._topView;
case "bottom": return this._bottomView;
case "left": return this._leftView;
case "right": return this._rightView;
}
}
toJSON() {
const edgeGroups = {};
const constraints = (view) => ({
minimumSize: view.configuredMinimumSize,
maximumSize: Number.isFinite(view.configuredMaximumSize) ? view.configuredMaximumSize : void 0,
collapsedSize: view.configuredCollapsedSize
});
const expandedSize = (view, isVisible, liveSize, cachedVisibleSize) => {
if (view.isCollapsed) return view.lastExpandedSize;
if (!isVisible) return cachedVisibleSize !== null && cachedVisibleSize !== void 0 ? cachedVisibleSize : view.lastExpandedSize;
return liveSize;
};
if (this._leftView && this._leftIndex !== void 0) {
const visible = this._outerSplitview.isViewVisible(this._leftIndex);
edgeGroups.left = _objectSpread2({
size: expandedSize(this._leftView, visible, this._outerSplitview.getViewSize(this._leftIndex), this._outerSplitview.getViewCachedVisibleSize(this._leftIndex)),
visible,
collapsed: this._leftView.isCollapsed || void 0
}, constraints(this._leftView));
}
if (this._rightView && this._rightIndex !== void 0) {
const visible = this._outerSplitview.isViewVisible(this._rightIndex);
edgeGroups.right = _objectSpread2({
size: expandedSize(this._rightView, visible, this._outerSplitview.getViewSize(this._rightIndex), this._outerSplitview.getViewCachedVisibleSize(this._rightIndex)),
visible,
collapsed: this._rightView.isCollapsed || void 0
}, constraints(this._rightView));
}
if (this._topView) {
const visible = this._middleColumn.isViewVisible("top");
edgeGroups.top = _objectSpread2({
size: expandedSize(this._topView, visible, this._middleColumn.getViewSize("top"), this._middleColumn.getViewCachedVisibleSize("top")),
visible,
collapsed: this._topView.isCollapsed || void 0
}, constraints(this._topView));
}
if (this._bottomView) {
const visible = this._middleColumn.isViewVisible("bottom");
edgeGroups.bottom = _objectSpread2({
size: expandedSize(this._bottomView, visible, this._middleColumn.getViewSize("bottom"), this._middleColumn.getViewCachedVisibleSize("bottom")),
visible,
collapsed: this._bottomView.isCollapsed || void 0
}, constraints(this._bottomView));
}
return edgeGroups;
}
fromJSON(data) {
if (data.left && this._leftIndex !== void 0) {
var _this$_leftView2, _this$_leftView3, _data$left$collapsed, _this$_leftView$colla, _this$_leftView4;
(_this$_leftView2 = this._leftView) === null || _this$_leftView2 === void 0 || _this$_leftView2.restoreExpandedSize(data.left.size);
(_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);
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);
if (!data.left.visible) this._outerSplitview.setViewVisible(this._leftIndex, false);
}
if (data.right && this._rightIndex !== void 0) {
var _this$_rightView2, _this$_rightView3, _data$right$collapsed, _this$_rightView$coll, _this$_rightView4;
(_this$_rightView2 = this._rightView) === null || _this$_rightView2 === void 0 || _this$_rightView2.restoreExpandedSize(data.right.size);
(_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);
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);
if (!data.right.visible) this._outerSplitview.setViewVisible(this._rightIndex, false);
}
if (data.top) {
var _this$_topView2, _this$_topView3, _data$top$collapsed, _this$_topView$collap, _this$_topView4;
(_this$_topView2 = this._topView) === null || _this$_topView2 === void 0 || _this$_topView2.restoreExpandedSize(data.top.size);
(_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);
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);
if (!data.top.visible) this._middleColumn.setViewVisible("top", false);
}
if (data.bottom) {
var _this$_bottomView2, _this$_bottomView3, _data$bottom$collapse, _this$_bottomView$col, _this$_bottomView4;
(_this$_bottomView2 = this._bottomView) === null || _this$_bottomView2 === void 0 || _this$_bottomView2.restoreExpandedSize(data.bottom.size);
(_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);
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);
if (!data.bottom.visible) this._middleColumn.setViewVisible("bottom", false);
}
}
dispose() {
this._disposables.dispose();
this._shellElement.remove();
}
};
function buildTabGroupColorPalette(options) {
var _options$tabGroupColo;
return new TabGroupColorPalette((_options$tabGroupColo = options.tabGroupColors) !== null && _options$tabGroupColo !== void 0 ? _options$tabGroupColo : DEFAULT_TAB_GROUP_COLORS, options.tabGroupAccent !== "off");
}
function moveGroupWithoutDestroying(options) {
const activePanel = options.from.activePanel;
[...options.from.panels].map((panel) => {
const removedPanel = options.from.model.removePanel(panel);
options.from.model.renderContainer.detatch(panel);
return removedPanel;
}).forEach((panel) => {
options.to.model.openPanel(panel, {
skipSetActive: activePanel !== panel,
skipSetGroupActive: true
});
});
}
/** A never-firing event, the fallback the api returns when the backing module
* is absent, so the api events are always valid and subscribable. */
const NO_EVENT = () => ({ dispose: () => {} });
/** A never-firing history-change event, the fallback `onDidChangeHistory`
* returns when the LayoutHistory module is absent. */
const NO_LAYOUT_HISTORY_CHANGES = NO_EVENT;
var DockviewComponent = class extends BaseGrid {
fireDidCreateTabGroup(event) {
this._onDidCreateTabGroup.fire(event);
}
fireDidDestroyTabGroup(event) {
this._onDidDestroyTabGroup.fire(event);
}
fireDidAddPanelToTabGroup(event) {
this._onDidAddPanelToTabGroup.fire(event);
}
fireDidRemovePanelFromTabGroup(event) {
this._onDidRemovePanelFromTabGroup.fire(event);
}
fireDidTabGroupChange(event) {
this._onDidTabGroupChange.fire(event);
}
fireDidTabGroupCollapsedChange(event) {
this._onDidTabGroupCollapsedChange.fire(event);
}
get orientation() {
return this.gridview.orientation;
}
get totalPanels() {
return this.panels.length;
}
get panels() {
return this.groups.flatMap((group) => group.panels);
}
get options() {
return this._options;
}
get tabGroupColorPalette() {
return this._tabGroupColorPalette;
}
get activePanel() {
const activeGroup = this.activeGroup;
if (!activeGroup) return;
return activeGroup.activePanel;
}
get renderer() {
var _this$options$default;
return (_this$options$default = this.options.defaultRenderer) !== null && _this$options$default !== void 0 ? _this$options$default : "onlyWhenVisible";
}
get defaultHeaderPosition() {
var _this$options$default2;
return (_this$options$default2 = this.options.defaultHeaderPosition) !== null && _this$options$default2 !== void 0 ? _this$options$default2 : "top";
}
get api() {
return this._api;
}
get floatingGroups() {
var _this$_moduleRegistry, _this$_moduleRegistry2;
return (_this$_moduleRegistry = (_this$_moduleRegistry2 = this._moduleRegistry) === null || _this$_moduleRegistry2 === void 0 || (_this$_moduleRegistry2 = _this$_moduleRegistry2.services.floatingGroupService) === null || _this$_moduleRegistry2 === void 0 ? void 0 : _this$_moduleRegistry2.floatingGroups) !== null && _this$_moduleRegistry !== void 0 ? _this$_moduleRegistry : [];
}
/**
* Resolve the floating window hosting `group`, matched by membership so it
* finds nested (non-anchor) members too — a floating window can host a
* whole nested gridview, not just its anchor group. Returns `undefined`
* when the group isn't in any floating window. `floatingGroups` alone only
* exposes each window's anchor, so consumers that must act on any member
* (e.g. lifting an `always`-rendered panel's overlay above the window)
* should use this instead of a `.find(f => f.group === group)`.
*/
getFloatingWindowForGroup(group) {
var _this$_floatingGroupS;
return (_this$_floatingGroupS = this._floatingGroupService) === null || _this$_floatingGroupS === void 0 ? void 0 : _this$_floatingGroupS.findByGroup(group);
}
/**
* Boxes of the floating groups other than `exclude`, in coordinates
* relative to the floating overlay container. Supplied to a
* `transformFloatingGroupDrag` callback as `context.others` so it can
* align the dragged float against its siblings.
*/
_gatherFloatingGroupBoxes(exclude) {
return this.getFloatingGroupSnapshots(exclude).map((s) => s.box);
}
/**
* ISmartGuidesHost: the positioning parent floats are placed in, also the
* coordinate space the Smart Guides overlay draws its alignment lines in.
*/
getFloatingContainer() {
var _this$_floatingOverla;
return (_this$_floatingOverla = this._floatingOverlayHost) !== null && _this$_floatingOverla !== void 0 ? _this$_floatingOverla : this.gridview.element;
}
/** ISmartGuidesHost: the other floats' group identity + container-relative
* box, for the snap-together detector. */
getFloatingGroupSnapshots(exclude) {
const containerRect = this.getFloatingContainer().getBoundingClientRect();
return this.floatingGroups.filter((floating) => floating.group !== exclude).map((floating) => {
const rect = floating.overlay.element.getBoundingClientRect();
return {
group: floating.group,
box: {
left: rect.left - containerRect.left,
top: rect.top - containerRect.top,
width: rect.width,
height: rect.height
}
};
});
}
/** ISmartGuidesHost: dock/merge a dragged float into a target group via the
* existing move primitive (so events + undo cover it). */
mergeFloatInto(dragged, target, position) {
if (dragged === target || !this.getPanel(target.id)) return;
this.moveGroupOrPanel({
from: { groupId: dragged.id },
to: {
group: target,
position
}
});
}
/** ISmartGuidesHost: the main grid's splitter (sash) rectangles, in the
* floating container's coordinate space, for the optional splitter target. */
getGridSplitterRects() {
const origin = this.getFloatingContainer().getBoundingClientRect();
const result = [];
this.gridview.element.querySelectorAll(".dv-sash").forEach((sash) => {
const r = sash.getBoundingClientRect();
result.push({
left: r.left - origin.left,
top: r.top - origin.top,
width: r.width,
height: r.height
});
});
return result;
}
get _smartGuidesService() {
return this._moduleRegistry.services.smartGuidesService;
}
/** Whether Smart Guides snapping is currently active. */
get smartGuidesEnabled() {
var _this$_smartGuidesSer, _this$_smartGuidesSer2;
return (_this$_smartGuidesSer = (_this$_smartGuidesSer2 = this._smartGuidesService) === null || _this$_smartGuidesSer2 === void 0 ? void 0 : _this$_smartGuidesSer2.enabled) !== null && _this$_smartGuidesSer !== void 0 ? _this$_smartGuidesSer : false;
}
/** Toggle Smart Guides snapping at runtime (no-op if the module is absent). */
setSmartGuidesEnabled(enabled) {
var _assertModule;
(_assertModule = assertModule(this._smartGuidesService, "SmartGuides", "api.setSmartGuidesEnabled")) === null || _assertModule === void 0 || _assertModule.setEnabled(enabled);
}
/** Merge a partial Smart Guides option override in at runtime. */
updateSmartGuidesOptions(options) {
var _assertModule2;
(_assertModule2 = assertModule(this._smartGuidesService, "SmartGuides", "api.updateSmartGuidesOptions")) === null || _assertModule2 === void 0 || _assertModule2.updateOptions(options);
}
/** Fires when a dragged float commits an alignment snap on drop. */
get onDidSnapFloat() {
var _this$_smartGuidesSer3, _this$_smartGuidesSer4;
return (_this$_smartGuidesSer3 = (_this$_smartGuidesSer4 = this._smartGuidesService) === null || _this$_smartGuidesSer4 === void 0 ? void 0 : _this$_smartGuidesSer4.onDidSnapFloat) !== null && _this$_smartGuidesSer3 !== void 0 ? _this$_smartGuidesSer3 : NO_EVENT;
}
/** Fires when a dragged float commits a dock/merge on drop. */
get onDidSnapTogether() {
var _this$_smartGuidesSer5, _this$_smartGuidesSer6;
return (_this$_smartGuidesSer5 = (_this$_smartGuidesSer6 = this._smartGuidesService) === null || _this$_smartGuidesSer6 === void 0 ? void 0 : _this$_smartGuidesSer6.onDidSnapTogether) !== null && _this$_smartGuidesSer5 !== void 0 ? _this$_smartGuidesSer5 : NO_EVENT;
}
/**
* Compose the float drag-position transform from the public
* `transformFloatingGroupDrag` option and the optional Smart Guides module,
* so an app and the module can both nudge a single drag rather than one
* clobbering the other. Returns `undefined` only when the app option is
* unset and the module is absent (the removability case); then no transform
* is installed and the sibling-box snapshot is skipped, so the drag loop is
* truly byte-for-byte unchanged. When the module is present but `smartGuides`
* is unset it installs a cheap pass-through (the service snaps nothing and
* early-returns), so observable drag behaviour is still unchanged.
*/
_buildFloatingDragTransform(anchorGroup, disableSmartGuides) {
const publicTransform = this.options.transformFloatingGroupDrag;
const useGuides = !disableSmartGuides;
if (!publicTransform && !(useGuides && this._smartGuidesService)) return;
return (context) => {
var _this$_smartGuidesSer7, _ref;
const appResult = publicTransform === null || publicTransform === void 0 ? void 0 : publicTransform({
group: anchorGroup,
proposed: context.proposed,
container: context.container,
others: context.others,
modifiers: context.modifiers
});
const proposed = appResult ? _objectSpread2(_objectSpread2({}, context.proposed), appResult) : context.proposed;
const snapped = useGuides ? (_this$_smartGuidesSer7 = this._smartGuidesService) === null || _this$_smartGuidesSer7 === void 0 ? void 0 : _this$_smartGuidesSer7.transformFloatingGroupDrag({
group: anchorGroup,
proposed,
container: context.container,
others: context.others,
modifiers: context.modifiers
}) : void 0;
return (_ref = snapped !== null && snapped !== void 0 ? snapped : appResult) !== null && _ref !== void 0 ? _ref : void 0;
};
}
get _floatingGroupService() {
return this._moduleRegistry.services.floatingGroupService;
}
get _popoutWindowService() {
return this._moduleRegistry.services.popoutWindowService;
}
get _watermarkService() {
return this._moduleRegistry.services.watermarkService;
}
get _edgeGroupService() {
return this._moduleRegistry.services.edgeGroupService;
}
get _rootDropTargetService() {
return this._moduleRegistry.services.rootDropTargetService;
}
get _advancedDnDService() {
return this._moduleRegistry.services.advancedDnDService;
}
get _dndCompassService() {
return this._moduleRegistry.services.dndCompassService;
}
/** IDndCompassHost: whether a content drop at `position` on `group` is
* allowed, for compass cell gating (only legal cells are shown). The same
* predicate the content drop target uses, so the compass and the real drop
* agree. */
canDropOnGroup(group, position, event) {
return group.model.canDisplayContentOverlay(event, position);
}
/** IDndCompassHost: the frame the content drop target measures (mirrors the
* `getOverlayOutline` rule in `content.ts`): the whole group when
* `dndPanelOverlay === 'group'`, else just the content. The compass paints
* in this frame so its cells align with where a drop resolves. */
getDropOverlayElement(group) {
var _this$options$theme, _content$parentElemen;
const content = group.element.querySelector(".dv-content-container");
if (!content) return;
return ((_this$options$theme = this.options.theme) === null || _this$options$theme === void 0 ? void 0 : _this$options$theme.dndPanelOverlay) === "group" ? (_content$parentElemen = content.parentElement) !== null && _content$parentElemen !== void 0 ? _content$parentElemen : content : content;
}
/** IMultiRowTabsHost: the group's scrollable tab list (`.dv-tabs-container`),
* the element the wrap controller toggles + measures. */
getTabsListElement(group) {
return group.model.header.hidden ? void 0 : group.model.tabsListElement;
}
/** IMultiRowTabsHost: re-run a group's layout so a wrapped-header height
* change propagates to the content + active panel. */
relayoutGroup(group) {
group.relayout();
}
/** IMultiRowTabsHost: force the wrap controller's surplus set (rows beyond
* `overflow.maxRows`) into the group's overflow dropdown. */
setForcedOverflow(group, fn) {
group.model.header.setForcedOverflow(fn);
}
/** IDndCompassHost: the layout root (`.dv-dockview`, a positioned element),
* the surface the outer-cell landing preview is drawn over. */
getLayoutElement() {
return this.gridview.element;
}
/**
* The drop-position resolver installed on the group content drop targets:
* the app's `dropPositionResolver` option if set, else the DnD compass
* module's compass resolver (undefined when the compass is disabled). Read
* live by the content drop targets; undefined ⇒ default cursor-quadrant.
*/
getDropPositionResolver() {
var _this$_dndCompassServ;
if (this.options.dropPositionResolver) return this.options.dropPositionResolver;
const compass = (_this$_dndCompassServ = this._dndCompassService) === null || _this$_dndCompassServ === void 0 ? void 0 : _this$_dndCompassServ.resolver;
const autoEdge = this._moduleRegistry.services.autoEdgeGroupService;
if (compass && autoEdge) return { resolve: (args) => {
var _autoEdge$resolveEdge;
return (_autoEdge$resolveEdge = autoEdge.resolveEdge(args)) !== null && _autoEdge$resolveEdge !== void 0 ? _autoEdge$resolveEdge : compass.resolve(args);
} };
return compass !== null && compass !== void 0 ? compass : autoEdge === null || autoEdge === void 0 ? void 0 : autoEdge.resolver;
}
/**
* Dock the dragged item against the whole-layout edge at `position` (the
* orthogonalized root group), as the layout-edge drop zones do. Shared by
* the root drop target and by an `edge`-flagged drop on a group content
* target (a position resolver that marks an outer "dock to the layout edge"
* cell). No-op when there is no active drag data.
*/
dockToLayoutEdge(nativeEvent, position) {
const willDropEvent = new DockviewWillDropEvent({
nativeEvent,
position,
panel: void 0,
api: this._api,
group: void 0,
getData: getPanelData,
kind: "edge"
});
this._onWillDrop.fire(willDropEvent);
if (willDropEvent.defaultPrevented) return;
const data = getPanelData();
if (data) {
var _data$panelId;
this.moveGroupOrPanel({
from: {
groupId: data.groupId,
panelId: (_data$panelId = data.panelId) !== null && _data$panelId !== void 0 ? _data$panelId : void 0
},
to: {
group: this.orthogonalize(position),
position: "center"
}
});
} else this._onDidDrop.fire(new DockviewDidDropEvent({
nativeEvent,
position,
panel: void 0,
api: this._api,
group: void 0,
getData: getPanelData
}));
}
get headerActionsService() {
return this._moduleRegistry.services.headerActionsService;
}
get _layoutHistoryService() {
return this._moduleRegistry.services.layoutHistoryService;
}
/** Undo the previous recorded layout mutation (no-op if nothing to undo or
* the LayoutHistory module is absent). Requires `layoutHistory.enabled`. */
undo() {
var _assertModule3;
(_assertModule3 = assertModule(this._layoutHistoryService, "LayoutHistory", "api.undo")) === null || _assertModule3 === void 0 || _assertModule3.undo();
}
/** Re-apply the next layout mutation (no-op if nothing to redo). */
redo() {
var _assertModule4;
(_assertModule4 = assertModule(this._layoutHistoryService, "LayoutHistory", "api.redo")) === null || _assertModule4 === void 0 || _assertModule4.redo();
}
get canUndo() {
var _this$_layoutHistoryS, _this$_layoutHistoryS2;
return (_this$_layoutHistoryS = (_this$_layoutHistoryS2 = this._layoutHistoryService) === null || _this$_layoutHistoryS2 === void 0 ? void 0 : _this$_layoutHistoryS2.canUndo) !== null && _this$_layoutHistoryS !== void 0 ? _this$_layoutHistoryS : false;
}
get canRedo() {
var _this$_layoutHistoryS3, _this$_layoutHistoryS4;
return (_this$_layoutHistoryS3 = (_this$_layoutHistoryS4 = this._layoutHistoryService) === null || _this$_layoutHistoryS4 === void 0 ? void 0 : _this$_layoutHistoryS4.canRedo) !== null && _this$_layoutHistoryS3 !== void 0 ? _this$_layoutHistoryS3 : false;
}
/** Drop both undo and redo stacks. */
clearHistory() {
var _this$_layoutHistoryS5;
(_this$_layoutHistoryS5 = this._layoutHistoryService) === null || _this$_layoutHistoryS5 === void 0 || _this$_layoutHistoryS5.clear();
}
get onDidChangeHistory() {
var _this$_layoutHistoryS6, _this$_layoutHistoryS7;
return (_this$_layoutHistoryS6 = (_this$_layoutHistoryS7 = this._layoutHistoryService) === null || _this$_layoutHistoryS7 === void 0 ? void 0 : _this$_layoutHistoryS7.onDidChangeHistory) !== null && _this$_layoutHistoryS6 !== void 0 ? _this$_layoutHistoryS6 : NO_LAYOUT_HISTORY_CHANGES;
}
/**
* Whether the two-band edge drag-reveal affordance is registered. See
* {@link IRootDropTargetHost.hasEdgeDragReveal}; must not be read during
* module initialisation, only from `init`/postConstruct onwards.
*/
get hasEdgeDragReveal() {
return !!this._moduleRegistry.services.autoEdgeGroupService;
}
isGridEmpty() {
return this.gridview.length === 0;
}
rootDropTargetOverrideTarget() {
var _this$rootDropTargetC;
return (_this$rootDropTargetC = this.rootDropTargetContainer) === null || _this$rootDropTargetC === void 0 ? void 0 : _this$rootDropTargetC.model;
}
dispatchUnhandledDragOver(nativeEvent, position) {
const event = new DockviewUnhandledDragOverEvent(nativeEvent, "edge", position, getPanelData);
this._onUnhandledDragOver.fire(event);
return event.isAccepted;
}
fireWillDragPanel(event) {
this._onWillDragPanel.fire(event);
}
fireWillDragGroup(event) {
this._onWillDragGroup.fire(event);
}
fireWillDrop(event) {
this._onWillDrop.fire(event);
}
fireWillShowOverlay(event) {
this._onWillShowOverlay.fire(event);
}
/**
* Resolve the custom group drag ghost (via the AdvancedDnD module), or
* `undefined` to fall back to the default chip. Returns `undefined` when
* the module is absent, and the default ghost then renders.
*/
buildGroupDragGhost(group) {
var _this$_advancedDnDSer;
return (_this$_advancedDnDSer = this._advancedDnDService) === null || _this$_advancedDnDSer === void 0 ? void 0 : _this$_advancedDnDSer.buildGroupDragGhost(group);
}
/**
* Resolve the app-supplied drop overlay model (via the AdvancedDnD module)
* for a group drop target, or `undefined` to keep the target's default.
*/
resolveDropOverlayModel(location, group) {
var _this$_advancedDnDSer2;
return (_this$_advancedDnDSer2 = this._advancedDnDService) === null || _this$_advancedDnDSer2 === void 0 ? void 0 : _this$_advancedDnDSer2.resolveOverlayModel(location, group);
}
/** Outermost element: the shell (incl. edge groups) once built, else the gridview. */
get rootElement() {
var _this$_shellManager$e, _this$_shellManager;
return (_this$_shellManager$e = (_this$_shellManager = this._shellManager) === null || _this$_shellManager === void 0 ? void 0 : _this$_shellManager.element) !== null && _this$_shellManager$e !== void 0 ? _this$_shellManager$e : this.element;
}
/**
* Does this dock own `node`, in any of its windows? True when the node is
* inside the main shell, or inside one of this component's popout documents.
* A popout window hosts only this component's content, so whole-document
* membership is sufficient there; the main document may hold sibling docks,
* so it must be a containment check. A same-document popout (the jsdom mock)
* is already covered by the main check and contributes nothing.
*/
ownsElement(node) {
if (this.rootElement.contains(node)) return true;
const mainDoc = this.rootElement.ownerDocument;
const doc = node.ownerDocument;
if (!doc || doc === mainDoc) return false;
return this.getPopoutWindows().some((win) => win.document === doc);
}
/**
* The next / previous group in gridview (spatial) order, wrapping round.
* The keyboard accessibility module's focus navigation is built on this
* primitive, the only piece that needs the grid internals; the rest of
* the navigation logic lives in the KeyboardNavigationService.
*/
adjacentGroup(group, reverse) {
var _ref2;
if (group.api.location.type !== "grid") return;
const location = getGridLocation(group.element);
return (_ref2 = reverse ? this.gridview.previous(location) : this.gridview.next(location)) === null || _ref2 === void 0 ? void 0 : _ref2.view;
}
/**
* The nearest grid group in a spatial direction from `group`, by
* comparing group centre points. Floating and popout groups sit outside
* the grid's geometry and are ignored. Returns `undefined` when there is
* no group in that direction.
*/
adjacentGroupInDirection(group, direction) {
if (group.api.location.type !== "grid") return;
const from = group.element.getBoundingClientRect();
const fromX = from.left + from.width / 2;
const fromY = from.top + from.height / 2;
let best;
let bestDistance = Number.POSITIVE_INFINITY;
for (const candidate of this.groups) {
if (candidate === group || candidate.api.location.type !== "grid") continue;
const rect = candidate.element.getBoundingClientRect();
const dx = rect.left + rect.width / 2 - fromX;
const dy = rect.top + rect.height / 2 - fromY;
let inDirection;
switch (direction) {
case "left":
inDirection = dx < 0 && Math.abs(dx) >= Math.abs(dy);
break;
case "right":
inDirection = dx > 0 && Math.abs(dx) >= Math.abs(dy);
break;
case "up":
inDirection = dy < 0 && Math.abs(dy) >= Math.abs(dx);
break;
default:
inDirection = dy > 0 && Math.abs(dy) >= Math.abs(dx);
break;
}
if (!inDirection) continue;
const distance = dx * dx + dy * dy;
if (distance < bestDistance) {
bestDistance = distance;
best = candidate;
}
}
return best;
}
showDropPreview(group, position) {
var _this$_advancedDnDSer3, _this$_advancedDnDSer4;
return (_this$_advancedDnDSer3 = (_this$_advancedDnDSer4 = this._advancedDnDService) === null || _this$_advancedDnDSer4 === void 0 ? void 0 : _this$_advancedDnDSer4.showPreviewOverlay(group, position)) !== null && _this$_advancedDnDSer3 !== void 0 ? _this$_advancedDnDSer3 : Disposable.NONE;
}
announce(message) {
var _this$_moduleRegistry3;
(_this$_moduleRegistry3 = this._moduleRegistry.services.liveRegionService) === null || _this$_moduleRegistry3 === void 0 || _this$_moduleRegistry3.announce(message);
}
dockPanel(panel, group, position) {
this.moveGroupOrPanel({
from: {
groupId: panel.group.id,
panelId: panel.id
},
to: {
group,
position
}
});
}
floatPanel(panel) {
this.addFloatingGroup(panel);
}
get contextMenuService() {
return this._moduleRegistry.services.contextMenuService;
}
get pinnedTabsService() {
return this._moduleRegistry.services.pinnedTabsService;
}
get advancedOverflowService() {
return this._moduleRegistry.services.advancedOverflowService;
}
/**
* Pin/unpin a panel's tab. The single gated entry point behind
* `panel.api.setPinned`. Dormant unless `pinnedTabs.enabled` is set (a
* silent no-op), and a silent no-op when the PinnedTabs module is not
* registered: reaching past the `enabled` check means the option was set,
* so the option rule has already named the missing module. When active it
* mutates the panel's pinned flag (which fires
* `panel.api.onDidChangePinned`) and the component-level
* `onDidPanelPinnedChange`; the module reacts to enforce pinned-first
* ordering.
*/
setPanelPinned(panel, pinned) {
var _this$options$pinnedT;
if (panel.isPinned === pinned) return;
if (!((_this$options$pinnedT = this.options.pinnedTabs) === null || _this$options$pinnedT === void 0 ? void 0 : _this$options$pinnedT.enabled)) return;
if (!this.pinnedTabsService) return;
panel.setPinned(pinned);
this._onDidPanelPinnedChange.fire({
panel,
isPinned: pinned
});
}
get mountElement() {
return this.gridview.element;
}
hasVisibleGridGroup() {
return this.groups.some((group) => group.api.location.type === "grid" && group.api.isVisible);
}
fireLayoutChange() {
this._bufferOnDidLayoutChange.fire();
}
/**
* Promise that resolves when all popout groups from the last fromJSON call are restored.
* Useful for tests that need to wait for delayed popout creation.
*/
get popoutRestorationPromise() {
var _this$_popoutWindowSe, _this$_popoutWindowSe2;
return (_this$_popoutWindowSe = (_this$_popoutWindowSe2 = this._popoutWindowService) === null || _this$_popoutWindowSe2 === void 0 ? void 0 : _this$_popoutWindowSe2.restorationPromise) !== null && _this$_popoutWindowSe !== void 0 ? _this$_popoutWindowSe : Promise.resolve();
}
constructor(container, options) {
var _options$theme$gap, _options$theme, _options$theme$gap2, _options$theme2, _options$theme3;
super(container, {
proportionalLayout: true,
orientation: "HORIZONTAL",
styles: options.hideBorders ? { separatorBorder: "transparent" } : void 0,
disableAutoResizing: options.disableAutoResizing,
locked: options.locked,
margin: (_options$theme$gap = (_options$theme = options.theme) === null || _options$theme === void 0 ? void 0 : _options$theme.gap) !== null && _options$theme$gap !== void 0 ? _options$theme$gap : 0,
className: options.className
});
this.nextGroupId = sequentialNumberGenerator();
this._deserializer = new DefaultDockviewDeserialzier(this);
this._moduleRegistry = new ModuleRegistry();
this._onWillDragPanel = new Emitter();
this.onWillDragPanel = this._onWillDragPanel.event;
this._onWillDragGroup = new Emitter();
this.onWillDragGroup = this._onWillDragGroup.event;
this._onDidDrop = new Emitter();
this.onDidDrop = this._onDidDrop.event;
this._onWillDrop = new Emitter();
this.onWillDrop = this._onWillDrop.event;
this._mutationDepth = 0;
this._pendingLocationChanges = /* @__PURE__ */ new Map();
this._origin = "user";
this._originDepth = 0;
this._onWillMutateLayout = new Emitter();
this.onWillMutateLayout = this._onWillMutateLayout.event;
this._onDidMutateLayout = new Emitter();
this.onDidMutateLayout = this._onDidMutateLayout.event;
this._onWillShowOverlay = new Emitter();
this.onWillShowOverlay = this._onWillShowOverlay.event;
this._onUnhandledDragOver = new Emitter();
this.onUnhandledDragOver = this._onUnhandledDragOver.event;
this._onDidRemovePanel = new Emitter();
this.onDidRemovePanel = this._onDidRemovePanel.event;
this._onDidAddPanel = new Emitter();
this.onDidAddPanel = this._onDidAddPanel.event;
this._onDidPopoutGroupSizeChange = new Emitter();
this.onDidPopoutGroupSizeChange = this._onDidPopoutGroupSizeChange.event;
this._onDidPopoutGroupPositionChange = new Emitter();
this.onDidPopoutGroupPositionChange = this._onDidPopoutGroupPositionChange.event;
this._onDidAddPopoutGroup = new Emitter();
this.onDidAddPopoutGroup = this._onDidAddPopoutGroup.event;
this._onDidRemovePopoutGroup = new Emitter();
this.onDidRemovePopoutGroup = this._onDidRemovePopoutGroup.event;
this._onDidChangePopouts = new Emitter();
this.onDidChangePopouts = this._onDidChangePopouts.event;
this._onDidOpenPopoutWindowFail = new Emitter();
this.onDidOpenPopoutWindowFail = this._onDidOpenPopoutWindowFail.event;
this._onDidStartFloatingGroupDrag = new Emitter();
this.onDidStartFloatingGroupDrag = this._onDidStartFloatingGroupDrag.event;
this._onDidEndFloatingGroupDrag = new Emitter();
this.onDidEndFloatingGroupDrag = this._onDidEndFloatingGroupDrag.event;
this._onDidLayoutFromJSON = new Emitter();
this.onDidLayoutFromJSON = this._onDidLayoutFromJSON.event;
this._onDidActivePanelChange = new Emitter({ replay: true });
this.onDidActivePanelChange = this._onDidActivePanelChange.event;
this._onDidPanelPinnedChange = new Emitter();
this.onDidPanelPinnedChange = this._onDidPanelPinnedChange.event;
this._onDidMovePanel = new Emitter();
this.onDidMovePanel = this._onDidMovePanel.event;
this._onDidCreateTabGroup = new Emitter();
this.onDidCreateTabGroup = this._onDidCreateTabGroup.event;
this._onDidDestroyTabGroup = new Emitter();
this.onDidDestroyTabGroup = this._onDidDestroyTabGroup.event;
this._onDidAddPanelToTabGroup = new Emitter();
this.onDidAddPanelToTabGroup = this._onDidAddPanelToTabGroup.event;
this._onDidRemovePanelFromTabGroup = new Emitter();
this.onDidRemovePanelFromTabGroup = this._onDidRemovePanelFromTabGroup.event;
this._onDidTabGroupChange = new Emitter();
this.onDidTabGroupChange = this._onDidTabGroupChange.event;
this._onDidTabGroupCollapsedChange = new Emitter();
this.onDidTabGroupCollapsedChange = this._onDidTabGroupCollapsedChange.event;
this._onDidMaximizedGroupChange = new Emitter();
this.onDidMaximizedGroupChange = this._onDidMaximizedGroupChange.event;
this._inShellLayout = false;
this._onDidRemoveGroup = new Emitter();
this.onDidRemoveGroup = this._onDidRemoveGroup.event;
this._onDidEdgeGroupAutoHideChange = new Emitter();
this.onDidEdgeGroupAutoHideChange = this._onDidEdgeGroupAutoHideChange.event;
this._onDidAddGroup = new Emitter();
this.onDidAddGroup = this._onDidAddGroup.event;
this._onDidOptionsChange = new Emitter();
this.onDidOptionsChange = this._onDidOptionsChange.event;
this._onDidActiveGroupChange = new Emitter();
this.onDidActiveGroupChange = this._onDidActiveGroupChange.event;
this._peekingGroups = /* @__PURE__ */ new Set();
this._moving = false;
this._options = options;
this._tabGroupColorPalette = buildTabGroupColorPalette(options);
const explicitModules = options.modules;
const modules = explicitModules !== null && explicitModules !== void 0 ? explicitModules : [...AllModules, ...getRegisteredModules()];
for (const module of modules) this._moduleRegistry.register(module);
this._moduleRegistry.initialize(this);
this.reportMissingOptionModules(options);
const popoutWindowService = this._popoutWindowService;
if (popoutWindowService) this.addDisposables(popoutWindowService.onDidRemove((entry) => {
this._onDidRemovePopoutGroup.fire({
id: entry.popoutGroup.id,
group: entry.popoutGroup,
window: entry.getWindow()
});
}));
this.popupService = new PopupService(this.element);
this._api = new DockviewApi(this);
this.disableResizing = true;
this.element.remove();
this._shellManager = new ShellManager(container, this.element, (w, h) => this._layoutFromShell(w, h), (_options$theme$gap2 = (_options$theme2 = options.theme) === null || _options$theme2 === void 0 ? void 0 : _options$theme2.gap) !== null && _options$theme$gap2 !== void 0 ? _options$theme$gap2 : 0, (_options$theme3 = options.theme) === null || _options$theme3 === void 0 ? void 0 : _options$theme3.edgeGroupCollapsedSize);
this.popupService.updateRoot(this._shellManager.element);
this._shellThemeClassnames = new Classnames(this._shellManager.element);
this.rootDropTargetContainer = new DropTargetAnchorContainer(this._shellManager.element, { disabled: true });
this.floatingDropTargetContainer = new DropTargetAnchorContainer(this._shellManager.element, { disabled: false });
this.overlayRenderContainer = new OverlayRenderContainer(this._shellManager.element, this);
this._floatingOverlayHost = document.createElement("div");
this._floatingOverlayHost.className = "dv-floating-overlay-host";
this._shellManager.element.appendChild(this._floatingOverlayHost);
toggleClass(this.gridview.element, "dv-dockview", true);
toggleClass(this.element, "dv-debug", !!options.debug);
this.updateTheme();
if (options.debug) this.addDisposables(new StrictEventsSequencing(this));
this.addDisposables(Disposable.from(() => this._pendingLocationChanges.clear()), this.rootDropTargetContainer, this.floatingDropTargetContainer, addDisposableListener(this._shellManager.element, "dragend", () => {
var _this$rootDropTargetC2, _this$floatingDropTar;
(_this$rootDropTargetC2 = this.rootDropTargetContainer.model) === null || _this$rootDropTargetC2 === void 0 || _this$rootDropTargetC2.clear();
(_this$floatingDropTar = this.floatingDropTargetContainer.model) === null || _this$floatingDropTar === void 0 || _this$floatingDropTar.clear();
}, true), this.overlayRenderContainer, this._onWillDragPanel, this._onWillDragGroup, this._onWillShowOverlay, this._onDidActivePanelChange, this._onDidPanelPinnedChange, this._onDidAddPanel, this._onDidRemovePanel, this._onDidLayoutFromJSON, this._onDidDrop, this._onWillDrop, this._onWillMutateLayout, this._onDidMutateLayout, this._onDidMovePanel, this._onDidMovePanel.event(() => {
/**
* Update overlay positions after DOM layout completes to prevent 0×0 dimensions.
* With defaultRenderer="always" this results in panel content not showing after move operations.
* Debounced to avoid multiple calls when moving groups with multiple panels.
*/
this.debouncedUpdateAllPositions();
}), this._onDidAddGroup, this._onDidRemoveGroup, this._onDidEdgeGroupAutoHideChange, this._onDidActiveGroupChange, this._onUnhandledDragOver, this._onDidMaximizedGroupChange, this._onDidPopoutGroupSizeChange, this._onDidPopoutGroupPositionChange, this._onDidAddPopoutGroup, this._onDidRemovePopoutGroup, this._onDidChangePopouts, this._onDidAddPopoutGroup.event(() => this._onDidChangePopouts.fire()), this._onDidRemovePopoutGroup.event(() => this._onDidChangePopouts.fire()), this._onDidOpenPopoutWindowFail, this._onDidStartFloatingGroupDrag, this._onDidEndFloatingGroupDrag, this._onDidCreateTabGroup, this._onDidDestroyTabGroup, this._onDidAddPanelToTabGroup, this._onDidRemovePanelFromTabGroup, this._onDidTabGroupChange, this._onDidTabGroupCollapsedChange, Event.any(this.onDidPopoutGroupSizeChange, this.onDidPopoutGroupPositionChange, this.onDidCreateTabGroup, this.onDidDestroyTabGroup, this.onDidAddPanelToTabGroup, this.onDidRemovePanelFromTabGroup, this.onDidTabGroupChange, this.onDidTabGroupCollapsedChange)(() => {
this.fireLayoutChange();
}), this._onDidOptionsChange, this.onDidAdd((event) => {
if (!this._moving) this._onDidAddGroup.fire(event);
}), this.onDidRemove((event) => {
if (!this._moving) this._onDidRemoveGroup.fire(event);
}), this.onDidActiveChange((event) => {
if (!this._moving) this._onDidActiveGroupChange.fire(event);
}), this.onDidMaximizedChange((event) => {
this._onDidMaximizedGroupChange.fire({
group: event.panel,
isMaximized: event.isMaximized
});
}), Event.any(this.onDidAddPanel, this.onDidRemovePanel, this.onDidAddGroup, this.onDidRemove, this.onDidRemoveGroup, this.onDidMovePanel, this.onDidActivePanelChange)(() => {
this._bufferOnDidLayoutChange.fire();
}), Disposable.from(() => {
var _this$_shellManager2;
this._moduleRegistry.dispose();
(_this$_shellManager2 = this._shellManager) === null || _this$_shellManager2 === void 0 || _this$_shellManager2.dispose();
}));
const rootDropTarget = this._rootDropTargetService;
if (rootDropTarget) this.addDisposables(rootDropTarget.onWillShowOverlay((event) => {
if (this.gridview.length > 0 && event.position === "center") return;
this._onWillShowOverlay.fire(new DockviewWillShowOverlayLocationEvent(event, {
kind: "edge",
panel: void 0,
api: this._api,
group: void 0,
getData: getPanelData
}));
}), rootDropTarget.onDrop((event) => this.dockToLayoutEdge(event.nativeEvent, event.position)));
this._moduleRegistry.postConstruct(this);
}
setVisible(panel, visible) {
switch (panel.api.location.type) {
case "grid":
super.setVisible(panel, visible);
break;
case "floating": {
const item = this.floatingGroups.find((floatingGroup) => floatingGroup.group === panel);
if (item) {
item.overlay.setVisible(visible);
panel.api._onDidVisibilityChange.fire({ isVisible: visible });
}
break;
}
case "popout":
console.warn("dockview: You cannot hide a group that is in a popout window");
break;
case "edge": break;
}
}
/**
* Returns the {@link PopupService} that should host popovers (context
* menus, tab overflow menus) for the given group. Popout groups have their
* own service rooted in their popout window so the popover renders there
* and dismisses on events from that window.
*/
getPopupServiceForGroup(group) {
var _this$_popoutWindowSe3, _this$_popoutWindowSe4;
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;
}
addPopoutGroup(itemToPopout, options) {
return this.mutationAsync("popout", () => this._doAddPopoutGroup(itemToPopout, options));
}
/** Enumerate the popout groups currently open in their own windows. */
getPopouts() {
var _this$_popoutWindowSe5, _this$_popoutWindowSe6;
return (_this$_popoutWindowSe5 = (_this$_popoutWindowSe6 = this._popoutWindowService) === null || _this$_popoutWindowSe6 === void 0 ? void 0 : _this$_popoutWindowSe6.entries.map((entry) => ({
id: entry.popoutGroup.id,
group: entry.popoutGroup,
window: entry.getWindow()
}))) !== null && _this$_popoutWindowSe5 !== void 0 ? _this$_popoutWindowSe5 : [];
}
/** The live popout `Window` handles, one per open popout group. The
* narrow surface accessibility services need to mirror per-window state. */
getPopoutWindows() {
return this.getPopouts().map((popout) => popout.window);
}
_doAddPopoutGroup(itemToPopout, options) {
var _options$overridePopo, _options$overridePopo2, _options$popoutUrl, _this$options, _this$options2;
const service = assertModule(this._popoutWindowService, "PopoutWindow", "api.addPopoutGroup");
if (!service) return Promise.resolve(false);
if (itemToPopout instanceof DockviewGroupPanel && itemToPopout.model.location.type === "edge") return Promise.resolve(false);
if (itemToPopout instanceof DockviewPanel && itemToPopout.group.size === 1) return this.addPopoutGroup(itemToPopout.group, options);
const theme = getDockviewTheme(this.gridview.element);
const element = this.element;
function getBox() {
var _itemToPopout$group$e, _itemToPopout$group;
if (options === null || options === void 0 ? void 0 : options.position) return options.position;
const rect = (itemToPopout instanceof DockviewGroupPanel ? itemToPopout.element : (_itemToPopout$group$e = (_itemToPopout$group = itemToPopout.group) === null || _itemToPopout$group === void 0 ? void 0 : _itemToPopout$group.element) !== null && _itemToPopout$group$e !== void 0 ? _itemToPopout$group$e : element).getBoundingClientRect();
return {
left: window.screenX + rect.left,
top: window.screenY + rect.top,
width: rect.width,
height: rect.height
};
}
const box = getBox();
const groupId = (_options$overridePopo = options === null || options === void 0 || (_options$overridePopo2 = options.overridePopoutGroup) === null || _options$overridePopo2 === void 0 ? void 0 : _options$overridePopo2.id) !== null && _options$overridePopo !== void 0 ? _options$overridePopo : this.getNextGroupId();
const resolvedPopoutUrl = (_options$popoutUrl = options === null || options === void 0 ? void 0 : options.popoutUrl) !== null && _options$popoutUrl !== void 0 ? _options$popoutUrl : (_this$options = this.options) === null || _this$options === void 0 ? void 0 : _this$options.popoutUrl;
const _window = new PopoutWindow(`${this.id}-${groupId}`, theme !== null && theme !== void 0 ? theme : "", {
url: resolvedPopoutUrl !== null && resolvedPopoutUrl !== void 0 ? resolvedPopoutUrl : "/popout.html",
left: box.left,
top: box.top,
width: box.width,
height: box.height,
onDidOpen: options === null || options === void 0 ? void 0 : options.onDidOpen,
onWillClose: options === null || options === void 0 ? void 0 : options.onWillClose,
nonce: (_this$options2 = this.options) === null || _this$options2 === void 0 ? void 0 : _this$options2.nonce
});
const popoutWindowDisposable = new CompositeDisposable(_window, _window.onDidClose(() => {
popoutWindowDisposable.dispose();
}));
return _window.open().then((popoutContainer) => {
var _options$referenceGro, _options$overridePopo4;
if (_window.isDisposed) return false;
const referenceGroup = (_options$referenceGro = options === null || options === void 0 ? void 0 : options.referenceGroup) !== null && _options$referenceGro !== void 0 ? _options$referenceGro : itemToPopout instanceof DockviewPanel ? itemToPopout.group : itemToPopout;
const referenceLocation = itemToPopout.api.location.type;
/**
* The group that is being added doesn't already exist within the DOM, the most likely occurrence
* of this case is when being called from the `fromJSON(...)` method
*/
const isGroupAddedToDom = referenceGroup.element.parentElement !== null;
let group;
if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) {
var _options$overridePopo3;
group = (_options$overridePopo3 = options.overridePopoutGroup) !== null && _options$overridePopo3 !== void 0 ? _options$overridePopo3 : referenceGroup;
} else if (!isGroupAddedToDom) group = referenceGroup;
else if (options === null || options === void 0 ? void 0 : options.overridePopoutGroup) group = options.overridePopoutGroup;
else {
group = this.createGroup({ id: groupId });
if (popoutContainer) this._onDidAddGroup.fire(group);
}
if (popoutContainer === null) {
this.handleBlockedPopout({
group,
referenceGroup,
options,
popoutWindowDisposable
});
return false;
}
const gready = document.createElement("div");
gready.className = "dv-overlay-render-container";
const overlayRenderContainer = new OverlayRenderContainer(gready, this);
group.model.renderContainer = overlayRenderContainer;
const popoutGridview = (_options$overridePopo4 = options === null || options === void 0 ? void 0 : options.overridePopoutGridview) !== null && _options$overridePopo4 !== void 0 ? _options$overridePopo4 : this.createNestedGridview();
if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGridview)) popoutGridview.addView(group, Sizing.Distribute, [0]);
popoutGridview.element.style.width = "100%";
popoutGridview.element.style.height = "100%";
popoutGridview.layout(_window.window.innerWidth, _window.window.innerHeight);
let popoutGridviewDisposed = false;
const disposePopoutGridview = () => {
if (!popoutGridviewDisposed) {
popoutGridviewDisposed = true;
popoutGridview.dispose();
}
};
let floatingBox;
/**
* Popping out rehomes panels into `group`, so like the floating
* case it owes the caller an `onDidMovePanel` per panel. The
* events are deferred until the window is wired up so listeners
* observe the panels' final `popout` location.
*/
let movedPanels = [];
if (!(options === null || options === void 0 ? void 0 : options.overridePopoutGroup) && !(options === null || options === void 0 ? void 0 : options.overridePopoutGridview) && isGroupAddedToDom) if (itemToPopout instanceof DockviewPanel) {
const sourceGroup = itemToPopout.group;
this.movingLock(() => {
const panel = referenceGroup.model.removePanel(itemToPopout);
group.model.openPanel(panel);
});
movedPanels = [{
panel: itemToPopout,
from: sourceGroup
}];
} else {
this.movingLock(() => moveGroupWithoutDestroying({
from: referenceGroup,
to: group
}));
movedPanels = group.panels.map((panel) => ({
panel,
from: referenceGroup
}));
switch (referenceLocation) {
case "grid":
referenceGroup.api.setVisible(false);
break;
case "floating":
case "popout":
var _this$floatingGroups$;
floatingBox = (_this$floatingGroups$ = this.floatingGroups.find((value) => value.group.api.id === itemToPopout.api.id)) === null || _this$floatingGroups$ === void 0 ? void 0 : _this$floatingGroups$.overlay.toJSON();
this.removeGroup(referenceGroup);
break;
}
}
popoutContainer.classList.add("dv-dockview");
popoutContainer.style.overflow = "hidden";
popoutContainer.appendChild(gready);
popoutContainer.appendChild(popoutGridview.element);
const anchor = document.createElement("div");
const dropTargetContainer = new DropTargetAnchorContainer(anchor, { disabled: this.rootDropTargetContainer.disabled });
popoutContainer.appendChild(anchor);
group.model.dropTargetContainer = dropTargetContainer;
const popoutPopupService = new PopupService(popoutContainer, _window.window);
popoutWindowDisposable.addDisposables(popoutPopupService);
group.model.location = {
type: "popout",
getWindow: () => _window.window,
popoutUrl: resolvedPopoutUrl
};
if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) {
const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
for (const member of members) {
member.model.renderContainer = overlayRenderContainer;
member.model.dropTargetContainer = dropTargetContainer;
member.model.location = {
type: "popout",
getWindow: () => _window.window,
popoutUrl: resolvedPopoutUrl
};
}
}
if (isGroupAddedToDom && itemToPopout.api.location.type === "grid") itemToPopout.api.setVisible(false);
this.doSetGroupAndPanelActive(group);
const resizeObserverDisposable = service.observeGridviewSize(_window, popoutGridview, overlayRenderContainer);
if (resizeObserverDisposable) popoutWindowDisposable.addDisposables(resizeObserverDisposable);
const focusRoutingDisposable = new MutableDisposable();
popoutWindowDisposable.addDisposables(focusRoutingDisposable);
const bindFocusRouting = (anchor) => {
focusRoutingDisposable.value = new CompositeDisposable(anchor.api.onDidActiveChange((event) => {
if (event.isActive) {
var _window$window;
(_window$window = _window.window) === null || _window$window === void 0 || _window$window.focus();
}
}), anchor.api.onWillFocus(() => {
var _window$window2;
(_window$window2 = _window.window) === null || _window$window2 === void 0 || _window$window2.focus();
}));
};
bindFocusRouting(group);
const closeResult = {};
const isValidReferenceGroup = isGroupAddedToDom && referenceGroup && this.getPanel(referenceGroup.id);
const value = {
window: _window,
popoutGroup: group,
gridview: popoutGridview,
overlayRenderContainer,
dropTargetContainer,
getWindow: () => _window.window,
popoutUrl: resolvedPopoutUrl,
referenceGroup: isValidReferenceGroup ? referenceGroup.id : void 0,
popupService: popoutPopupService,
setAnchorGroup: (newAnchor) => {
value.popoutGroup = newAnchor;
bindFocusRouting(newAnchor);
},
disposable: { dispose: () => {
popoutWindowDisposable.dispose();
return closeResult.returnedGroup;
} }
};
const _onDidWindowPositionChange = onDidWindowMoveEnd(_window.window);
popoutWindowDisposable.addDisposables(_onDidWindowPositionChange, onDidWindowResizeEnd(_window.window, () => {
this._onDidPopoutGroupSizeChange.fire({
width: _window.window.innerWidth,
height: _window.window.innerHeight,
group: value.popoutGroup
});
}), _onDidWindowPositionChange.event(() => {
this._onDidPopoutGroupPositionChange.fire({
screenX: _window.window.screenX,
screenY: _window.window.screenY,
group: value.popoutGroup
});
}), addDisposableListener(_window.window, "resize", () => {
popoutGridview.layout(_window.window.innerWidth, _window.window.innerHeight);
}), overlayRenderContainer, Disposable.from(() => this.disposePopoutWindow({
group,
referenceGroup,
popoutGridview,
isGroupAddedToDom,
floatingBox,
disposePopoutGridview,
closeResult
})));
service.add(value);
this._onDidAddPopoutGroup.fire({
id: value.popoutGroup.id,
group: value.popoutGroup,
window: value.getWindow()
});
for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
return true;
}).catch((err) => {
console.error("dockview: failed to create popout.", err);
return false;
});
}
/**
* The popout window was blocked (e.g. by the browser's popup blocker,
* common when restoring popouts on load). Fall back gracefully so the
* group(s) end up valid and visible in the main grid rather than as
* orphans that later crash clear()/remove().
*/
handleBlockedPopout(params) {
const { group, referenceGroup, options, popoutWindowDisposable } = params;
console.error("dockview: failed to create popout. perhaps you need to allow pop-ups for this website");
popoutWindowDisposable.dispose();
this._onDidOpenPopoutWindowFail.fire();
if (options === null || options === void 0 ? void 0 : options.overridePopoutGridview) {
const blockedGridview = options.overridePopoutGridview;
const members = this.groups.filter((candidate) => blockedGridview.element.contains(candidate.element));
for (const member of members) this.movingLock(() => {
blockedGridview.remove(member);
this.redockGroupToMainGrid(member);
});
blockedGridview.dispose();
if (referenceGroup && !referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
return;
}
if (group === referenceGroup) {
if (!this.gridview.element.contains(group.element)) {
this.movingLock(() => this.doAddGroup(group, [0]));
group.model.location = { type: "grid" };
}
} else {
this.movingLock(() => moveGroupWithoutDestroying({
from: group,
to: referenceGroup
}));
if (group.model.size === 0 && this._groups.has(group.id)) {
group.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
}
}
if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
}
/**
* Wire a group that has been displaced from a floating / popout window back
* to the main grid's render & drop-target containers and dock it at the
* root. The caller is responsible for first detaching it from its old
* gridview; the detach strategy differs between the window-teardown path
* (`doRemoveGroup`) and the blocked-window path (`gridview.remove`).
*/
redockGroupToMainGrid(group) {
group.model.renderContainer = this.overlayRenderContainer;
group.model.dropTargetContainer = this.rootDropTargetContainer;
group.model.location = { type: "grid" };
this.doAddGroup(group, [0]);
}
/**
* Teardown for a popout window's `popoutWindowDisposable`. Runs when the
* window closes (by user, by `removeGroup`, or by component disposal):
* relocates every member group back to the main grid (or to a floating
* window when the anchor came from one), then disposes the nested gridview.
* `closeResult.returnedGroup` is read by the entry's `dispose()` contract.
*/
disposePopoutWindow(params) {
var _this$_popoutWindowSe7;
const { group, referenceGroup, popoutGridview, isGroupAddedToDom, floatingBox, disposePopoutGridview, closeResult } = params;
if (this.isDisposed) {
disposePopoutGridview();
return;
}
const genuineClose = !!((_this$_popoutWindowSe7 = this._popoutWindowService) === null || _this$_popoutWindowSe7 === void 0 ? void 0 : _this$_popoutWindowSe7.entries.find((entry) => entry.gridview === popoutGridview));
const members = this.groups.filter((candidate) => popoutGridview.element.contains(candidate.element));
const anchorPresent = members.includes(group);
const anchorIsSoleMember = anchorPresent && members.length === 1;
/**
* Popping a group out fires `onDidMovePanel` per panel, so closing the
* window owes the caller the same: every panel in it is rehomed, into
* the reference group the popout left behind or into a fresh grid slot.
* `movingLock` suppresses onDidAddPanel / onDidRemovePanel on these
* paths, so `onDidMovePanel` is the only event that can carry it. The
* events are deferred to the end so listeners observe the panels at
* their final group and location.
*/
const movedPanels = [];
if (genuineClose) for (const member of members) {
if (member === group) continue;
this.movingLock(() => {
this.doRemoveGroup(member, {
skipDispose: true,
skipActive: true,
skipPopoutReturn: true
});
this.redockGroupToMainGrid(member);
});
for (const panel of member.panels) movedPanels.push({
panel,
from: member
});
}
if (anchorPresent && isGroupAddedToDom && this.getPanel(referenceGroup.id)) {
const rehomed = [...group.panels];
this.movingLock(() => moveGroupWithoutDestroying({
from: group,
to: referenceGroup
}));
for (const panel of rehomed) movedPanels.push({
panel,
from: group
});
if (!referenceGroup.api.isVisible) referenceGroup.api.setVisible(true);
if (this.getPanel(group.id)) this.doRemoveGroup(group, { skipPopoutAssociated: true });
} else if (anchorPresent && this.getPanel(group.id)) {
group.model.renderContainer = this.overlayRenderContainer;
group.model.dropTargetContainer = this.rootDropTargetContainer;
closeResult.returnedGroup = group;
if (!genuineClose) {
/**
* If this popout group was explicitly removed then we shouldn't run the additional
* steps. The explicit remover re-docks the returned group itself; here we only hand
* it back (above) and tear down the nested gridview.
*/
disposePopoutGridview();
return;
}
if (floatingBox && anchorIsSoleMember) this.addFloatingGroup(group, {
height: floatingBox.height,
width: floatingBox.width,
position: floatingBox
});
else {
this.doRemoveGroup(group, {
skipDispose: true,
skipActive: true,
skipPopoutReturn: true
});
group.model.location = { type: "grid" };
this.movingLock(() => {
this.doAddGroup(group, [0]);
});
for (const panel of group.panels) movedPanels.push({
panel,
from: group
});
}
this.doSetGroupAndPanelActive(group);
}
disposePopoutGridview();
for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
}
addFloatingGroup(item, options) {
this.mutation("float", () => this._doAddFloatingGroup(item, options));
}
_doAddFloatingGroup(item, options) {
if (!assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup")) return;
if (item instanceof DockviewGroupPanel && item.model.location.type === "edge") return;
let group;
/**
* Floating relocates panels exactly like a drop onto the grid does, so
* it owes the caller an `onDidMovePanel` per panel. The events are
* deferred until the window is mounted below: only then do the panels
* report their final `floating` location.
*/
let movedPanels = [];
if (item instanceof DockviewPanel) {
const sourceGroup = item.group;
group = this.createGroup();
this._onDidAddGroup.fire(group);
this.movingLock(() => this.removePanel(item, {
removeEmptyGroup: true,
skipDispose: true,
skipSetActiveGroup: true
}));
this.movingLock(() => group.model.openPanel(item, { skipSetGroupActive: true }));
movedPanels = [{
panel: item,
from: sourceGroup
}];
} else {
var _this$_popoutWindowSe8;
group = item;
const popoutReferenceGroupId = (_this$_popoutWindowSe8 = this._popoutWindowService) === null || _this$_popoutWindowSe8 === void 0 ? void 0 : _this$_popoutWindowSe8.findReferenceGroupId(group);
const popoutReferenceGroup = popoutReferenceGroupId ? this.getPanel(popoutReferenceGroupId) : void 0;
if (!(typeof (options === null || options === void 0 ? void 0 : options.skipRemoveGroup) === "boolean" && options.skipRemoveGroup)) if (popoutReferenceGroup) {
this.movingLock(() => moveGroupWithoutDestroying({
from: item,
to: popoutReferenceGroup
}));
this.doRemoveGroup(item, {
skipPopoutReturn: true,
skipPopoutAssociated: true
});
this.doRemoveGroup(popoutReferenceGroup, { skipDispose: true });
group = popoutReferenceGroup;
movedPanels = group.panels.map((panel) => ({
panel,
from: item
}));
} else {
this.doRemoveGroup(item, {
skipDispose: true,
skipPopoutReturn: true,
skipPopoutAssociated: false
});
movedPanels = group.panels.map((panel) => ({
panel,
from: group
}));
}
}
function getAnchoredBox() {
if (options === null || options === void 0 ? void 0 : options.position) {
const result = {};
if ("left" in options.position) result.left = Math.max(options.position.left, 0);
else if ("right" in options.position) result.right = Math.max(options.position.right, 0);
else result.left = DEFAULT_FLOATING_GROUP_POSITION.left;
if ("top" in options.position) result.top = Math.max(options.position.top, 0);
else if ("bottom" in options.position) result.bottom = Math.max(options.position.bottom, 0);
else result.top = DEFAULT_FLOATING_GROUP_POSITION.top;
if (typeof options.width === "number") result.width = Math.max(options.width, 0);
else result.width = DEFAULT_FLOATING_GROUP_POSITION.width;
if (typeof options.height === "number") result.height = Math.max(options.height, 0);
else result.height = DEFAULT_FLOATING_GROUP_POSITION.height;
return result;
}
return {
left: typeof (options === null || options === void 0 ? void 0 : options.x) === "number" ? Math.max(options.x, 0) : DEFAULT_FLOATING_GROUP_POSITION.left,
top: typeof (options === null || options === void 0 ? void 0 : options.y) === "number" ? Math.max(options.y, 0) : DEFAULT_FLOATING_GROUP_POSITION.top,
width: typeof (options === null || options === void 0 ? void 0 : options.width) === "number" ? Math.max(options.width, 0) : DEFAULT_FLOATING_GROUP_POSITION.width,
height: typeof (options === null || options === void 0 ? void 0 : options.height) === "number" ? Math.max(options.height, 0) : DEFAULT_FLOATING_GROUP_POSITION.height
};
}
const anchoredBox = getAnchoredBox();
const floatingGridview = this.createNestedGridview();
floatingGridview.addView(group, Sizing.Distribute, [0]);
this.mountFloatingWindow(floatingGridview, group, [group], anchoredBox, {
dragHandle: options === null || options === void 0 ? void 0 : options.dragHandle,
inDragMode: options === null || options === void 0 ? void 0 : options.inDragMode,
skipActiveGroup: options === null || options === void 0 ? void 0 : options.skipActiveGroup,
disableSmartGuides: options === null || options === void 0 ? void 0 : options.disableSmartGuides
});
for (const { panel, from } of movedPanels) this.fireDidMovePanel(panel, from);
}
/**
* Build an empty gridview configured to match the main grid's styling, for
* hosting a nested layout inside a floating or popout window.
*/
createNestedGridview(orientation = "HORIZONTAL") {
var _this$options$theme$g, _this$options$theme2;
return new Gridview(true, this.options.hideBorders ? { separatorBorder: "transparent" } : void 0, orientation, false, (_this$options$theme$g = (_this$options$theme2 = this.options.theme) === null || _this$options$theme2 === void 0 ? void 0 : _this$options$theme2.gap) !== null && _this$options$theme$g !== void 0 ? _this$options$theme$g : 0);
}
/**
* Wrap a (populated) floating gridview in an overlay window: title bar /
* move handle, drag wiring, the floating-group service entry and the
* `floating` location tag for every member group.
*/
mountFloatingWindow(floatingGridview, anchorGroup, members, anchoredBox, options) {
var _ref3, _options$dragHandle, _this$_floatingOverla2, _this$options$floatin, _this$options$floatin2, _this$options$floatin3, _this$options$floatin4, _options$disableSmart, _titleBar$element;
const service = assertModule(this._floatingGroupService, "FloatingGroup", "api.addFloatingGroup");
if (!service) return;
const titleBar = ((_ref3 = (_options$dragHandle = options === null || options === void 0 ? void 0 : options.dragHandle) !== null && _options$dragHandle !== void 0 ? _options$dragHandle : this.options.floatingGroupDragHandle) !== null && _ref3 !== void 0 ? _ref3 : "titlebar") === "titlebar" ? new FloatingTitleBar(this, anchorGroup) : void 0;
const overlay = new Overlay(_objectSpread2(_objectSpread2({
container: (_this$_floatingOverla2 = this._floatingOverlayHost) !== null && _this$_floatingOverla2 !== void 0 ? _this$_floatingOverla2 : this.gridview.element,
content: floatingGridview.element,
header: titleBar === null || titleBar === void 0 ? void 0 : titleBar.element
}, anchoredBox), {}, {
minimumInViewportWidth: this.options.floatingGroupBounds === "boundedWithinViewport" ? void 0 : (_this$options$floatin = (_this$options$floatin2 = this.options.floatingGroupBounds) === null || _this$options$floatin2 === void 0 ? void 0 : _this$options$floatin2.minimumWidthWithinViewport) !== null && _this$options$floatin !== void 0 ? _this$options$floatin : 100,
minimumInViewportHeight: this.options.floatingGroupBounds === "boundedWithinViewport" ? void 0 : (_this$options$floatin3 = (_this$options$floatin4 = this.options.floatingGroupBounds) === null || _this$options$floatin4 === void 0 ? void 0 : _this$options$floatin4.minimumHeightWithinViewport) !== null && _this$options$floatin3 !== void 0 ? _this$options$floatin3 : 100,
transformDragPosition: this._buildFloatingDragTransform(anchorGroup, (_options$disableSmart = options === null || options === void 0 ? void 0 : options.disableSmartGuides) !== null && _options$disableSmart !== void 0 ? _options$disableSmart : false),
getSiblingBoxes: () => this._gatherFloatingGroupBoxes(anchorGroup)
}));
const dragHandle = (_titleBar$element = titleBar === null || titleBar === void 0 ? void 0 : titleBar.element) !== null && _titleBar$element !== void 0 ? _titleBar$element : anchorGroup.element.querySelector(".dv-void-container");
if (!dragHandle) throw new Error("dockview: failed to find drag handle");
overlay.setupDrag(dragHandle, { inDragMode: typeof (options === null || options === void 0 ? void 0 : options.inDragMode) === "boolean" ? options.inDragMode : false });
const floatingGroupPanel = service.add(anchorGroup, overlay, floatingGridview);
floatingGroupPanel.addDisposables(overlay.onDidStartMoving(() => this._onDidStartFloatingGroupDrag.fire(anchorGroup)), overlay.onDidChangeEnd(() => this._onDidEndFloatingGroupDrag.fire(anchorGroup)));
if (titleBar) {
floatingGroupPanel.setTitleBar(titleBar);
floatingGroupPanel.addDisposables(titleBar, Disposable.from(() => floatingGroupPanel.setTitleBar(void 0)), titleBar.onDragStart((event) => {
this._onWillDragGroup.fire({
nativeEvent: event,
group: floatingGroupPanel.group
});
}));
}
for (const member of members) member.model.location = { type: "floating" };
if (!(options === null || options === void 0 ? void 0 : options.skipActiveGroup)) this.doSetGroupAndPanelActive(anchorGroup);
}
orthogonalize(position, options) {
this.gridview.normalize();
switch (position) {
case "top":
case "bottom":
if (this.gridview.orientation === "HORIZONTAL") this.gridview.insertOrthogonalSplitviewAtRoot();
break;
case "left":
case "right":
if (this.gridview.orientation === "VERTICAL") this.gridview.insertOrthogonalSplitviewAtRoot();
break;
default: break;
}
switch (position) {
case "top":
case "left":
case "center": return this.createGroupAtLocation([0], void 0, options);
case "bottom":
case "right": return this.createGroupAtLocation([this.gridview.length], void 0, options);
default: throw new Error(`dockview: unsupported position ${position}`);
}
}
updateOptions(options) {
var _this$_floatingGroupS2, _this$_rootDropTarget;
super.updateOptions(options);
this.reportMissingOptionModules(options);
(_this$_floatingGroupS2 = this._floatingGroupService) === null || _this$_floatingGroupS2 === void 0 || _this$_floatingGroupS2.updateBounds(options);
(_this$_rootDropTarget = this._rootDropTargetService) === null || _this$_rootDropTarget === void 0 || _this$_rootDropTarget.setOptions(options);
const oldDisableDnd = this.options.disableDnd;
const oldDndStrategy = this.options.dndStrategy;
this._options = _objectSpread2(_objectSpread2({}, this.options), options);
const newDisableDnd = this.options.disableDnd;
const newDndStrategy = this.options.dndStrategy;
if (oldDisableDnd !== newDisableDnd || oldDndStrategy !== newDndStrategy) this.updateDragAndDropState();
if ("theme" in options) this.updateTheme();
if ("createRightHeaderActionComponent" in options || "createLeftHeaderActionComponent" in options || "createPrefixHeaderActionComponent" in options) {
var _this$headerActionsSe;
(_this$headerActionsSe = this.headerActionsService) === null || _this$headerActionsSe === void 0 || _this$headerActionsSe.refreshAll();
}
if ("createWatermarkComponent" in options) {
var _this$_watermarkServi;
(_this$_watermarkServi = this._watermarkService) === null || _this$_watermarkServi === void 0 || _this$_watermarkServi.refresh();
for (const group of this.groups) group.model.refreshWatermark();
}
if ("tabGroupColors" in options || "tabGroupAccent" in options) {
var _this$_options$tabGro;
this._tabGroupColorPalette.setEntries((_this$_options$tabGro = this._options.tabGroupColors) !== null && _this$_options$tabGro !== void 0 ? _this$_options$tabGro : DEFAULT_TAB_GROUP_COLORS);
this._tabGroupColorPalette.enabled = this._options.tabGroupAccent !== "off";
for (const group of this.groups) group.model.refreshTabGroupAccent();
}
this._onDidOptionsChange.fire();
this._layoutFromShell(this.gridview.width, this.gridview.height);
}
/**
* Report any option the caller set whose module isn't registered. Logs
* once per module+reason; never throws, matching the module system's
* degrade-to-no-op contract.
*/
reportMissingOptionModules(options) {
validateOptionModules(options, (moduleName) => this._moduleRegistry.has(moduleName));
}
layout(width, height, forceResize) {
var _this$_moduleRegistry4;
if (this._shellManager && !this._inShellLayout) this._shellManager.layout(width, height);
else super.layout(width, height, forceResize);
this._syncFloatingOverlayHost();
(_this$_moduleRegistry4 = this._moduleRegistry) === null || _this$_moduleRegistry4 === void 0 || (_this$_moduleRegistry4 = _this$_moduleRegistry4.services.floatingGroupService) === null || _this$_moduleRegistry4 === void 0 || _this$_moduleRegistry4.constrainBounds();
}
_syncFloatingOverlayHost() {
if (!this._floatingOverlayHost || !this._shellManager) return;
const shellRect = this._shellManager.element.getBoundingClientRect();
const gridRect = this.element.getBoundingClientRect();
const host = this._floatingOverlayHost;
host.style.left = `${gridRect.left - shellRect.left}px`;
host.style.top = `${gridRect.top - shellRect.top}px`;
host.style.width = `${gridRect.width}px`;
host.style.height = `${gridRect.height}px`;
}
_layoutFromShell(width, height) {
this._inShellLayout = true;
this.layout(width, height, true);
this._inShellLayout = false;
}
forceRelayout() {
if (this._shellManager) this._layoutFromShell(this.width, this.height);
else super.forceRelayout();
}
addEdgeGroup(position, options) {
const service = this._edgeGroupService;
if (!service) throw new Error(missingModuleMessage("EdgeGroup", "api.addEdgeGroup"));
if (service.has(position)) throw new Error(`dockview: edge group already exists at position '${position}'`);
return this.mutation("add", () => {
const group = this.createGroup({ id: options.id });
group.model.location = {
type: "edge",
position
};
group.model.headerPosition = position;
const autoCollapseDisposable = group.model.onDidRemovePanel(() => {
if (!group.model.isEmpty) return;
if (service.isAutoReveal(group)) queueMicrotask(() => {
var _this$_edgeGroupServi;
if (group.model.isEmpty && ((_this$_edgeGroupServi = this._edgeGroupService) === null || _this$_edgeGroupServi === void 0 ? void 0 : _this$_edgeGroupServi.includes(group))) this.removeEdgeGroup(position);
});
else this.setEdgeGroupCollapsed(group, true);
});
service.add(position, group, autoCollapseDisposable);
if (options.autoHide !== void 0) service.setAutoHide(group, options.autoHide);
if (options.autoReveal !== void 0) service.setAutoReveal(group, options.autoReveal);
this._onDidAddGroup.fire(group);
this._shellManager.addEdgeView(position, options, group);
return group.api;
});
}
/**
* Reveal (create-or-fill) the edge group at `position` and move the dragged
* item described by `data` into it. A newly created edge group is created
* collapsed, flagged `autoReveal` so it tears down to zero footprint when
* later emptied, and takes its auto-hide state from `options.autoHide`. If an
* edge group already exists there it is reused: the panel is added to its
* tabs and its collapsed/toggled *and auto-hide* state are left as-is (never
* re-created; `addEdgeGroup` throws on a duplicate position). This keeps a
* drag-reveal from silently converting a static edge group into an
* auto-hiding one; to change an existing group's auto-hide, call
* `api.getEdgeGroup(position)?.setAutoHide(...)` directly. No-op if the
* EdgeGroup module is absent.
*
* This is the primitive behind the dock-to-edge groups: the two-band
* drag-reveal affordance routes its outer-band drops here.
*/
revealEdgeGroupWithData(position, data, options) {
const service = this._edgeGroupService;
if (!service) return;
this.mutation("add", () => {
var _data$panelId2;
let group = service.get(position);
if (!group) {
this.addEdgeGroup(position, {
id: this.getNextGroupId(),
autoReveal: true,
autoHide: options === null || options === void 0 ? void 0 : options.autoHide,
collapsed: true
});
group = service.get(position);
}
if (!group) return;
this.moveGroupOrPanel({
from: {
groupId: data.groupId,
panelId: (_data$panelId2 = data.panelId) !== null && _data$panelId2 !== void 0 ? _data$panelId2 : void 0
},
to: {
group,
position: "center"
}
});
});
}
getEdgeGroup(position) {
var _this$_edgeGroupServi2;
return (_this$_edgeGroupServi2 = this._edgeGroupService) === null || _this$_edgeGroupServi2 === void 0 || (_this$_edgeGroupServi2 = _this$_edgeGroupServi2.get(position)) === null || _this$_edgeGroupServi2 === void 0 ? void 0 : _this$_edgeGroupServi2.api;
}
/** The edge group panel at a position (the model, not the api). */
getEdgeGroupPanel(position) {
var _this$_edgeGroupServi3;
return (_this$_edgeGroupServi3 = this._edgeGroupService) === null || _this$_edgeGroupServi3 === void 0 ? void 0 : _this$_edgeGroupServi3.get(position);
}
/** Pin (expand) the edge group at a position. Reports the missing module if
* AutoHideEdgeGroup is absent, since this command is reachable without the
* `autoHideEdgeGroups` option that would otherwise have named it. */
pinEdgeGroup(position) {
var _assertModule5;
(_assertModule5 = assertModule(this._moduleRegistry.services.autoHideEdgeGroupService, "AutoHideEdgeGroup", "api.pinEdgeGroup")) === null || _assertModule5 === void 0 || _assertModule5.pin(position);
}
/** Auto-hide (collapse to strip) the edge group at a position. */
autoHideEdgeGroup(position) {
var _assertModule6;
(_assertModule6 = assertModule(this._moduleRegistry.services.autoHideEdgeGroupService, "AutoHideEdgeGroup", "api.autoHideEdgeGroup")) === null || _assertModule6 === void 0 || _assertModule6.autoHide(position);
}
/** Peek (slide out) / close the collapsed edge group at a position. */
peekEdgeGroup(position, peek) {
var _assertModule7;
(_assertModule7 = assertModule(this._moduleRegistry.services.autoHideEdgeGroupService, "AutoHideEdgeGroup", "api.peekEdgeGroup")) === null || _assertModule7 === void 0 || _assertModule7.peek(position, peek);
}
/** The auto-hide peek mounts on the shell, the same element the
* `OverlayRenderContainer` roots on, so `always` content re-anchors in the
* peek's coordinate space. */
get overlayRoot() {
return this.rootElement;
}
/** Viewport rect of the docked content area (the element the root/group drop
* targets hit-test against), used by the two-band drag-reveal affordance to
* classify a pointer's distance from each edge. This is the gridview/center
* container, which is inset when edge groups are present, so the outer band
* sits at the boundary of the content area (adjacent to any existing edge
* group) rather than at the shell's outer edge. */
getDropZoneRect() {
return this.element.getBoundingClientRect();
}
/** The size an edge group expands to (pre-collapse); sizes the peek. */
getEdgeGroupExpandedSize(position) {
var _this$_shellManager$g, _this$_shellManager3;
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;
}
/** Reposition a single `renderer:'always'` panel's overlay over its
* reference container, optionally forcing it visible (the auto-hide peek
* slides an `always` panel out without reparenting it or touching its
* visibility state). No-op for non-overlay-rendered panels. */
repositionPanelOverlay(panel, forceVisible, clip) {
this.overlayRenderContainer.repositionPanelOverlay(panel.api.id, forceVisible, clip);
}
setEdgeGroupVisible(position, visible) {
this._shellManager.setEdgeGroupVisible(position, visible);
}
isEdgeGroupVisible(position) {
return this._shellManager.isEdgeGroupVisible(position);
}
removeEdgeGroup(position) {
const service = assertModule(this._edgeGroupService, "EdgeGroup", "api.removeEdgeGroup");
if (!service) return;
const group = service.get(position);
if (!group) throw new Error(`dockview: no edge group exists at position '${position}'`);
this.mutation("remove", () => {
for (const panel of [...group.panels]) this.removePanel(panel, {
removeEmptyGroup: false,
skipDispose: false
});
this._shellManager.removeEdgeView(position);
service.remove(position);
group.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
});
}
setEdgeGroupCollapsed(group, collapsed) {
var _this$_edgeGroupServi4;
const position = (_this$_edgeGroupServi4 = this._edgeGroupService) === null || _this$_edgeGroupServi4 === void 0 ? void 0 : _this$_edgeGroupServi4.findPositionOf(group);
if (!position) return;
if (this._shellManager.isEdgeGroupCollapsed(position) === collapsed) return;
this._shellManager.setEdgeGroupCollapsed(position, collapsed);
group.api._onDidCollapsedChange.fire({ isCollapsed: collapsed });
}
isEdgeGroupCollapsed(group) {
var _this$_edgeGroupServi5;
const position = (_this$_edgeGroupServi5 = this._edgeGroupService) === null || _this$_edgeGroupServi5 === void 0 ? void 0 : _this$_edgeGroupServi5.findPositionOf(group);
return position ? this._shellManager.isEdgeGroupCollapsed(position) : false;
}
isEdgeGroupPeeking(group) {
return this._peekingGroups.has(group);
}
/** Set the peek state and fire the group's `onDidPeekChange` (called by the
* auto-hide service). */
setEdgeGroupPeeking(group, peeking) {
if (this._peekingGroups.has(group) === peeking) return;
if (peeking) this._peekingGroups.add(group);
else this._peekingGroups.delete(group);
group.api._onDidPeekChange.fire({ isPeeking: peeking });
}
/**
* Resolve whether an edge group should behave as an auto-hide (pinnable)
* tool window: the per-group flag when set, otherwise the per-edge
* `autoHideEdgeGroups` option for the group's edge. This is what lets a
* static edge group and an auto-hiding one co-exist in the same layout.
*/
isEdgeGroupAutoHide(group) {
var _this$_edgeGroupServi6, _this$_edgeGroupServi7;
const perGroup = (_this$_edgeGroupServi6 = this._edgeGroupService) === null || _this$_edgeGroupServi6 === void 0 ? void 0 : _this$_edgeGroupServi6.isAutoHide(group);
if (perGroup !== void 0) return perGroup;
const position = (_this$_edgeGroupServi7 = this._edgeGroupService) === null || _this$_edgeGroupServi7 === void 0 ? void 0 : _this$_edgeGroupServi7.findPositionOf(group);
if (position === void 0) return false;
return isEdgeGroupEnabled(this.options.autoHideEdgeGroups, position);
}
/**
* Set (or clear, via `undefined`) the per-group auto-hide flag and notify
* the auto-hide module so it can dock/undock the group at runtime.
*/
setEdgeGroupAutoHide(group, value) {
const service = this._edgeGroupService;
if (!(service === null || service === void 0 ? void 0 : service.includes(group))) return;
service.setAutoHide(group, value);
this._onDidEdgeGroupAutoHideChange.fire(group);
}
updateDragAndDropState() {
for (const group of this.groups) group.model.updateDragAndDropState();
}
focus() {
var _this$activeGroup;
(_this$activeGroup = this.activeGroup) === null || _this$activeGroup === void 0 || _this$activeGroup.focus();
}
getGroupPanel(id) {
return this.panels.find((panel) => panel.id === id);
}
setActivePanel(panel) {
panel.group.model.openPanel(panel);
this.doSetGroupAndPanelActive(panel.group);
}
activateNext(options = {}) {
var _this$gridview$next;
if (!options.group) {
if (!this.activeGroup) return;
options.group = this.activeGroup;
}
if (options.includePanel && options.group) {
if (options.group.activePanel !== options.group.panels[options.group.panels.length - 1]) {
options.group.model.moveToNext({ suppressRoll: true });
return;
}
}
const location = getGridLocation(options.group.element);
const next = (_this$gridview$next = this.gridview.next(location)) === null || _this$gridview$next === void 0 ? void 0 : _this$gridview$next.view;
this.doSetGroupAndPanelActive(next);
}
activatePrevious(options = {}) {
var _this$gridview$previo;
if (!options.group) {
if (!this.activeGroup) return;
options.group = this.activeGroup;
}
if (options.includePanel && options.group) {
if (options.group.activePanel !== options.group.panels[0]) {
options.group.model.moveToPrevious({ suppressRoll: true });
return;
}
}
const location = getGridLocation(options.group.element);
const next = (_this$gridview$previo = this.gridview.previous(location)) === null || _this$gridview$previo === void 0 ? void 0 : _this$gridview$previo.view;
if (next) this.doSetGroupAndPanelActive(next);
}
/**
* Serialize the current state of the layout
*
* @returns A JSON respresentation of the layout
*/
toJSON() {
var _this$_floatingGroupS3, _this$_floatingGroupS4, _this$_popoutWindowSe9, _this$_popoutWindowSe10, _this$activeGroup2;
const data = this.gridview.serialize();
const panels = this.panels.reduce((collection, panel) => {
collection[panel.id] = panel.toJSON();
return collection;
}, {});
const floats = (_this$_floatingGroupS3 = (_this$_floatingGroupS4 = this._floatingGroupService) === null || _this$_floatingGroupS4 === void 0 ? void 0 : _this$_floatingGroupS4.serialize()) !== null && _this$_floatingGroupS3 !== void 0 ? _this$_floatingGroupS3 : [];
const popoutGroups = (_this$_popoutWindowSe9 = (_this$_popoutWindowSe10 = this._popoutWindowService) === null || _this$_popoutWindowSe10 === void 0 ? void 0 : _this$_popoutWindowSe10.serialize()) !== null && _this$_popoutWindowSe9 !== void 0 ? _this$_popoutWindowSe9 : [];
const result = {
grid: data,
panels,
activeGroup: (_this$activeGroup2 = this.activeGroup) === null || _this$activeGroup2 === void 0 ? void 0 : _this$activeGroup2.id
};
if (floats.length > 0) result.floatingGroups = floats;
if (popoutGroups.length > 0) result.popoutGroups = popoutGroups;
const edgeGroups = this._serializeEdgeGroups();
if (edgeGroups) result.edgeGroups = edgeGroups;
return result;
}
/**
* Serialize the edge groups, augmenting each shell entry with the serialized
* group state + the component-owned per-group presentation flags (the shell
* only knows geometry/collapse). Returns `undefined` when there are none.
*/
_serializeEdgeGroups() {
var _this$_edgeGroupServi8;
if (!((_this$_edgeGroupServi8 = this._edgeGroupService) === null || _this$_edgeGroupServi8 === void 0 ? void 0 : _this$_edgeGroupServi8.hasAny())) return;
const shellSerialized = this._shellManager.toJSON();
for (const [position, group] of this._edgeGroupService.entries()) {
const entry = shellSerialized[position];
if (!entry) continue;
if (this._edgeGroupService.isAutoReveal(group) && group.model.isEmpty) {
delete shellSerialized[position];
continue;
}
entry.group = group.toJSON();
if (this._edgeGroupService.isAutoReveal(group)) entry.autoReveal = true;
const autoHide = this._edgeGroupService.isAutoHide(group);
if (autoHide !== void 0) entry.autoHide = autoHide;
}
return shellSerialized;
}
fromJSON(data, options) {
this.mutation("load", () => this._doFromJSON(data, options));
}
_doFromJSON(data, options) {
var _this$_popoutWindowSe11, _data$grid$root;
(_this$_popoutWindowSe11 = this._popoutWindowService) === null || _this$_popoutWindowSe11 === void 0 || _this$_popoutWindowSe11.cancelPendingRestorations();
if (typeof data !== "object" || data === null) throw new Error("dockview: serialized layout must be a non-null object");
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");
const existingPanels = /* @__PURE__ */ new Map();
const temporaryGroups = /* @__PURE__ */ new Map();
const stagedPanels = [];
const temporaryGroupDisposables = [];
/**
* The staging groups are detached from `_groups` and never enter the
* grid, so nothing else will ever tear them down.
*
* `dispose()` reaches consumer `IContentRenderer.dispose()`, so one
* throwing renderer must not abort the rest of the cleanup — that would
* re-introduce the very leak this reclaims — nor replace the
* deserialization error the caller is being given. Draining the list
* also makes this safe to call more than once.
*/
const disposeTemporaryGroups = () => {
for (const disposable of temporaryGroupDisposables) try {
disposable.dispose();
} catch (err) {
console.error("dockview: failed to dispose a temporary group created for reuseExistingPanels", err);
}
temporaryGroupDisposables.length = 0;
};
if (options === null || options === void 0 ? void 0 : options.reuseExistingPanels) {
/**
* Visible, always-rendered panels need individual staging groups
* to remain active. Other reused panels can share a staging group.
* The staging groups are excluded from the layout clear below.
*/
const newPanels = Object.keys(data.panels);
let sharedTemporaryGroup;
const createTemporaryGroup = () => {
const temporaryGroup = this.createGroup();
/**
* Removing the group from `_groups` also drops the record's
* `CompositeDisposable`, so capture it first and tear both it
* and the group down in the `finally` below. Left undisposed a
* staging group leaks its `ResizeObserver`, its
* `onDidOptionsChange` subscription (retained by this
* component's emitter) and the watermark its model mounts when
* the group empties — once per reused panel, per `fromJSON`.
*/
const record = this._groups.get(temporaryGroup.api.id);
this._groups.delete(temporaryGroup.api.id);
temporaryGroupDisposables.push(Disposable.from(() => {
/**
* Reclaim the group's own resources, never its panels.
* Staging is driven by the ids in `data.panels` while
* reclaiming is driven by the group `views` that
* reference them, so a panel whose state is present but
* unreferenced is still staged here — and `dispose()`
* on a non-empty group reaches the consumer's
* `IContentRenderer.dispose()`. Emptying the group
* first leaves those panels exactly as they were before
* this teardown existed, which also keeps this safe
* against any future path that rebuilds a panel under
* an id that is live in the new layout.
*/
this.movingLock(() => {
for (const panel of temporaryGroup.panels.slice()) temporaryGroup.model.removePanel(panel);
});
record === null || record === void 0 || record.disposable.dispose();
temporaryGroup.dispose();
}));
return temporaryGroup;
};
/**
* Staging and the clear below run before the deserialization
* `try`, so a throw here (a consumer `onDidRemovePanel` handler, a
* renderer teardown during `clear`) would escape without reclaiming
* the staging groups already created.
*
* The creation loop is inside the guard too, not just the moves and
* the clear: `createGroup()` reaches consumer code of its own —
* `initialize()` mounts the watermark through
* `createWatermarkComponent()` — so a throw on the n-th panel would
* otherwise strand the n-1 staging groups already built.
*/
try {
for (const panel of this.panels) if (newPanels.includes(panel.api.id)) {
existingPanels.set(panel.api.id, panel);
let temporaryGroup;
if (panel.api.renderer === "always" && panel.api.isVisible) temporaryGroup = createTemporaryGroup();
else {
var _sharedTemporaryGroup;
(_sharedTemporaryGroup = sharedTemporaryGroup) !== null && _sharedTemporaryGroup !== void 0 || (sharedTemporaryGroup = createTemporaryGroup());
temporaryGroup = sharedTemporaryGroup;
}
temporaryGroups.set(panel.api.id, temporaryGroup);
stagedPanels.push({
panel,
temporaryGroup
});
}
this.movingLock(() => {
stagedPanels.forEach(({ panel, temporaryGroup }) => {
this.moveGroupOrPanel({
from: {
groupId: panel.api.group.api.id,
panelId: panel.api.id
},
to: {
group: temporaryGroup,
position: "center"
},
keepEmptyGroups: true
});
});
});
this.clear();
} catch (err) {
disposeTemporaryGroups();
throw err;
}
} else this.clear();
const { grid, panels, activeGroup } = data;
try {
var _data$floatingGroups, _data$popoutGroups, _this$_popoutWindowSe12, _this$_floatingGroupS5, _this$_watermarkServi2;
const width = this.width;
const height = this.height;
const createGroupFromSerializedState = (data) => {
const { id, locked, hideHeader, headerPosition, views, activeView } = data;
if (typeof id !== "string") throw new TypeError("dockview: group id must be of type string");
const group = this.createGroup({
id,
locked: !!locked,
hideHeader: !!hideHeader,
headerPosition
});
this._onDidAddGroup.fire(group);
const createdPanels = [];
/**
* Skip any view whose panel state is missing from the panels
* map rather than passing `undefined` to the deserializer
* (which would dereference `panelData.id` and throw, aborting
* the whole layout restore). Mirrors the guard on the
* edge-group deserialization path.
*/
const presentViews = views.filter((child) => panels[child]);
for (const child of presentViews) {
/**
* Run the deserializer step seperately since this may fail to due corrupted external state.
* In running this section first we avoid firing lots of 'add' events in the event of a failure
* due to a corruption of input data.
*/
const existingPanel = existingPanels.get(child);
const temporaryGroup = temporaryGroups.get(child);
if (temporaryGroup && existingPanel) {
this.movingLock(() => {
temporaryGroup.model.removePanel(existingPanel);
});
createdPanels.push(existingPanel);
existingPanel.updateFromStateModel(panels[child]);
} else {
const panel = this._deserializer.fromJSON(panels[child], group);
createdPanels.push(panel);
}
}
for (const panel of createdPanels) {
const isActive = typeof activeView === "string" && activeView === panel.id;
if (existingPanels.has(panel.api.id)) this.movingLock(() => {
group.model.openPanel(panel, {
skipSetActive: !isActive,
skipSetGroupActive: true
});
});
else group.model.openPanel(panel, {
skipSetActive: !isActive,
skipSetGroupActive: true
});
}
if (data.tabGroups && data.tabGroups.length > 0) group.model.restoreTabGroups(data.tabGroups);
if (!group.activePanel && group.panels.length > 0) group.model.openPanel(group.panels[group.panels.length - 1], { skipSetGroupActive: true });
return group;
};
this.gridview.deserialize(grid, { fromJSON: (node) => {
return createGroupFromSerializedState(node.data);
} });
this._layoutFromShell(width, height);
if (data.edgeGroups) this.deserializeEdgeGroups(data.edgeGroups, panels, existingPanels, temporaryGroups);
this.deserializeFloatingWindows((_data$floatingGroups = data.floatingGroups) !== null && _data$floatingGroups !== void 0 ? _data$floatingGroups : [], createGroupFromSerializedState);
const popoutPromises = this.deserializePopoutWindows((_data$popoutGroups = data.popoutGroups) !== null && _data$popoutGroups !== void 0 ? _data$popoutGroups : [], createGroupFromSerializedState);
(_this$_popoutWindowSe12 = this._popoutWindowService) === null || _this$_popoutWindowSe12 === void 0 || _this$_popoutWindowSe12.finishRestoration(popoutPromises);
(_this$_floatingGroupS5 = this._floatingGroupService) === null || _this$_floatingGroupS5 === void 0 || _this$_floatingGroupS5.constrainBounds();
if (typeof activeGroup === "string") {
const panel = this.getPanel(activeGroup);
if (panel) this.doSetGroupAndPanelActive(panel);
}
(_this$_watermarkServi2 = this._watermarkService) === null || _this$_watermarkServi2 === void 0 || _this$_watermarkServi2.update();
} catch (err) {
var _this$_floatingGroupS6;
console.error("dockview: failed to deserialize layout. Reverting changes", err);
/**
* Takes all the successfully created groups and remove all of their panels.
*/
for (const group of this.groups) for (const panel of group.panels) this.removePanel(panel, {
removeEmptyGroup: false,
skipDispose: false
});
/**
* To remove a group we cannot call this.removeGroup(...) since this makes assumptions about
* the underlying HTMLElement existing in the Gridview.
*/
for (const group of this.groups) {
group.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
}
(_this$_floatingGroupS6 = this._floatingGroupService) === null || _this$_floatingGroupS6 === void 0 || _this$_floatingGroupS6.disposeAll();
this.clear();
/**
* even though we have cleaned-up we still want to inform the caller of their error
* and we'll do this through re-throwing the original error since afterall you would
* expect trying to load a corrupted layout to result in an error and not silently fail...
*/
throw err;
} finally {
disposeTemporaryGroups();
}
this.debouncedUpdateAllPositions();
this._onDidLayoutFromJSON.fire();
}
/**
* Rebuild a floating / popout window's nested gridview from its serialized
* tree, collecting the member groups (in deserialization order) so the
* caller can mount or restore the window.
*/
deserializeNestedGridview(grid, createGroup) {
const gridview = this.createNestedGridview(grid.orientation);
const members = [];
gridview.deserialize(grid, { fromJSON: (node) => {
const group = createGroup(node.data);
members.push(group);
return group;
} });
return {
gridview,
members
};
}
deserializeEdgeGroups(edgeGroups, panels, existingPanels, temporaryGroups) {
const edgeService = assertModule(this._edgeGroupService, "EdgeGroup", "fromJSON edge restoration");
if (!edgeService) return;
for (const _position of [
"top",
"bottom",
"left",
"right"
]) {
const fixedData = edgeGroups[_position];
if (fixedData && !edgeService.has(_position)) {
var _groupState$id;
const groupState = fixedData.group;
const id = (_groupState$id = groupState === null || groupState === void 0 ? void 0 : groupState.id) !== null && _groupState$id !== void 0 ? _groupState$id : `${_position}-group`;
this.addEdgeGroup(_position, {
id,
autoReveal: fixedData.autoReveal,
autoHide: fixedData.autoHide,
minimumSize: fixedData.minimumSize,
maximumSize: fixedData.maximumSize,
collapsedSize: fixedData.collapsedSize
});
}
}
for (const [position, edgeGroup] of edgeService.entries()) {
const edgeData = edgeGroups[position];
const groupState = edgeData === null || edgeData === void 0 ? void 0 : edgeData.group;
if (groupState) {
const { views, activeView } = groupState;
const createdPanels = [];
for (const panelId of views) {
if (!panels[panelId]) continue;
/**
* Reclaim a staged panel rather than rebuilding it, the
* same way the grid path does. Deserializing here instead
* would honour `reuseExistingPanels` everywhere except edge
* groups: the live panel keeps sitting in its staging group
* while a second panel is built under the same id, so the
* consumer's renderer for the original is orphaned — never
* disposed, and its element left inside the id-keyed
* overlay entry the replacement now shares.
*/
const existingPanel = existingPanels.get(panelId);
const temporaryGroup = temporaryGroups.get(panelId);
if (temporaryGroup && existingPanel) {
this.movingLock(() => {
temporaryGroup.model.removePanel(existingPanel);
});
createdPanels.push(existingPanel);
existingPanel.updateFromStateModel(panels[panelId]);
} else {
const panel = this._deserializer.fromJSON(panels[panelId], edgeGroup);
createdPanels.push(panel);
}
}
for (const panel of createdPanels) {
const isActive = activeView === panel.id;
if (existingPanels.has(panel.api.id)) this.movingLock(() => {
edgeGroup.model.openPanel(panel, {
skipSetActive: !isActive,
skipSetGroupActive: true
});
});
else edgeGroup.model.openPanel(panel, {
skipSetActive: !isActive,
skipSetGroupActive: true
});
}
if (groupState.tabGroups && groupState.tabGroups.length > 0) edgeGroup.model.restoreTabGroups(groupState.tabGroups);
if (!edgeGroup.activePanel && edgeGroup.panels.length > 0) edgeGroup.model.openPanel(edgeGroup.panels[edgeGroup.panels.length - 1], { skipSetGroupActive: true });
}
}
this._shellManager.fromJSON(edgeGroups);
}
deserializeFloatingWindows(serialized, createGroup) {
for (const serializedFloatingGroup of serialized) {
const { data, grid, position } = serializedFloatingGroup;
if (grid) {
const { gridview: floatingGridview, members } = this.deserializeNestedGridview(grid, createGroup);
if (members.length === 0) continue;
this.mountFloatingWindow(floatingGridview, members[0], members, position, { inDragMode: false });
} else if (data) {
const group = createGroup(data);
this.addFloatingGroup(group, {
position,
width: position.width,
height: position.height,
skipRemoveGroup: true,
inDragMode: false
});
}
}
}
deserializePopoutWindows(serialized, createGroup) {
const popoutService = serialized.length > 0 ? assertModule(this._popoutWindowService, "PopoutWindow", "fromJSON popout restoration") : this._popoutWindowService;
if (!popoutService) return [];
return serialized.flatMap((serializedPopoutGroup, index) => {
const { data, grid, position, gridReferenceGroup, url } = serializedPopoutGroup;
let overridePopoutGridview;
let members = [];
if (grid) {
const built = this.deserializeNestedGridview(grid, createGroup);
overridePopoutGridview = built.gridview;
members = built.members;
if (members.length === 0) {
overridePopoutGridview.dispose();
return [];
}
}
const group = grid ? members[0] : createGroup(data);
return popoutService.scheduleRestoration(index * 100, () => {
this.addPopoutGroup(group, {
position: position !== null && position !== void 0 ? position : void 0,
overridePopoutGroup: gridReferenceGroup ? group : void 0,
overridePopoutGridview,
referenceGroup: gridReferenceGroup ? this.getPanel(gridReferenceGroup) : void 0,
popoutUrl: url
});
}, () => {
for (const orphan of members.length > 0 ? members : [group]) if (!this.isDisposed && this._groups.has(orphan.id) && orphan.element.parentElement === null) {
for (const panel of [...orphan.panels]) this.removePanel(panel, { removeEmptyGroup: false });
orphan.dispose();
this._groups.delete(orphan.id);
this._onDidRemoveGroup.fire(orphan);
}
});
});
}
clear() {
this.mutation("clear", () => this._doClear());
}
_doClear() {
const groups = Array.from(this._groups.values()).map((_) => _.value);
const hasActiveGroup = !!this.activeGroup;
for (const group of groups) {
var _this$_edgeGroupServi9;
if ((_this$_edgeGroupServi9 = this._edgeGroupService) === null || _this$_edgeGroupServi9 === void 0 ? void 0 : _this$_edgeGroupServi9.includes(group)) {
const panels = [...group.panels];
for (const panel of panels) this.removePanel(panel, { removeEmptyGroup: false });
continue;
}
this.removeGroup(group, { skipActive: true });
}
if (hasActiveGroup) this.doSetGroupAndPanelActive(void 0);
this.gridview.clear();
}
closeAllGroups() {
this.mutation("remove", () => {
for (const entry of this._groups.entries()) {
const [_, group] = entry;
group.value.model.closeAllPanels();
}
});
}
addPanel(options) {
return this.mutation("add", () => this._doAddPanel(options));
}
_doAddPanel(options) {
if (this.panels.some((_) => _.id === options.id)) throw new Error(`dockview: panel with id ${options.id} already exists`);
let referenceGroup;
if (options.position && options.floating) throw new Error("dockview: you can only provide one of: position, floating as arguments to .addPanel(...)");
const initial = {
width: options.initialWidth,
height: options.initialHeight
};
let index;
const shouldSkipSetActive = (group) => !!options.inactive && group.model.size > 0;
if (options.position) if (isPanelOptionsWithPanel(options.position)) {
const referencePanel = typeof options.position.referencePanel === "string" ? this.getGroupPanel(options.position.referencePanel) : options.position.referencePanel;
index = options.position.index;
if (!referencePanel) {
var _options$position$ref;
const referenceId = typeof options.position.referencePanel === "string" ? options.position.referencePanel : (_options$position$ref = options.position.referencePanel) === null || _options$position$ref === void 0 ? void 0 : _options$position$ref.id;
throw new Error(`dockview: referencePanel '${referenceId}' does not exist`);
}
referenceGroup = this.findGroup(referencePanel);
} else if (isPanelOptionsWithGroup(options.position)) {
var _this$_groups$get;
referenceGroup = typeof options.position.referenceGroup === "string" ? (_this$_groups$get = this._groups.get(options.position.referenceGroup)) === null || _this$_groups$get === void 0 ? void 0 : _this$_groups$get.value : options.position.referenceGroup;
index = options.position.index;
if (!referenceGroup) {
var _options$position$ref2;
const referenceId = typeof options.position.referenceGroup === "string" ? options.position.referenceGroup : (_options$position$ref2 = options.position.referenceGroup) === null || _options$position$ref2 === void 0 ? void 0 : _options$position$ref2.id;
throw new Error(`dockview: referenceGroup '${referenceId}' does not exist`);
}
} else {
const group = this.orthogonalize(directionToPosition(options.position.direction));
const panel = this.createPanel(options, group);
group.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(group),
skipSetGroupActive: options.inactive,
index
});
if (!options.inactive) this.doSetGroupAndPanelActive(group);
group.api.setSize({
height: initial === null || initial === void 0 ? void 0 : initial.height,
width: initial === null || initial === void 0 ? void 0 : initial.width
});
return panel;
}
else referenceGroup = this.activeGroup;
let panel;
if (referenceGroup) {
var _options$position;
const target = toTarget(((_options$position = options.position) === null || _options$position === void 0 ? void 0 : _options$position.direction) || "within");
if (options.floating) {
const group = this.createGroup();
this._onDidAddGroup.fire(group);
const floatingGroupOptions = typeof options.floating === "object" && options.floating !== null ? options.floating : {};
this.addFloatingGroup(group, _objectSpread2(_objectSpread2({}, floatingGroupOptions), {}, {
inDragMode: false,
skipRemoveGroup: true,
skipActiveGroup: true
}));
panel = this.createPanel(options, group);
group.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(group),
skipSetGroupActive: options.inactive,
index
});
} else if (referenceGroup.api.location.type === "floating" || referenceGroup.api.location.type === "edge" || target === "center") {
panel = this.createPanel(options, referenceGroup);
referenceGroup.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(referenceGroup),
skipSetGroupActive: options.inactive,
index
});
referenceGroup.api.setSize({
width: initial === null || initial === void 0 ? void 0 : initial.width,
height: initial === null || initial === void 0 ? void 0 : initial.height
});
if (!options.inactive) this.doSetGroupAndPanelActive(referenceGroup);
} else {
const location = getGridLocation(referenceGroup.element);
const relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
const group = this.createGroupAtLocation(relativeLocation, this.orientationAtLocation(relativeLocation) === "VERTICAL" ? initial === null || initial === void 0 ? void 0 : initial.height : initial === null || initial === void 0 ? void 0 : initial.width);
panel = this.createPanel(options, group);
group.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(group),
skipSetGroupActive: options.inactive,
index
});
if (!options.inactive) this.doSetGroupAndPanelActive(group);
}
} else if (options.floating) {
const group = this.createGroup();
this._onDidAddGroup.fire(group);
const coordinates = typeof options.floating === "object" && options.floating !== null ? options.floating : {};
this.addFloatingGroup(group, _objectSpread2(_objectSpread2({}, coordinates), {}, {
inDragMode: false,
skipRemoveGroup: true,
skipActiveGroup: true
}));
panel = this.createPanel(options, group);
group.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(group),
skipSetGroupActive: options.inactive,
index
});
} else {
const group = this.createGroupAtLocation([0], this.gridview.orientation === "VERTICAL" ? initial === null || initial === void 0 ? void 0 : initial.height : initial === null || initial === void 0 ? void 0 : initial.width);
panel = this.createPanel(options, group);
group.model.openPanel(panel, {
skipSetActive: shouldSkipSetActive(group),
skipSetGroupActive: options.inactive,
index
});
if (!options.inactive) this.doSetGroupAndPanelActive(group);
}
return panel;
}
removePanel(panel, options) {
this.mutation("remove", () => {
var _options$removeEmptyG;
return this._doRemovePanel(panel, {
removeEmptyGroup: (_options$removeEmptyG = options === null || options === void 0 ? void 0 : options.removeEmptyGroup) !== null && _options$removeEmptyG !== void 0 ? _options$removeEmptyG : true,
skipDispose: options === null || options === void 0 ? void 0 : options.skipDispose,
skipSetActiveGroup: options === null || options === void 0 ? void 0 : options.skipSetActiveGroup
});
});
}
_doRemovePanel(panel, options) {
const group = panel.group;
if (!group) throw new Error(`dockview: cannot remove panel ${panel.id}. it's missing a group.`);
group.model.removePanel(panel, { skipSetActiveGroup: options.skipSetActiveGroup });
if (!options.skipDispose) {
panel.group.model.renderContainer.detatch(panel);
panel.dispose();
}
if (group.size === 0 && options.removeEmptyGroup) this.removeGroup(group, { skipActive: options.skipSetActiveGroup });
}
createWatermarkComponent() {
if (this.options.createWatermarkComponent) return this.options.createWatermarkComponent();
return new Watermark();
}
addGroup(options) {
return this.mutation("add", () => this._doAddGroup(options));
}
_doAddGroup(options) {
if (options) {
let referenceGroup;
if (isGroupOptionsWithPanel(options)) {
var _options$referencePan;
const referencePanel = typeof options.referencePanel === "string" ? this.panels.find((panel) => panel.id === options.referencePanel) : options.referencePanel;
const referencePanelId = typeof options.referencePanel === "string" ? options.referencePanel : (_options$referencePan = options.referencePanel) === null || _options$referencePan === void 0 ? void 0 : _options$referencePan.id;
if (!referencePanel) throw new Error(`dockview: reference panel ${referencePanelId} does not exist`);
referenceGroup = this.findGroup(referencePanel);
if (!referenceGroup) throw new Error(`dockview: reference group for reference panel ${referencePanelId} does not exist`);
} else if (isGroupOptionsWithGroup(options)) {
var _this$_groups$get2;
referenceGroup = typeof options.referenceGroup === "string" ? (_this$_groups$get2 = this._groups.get(options.referenceGroup)) === null || _this$_groups$get2 === void 0 ? void 0 : _this$_groups$get2.value : options.referenceGroup;
if (!referenceGroup) {
var _options$referenceGro2;
const referenceId = typeof options.referenceGroup === "string" ? options.referenceGroup : (_options$referenceGro2 = options.referenceGroup) === null || _options$referenceGro2 === void 0 ? void 0 : _options$referenceGro2.id;
throw new Error(`dockview: reference group ${referenceId} does not exist`);
}
} else {
const group = this.orthogonalize(directionToPosition(options.direction), options);
if (!options.skipSetActive) this.doSetGroupAndPanelActive(group);
return group;
}
const target = toTarget(options.direction || "within");
const location = getGridLocation(referenceGroup.element);
const relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
const group = this.createGroup(options);
const size = this.getLocationOrientation(relativeLocation) === "VERTICAL" ? options.initialHeight : options.initialWidth;
this.doAddGroup(group, relativeLocation, size);
if (!options.skipSetActive) this.doSetGroupAndPanelActive(group);
return group;
} else {
const group = this.createGroup(options);
this.doAddGroup(group);
this.doSetGroupAndPanelActive(group);
return group;
}
}
getLocationOrientation(location) {
return location.length % 2 == 0 && this.gridview.orientation === "HORIZONTAL" ? "HORIZONTAL" : "VERTICAL";
}
removeGroup(group, options) {
this.mutation("remove", () => this.doRemoveGroup(group, options));
}
/**
* Detach a single group from the nested gridview of its floating / popout
* window, keeping the window and its remaining members alive, and reassign
* the window's anchor if the detached group was it.
*
* @returns `true` if the group was detached from a multi-member window;
* `false` if `group` is not in a nested window, or is the window's only
* member, in which case the caller is responsible for disposing the whole
* window.
*/
detachFromNestedWindow(group) {
var _this$_floatingGroupS7, _this$_popoutWindowSe13;
const floating = (_this$_floatingGroupS7 = this._floatingGroupService) === null || _this$_floatingGroupS7 === void 0 ? void 0 : _this$_floatingGroupS7.findByGroup(group);
if (floating) {
const members = this.nestedWindowMembers(group);
if (members.length <= 1) return false;
floating.gridview.remove(group);
if (floating.group === group) floating.setAnchorGroup(members.find((m) => m !== group));
return true;
}
const popout = (_this$_popoutWindowSe13 = this._popoutWindowService) === null || _this$_popoutWindowSe13 === void 0 ? void 0 : _this$_popoutWindowSe13.findByGroup(group);
if (popout) {
const members = this.nestedWindowMembers(group);
if (members.length <= 1) return false;
popout.gridview.remove(group);
if (popout.popoutGroup === group) popout.setAnchorGroup(members.find((m) => m !== group));
return true;
}
return false;
}
/**
* Dispose a group and forget it: remove it from `_groups` and fire the
* removed event.
*/
disposeGroupRecord(group) {
group.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
}
/**
* When `removed` was the active group, fall the active selection back to
* the first remaining group (or clear it when none remain).
*/
activateFallbackGroupIfRemoved(removed, skipActive) {
if (!skipActive && this._activeGroup === removed) {
const groups = Array.from(this._groups.values());
this.doSetGroupAndPanelActive(groups.length > 0 ? groups[0].value : void 0);
}
}
doRemoveGroup(group, options) {
var _this$_edgeGroupServi10;
if ((_this$_edgeGroupServi10 = this._edgeGroupService) === null || _this$_edgeGroupServi10 === void 0 ? void 0 : _this$_edgeGroupServi10.includes(group)) return group;
const panels = [...group.panels];
if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) for (const panel of panels) {
var _options$skipDispose;
this.removePanel(panel, {
removeEmptyGroup: false,
skipDispose: (_options$skipDispose = options === null || options === void 0 ? void 0 : options.skipDispose) !== null && _options$skipDispose !== void 0 ? _options$skipDispose : false
});
}
const activePanel = this.activePanel;
if (group.api.location.type === "floating") {
var _this$_floatingGroupS8;
const floatingGroup = (_this$_floatingGroupS8 = this._floatingGroupService) === null || _this$_floatingGroupS8 === void 0 ? void 0 : _this$_floatingGroupS8.findByGroup(group);
if (!floatingGroup) throw new Error("dockview: failed to find floating group");
if (this.detachFromNestedWindow(group)) {
if (options === null || options === void 0 ? void 0 : options.skipDispose) group.model.location = { type: "grid" };
else this.disposeGroupRecord(group);
this.activateFallbackGroupIfRemoved(group, options === null || options === void 0 ? void 0 : options.skipActive);
return group;
}
if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) this.disposeGroupRecord(group);
floatingGroup.dispose();
this.activateFallbackGroupIfRemoved(group, options === null || options === void 0 ? void 0 : options.skipActive);
return group;
}
if (group.api.location.type === "popout") {
var _this$_popoutWindowSe14, _this$_popoutWindowSe15;
const selectedGroup = (_this$_popoutWindowSe14 = this._popoutWindowService) === null || _this$_popoutWindowSe14 === void 0 ? void 0 : _this$_popoutWindowSe14.findByGroup(group);
if (!selectedGroup) throw new Error("dockview: failed to find popout group");
if (this.detachFromNestedWindow(group)) {
if (options === null || options === void 0 ? void 0 : options.skipDispose) group.model.location = { type: "grid" };
else this.disposeGroupRecord(group);
this.activateFallbackGroupIfRemoved(group, options === null || options === void 0 ? void 0 : options.skipActive);
return group;
}
if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
if (!(options === null || options === void 0 ? void 0 : options.skipPopoutAssociated)) {
const refGroup = selectedGroup.referenceGroup ? this.getPanel(selectedGroup.referenceGroup) : void 0;
if ((refGroup === null || refGroup === void 0 ? void 0 : refGroup.panels.length) === 0) this.removeGroup(refGroup);
}
selectedGroup.popoutGroup.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
}
(_this$_popoutWindowSe15 = this._popoutWindowService) === null || _this$_popoutWindowSe15 === void 0 || _this$_popoutWindowSe15.remove(selectedGroup);
const removedGroup = selectedGroup.disposable.dispose();
if (!(options === null || options === void 0 ? void 0 : options.skipPopoutReturn) && removedGroup) {
this.doAddGroup(removedGroup, [0]);
this.doSetGroupAndPanelActive(removedGroup);
}
this.activateFallbackGroupIfRemoved(group, options === null || options === void 0 ? void 0 : options.skipActive);
return selectedGroup.popoutGroup;
}
if (!this.gridview.element.contains(group.element)) {
if (!(options === null || options === void 0 ? void 0 : options.skipDispose)) {
const item = this._groups.get(group.id);
item === null || item === void 0 || item.disposable.dispose();
this.disposeGroupRecord(group);
}
this.activateFallbackGroupIfRemoved(group, options === null || options === void 0 ? void 0 : options.skipActive);
return group;
}
const re = super.doRemoveGroup(group, options);
if (!(options === null || options === void 0 ? void 0 : options.skipActive)) {
if (this.activePanel !== activePanel) this.fireActivePanelChange(this.activePanel);
}
return re;
}
debouncedUpdateAllPositions() {
if (this._updatePositionsFrameId !== void 0) cancelAnimationFrame(this._updatePositionsFrameId);
this._updatePositionsFrameId = requestAnimationFrame(() => {
var _this$_popoutWindowSe16, _this$_popoutWindowSe17;
this._updatePositionsFrameId = void 0;
this.overlayRenderContainer.updateAllPositions();
for (const entry of (_this$_popoutWindowSe16 = (_this$_popoutWindowSe17 = this._popoutWindowService) === null || _this$_popoutWindowSe17 === void 0 ? void 0 : _this$_popoutWindowSe17.entries) !== null && _this$_popoutWindowSe16 !== void 0 ? _this$_popoutWindowSe16 : []) entry.overlayRenderContainer.updateAllPositions();
});
}
movingLock(func) {
const isMoving = this._moving;
try {
this._moving = true;
return func();
} finally {
this._moving = isMoving;
}
}
/**
* Bracket a structural mutation with `onWillMutateLayout` /
* `onDidMutateLayout`. Re-entrant: nested calls (a compound operation such
* as a drag that relocates a panel) join the outermost transaction, so the
* events fire exactly once around the whole operation. `kind` reflects the
* outermost mutation.
*/
mutation(kind, func) {
const close = this.openMutation(kind);
try {
return func();
} finally {
close();
}
}
/**
* `mutation()` for an operation whose work continues after the synchronous
* call returns. The transaction stays open until the returned promise
* settles, so everything the operation does - the group it adds, the panels
* it relocates - lands inside the bracket, matching the synchronous
* move / float paths.
*/
mutationAsync(kind, func) {
var _this = this;
return _asyncToGenerator(function* () {
const close = _this.openMutation(kind);
try {
return yield func();
} finally {
close();
}
})();
}
/**
* Open a transaction and return the function that closes it. Shared by the
* synchronous and asynchronous brackets; the returned function must be
* called exactly once.
*
* Both ends key off the depth counter reaching zero rather than off which
* bracket opened first. For synchronous nesting the two are the same thing
* - brackets close in the order they opened - but asynchronous transactions
* can *overlap* rather than nest (two `addPopoutGroup` calls in flight at
* once, or a restore staggering several), and there the first to open is
* not the last to close. Reporting on the opener would fire `didMutate`
* while the other transaction was still doing structural work, which is the
* very thing the async bracket exists to prevent. Overlapping transactions
* therefore report as one, tagged with the kind of the last to finish.
*/
openMutation(kind) {
const origin = this._origin;
if (this._mutationDepth === 0) this._onWillMutateLayout.fire({
kind,
origin
});
this._mutationDepth++;
return () => {
this._mutationDepth--;
if (this._mutationDepth === 0) {
this.flushLocationChanges();
this._onDidMutateLayout.fire({
kind,
origin
});
}
};
}
/**
* Coalesce a panel's `onDidLocationChange` to the end of the enclosing
* transaction, or fire it immediately when no transaction is in flight.
*
* Relocating a panel touches its location twice: once as it is reparented
* into the destination group - which, when that group has just been created
* for a floating or popout window, has not been told where it lives yet -
* and once as the group is tagged with its final location. Reporting each
* signal as it happens therefore leaks an intermediate `grid` location the
* panel was never in, at a moment when it belongs to neither group's panel
* list and so is missing from `api.panels`.
*
* Deferring collapses those signals into a single event carrying the
* settled location. Note the signals are deliberately *not* compared
* against the panel's previous location: a panel can move between two
* floating windows without its location type changing, and this event is
* the only signal `OverlayRenderContainer` has to re-resolve the panel's
* z-index against its new host window.
*/
deferLocationChange(key, fire) {
if (this._mutationDepth === 0) {
fire();
return;
}
this._pendingLocationChanges.set(key, fire);
}
/**
* Report every panel whose location moved during the transaction that has
* just closed. Each callback reads the panel's location at this point, so
* what it reports is where the panel ended up.
*/
flushLocationChanges() {
if (this._pendingLocationChanges.size === 0) return;
const pending = Array.from(this._pendingLocationChanges.values());
this._pendingLocationChanges.clear();
for (const fire of pending) fire();
}
/**
* The origin of the operation currently in progress (`'user'` by default).
* Read inside a `mutation()` or active-panel change to learn whether the
* change was driven by application code (via the {@link DockviewApi}) or a
* user gesture.
*/
currentOrigin() {
return this._origin;
}
/**
* Run `func` with the operation origin set to `origin`, restoring the
* previous value afterwards. Used by the DockviewApi boundary to tag
* programmatic operations as `'api'`, and by user-gesture handlers to tag
* `'user'`. Only the outermost caller sets the origin; a nested call (or a
* call made while a mutation is already in flight) keeps whatever the
* enclosing operation established, so the trigger always wins.
*/
withOrigin(origin, func) {
if (this._originDepth > 0 || this._mutationDepth > 0) return func();
const previous = this._origin;
this._origin = origin;
this._originDepth++;
try {
return func();
} finally {
this._originDepth--;
this._origin = previous;
}
}
/**
* Fire `onDidActivePanelChange` with the panel and the current operation
* {@link DockviewOrigin}. Callers keep their own dedupe guards.
*/
fireActivePanelChange(panel) {
this._onDidActivePanelChange.fire({
panel,
origin: this._origin
});
}
/**
* Announce that `panel` has finished relocating. Must be called once the
* move has settled: `to` is read from the panel's current group, so an
* early call would report the source group as the destination.
*/
fireDidMovePanel(panel, from) {
this._onDidMovePanel.fire({
panel,
from,
to: panel.group
});
}
moveGroupOrPanel(options) {
this.mutation("move", () => this._doMoveGroupOrPanel(options));
}
_doMoveGroupOrPanel(options) {
var _this$_groups$get3;
const destinationGroup = options.to.group;
const sourceGroupId = options.from.groupId;
const sourceItemId = options.from.panelId;
const destinationTarget = options.to.position;
const destinationIndex = options.to.index;
const sourceGroup = sourceGroupId ? (_this$_groups$get3 = this._groups.get(sourceGroupId)) === null || _this$_groups$get3 === void 0 ? void 0 : _this$_groups$get3.value : void 0;
if (!sourceGroup) throw new Error(`dockview: Failed to find group id ${sourceGroupId}`);
if (sourceItemId === void 0) {
if (options.from.tabGroupId)
/**
* Moving a tab group (subset of panels) into another group
*/
this.moveTabGroupToGroup({
sourceGroup,
tabGroupId: options.from.tabGroupId,
destinationGroup,
destinationTarget,
destinationIndex,
skipSetActive: options.skipSetActive,
keepEmptyGroups: options.keepEmptyGroups
});
else
/**
* Moving an entire group into another group
*/
this.moveGroup({
from: { group: sourceGroup },
to: {
group: destinationGroup,
position: destinationTarget
},
skipSetActive: options.skipSetActive
});
return;
}
if (!destinationTarget || destinationTarget === "center") {
/**
* Dropping a panel within another group
*/
const removedPanel = this.movingLock(() => sourceGroup.model.removePanel(sourceItemId, {
skipSetActive: false,
skipSetActiveGroup: true
}));
if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
if (!options.keepEmptyGroups && sourceGroup.model.size === 0) this.doRemoveGroup(sourceGroup, { skipActive: true });
const isDestinationGroupEmpty = destinationGroup.model.size === 0;
this.movingLock(() => {
var _options$skipSetActiv;
return destinationGroup.model.openPanel(removedPanel, {
index: destinationIndex,
skipSetActive: ((_options$skipSetActiv = options.skipSetActive) !== null && _options$skipSetActiv !== void 0 ? _options$skipSetActiv : false) && !isDestinationGroupEmpty,
skipSetGroupActive: true
});
});
if (!options.skipSetActive) this.doSetGroupAndPanelActive(destinationGroup);
this.fireDidMovePanel(removedPanel, sourceGroup);
} else {
/**
* Dropping a panel to the extremities of a group which will place that panel
* into an adjacent group
*/
const destinationGridview = this.getGridviewForGroup(destinationGroup);
const referenceLocation = getGridLocation(destinationGroup.element);
const targetLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
if (sourceGroup.size < 2) {
/**
* If we are moving from a group which only has one panel left we will consider
* moving the group itself rather than moving the panel into a newly created group
*/
const [targetParentLocation, to] = tail(targetLocation);
if (sourceGroup.api.location.type === "grid" && destinationGridview === this.gridview) {
const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
this.gridview.moveView(sourceParentLocation, from, to);
this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
return;
}
}
if (sourceGroup.api.location.type === "popout" && this.nestedWindowMembers(sourceGroup).length <= 1) {
var _this$_popoutWindowSe18;
/**
* the source group is the only group in a popout window and
* has a single panel
*
* 1. remove the panel from the group without triggering any events
* 2. remove the popout group; this may cascade-remove the empty
* reference group it left behind in the main grid (see
* doRemoveGroup for popout groups), which can shift grid indices
* 3. recompute the target location now that the grid is stable
* 4. create a new group at the recomputed location and add that panel
*
* Multi-group popout windows fall through to the generic
* detach-and-re-add path so the window stays alive.
*/
const popoutGroup = (_this$_popoutWindowSe18 = this._popoutWindowService) === null || _this$_popoutWindowSe18 === void 0 ? void 0 : _this$_popoutWindowSe18.findByGroup(sourceGroup);
if (!popoutGroup) return;
const removedPanel = this.movingLock(() => popoutGroup.popoutGroup.model.removePanel(popoutGroup.popoutGroup.panels[0], {
skipSetActive: true,
skipSetActiveGroup: true
}));
this.doRemoveGroup(sourceGroup, { skipActive: true });
const updatedTargetLocation = getRelativeLocation(destinationGridview.orientation, getGridLocation(destinationGroup.element), destinationTarget);
const newGroup = this.createGroupAtLocation(updatedTargetLocation, void 0, void 0, destinationGridview);
this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetActive: true }));
this.doSetGroupAndPanelActive(newGroup);
this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
return;
}
if (sourceGroup.api.location.type === "edge") {
/**
* Edge groups are permanent structural elements; never move the
* group itself. Instead extract the panel and create a new grid group,
* leaving the edge slot intact (same behaviour as the size >= 2 path).
*/
const removedPanel = this.movingLock(() => sourceGroup.model.removePanel(sourceItemId, {
skipSetActive: false,
skipSetActiveGroup: true
}));
if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
const newGroup = this.createGroupAtLocation(targetLocation, void 0, void 0, destinationGridview);
this.movingLock(() => newGroup.model.openPanel(removedPanel, { skipSetGroupActive: true }));
this.doSetGroupAndPanelActive(newGroup);
this.fireDidMovePanel(removedPanel, sourceGroup);
return;
}
const targetGroup = this.movingLock(() => this.doRemoveGroup(sourceGroup, {
skipActive: true,
skipDispose: true
}));
const updatedReferenceLocation = getGridLocation(destinationGroup.element);
const location = getRelativeLocation(destinationGridview.orientation, updatedReferenceLocation, destinationTarget);
this.movingLock(() => this.doAddGroup(targetGroup, location, void 0, destinationGridview));
this.setGroupLocationForRoot(targetGroup, destinationGridview);
this.doSetGroupAndPanelActive(targetGroup);
this.fireDidMovePanel(this.getGroupPanel(sourceItemId), sourceGroup);
} else {
/**
* The group we are removing from has many panels, we need to remove the panels we are moving,
* create a new group, add the panels to that new group and add the new group in an appropiate position
*/
const removedPanel = this.movingLock(() => sourceGroup.model.removePanel(sourceItemId, {
skipSetActive: false,
skipSetActiveGroup: true
}));
if (!removedPanel) throw new Error(`dockview: No panel with id ${sourceItemId}`);
const dropLocation = getRelativeLocation(destinationGridview.orientation, referenceLocation, destinationTarget);
const group = this.createGroupAtLocation(dropLocation, void 0, void 0, destinationGridview);
this.movingLock(() => group.model.openPanel(removedPanel, { skipSetGroupActive: true }));
this.doSetGroupAndPanelActive(group);
this.fireDidMovePanel(removedPanel, sourceGroup);
}
}
}
moveTabGroupToGroup(options) {
const { sourceGroup, tabGroupId, destinationGroup, destinationTarget, destinationIndex } = options;
const tabGroup = sourceGroup.model.getTabGroups().find((tg) => tg.id === tabGroupId);
if (!tabGroup || tabGroup.panelIds.length === 0) return;
const label = tabGroup.label;
const color = tabGroup.color;
const collapsed = tabGroup.collapsed;
const componentParams = tabGroup.componentParams;
const panelIds = [...tabGroup.panelIds];
const referenceLocation = destinationTarget && destinationTarget !== "center" ? getGridLocation(destinationGroup.element) : void 0;
const removedPanels = this.movingLock(() => panelIds.map((pid) => sourceGroup.model.removePanel(pid, {
skipSetActive: false,
skipSetActiveGroup: true
})).filter((p) => p !== void 0));
if (removedPanels.length === 0) return;
const addPanelsToGroup = (targetGroup) => {
this.movingLock(() => {
for (const panel of removedPanels) targetGroup.model.openPanel(panel, {
index: destinationIndex,
skipSetActive: true,
skipSetGroupActive: true
});
});
const newTabGroup = targetGroup.model.createTabGroup({
label,
color,
collapsed,
componentParams
});
for (const panel of removedPanels) targetGroup.model.addPanelToTabGroup(newTabGroup.id, panel.id);
if (!options.skipSetActive) this.doSetGroupAndPanelActive(targetGroup);
for (const panel of removedPanels) this.fireDidMovePanel(panel, sourceGroup);
};
let targetGroup;
if (!destinationTarget || destinationTarget === "center" || !referenceLocation) targetGroup = destinationGroup;
else {
const dropLocation = getRelativeLocation(this.gridview.orientation, referenceLocation, destinationTarget);
targetGroup = this.createGroupAtLocation(dropLocation);
}
if (!options.keepEmptyGroups && sourceGroup.model.size === 0 && sourceGroup !== targetGroup) this.doRemoveGroup(sourceGroup, { skipActive: true });
addPanelsToGroup(targetGroup);
}
moveGroup(options) {
this.mutation("move", () => this._doMoveGroup(options));
}
maximizeGroup(panel) {
this.mutation("maximize", () => super.maximizeGroup(panel));
}
exitMaximizedGroup() {
this.mutation("maximize", () => super.exitMaximizedGroup());
}
_doMoveGroup(options) {
var _mergedPanels;
const from = options.from.group;
const to = options.to.group;
const target = options.to.position;
let source = from;
let mergedPanels;
if (target === "center") {
const activePanel = from.activePanel;
const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
label: tg.label,
color: tg.color,
collapsed: tg.collapsed,
componentParams: tg.componentParams,
panelIds: [...tg.panelIds]
}));
const panels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, { skipSetActive: true })));
if ((from === null || from === void 0 ? void 0 : from.model.size) === 0) this.doRemoveGroup(from, { skipActive: true });
this.movingLock(() => {
for (const panel of panels) to.model.openPanel(panel, {
skipSetActive: panel !== activePanel,
skipSetGroupActive: true
});
});
mergedPanels = panels;
for (const snapshot of tabGroupSnapshots) {
const newTabGroup = to.model.createTabGroup({
label: snapshot.label,
color: snapshot.color,
collapsed: snapshot.collapsed,
componentParams: snapshot.componentParams
});
for (const panelId of snapshot.panelIds) to.model.addPanelToTabGroup(newTabGroup.id, panelId);
}
if (options.skipSetActive !== true) this.doSetGroupAndPanelActive(to);
else if (!this.activePanel) this.doSetGroupAndPanelActive(to);
} else {
if (from.api.location.type === "edge") {
/**
* Edge groups are permanent structural elements and must
* stay anchored in their edge slot. Move the panels into a
* new group; the auto-collapse listener registered in
* addEdgeGroup will collapse the now-empty edge slot once
* the last panel leaves. The placement code below then
* positions `source` like any other moved group.
*/
const activePanel = from.activePanel;
const tabGroupSnapshots = from.model.getTabGroups().map((tg) => ({
label: tg.label,
color: tg.color,
collapsed: tg.collapsed,
componentParams: tg.componentParams,
panelIds: [...tg.panelIds]
}));
const movedPanels = this.movingLock(() => [...from.panels].map((p) => from.model.removePanel(p.id, { skipSetActive: true })));
source = this.createGroup();
this._onDidAddGroup.fire(source);
this.movingLock(() => {
for (const panel of movedPanels) source.model.openPanel(panel, {
skipSetActive: panel !== activePanel,
skipSetGroupActive: true
});
});
for (const snapshot of tabGroupSnapshots) {
const newTabGroup = source.model.createTabGroup({
label: snapshot.label,
color: snapshot.color,
collapsed: snapshot.collapsed,
componentParams: snapshot.componentParams
});
for (const panelId of snapshot.panelIds) source.model.addPanelToTabGroup(newTabGroup.id, panelId);
}
} else switch (from.api.location.type) {
case "grid":
this.gridview.removeView(getGridLocation(from.element));
break;
case "floating": {
var _this$_floatingGroupS9;
const selectedFloatingGroup = (_this$_floatingGroupS9 = this._floatingGroupService) === null || _this$_floatingGroupS9 === void 0 ? void 0 : _this$_floatingGroupS9.findByGroup(from);
if (!selectedFloatingGroup) throw new Error("dockview: failed to find floating group");
if (!this.detachFromNestedWindow(from)) selectedFloatingGroup.dispose();
break;
}
case "popout": {
var _this$_popoutWindowSe19, _this$_popoutWindowSe20;
const selectedPopoutGroup = (_this$_popoutWindowSe19 = this._popoutWindowService) === null || _this$_popoutWindowSe19 === void 0 ? void 0 : _this$_popoutWindowSe19.findByGroup(from);
if (!selectedPopoutGroup) throw new Error("dockview: failed to find popout group");
if (this.detachFromNestedWindow(from)) break;
(_this$_popoutWindowSe20 = this._popoutWindowService) === null || _this$_popoutWindowSe20 === void 0 || _this$_popoutWindowSe20.remove(selectedPopoutGroup);
if (selectedPopoutGroup.referenceGroup) {
const referenceGroup = this.getPanel(selectedPopoutGroup.referenceGroup);
if (referenceGroup && !referenceGroup.api.isVisible) this.doRemoveGroup(referenceGroup, { skipActive: true });
}
selectedPopoutGroup.window.dispose();
break;
}
}
if (to.api.location.type === "grid" || to.api.location.type === "floating" || to.api.location.type === "popout") {
const destGridview = this.getGridviewForGroup(to);
const referenceLocation = getGridLocation(to.element);
const dropLocation = getRelativeLocation(destGridview.orientation, referenceLocation, target);
let size;
switch (destGridview.orientation) {
case "VERTICAL":
size = referenceLocation.length % 2 == 0 ? from.api.width : from.api.height;
break;
case "HORIZONTAL":
size = referenceLocation.length % 2 == 0 ? from.api.height : from.api.width;
break;
}
destGridview.addView(source, size, dropLocation);
this.setGroupLocationForRoot(source, destGridview);
}
}
((_mergedPanels = mergedPanels) !== null && _mergedPanels !== void 0 ? _mergedPanels : source.panels).forEach((panel) => {
this.fireDidMovePanel(panel, from);
});
this.debouncedUpdateAllPositions();
if (options.skipSetActive === false) {
const targetGroup = to !== null && to !== void 0 ? to : from;
this.doSetGroupAndPanelActive(targetGroup);
} else if (source !== from && options.skipSetActive !== true) this.doSetGroupAndPanelActive(source);
}
doSetGroupActive(group) {
var _this$_onDidActivePan;
super.doSetGroupActive(group);
const activePanel = this.activePanel;
if (!this._moving && activePanel !== ((_this$_onDidActivePan = this._onDidActivePanelChange.value) === null || _this$_onDidActivePan === void 0 ? void 0 : _this$_onDidActivePan.panel)) this.fireActivePanelChange(activePanel);
}
doSetGroupAndPanelActive(group) {
var _this$_onDidActivePan2;
super.doSetGroupActive(group);
const activePanel = this.activePanel;
if (group && this.hasMaximizedGroup() && !this.isMaximizedGroup(group)) this.exitMaximizedGroup();
if (!this._moving && activePanel !== ((_this$_onDidActivePan2 = this._onDidActivePanelChange.value) === null || _this$_onDidActivePan2 === void 0 ? void 0 : _this$_onDidActivePan2.panel)) this.fireActivePanelChange(activePanel);
}
getNextGroupId() {
let id = this.nextGroupId.next();
while (this._groups.has(id)) id = this.nextGroupId.next();
return id;
}
createGroup(options) {
var _options;
(_options = options) !== null && _options !== void 0 || (options = {});
let id = options === null || options === void 0 ? void 0 : options.id;
if (id && this._groups.has(options.id)) {
console.warn(`dockview: Duplicate group id ${options === null || options === void 0 ? void 0 : options.id}. reassigning group id to avoid errors`);
id = void 0;
}
if (!id) {
id = this.nextGroupId.next();
while (this._groups.has(id)) id = this.nextGroupId.next();
}
const view = new DockviewGroupPanel(this, id, options);
view.init({
params: {},
accessor: this
});
if (!this._groups.has(view.id)) {
const disposable = new CompositeDisposable(view.model.onTabDragStart((event) => {
var _this$_advancedDnDSer5;
(_this$_advancedDnDSer5 = this._advancedDnDService) === null || _this$_advancedDnDSer5 === void 0 || _this$_advancedDnDSer5.dispatchWillDragPanel(event);
}), view.model.onGroupDragStart((event) => {
var _this$_advancedDnDSer6;
(_this$_advancedDnDSer6 = this._advancedDnDService) === null || _this$_advancedDnDSer6 === void 0 || _this$_advancedDnDSer6.dispatchWillDragGroup(event);
}), view.model.onMove((event) => {
const { groupId, itemId, target, index, tabGroupId } = event;
this.moveGroupOrPanel({
from: {
groupId,
panelId: itemId,
tabGroupId
},
to: {
group: view,
position: target,
index
}
});
}), view.model.onDidDrop((event) => {
this._onDidDrop.fire(event);
}), view.model.onWillDrop((event) => {
var _this$_advancedDnDSer7;
(_this$_advancedDnDSer7 = this._advancedDnDService) === null || _this$_advancedDnDSer7 === void 0 || _this$_advancedDnDSer7.dispatchWillDrop(event);
}), view.model.onWillShowOverlay((event) => {
var _this$_advancedDnDSer8;
if (this.options.disableDnd) {
event.preventDefault();
return;
}
(_this$_advancedDnDSer8 = this._advancedDnDService) === null || _this$_advancedDnDSer8 === void 0 || _this$_advancedDnDSer8.dispatchWillShowOverlay(event);
}), view.model.onUnhandledDragOver((event) => {
this._onUnhandledDragOver.fire(event);
}), view.model.onDidAddPanel((event) => {
if (this._moving) return;
this._onDidAddPanel.fire(event.panel);
}), view.model.onDidRemovePanel((event) => {
if (this._moving) return;
this._onDidRemovePanel.fire(event.panel);
}), view.model.onDidActivePanelChange((event) => {
var _this$_onDidActivePan3;
if (this._moving) return;
if (event.panel !== this.activePanel) return;
if (((_this$_onDidActivePan3 = this._onDidActivePanelChange.value) === null || _this$_onDidActivePan3 === void 0 ? void 0 : _this$_onDidActivePan3.panel) !== event.panel) this.fireActivePanelChange(event.panel);
}), Event.any(view.model.onDidPanelTitleChange, view.model.onDidPanelParametersChange)(() => {
this._bufferOnDidLayoutChange.fire();
}));
this._groups.set(view.id, {
value: view,
disposable
});
}
view.initialize();
return view;
}
createPanel(options, group) {
var _options$tabComponent, _options$title, _options$params;
const contentComponent = options.component;
const tabComponent = (_options$tabComponent = options.tabComponent) !== null && _options$tabComponent !== void 0 ? _options$tabComponent : this.options.defaultTabComponent;
const view = new DockviewPanelModel(this, options.id, contentComponent, tabComponent);
const panel = new DockviewPanel(options.id, contentComponent, tabComponent, this, this._api, group, view, {
renderer: options.renderer,
minimumWidth: options.minimumWidth,
minimumHeight: options.minimumHeight,
maximumWidth: options.maximumWidth,
maximumHeight: options.maximumHeight
});
panel.init({
title: (_options$title = options.title) !== null && _options$title !== void 0 ? _options$title : options.id,
params: (_options$params = options === null || options === void 0 ? void 0 : options.params) !== null && _options$params !== void 0 ? _options$params : {}
});
return panel;
}
createGroupAtLocation(location, size, options, gridview = this.gridview) {
const group = this.createGroup(options);
this.doAddGroup(group, location, size, gridview);
this.setGroupLocationForRoot(group, gridview);
return group;
}
/**
* Tag a group with the location and render / drop-target containers
* matching the gridview root it now lives in: the main grid, a floating
* window (shares the main containers), or a popout window (uses its own
* window-local containers).
*/
setGroupLocationForRoot(group, gridview) {
var _this$_popoutWindowSe21;
const popout = (_this$_popoutWindowSe21 = this._popoutWindowService) === null || _this$_popoutWindowSe21 === void 0 ? void 0 : _this$_popoutWindowSe21.entries.find((entry) => entry.gridview === gridview);
if (popout) {
if (group.model.renderContainer !== popout.overlayRenderContainer) group.model.renderContainer = popout.overlayRenderContainer;
group.model.dropTargetContainer = popout.dropTargetContainer;
group.model.location = {
type: "popout",
getWindow: popout.getWindow,
popoutUrl: popout.popoutUrl
};
return;
}
if (group.model.renderContainer !== this.overlayRenderContainer) group.model.renderContainer = this.overlayRenderContainer;
group.model.dropTargetContainer = this.rootDropTargetContainer;
group.model.location = gridview === this.gridview ? { type: "grid" } : { type: "floating" };
}
/**
* Resolve which gridview root currently owns a group: the main grid, or
* the nested gridview of the floating / popout window it lives in.
*/
getGridviewForGroup(group) {
var _this$_floatingGroupS10, _this$_popoutWindowSe22;
const floating = (_this$_floatingGroupS10 = this._floatingGroupService) === null || _this$_floatingGroupS10 === void 0 ? void 0 : _this$_floatingGroupS10.findByGroup(group);
if (floating) return floating.gridview;
const popout = (_this$_popoutWindowSe22 = this._popoutWindowService) === null || _this$_popoutWindowSe22 === void 0 ? void 0 : _this$_popoutWindowSe22.findByGroup(group);
if (popout) return popout.gridview;
return this.gridview;
}
/**
* The groups that live within the same floating / popout window as `group`
* (including `group` itself). Empty when `group` is in the main grid.
*/
nestedWindowMembers(group) {
const gridview = this.getGridviewForGroup(group);
if (gridview === this.gridview) return [];
return this.groups.filter((candidate) => gridview.element.contains(candidate.element));
}
findGroup(panel) {
var _Array$from$find;
return (_Array$from$find = Array.from(this._groups.values()).find((group) => group.value.model.containsPanel(panel))) === null || _Array$from$find === void 0 ? void 0 : _Array$from$find.value;
}
orientationAtLocation(location) {
const rootOrientation = this.gridview.orientation;
return location.length % 2 == 1 ? rootOrientation : orthogonal(rootOrientation);
}
updateTheme() {
var _this$_options$theme, _this$_shellThemeClas, _theme$gap, _this$_popoutWindowSe23, _this$_popoutWindowSe24, _this$_shellManager4, _theme$edgeGroupColla, _theme$tabGroupIndica;
const theme = (_this$_options$theme = this._options.theme) !== null && _this$_options$theme !== void 0 ? _this$_options$theme : themeAbyss;
(_this$_shellThemeClas = this._shellThemeClassnames) === null || _this$_shellThemeClas === void 0 || _this$_shellThemeClas.setClassNames(theme.className);
const gap = (_theme$gap = theme.gap) !== null && _theme$gap !== void 0 ? _theme$gap : 0;
this.gridview.margin = gap;
for (const floating of this.floatingGroups) floating.gridview.margin = gap;
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;
(_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);
if (theme.dndOverlayBorder === void 0) {
var _this$_shellManager5;
this.element.style.removeProperty("--dv-drag-over-border");
(_this$_shellManager5 = this._shellManager) === null || _this$_shellManager5 === void 0 || _this$_shellManager5.element.style.removeProperty("--dv-drag-over-border");
} else {
var _this$_shellManager6;
this.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
(_this$_shellManager6 = this._shellManager) === null || _this$_shellManager6 === void 0 || _this$_shellManager6.element.style.setProperty("--dv-drag-over-border", theme.dndOverlayBorder);
}
switch (theme.dndOverlayMounting) {
case "absolute":
this.rootDropTargetContainer.disabled = false;
break;
default:
this.rootDropTargetContainer.disabled = true;
break;
}
const indicatorNone = ((_theme$tabGroupIndica = theme.tabGroupIndicator) !== null && _theme$tabGroupIndica !== void 0 ? _theme$tabGroupIndica : "wrap") === "none";
toggleClass(this.element, "dv-tab-group-indicator-none", indicatorNone);
if (this._shellManager) toggleClass(this._shellManager.element, "dv-tab-group-indicator-none", indicatorNone);
for (const group of this.groups) group.model.updateTabGroups();
}
};
var GridviewComponent = class extends BaseGrid {
get orientation() {
return this.gridview.orientation;
}
set orientation(value) {
this.gridview.orientation = value;
}
get options() {
return this._options;
}
get deserializer() {
return this._deserializer;
}
set deserializer(value) {
this._deserializer = value;
}
constructor(container, options) {
var _options$proportional;
super(container, {
proportionalLayout: (_options$proportional = options.proportionalLayout) !== null && _options$proportional !== void 0 ? _options$proportional : true,
orientation: options.orientation,
styles: options.hideBorders ? { separatorBorder: "transparent" } : void 0,
disableAutoResizing: options.disableAutoResizing,
className: options.className
});
this._onDidLayoutfromJSON = new Emitter();
this.onDidLayoutFromJSON = this._onDidLayoutfromJSON.event;
this._onDidRemoveGroup = new Emitter();
this.onDidRemoveGroup = this._onDidRemoveGroup.event;
this._onDidAddGroup = new Emitter();
this.onDidAddGroup = this._onDidAddGroup.event;
this._onDidActiveGroupChange = new Emitter();
this.onDidActiveGroupChange = this._onDidActiveGroupChange.event;
this._options = options;
this.addDisposables(this._onDidAddGroup, this._onDidRemoveGroup, this._onDidActiveGroupChange, this.onDidAdd((event) => {
this._onDidAddGroup.fire(event);
}), this.onDidRemove((event) => {
this._onDidRemoveGroup.fire(event);
}), this.onDidActiveChange((event) => {
this._onDidActiveGroupChange.fire(event);
}));
}
updateOptions(options) {
super.updateOptions(options);
const hasOrientationChanged = typeof options.orientation === "string" && this.gridview.orientation !== options.orientation;
this._options = _objectSpread2(_objectSpread2({}, this.options), options);
if (hasOrientationChanged) this.gridview.orientation = options.orientation;
this.layout(this.gridview.width, this.gridview.height, true);
}
removePanel(panel) {
this.removeGroup(panel);
}
/**
* Serialize the current state of the layout
*
* @returns A JSON respresentation of the layout
*/
toJSON() {
var _this$activeGroup;
return {
grid: this.gridview.serialize(),
activePanel: (_this$activeGroup = this.activeGroup) === null || _this$activeGroup === void 0 ? void 0 : _this$activeGroup.id
};
}
setVisible(panel, visible) {
this.gridview.setViewVisible(getGridLocation(panel.element), visible);
}
setActive(panel) {
this._groups.forEach((value, _key) => {
value.value.setActive(panel === value.value);
});
}
focus() {
var _this$activeGroup2;
(_this$activeGroup2 = this.activeGroup) === null || _this$activeGroup2 === void 0 || _this$activeGroup2.focus();
}
fromJSON(serializedGridview) {
this.clear();
const { grid, activePanel } = serializedGridview;
try {
const queue = [];
const width = this.width;
const height = this.height;
this.gridview.deserialize(grid, { fromJSON: (node) => {
const { data } = node;
const view = this.options.createComponent({
id: data.id,
name: data.component
});
queue.push(() => view.init({
params: data.params,
minimumWidth: data.minimumWidth,
maximumWidth: data.maximumWidth,
minimumHeight: data.minimumHeight,
maximumHeight: data.maximumHeight,
priority: data.priority,
snap: !!data.snap,
accessor: this,
isVisible: node.visible
}));
this._onDidAddGroup.fire(view);
this.registerPanel(view);
return view;
} });
this.layout(width, height, true);
queue.forEach((f) => f());
if (typeof activePanel === "string") {
const panel = this.getPanel(activePanel);
if (panel) this.doSetGroupActive(panel);
}
} catch (err) {
/**
* To remove a group we cannot call this.removeGroup(...) since this makes assumptions about
* the underlying HTMLElement existing in the Gridview.
*/
for (const group of this.groups) {
group.dispose();
this._groups.delete(group.id);
this._onDidRemoveGroup.fire(group);
}
this.clear();
/**
* even though we have cleaned-up we still want to inform the caller of their error
* and we'll do this through re-throwing the original error since afterall you would
* expect trying to load a corrupted layout to result in an error and not silently fail...
*/
throw err;
}
this._onDidLayoutfromJSON.fire();
}
clear() {
const hasActiveGroup = this.activeGroup;
const groups = Array.from(this._groups.values());
for (const group of groups) {
group.disposable.dispose();
this.doRemoveGroup(group.value, { skipActive: true });
}
if (hasActiveGroup) this.doSetGroupActive(void 0);
this.gridview.clear();
}
movePanel(panel, options) {
var _this$_groups$get;
const referenceGroup = (_this$_groups$get = this._groups.get(options.reference)) === null || _this$_groups$get === void 0 ? void 0 : _this$_groups$get.value;
if (!referenceGroup) throw new Error(`reference group ${options.reference} does not exist`);
const target = toTarget(options.direction);
if (target === "center") throw new Error(`${target} not supported as an option`);
const location = getGridLocation(referenceGroup.element);
const relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
const removedPanel = this.gridview.remove(panel);
this.doAddGroup(removedPanel, relativeLocation, options.size);
}
addPanel(options) {
var _options$location, _options$position, _options$params;
let relativeLocation = (_options$location = options.location) !== null && _options$location !== void 0 ? _options$location : [0];
if ((_options$position = options.position) === null || _options$position === void 0 ? void 0 : _options$position.referencePanel) {
var _this$_groups$get2;
const referenceGroup = (_this$_groups$get2 = this._groups.get(options.position.referencePanel)) === null || _this$_groups$get2 === void 0 ? void 0 : _this$_groups$get2.value;
if (!referenceGroup) throw new Error(`reference group ${options.position.referencePanel} does not exist`);
const target = toTarget(options.position.direction);
if (target === "center") throw new Error(`${target} not supported as an option`);
else {
const location = getGridLocation(referenceGroup.element);
relativeLocation = getRelativeLocation(this.gridview.orientation, location, target);
}
}
const view = this.options.createComponent({
id: options.id,
name: options.component
});
view.init({
params: (_options$params = options.params) !== null && _options$params !== void 0 ? _options$params : {},
minimumWidth: options.minimumWidth,
maximumWidth: options.maximumWidth,
minimumHeight: options.minimumHeight,
maximumHeight: options.maximumHeight,
priority: options.priority,
snap: !!options.snap,
accessor: this,
isVisible: true
});
this.doAddGroup(view, relativeLocation, options.size);
this.registerPanel(view);
this.doSetGroupActive(view);
return view;
}
registerPanel(panel) {
const disposable = new CompositeDisposable(panel.api.onDidFocusChange((event) => {
if (!event.isFocused) return;
this._groups.forEach((groupItem) => {
const group = groupItem.value;
group.setActive(group === panel);
});
}));
this._groups.set(panel.id, {
value: panel,
disposable
});
}
moveGroup(referenceGroup, groupId, target) {
const sourceGroup = this.getPanel(groupId);
if (!sourceGroup) throw new Error("invalid operation");
const referenceLocation = getGridLocation(referenceGroup.element);
const [targetParentLocation, to] = tail(getRelativeLocation(this.gridview.orientation, referenceLocation, target));
const [sourceParentLocation, from] = tail(getGridLocation(sourceGroup.element));
if (sequenceEquals(sourceParentLocation, targetParentLocation)) {
this.gridview.moveView(sourceParentLocation, from, to);
return;
}
const targetGroup = this.doRemoveGroup(sourceGroup, {
skipActive: true,
skipDispose: true
});
const updatedReferenceLocation = getGridLocation(referenceGroup.element);
const location = getRelativeLocation(this.gridview.orientation, updatedReferenceLocation, target);
this.doAddGroup(targetGroup, location);
}
removeGroup(group) {
super.removeGroup(group);
}
dispose() {
super.dispose();
this._onDidLayoutfromJSON.dispose();
}
};
/**
* A high-level implementation of splitview that works using 'panels'
*/
var SplitviewComponent = class extends Resizable {
get panels() {
return this.splitview.getViews();
}
get options() {
return this._options;
}
get length() {
return this._panels.size;
}
get orientation() {
return this.splitview.orientation;
}
get splitview() {
return this._splitview;
}
set splitview(value) {
if (this._splitview) this._splitview.dispose();
this._splitview = value;
this._splitviewChangeDisposable.value = new CompositeDisposable(this._splitview.onDidSashEnd(() => {
this._onDidLayoutChange.fire(void 0);
}), this._splitview.onDidAddView((e) => this._onDidAddView.fire(e)), this._splitview.onDidRemoveView((e) => this._onDidRemoveView.fire(e)));
}
get minimumSize() {
return this.splitview.minimumSize;
}
get maximumSize() {
return this.splitview.maximumSize;
}
get height() {
return this.splitview.orientation === "HORIZONTAL" ? this.splitview.orthogonalSize : this.splitview.size;
}
get width() {
return this.splitview.orientation === "HORIZONTAL" ? this.splitview.size : this.splitview.orthogonalSize;
}
constructor(container, options) {
var _options$className;
super(document.createElement("div"), options.disableAutoResizing);
this._splitviewChangeDisposable = new MutableDisposable();
this._panels = /* @__PURE__ */ new Map();
this._onDidLayoutfromJSON = new Emitter();
this.onDidLayoutFromJSON = this._onDidLayoutfromJSON.event;
this._onDidAddView = new Emitter();
this.onDidAddView = this._onDidAddView.event;
this._onDidRemoveView = new Emitter();
this.onDidRemoveView = this._onDidRemoveView.event;
this._onDidLayoutChange = new Emitter();
this.onDidLayoutChange = this._onDidLayoutChange.event;
this.element.style.height = "100%";
this.element.style.width = "100%";
this._classNames = new Classnames(this.element);
this._classNames.setClassNames((_options$className = options.className) !== null && _options$className !== void 0 ? _options$className : "");
container.appendChild(this.element);
this._options = options;
this.splitview = new Splitview(this.element, options);
this.addDisposables(this._onDidAddView, this._onDidLayoutfromJSON, this._onDidRemoveView, this._onDidLayoutChange);
}
updateOptions(options) {
if ("className" in options) {
var _options$className2;
this._classNames.setClassNames((_options$className2 = options.className) !== null && _options$className2 !== void 0 ? _options$className2 : "");
}
if ("disableResizing" in options) {
var _options$disableAutoR;
this.disableResizing = (_options$disableAutoR = options.disableAutoResizing) !== null && _options$disableAutoR !== void 0 ? _options$disableAutoR : false;
}
if (typeof options.orientation === "string") this.splitview.orientation = options.orientation;
this._options = _objectSpread2(_objectSpread2({}, this.options), options);
this.splitview.layout(this.splitview.size, this.splitview.orthogonalSize);
}
focus() {
var _this$_activePanel;
(_this$_activePanel = this._activePanel) === null || _this$_activePanel === void 0 || _this$_activePanel.focus();
}
movePanel(from, to) {
this.splitview.moveView(from, to);
}
setVisible(panel, visible) {
const index = this.panels.indexOf(panel);
this.splitview.setViewVisible(index, visible);
}
setActive(panel, skipFocus) {
this._activePanel = panel;
this.panels.filter((v) => v !== panel).forEach((v) => {
v.api._onDidActiveChange.fire({ isActive: false });
if (!skipFocus) v.focus();
});
panel.api._onDidActiveChange.fire({ isActive: true });
if (!skipFocus) panel.focus();
}
removePanel(panel, sizing) {
const item = this._panels.get(panel.id);
if (!item) throw new Error(`unknown splitview panel ${panel.id}`);
item.dispose();
this._panels.delete(panel.id);
const index = this.panels.indexOf(panel);
this.splitview.removeView(index, sizing).dispose();
const panels = this.panels;
if (panels.length > 0) this.setActive(panels[panels.length - 1]);
}
getPanel(id) {
return this.panels.find((view) => view.id === id);
}
addPanel(options) {
var _options$params;
if (this._panels.has(options.id)) throw new Error(`panel ${options.id} already exists`);
const view = this.options.createComponent({
id: options.id,
name: options.component
});
view.orientation = this.splitview.orientation;
view.init({
params: (_options$params = options.params) !== null && _options$params !== void 0 ? _options$params : {},
minimumSize: options.minimumSize,
maximumSize: options.maximumSize,
snap: options.snap,
priority: options.priority,
accessor: this
});
const size = typeof options.size === "number" ? options.size : Sizing.Distribute;
const index = typeof options.index === "number" ? options.index : void 0;
this.splitview.addView(view, size, index);
this.doAddView(view);
this.setActive(view);
return view;
}
layout(width, height) {
const [size, orthogonalSize] = this.splitview.orientation === "HORIZONTAL" ? [width, height] : [height, width];
this.splitview.layout(size, orthogonalSize);
}
doAddView(view) {
const disposable = view.api.onDidFocusChange((event) => {
if (!event.isFocused) return;
this.setActive(view, true);
});
this._panels.set(view.id, disposable);
}
toJSON() {
var _this$_activePanel2;
return {
views: this.splitview.getViews().map((view, i) => {
return {
size: this.splitview.getViewSize(i),
data: view.toJSON(),
snap: !!view.snap,
priority: view.priority
};
}),
activeView: (_this$_activePanel2 = this._activePanel) === null || _this$_activePanel2 === void 0 ? void 0 : _this$_activePanel2.id,
size: this.splitview.size,
orientation: this.splitview.orientation
};
}
fromJSON(serializedSplitview) {
this.clear();
const { views, orientation, size, activeView } = serializedSplitview;
const queue = [];
const width = this.width;
const height = this.height;
this.splitview = new Splitview(this.element, {
orientation,
proportionalLayout: this.options.proportionalLayout,
descriptor: {
size,
views: views.map((view) => {
const data = view.data;
if (this._panels.has(data.id)) throw new Error(`panel ${data.id} already exists`);
const panel = this.options.createComponent({
id: data.id,
name: data.component
});
queue.push(() => {
var _data$params;
panel.init({
params: (_data$params = data.params) !== null && _data$params !== void 0 ? _data$params : {},
minimumSize: data.minimumSize,
maximumSize: data.maximumSize,
snap: view.snap,
priority: view.priority,
accessor: this
});
});
panel.orientation = orientation;
this.doAddView(panel);
setTimeout(() => {
this._onDidAddView.fire(panel);
}, 0);
return {
size: view.size,
view: panel
};
})
}
});
this.layout(width, height);
queue.forEach((f) => f());
if (typeof activeView === "string") {
const panel = this.getPanel(activeView);
if (panel) this.setActive(panel);
}
this._onDidLayoutfromJSON.fire();
}
clear() {
for (const disposable of this._panels.values()) disposable.dispose();
this._panels.clear();
while (this.splitview.length > 0) this.splitview.removeView(0, Sizing.Distribute, true).dispose();
}
dispose() {
for (const disposable of this._panels.values()) disposable.dispose();
this._panels.clear();
const views = this.splitview.getViews();
this._splitviewChangeDisposable.dispose();
this.splitview.dispose();
for (const view of views) view.dispose();
this.element.remove();
super.dispose();
}
};
var DefaultHeader = class extends CompositeDisposable {
get element() {
return this._element;
}
constructor() {
super();
this._expandedIcon = createExpandMoreButton();
this._collapsedIcon = createChevronRightButton();
this.disposable = new MutableDisposable();
this.apiRef = { api: null };
this._element = document.createElement("div");
this.element.className = "dv-default-header";
this._content = document.createElement("span");
this._expander = document.createElement("div");
this._expander.className = "dv-pane-header-icon";
this.element.appendChild(this._expander);
this.element.appendChild(this._content);
this.addDisposables(addDisposableListener(this._element, "click", () => {
var _this$apiRef$api;
(_this$apiRef$api = this.apiRef.api) === null || _this$apiRef$api === void 0 || _this$apiRef$api.setExpanded(!this.apiRef.api.isExpanded);
}));
}
init(params) {
this.apiRef.api = params.api;
this._content.textContent = params.title;
this.updateIcon();
this.disposable.value = params.api.onDidExpansionChange(() => {
this.updateIcon();
});
}
updateIcon() {
var _this$apiRef$api2;
const isExpanded = !!((_this$apiRef$api2 = this.apiRef.api) === null || _this$apiRef$api2 === void 0 ? void 0 : _this$apiRef$api2.isExpanded);
toggleClass(this._expander, "collapsed", !isExpanded);
if (isExpanded) {
if (this._expander.contains(this._collapsedIcon)) this._collapsedIcon.remove();
if (!this._expander.contains(this._expandedIcon)) this._expander.appendChild(this._expandedIcon);
} else {
if (this._expander.contains(this._expandedIcon)) this._expandedIcon.remove();
if (!this._expander.contains(this._collapsedIcon)) this._expander.appendChild(this._collapsedIcon);
}
}
update(_params) {}
dispose() {
this.disposable.dispose();
super.dispose();
}
};
const nextLayoutId = sequentialNumberGenerator();
const HEADER_SIZE = 22;
const MINIMUM_BODY_SIZE = 0;
const MAXIMUM_BODY_SIZE = Number.MAX_SAFE_INTEGER;
var PaneFramework = class extends DraggablePaneviewPanel {
constructor(options) {
super({
accessor: options.accessor,
id: options.id,
component: options.component,
headerComponent: options.headerComponent,
orientation: options.orientation,
isExpanded: options.isExpanded,
disableDnd: options.disableDnd,
headerSize: options.headerSize,
minimumBodySize: options.minimumBodySize,
maximumBodySize: options.maximumBodySize
});
this.options = options;
}
getBodyComponent() {
return this.options.body;
}
getHeaderComponent() {
return this.options.header;
}
};
var PaneviewComponent = class extends Resizable {
get id() {
return this._id;
}
get panels() {
return this.paneview.getPanes();
}
set paneview(value) {
this._paneview = value;
this._disposable.value = new CompositeDisposable(this._paneview.onDidChange(() => {
this._onDidLayoutChange.fire(void 0);
}), this._paneview.onDidAddView((e) => this._onDidAddView.fire(e)), this._paneview.onDidRemoveView((e) => this._onDidRemoveView.fire(e)));
}
get paneview() {
return this._paneview;
}
get minimumSize() {
return this.paneview.minimumSize;
}
get maximumSize() {
return this.paneview.maximumSize;
}
get height() {
return this.paneview.orientation === "HORIZONTAL" ? this.paneview.orthogonalSize : this.paneview.size;
}
get width() {
return this.paneview.orientation === "HORIZONTAL" ? this.paneview.size : this.paneview.orthogonalSize;
}
get options() {
return this._options;
}
constructor(container, options) {
var _options$className;
super(document.createElement("div"), options.disableAutoResizing);
this._id = nextLayoutId.next();
this._disposable = new MutableDisposable();
this._viewDisposables = /* @__PURE__ */ new Map();
this._onDidLayoutfromJSON = new Emitter();
this.onDidLayoutFromJSON = this._onDidLayoutfromJSON.event;
this._onDidLayoutChange = new Emitter();
this.onDidLayoutChange = this._onDidLayoutChange.event;
this._onDidDrop = new Emitter();
this.onDidDrop = this._onDidDrop.event;
this._onDidAddView = new Emitter();
this.onDidAddView = this._onDidAddView.event;
this._onDidRemoveView = new Emitter();
this.onDidRemoveView = this._onDidRemoveView.event;
this._onUnhandledDragOver = new Emitter();
this.onUnhandledDragOver = this._onUnhandledDragOver.event;
this.element.style.height = "100%";
this.element.style.width = "100%";
this.addDisposables(this._onDidLayoutChange, this._onDidLayoutfromJSON, this._onDidDrop, this._onDidAddView, this._onDidRemoveView, this._onUnhandledDragOver);
this._classNames = new Classnames(this.element);
this._classNames.setClassNames((_options$className = options.className) !== null && _options$className !== void 0 ? _options$className : "");
container.appendChild(this.element);
this._options = options;
this.paneview = new Paneview(this.element, { orientation: "VERTICAL" });
this.addDisposables(this._disposable);
}
setVisible(panel, visible) {
const index = this.panels.indexOf(panel);
this.paneview.setViewVisible(index, visible);
}
focus() {}
updateOptions(options) {
if ("className" in options) {
var _options$className2;
this._classNames.setClassNames((_options$className2 = options.className) !== null && _options$className2 !== void 0 ? _options$className2 : "");
}
if ("disableAutoResizing" in options) {
var _options$disableAutoR;
this.disableResizing = (_options$disableAutoR = options.disableAutoResizing) !== null && _options$disableAutoR !== void 0 ? _options$disableAutoR : false;
}
this._options = _objectSpread2(_objectSpread2({}, this.options), options);
}
addPanel(options) {
var _header, _options$headerSize, _options$params;
const body = this.options.createComponent({
id: options.id,
name: options.component
});
let header;
if (options.headerComponent && this.options.createHeaderComponent) header = this.options.createHeaderComponent({
id: options.id,
name: options.headerComponent
});
(_header = header) !== null && _header !== void 0 || (header = new DefaultHeader());
const view = new PaneFramework({
id: options.id,
component: options.component,
headerComponent: options.headerComponent,
header,
body,
orientation: "VERTICAL",
isExpanded: !!options.isExpanded,
disableDnd: !!this.options.disableDnd,
accessor: this,
headerSize: (_options$headerSize = options.headerSize) !== null && _options$headerSize !== void 0 ? _options$headerSize : HEADER_SIZE,
minimumBodySize: MINIMUM_BODY_SIZE,
maximumBodySize: MAXIMUM_BODY_SIZE
});
this.doAddPanel(view);
const size = typeof options.size === "number" ? options.size : Sizing.Distribute;
const index = typeof options.index === "number" ? options.index : void 0;
view.init({
params: (_options$params = options.params) !== null && _options$params !== void 0 ? _options$params : {},
minimumBodySize: options.minimumBodySize,
maximumBodySize: options.maximumBodySize,
isExpanded: options.isExpanded,
title: options.title,
containerApi: new PaneviewApi(this),
accessor: this
});
this.paneview.addPane(view, size, index);
view.orientation = this.paneview.orientation;
return view;
}
removePanel(panel) {
const index = this.panels.indexOf(panel);
this.paneview.removePane(index);
this.doRemovePanel(panel);
}
movePanel(from, to) {
this.paneview.moveView(from, to);
}
getPanel(id) {
return this.panels.find((view) => view.id === id);
}
layout(width, height) {
const [size, orthogonalSize] = this.paneview.orientation === "HORIZONTAL" ? [width, height] : [height, width];
this.paneview.layout(size, orthogonalSize);
}
toJSON() {
const maximum = (value) => value === Number.MAX_SAFE_INTEGER || value === Number.POSITIVE_INFINITY ? void 0 : value;
const minimum = (value) => value <= 0 ? void 0 : value;
return {
views: this.paneview.getPanes().map((view, i) => {
const cachedVisibleSize = this.paneview.getViewCachedVisibleSize(i);
const hidden = typeof cachedVisibleSize === "number";
return _objectSpread2({
size: hidden ? cachedVisibleSize : this.paneview.getViewSize(i),
data: view.toJSON(),
minimumSize: minimum(view.minimumBodySize),
maximumSize: maximum(view.maximumBodySize),
headerSize: view.headerSize,
expanded: view.isExpanded()
}, hidden ? { visible: false } : {});
}),
size: this.paneview.size
};
}
fromJSON(serializedPaneview) {
this.clear();
const { views, size } = serializedPaneview;
const queue = [];
const width = this.width;
const height = this.height;
this.paneview = new Paneview(this.element, {
orientation: "VERTICAL",
descriptor: {
size,
views: views.map((view) => {
var _header2, _view$headerSize, _view$minimumSize, _view$maximumSize;
const data = view.data;
const body = this.options.createComponent({
id: data.id,
name: data.component
});
let header;
if (data.headerComponent && this.options.createHeaderComponent) header = this.options.createHeaderComponent({
id: data.id,
name: data.headerComponent
});
(_header2 = header) !== null && _header2 !== void 0 || (header = new DefaultHeader());
const panel = new PaneFramework({
id: data.id,
component: data.component,
headerComponent: data.headerComponent,
header,
body,
orientation: "VERTICAL",
isExpanded: !!view.expanded,
disableDnd: !!this.options.disableDnd,
accessor: this,
headerSize: (_view$headerSize = view.headerSize) !== null && _view$headerSize !== void 0 ? _view$headerSize : HEADER_SIZE,
minimumBodySize: (_view$minimumSize = view.minimumSize) !== null && _view$minimumSize !== void 0 ? _view$minimumSize : MINIMUM_BODY_SIZE,
maximumBodySize: (_view$maximumSize = view.maximumSize) !== null && _view$maximumSize !== void 0 ? _view$maximumSize : MAXIMUM_BODY_SIZE
});
this.doAddPanel(panel);
queue.push(() => {
var _data$params;
panel.init({
params: (_data$params = data.params) !== null && _data$params !== void 0 ? _data$params : {},
minimumBodySize: view.minimumSize,
maximumBodySize: view.maximumSize,
title: data.title,
isExpanded: !!view.expanded,
containerApi: new PaneviewApi(this),
accessor: this
});
panel.orientation = this.paneview.orientation;
});
setTimeout(() => {
this._onDidAddView.fire(panel);
}, 0);
return {
size: view.size,
view: panel,
visible: view.visible
};
})
}
});
this.layout(width, height);
queue.forEach((f) => f());
this._onDidLayoutfromJSON.fire();
}
clear() {
for (const [_, value] of this._viewDisposables.entries()) value.dispose();
this._viewDisposables.clear();
this.paneview.dispose();
}
doAddPanel(panel) {
const disposable = new CompositeDisposable(panel.onDidDrop((event) => {
this._onDidDrop.fire(event);
}), panel.onUnhandledDragOver((event) => {
this._onUnhandledDragOver.fire(event);
}));
this._viewDisposables.set(panel.id, disposable);
}
doRemovePanel(panel) {
const disposable = this._viewDisposables.get(panel.id);
if (disposable) {
disposable.dispose();
this._viewDisposables.delete(panel.id);
}
}
dispose() {
super.dispose();
for (const [_, value] of this._viewDisposables.entries()) value.dispose();
this._viewDisposables.clear();
this.element.remove();
this.paneview.dispose();
}
};
var SplitviewPanel = class extends BasePanelView {
get priority() {
return this._priority;
}
set orientation(value) {
this._orientation = value;
}
get orientation() {
return this._orientation;
}
get minimumSize() {
const size = typeof this._minimumSize === "function" ? this._minimumSize() : this._minimumSize;
if (size !== this._evaluatedMinimumSize) {
this._evaluatedMinimumSize = size;
this.updateConstraints();
}
return size;
}
get maximumSize() {
const size = typeof this._maximumSize === "function" ? this._maximumSize() : this._maximumSize;
if (size !== this._evaluatedMaximumSize) {
this._evaluatedMaximumSize = size;
this.updateConstraints();
}
return size;
}
get snap() {
return this._snap;
}
constructor(id, componentName) {
super(id, componentName, new SplitviewPanelApiImpl(id, componentName));
this._evaluatedMinimumSize = 0;
this._evaluatedMaximumSize = Number.POSITIVE_INFINITY;
this._minimumSize = 0;
this._maximumSize = Number.POSITIVE_INFINITY;
this._snap = false;
this._onDidChange = new Emitter();
this.onDidChange = this._onDidChange.event;
this.api.initialize(this);
this.addDisposables(this._onDidChange, this.api.onWillVisibilityChange((event) => {
const { isVisible } = event;
const { accessor } = this._params;
accessor.setVisible(this, isVisible);
}), this.api.onActiveChange(() => {
const { accessor } = this._params;
accessor.setActive(this);
}), this.api.onDidConstraintsChangeInternal((event) => {
if (typeof event.minimumSize === "number" || typeof event.minimumSize === "function") this._minimumSize = event.minimumSize;
if (typeof event.maximumSize === "number" || typeof event.maximumSize === "function") this._maximumSize = event.maximumSize;
this.updateConstraints();
}), this.api.onDidSizeChange((event) => {
this._onDidChange.fire({ size: event.size });
}));
}
setVisible(isVisible) {
this.api._onDidVisibilityChange.fire({ isVisible });
}
setActive(isActive) {
this.api._onDidActiveChange.fire({ isActive });
}
layout(size, orthogonalSize) {
const [width, height] = this.orientation === "HORIZONTAL" ? [size, orthogonalSize] : [orthogonalSize, size];
super.layout(width, height);
}
init(parameters) {
super.init(parameters);
this._priority = parameters.priority;
if (parameters.minimumSize) this._minimumSize = parameters.minimumSize;
if (parameters.maximumSize) this._maximumSize = parameters.maximumSize;
if (parameters.snap) this._snap = parameters.snap;
}
toJSON() {
const maximum = (value) => value === Number.MAX_SAFE_INTEGER || value === Number.POSITIVE_INFINITY ? void 0 : value;
const minimum = (value) => value <= 0 ? void 0 : value;
return _objectSpread2(_objectSpread2({}, super.toJSON()), {}, {
minimumSize: minimum(this.minimumSize),
maximumSize: maximum(this.maximumSize)
});
}
updateConstraints() {
this.api._onDidConstraintsChange.fire({
maximumSize: this._evaluatedMaximumSize,
minimumSize: this._evaluatedMinimumSize
});
}
};
function createDockview(element, options) {
return new DockviewComponent(element, options).api;
}
function createSplitview(element, options) {
return new SplitviewApi(new SplitviewComponent(element, options));
}
function createGridview(element, options) {
return new GridviewApi(new GridviewComponent(element, options));
}
function createPaneview(element, options) {
return new PaneviewApi(new PaneviewComponent(element, options));
}
//#endregion
exports.AdvancedDnDModule = AdvancedDnDModule;
exports.AdvancedDnDService = AdvancedDnDService;
exports.BaseGrid = BaseGrid;
exports.ContentContainer = ContentContainer;
exports.DEFAULT_TAB_GROUP_COLORS = DEFAULT_TAB_GROUP_COLORS;
exports.DefaultDockviewDeserialzier = DefaultDockviewDeserialzier;
exports.DefaultTab = DefaultTab;
exports.DockviewApi = DockviewApi;
exports.DockviewComponent = DockviewComponent;
exports.DockviewCompositeDisposable = CompositeDisposable;
exports.DockviewDidDropEvent = DockviewDidDropEvent;
Object.defineProperty(exports, 'DockviewDisposable', {
enumerable: true,
get: function () {
return Disposable;
}
});
exports.DockviewEmitter = Emitter;
Object.defineProperty(exports, 'DockviewEvent', {
enumerable: true,
get: function () {
return Event;
}
});
exports.DockviewGroupPanel = DockviewGroupPanel;
exports.DockviewGroupPanelModel = DockviewGroupPanelModel;
exports.DockviewMutableDisposable = MutableDisposable;
exports.DockviewPanel = DockviewPanel;
exports.DockviewUnhandledDragOverEvent = DockviewUnhandledDragOverEvent;
exports.DockviewWillDropEvent = DockviewWillDropEvent;
exports.DockviewWillShowOverlayLocationEvent = DockviewWillShowOverlayLocationEvent;
exports.DraggablePaneviewPanel = DraggablePaneviewPanel;
exports.EdgeGroupModule = EdgeGroupModule;
exports.FloatingGroupModule = FloatingGroupModule;
exports.Gridview = Gridview;
exports.GridviewApi = GridviewApi;
exports.GridviewComponent = GridviewComponent;
exports.GridviewPanel = GridviewPanel;
exports.LayoutPriority = LayoutPriority;
exports.LiveRegionModule = LiveRegionModule;
exports.LocalSelectionTransfer = LocalSelectionTransfer;
exports.OVERFLOW_MAX_TAB_ROWS_VARIABLE = OVERFLOW_MAX_TAB_ROWS_VARIABLE;
exports.OVERFLOW_WRAP_TABS_CAPPED_CLASS = OVERFLOW_WRAP_TABS_CAPPED_CLASS;
exports.OVERFLOW_WRAP_TABS_CLASS = OVERFLOW_WRAP_TABS_CLASS;
exports.OVERFLOW_WRAP_TABS_VERTICAL_TAB_HEIGHT_VARIABLE = OVERFLOW_WRAP_TABS_VERTICAL_TAB_HEIGHT_VARIABLE;
exports.Orientation = Orientation;
exports.PROPERTY_KEYS_DOCKVIEW = PROPERTY_KEYS_DOCKVIEW;
exports.PROPERTY_KEYS_GRIDVIEW = PROPERTY_KEYS_GRIDVIEW;
exports.PROPERTY_KEYS_PANEVIEW = PROPERTY_KEYS_PANEVIEW;
exports.PROPERTY_KEYS_SPLITVIEW = PROPERTY_KEYS_SPLITVIEW;
exports.PaneFramework = PaneFramework;
exports.PaneTransfer = PaneTransfer;
exports.PanelTransfer = PanelTransfer;
exports.Paneview = Paneview;
exports.PaneviewApi = PaneviewApi;
exports.PaneviewComponent = PaneviewComponent;
exports.PaneviewPanel = PaneviewPanel;
exports.PaneviewUnhandledDragOverEvent = PaneviewUnhandledDragOverEvent;
exports.SashState = SashState;
Object.defineProperty(exports, 'Sizing', {
enumerable: true,
get: function () {
return Sizing;
}
});
exports.Splitview = Splitview;
exports.SplitviewApi = SplitviewApi;
exports.SplitviewComponent = SplitviewComponent;
exports.SplitviewPanel = SplitviewPanel;
exports.Tab = Tab;
exports.TabGroupChipsModule = TabGroupChipsModule;
exports.TabGroupChipsService = TabGroupChipsService;
exports.TabGroupColorPalette = TabGroupColorPalette;
exports.applyTabGroupAccent = applyTabGroupAccent;
exports.clearRegisteredModules = clearRegisteredModules;
exports.createCloseButton = createCloseButton;
exports.createDismissableLayer = createDismissableLayer;
exports.createDockview = createDockview;
exports.createGridview = createGridview;
exports.createPaneview = createPaneview;
exports.createPinButton = createPinButton;
exports.createSplitview = createSplitview;
exports.defineModule = defineModule;
exports.directionToPosition = directionToPosition;
exports.findRelativeZIndexParent = findRelativeZIndexParent;
exports.getDirectionOrientation = getDirectionOrientation;
exports.getGridLocation = getGridLocation;
exports.getLocationOrientation = getLocationOrientation;
exports.getPaneData = getPaneData;
exports.getPanelData = getPanelData;
exports.getRegisteredModules = getRegisteredModules;
exports.getRelativeLocation = getRelativeLocation;
exports.indexInParent = indexInParent;
exports.isAnyEdgeGroupEnabled = isAnyEdgeGroupEnabled;
exports.isEdgeGroupEnabled = isEdgeGroupEnabled;
exports.isGridBranchNode = isGridBranchNode;
exports.isGroupOptionsWithGroup = isGroupOptionsWithGroup;
exports.isGroupOptionsWithPanel = isGroupOptionsWithPanel;
exports.isPanelOptionsWithGroup = isPanelOptionsWithGroup;
exports.isPanelOptionsWithPanel = isPanelOptionsWithPanel;
exports.orthogonal = orthogonal;
exports.positionToDirection = positionToDirection;
exports.prefersReducedMotion = prefersReducedMotion;
exports.registerModules = registerModules;
exports.resolveMessages = resolveMessages;
exports.resolveOpaqueBackground = resolveOpaqueBackground;
exports.resolveTabGroupAccent = resolveTabGroupAccent;
exports.themeAbyss = themeAbyss;
exports.themeAbyssSpaced = themeAbyssSpaced;
exports.themeCatppuccinMocha = themeCatppuccinMocha;
exports.themeCatppuccinMochaSpaced = themeCatppuccinMochaSpaced;
exports.themeDark = themeDark;
exports.themeDracula = themeDracula;
exports.themeGithubDark = themeGithubDark;
exports.themeGithubDarkSpaced = themeGithubDarkSpaced;
exports.themeGithubLight = themeGithubLight;
exports.themeGithubLightSpaced = themeGithubLightSpaced;
exports.themeLight = themeLight;
exports.themeLightSpaced = themeLightSpaced;
exports.themeMonokai = themeMonokai;
exports.themeNord = themeNord;
exports.themeNordSpaced = themeNordSpaced;
exports.themeSolarizedLight = themeSolarizedLight;
exports.themeSolarizedLightSpaced = themeSolarizedLightSpaced;
exports.themeVisualStudio = themeVisualStudio;
exports.toTarget = toTarget;
});