@mochabug/adapt-web
Version:
Browser client library for Adapt automation platform
1 lines • 51.8 kB
JavaScript
import{SVG_POPOUT,SVG_WINDOW}from"./panel-setup.js";let iframeDragDepth=0;function disableIframePointEvents(){return iframeDragDepth++,document.documentElement.classList.add("mb-dragging"),{release(){--iframeDragDepth<=0&&(iframeDragDepth=0,document.documentElement.classList.remove("mb-dragging"))}}}class Panel{constructor(id,element,title){this.group=null;this.id=id,this.element=element,this.title=title||id,this.wrapper=document.createElement("div"),this.wrapper.className="mb-panel",this.wrapper.dataset.panelId=id,this.wrapper.dataset.active="false",this.wrapper.appendChild(element)}}let groupIdCounter=0;function syncGroupIdCounter(existingIds){for(let i=0;i<existingIds.length;i++){const m=existingIds[i].match(/^group-(\d+)$/);if(m){const n=parseInt(m[1],10);n>groupIdCounter&&(groupIdCounter=n)}}}class PanelGroup{constructor(id){this._panels=[];this._activePanel=null;this._headerHidden=!1;this._locked=!1;this._onTabClose=null;this._onTabPointerDown=null;this._onTabPopOut=null;this._location="grid";this._onLocationChangedCb=null;this._isInFloatingSplit=!1;this._onSplitStateChangedCb=null;this._dragConstraint="any";this._dockingBlocked=!1;this._dialogDockingBlocked=!1;this._onPanelCountChangedCb=null;this._onActivePanelChangedCb=null;this.id=id||`group-${++groupIdCounter}`,this.element=document.createElement("div"),this.element.className="mb-group",this.element.dataset.groupId=this.id,this.headerElement=document.createElement("div"),this.headerElement.className="mb-group-header",this.headerPrefix=document.createElement("div"),this.headerPrefix.className="mb-group-header-prefix",this.tabsElement=document.createElement("div"),this.tabsElement.className="mb-group-tabs",this.headerVoid=document.createElement("div"),this.headerVoid.className="mb-group-header-void",this.headerActions=document.createElement("div"),this.headerActions.className="mb-group-header-actions",this.headerElement.appendChild(this.headerPrefix),this.headerElement.appendChild(this.tabsElement),this.headerElement.appendChild(this.headerVoid),this.headerElement.appendChild(this.headerActions),this.contentElement=document.createElement("div"),this.contentElement.className="mb-group-content",this.element.appendChild(this.headerElement),this.element.appendChild(this.contentElement)}get panels(){return this._panels}get activePanel(){return this._activePanel}get size(){return this._panels.length}get locked(){return this._locked}set locked(value){this._locked=value}setHeaderHidden(hidden){this._headerHidden=hidden,this.headerElement.style.display=hidden?"none":""}get headerHidden(){return this._headerHidden}setHeaderActions(element){this.headerActions.innerHTML="",this.headerActions.appendChild(element)}setHeaderPrefix(element){this.headerPrefix.innerHTML="",this.headerPrefix.appendChild(element)}onTabClose(cb){this._onTabClose=cb}onTabPointerDown(cb){this._onTabPointerDown=cb}get location(){return this._location}set location(value){this._location!==value&&(this._location=value,this._onLocationChangedCb?.(value))}onLocationChanged(cb){this._onLocationChangedCb=cb}fireLocationChanged(){this._onLocationChangedCb?.(this._location)}get isInFloatingSplit(){return this._isInFloatingSplit}set isInFloatingSplit(value){this._isInFloatingSplit!==value&&(this._isInFloatingSplit=value,this._onSplitStateChangedCb?.(value))}onSplitStateChanged(cb){this._onSplitStateChangedCb=cb}onTabPopOut(cb){this._onTabPopOut=cb}get dragConstraint(){return this._dragConstraint}set dragConstraint(v){this._dragConstraint=v}get dockingBlocked(){return this._dockingBlocked}set dockingBlocked(v){this._dockingBlocked=v}get dialogDockingBlocked(){return this._dialogDockingBlocked}set dialogDockingBlocked(v){this._dialogDockingBlocked=v}onPanelCountChanged(cb){this._onPanelCountChangedCb=cb}onActivePanelChanged(cb){this._onActivePanelChangedCb=cb}addPanel(panel){if(this._panels.some(p=>p.id===panel.id))return;panel.group=this,this._panels.push(panel),this.contentElement.appendChild(panel.wrapper);const tab=this.createTab(panel);this.tabsElement.appendChild(tab),this.activatePanel(panel.id),this.element.dataset.tabCount=String(this._panels.length),this._onPanelCountChangedCb?.(this._panels.length)}removePanel(panelId){const idx=this._panels.findIndex(p=>p.id===panelId);if(idx===-1)return null;const panel=this._panels[idx];if(this._panels.splice(idx,1),panel.wrapper.remove(),this.tabsElement.querySelector(`[data-tab-id="${panelId}"]`)?.remove(),panel.group=null,this._activePanel===panel&&(this._activePanel=null,this._panels.length>0)){const newActive=this._panels[Math.min(idx,this._panels.length-1)];newActive&&this.activatePanel(newActive.id)}return this.element.dataset.tabCount=String(this._panels.length),this._onPanelCountChangedCb?.(this._panels.length),panel}detachPanel(panelId){const idx=this._panels.findIndex(p=>p.id===panelId);if(idx===-1)return null;const panel=this._panels[idx];if(this._panels.splice(idx,1),this.tabsElement.querySelector(`[data-tab-id="${panelId}"]`)?.remove(),panel.group=null,this._activePanel===panel&&(this._activePanel=null,this._panels.length>0)){const newActive=this._panels[Math.min(idx,this._panels.length-1)];newActive&&this.activatePanel(newActive.id)}return this.element.dataset.tabCount=String(this._panels.length),this._onPanelCountChangedCb?.(this._panels.length),panel}activatePanel(panelId){const panel=this._panels.find(p=>p.id===panelId);if(!panel)return;this._activePanel=panel;for(const p of this._panels)p.wrapper.dataset.active=p===panel?"true":"false";const tabs=this.tabsElement.querySelectorAll(".mb-tab");for(const tab of tabs){const isActive=tab.dataset.tabId===panelId;tab.dataset.active=isActive?"true":"false"}this._onActivePanelChangedCb?.(panelId)}reorderPanel(panelId,dropX){const panelIdx=this._panels.findIndex(p=>p.id===panelId);if(panelIdx===-1)return;const panel=this._panels[panelIdx],tabs=Array.from(this.tabsElement.querySelectorAll(".mb-tab"));let targetIdx=this._panels.length;for(let i=0;i<tabs.length;i++){const rect=tabs[i].getBoundingClientRect();if(dropX<rect.left+rect.width/2){targetIdx=i;break}}if(targetIdx===panelIdx||targetIdx===panelIdx+1)return;this._panels.splice(panelIdx,1);const insertAt=panelIdx<targetIdx?targetIdx-1:targetIdx;this._panels.splice(insertAt,0,panel);const tabEl=this.tabsElement.querySelector(`[data-tab-id="${panelId}"]`);if(tabEl){const refTab=this.tabsElement.querySelectorAll(".mb-tab")[insertAt];refTab&&refTab!==tabEl?this.tabsElement.insertBefore(tabEl,refTab):this.tabsElement.appendChild(tabEl)}}createTab(panel){const tab=document.createElement("div");tab.className="mb-tab",tab.dataset.tabId=panel.id,tab.dataset.active="false";const label=document.createElement("span");label.className="mb-tab-label",label.innerHTML=SVG_WINDOW,label.title=panel.title,tab.appendChild(label);const actions=document.createElement("div");actions.className="mb-tab-actions";const popOutBtn=document.createElement("button");popOutBtn.className="mb-tab-popout",popOutBtn.title="Pop out",popOutBtn.innerHTML=SVG_POPOUT,popOutBtn.addEventListener("click",e=>{e.stopPropagation(),this._onTabPopOut?.(panel.id)}),actions.appendChild(popOutBtn);const closeBtn=document.createElement("button");return closeBtn.className="mb-tab-close",closeBtn.title="Close",closeBtn.innerHTML='<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>',closeBtn.addEventListener("click",e=>{e.stopPropagation(),this._onTabClose?.(panel.id)}),actions.appendChild(closeBtn),tab.appendChild(actions),tab.addEventListener("pointerdown",e=>{e.target.closest(".mb-tab-close")||e.target.closest(".mb-tab-popout")||e.button===0&&(e.preventDefault(),this._onTabPointerDown?.(panel.id,e))}),tab}dispose(){for(const p of this._panels)p.group=null;this._panels=[],this._activePanel=null,this.element.remove()}}let zIndexCounter=1e5;class FloatingOverlay{constructor(group,opts,parentElement,insertBefore){this.internalLayout=null;this.toolbarElement=null;this.savedBounds=null;this._isMaximized=!1;this._onDragEnd=null;this._onDragMove=null;this._onBoundsChanged=null;this.group=group,this.bounds={...opts},this.element=document.createElement("div"),this.element.className="mb-floating-overlay",this.element.dataset.groupId=group.id,this.applyBounds();const directions=["n","s","e","w","ne","nw","se","sw"];for(const dir of directions){const handle=document.createElement("div");handle.className=`mb-resize-handle mb-resize-${dir}`,handle.addEventListener("pointerdown",e=>this.onResizeStart(e,dir)),this.element.appendChild(handle)}this.element.addEventListener("pointerdown",()=>this.bringToFront(),!0),group.headerVoid.addEventListener("pointerdown",e=>this.onDragStart(e)),group.headerVoid.style.cursor="grab",this.wireDragOnActions(group);const parent=parentElement??document.body;insertBefore?parent.insertBefore(this.element,insertBefore):parent.appendChild(this.element),this.element.appendChild(group.element),this.bringToFront()}get isMaximized(){return this._isMaximized}set onDragEnd(cb){this._onDragEnd=cb}set onDragMove(cb){this._onDragMove=cb}set onBoundsChanged(cb){this._onBoundsChanged=cb}bringToFront(){zIndexCounter+=2,this.element.style.zIndex=String(zIndexCounter)}maximize(){this._isMaximized||(this.savedBounds={...this.bounds},this._isMaximized=!0,this.element.style.inset="0",this.element.style.width="auto",this.element.style.height="auto",this.element.classList.add("mb-floating-maximized"))}restore(){!this._isMaximized||!this.savedBounds||(this._isMaximized=!1,this.bounds={...this.savedBounds},this.savedBounds=null,this.element.classList.remove("mb-floating-maximized"),this.element.style.inset="",this.applyBounds())}hide(){this.element.style.display="none"}show(){this.element.style.display=""}dispose(){this.internalLayout&&(this.internalLayout.dispose(),this.internalLayout=null),this.removeToolbar(),this.element.remove()}getBounds(){return{...this.bounds}}setBounds(partial){if(this._isMaximized)return;const pad=10;partial.width!==void 0&&(this.bounds.width=Math.min(partial.width,window.innerWidth-this.bounds.x-pad)),partial.height!==void 0&&(this.bounds.height=Math.min(partial.height,window.innerHeight-this.bounds.y-pad)),this.applyBounds()}setFullBounds(bounds){if(this._isMaximized)return;const vw=window.innerWidth,vh=window.innerHeight,pad=10,minVisible=100;this.bounds={width:Math.max(minVisible,Math.min(bounds.width,vw-pad)),height:Math.max(minVisible,Math.min(bounds.height,vh-pad)),x:Math.max(0,Math.min(bounds.x,vw-minVisible)),y:Math.max(0,Math.min(bounds.y,vh-minVisible))},this.applyBounds()}applyBounds(){this.element.style.position="fixed",this.element.style.left=`${this.bounds.x}px`,this.element.style.top=`${this.bounds.y}px`,this.element.style.width=`${this.bounds.width}px`,this.element.style.height=`${this.bounds.height}px`}setDragGroup(newGroup){newGroup.headerVoid.addEventListener("pointerdown",e=>this.onDragStart(e)),newGroup.headerVoid.style.cursor="grab",this.wireDragOnActions(newGroup)}wireDragOnActions(group){group.headerActions.addEventListener("pointerdown",e=>{e.target.closest("button")||this.onDragStart(e)}),group.headerActions.style.cursor="grab"}setToolbar(el){this.removeToolbar(),this.toolbarElement=el,this.getToolbarHost().headerActions.appendChild(el)}removeToolbar(){this.toolbarElement&&(this.toolbarElement.remove(),this.toolbarElement=null)}repositionToolbar(){this.toolbarElement&&this.getToolbarHost().headerActions.appendChild(this.toolbarElement)}getToolbarHost(){if(!this.internalLayout)return this.group;const root=this.internalLayout.getRoot();return root?FloatingOverlay.topRightGroup(root):this.group}static topRightGroup(node){return node.kind==="leaf"?node.group:node.direction==="horizontal"?FloatingOverlay.topRightGroup(node.second):FloatingOverlay.topRightGroup(node.first)}get hasToolbar(){return this.toolbarElement!==null}getAllGroups(){return this.internalLayout?.allGroups()??[this.group]}updateTotalTabCount(){const groups=this.getAllGroups();let total=0;for(const g of groups)total+=g.size;this.element.dataset.totalTabCount=String(total)}onDragStart(e){if(this._isMaximized)return;e.preventDefault();const handle=e.currentTarget,startX=e.clientX,startY=e.clientY,origLeft=this.bounds.x,origTop=this.bounds.y,iframes=disableIframePointEvents();handle.style.cursor="grabbing",handle.setPointerCapture(e.pointerId);const onMove=ev=>{const dx=ev.clientX-startX,dy=ev.clientY-startY;this.bounds.x=Math.max(0,Math.min(origLeft+dx,window.innerWidth-this.bounds.width)),this.bounds.y=Math.max(0,Math.min(origTop+dy,window.innerHeight-this.bounds.height)),this.applyBounds(),this._onDragMove?.(this.bounds.x+this.bounds.width/2,this.bounds.y+this.bounds.height/2)},onUp=()=>{iframes.release(),handle.style.cursor="grab",handle.removeEventListener("pointermove",onMove),handle.removeEventListener("pointerup",onUp),handle.removeEventListener("pointercancel",onUp),this._onDragEnd?.(this.bounds.x+this.bounds.width/2,this.bounds.y+this.bounds.height/2),this._onBoundsChanged?.()};handle.addEventListener("pointermove",onMove),handle.addEventListener("pointerup",onUp),handle.addEventListener("pointercancel",onUp)}onResizeStart(e,dir){if(this._isMaximized)return;e.preventDefault(),e.stopPropagation();const startX=e.clientX,startY=e.clientY,orig={...this.bounds},handle=e.currentTarget,minW=200,minH=100,iframes=disableIframePointEvents();handle.setPointerCapture(e.pointerId);const onMove=ev=>{const dx=ev.clientX-startX,dy=ev.clientY-startY;if(dir.includes("e")&&(this.bounds.width=Math.max(minW,orig.width+dx)),dir.includes("w")){const newW=Math.max(minW,orig.width-dx);this.bounds.x=orig.x+(orig.width-newW),this.bounds.width=newW}if(dir.includes("s")&&(this.bounds.height=Math.max(minH,orig.height+dy)),dir.includes("n")){const newH=Math.max(minH,orig.height-dy);this.bounds.y=orig.y+(orig.height-newH),this.bounds.height=newH}const pad=10;this.bounds.x=Math.max(0,this.bounds.x),this.bounds.y=Math.max(0,this.bounds.y),this.bounds.x+this.bounds.width>window.innerWidth-pad&&(this.bounds.width=window.innerWidth-this.bounds.x-pad),this.bounds.y+this.bounds.height>window.innerHeight-pad&&(this.bounds.height=window.innerHeight-this.bounds.y-pad),this.applyBounds()},onUp=()=>{iframes.release(),handle.removeEventListener("pointermove",onMove),handle.removeEventListener("pointerup",onUp),handle.removeEventListener("pointercancel",onUp),this._onBoundsChanged?.()};handle.addEventListener("pointermove",onMove),handle.addEventListener("pointerup",onUp),handle.addEventListener("pointercancel",onUp)}}class SplitLayout{constructor(container){this.root=null;this.onSplitResized=null;this.element=document.createElement("div"),this.element.className="mb-layout",container.appendChild(this.element)}getRoot(){return this.root}setMainGroup(group){const leaf=this.createLeaf(group);leaf.element.style.flex="1",this.root=leaf,this.element.appendChild(leaf.element)}splitGroup(targetGroup,newGroup,direction,newGroupSize){const splitDir=this.dockToSplitDirection(direction),leafNode=this.findGroupNode(targetGroup);if(!leafNode)return!1;const newLeafEl=document.createElement("div");newLeafEl.className="mb-split-leaf",newLeafEl.style.flex="1";const splitEl=document.createElement("div");splitEl.className=`mb-split-container ${splitDir==="horizontal"?"mb-split-horizontal":"mb-split-vertical"}`,splitEl.style.flex=leafNode.element.style.flex||"1",splitEl.style.minWidth="0",splitEl.style.minHeight="0";const separator=document.createElement("div"),isVerticalSplit=splitDir==="vertical";separator.className=`mb-layout-separator ${isVerticalSplit?"mb-layout-separator-h":"mb-layout-separator-v"}`;const existingFirst=direction==="right"||direction==="bottom";leafNode.element.parentElement.insertBefore(splitEl,leafNode.element),existingFirst?(splitEl.appendChild(leafNode.element),splitEl.appendChild(separator),splitEl.appendChild(newLeafEl)):(splitEl.appendChild(newLeafEl),splitEl.appendChild(separator),splitEl.appendChild(leafNode.element)),newLeafEl.appendChild(newGroup.element),newGroupSize!==void 0&&newGroupSize>0?existingFirst?(leafNode.element.style.flex="1",newLeafEl.style.flex=`0 0 ${newGroupSize}px`):(newLeafEl.style.flex=`0 0 ${newGroupSize}px`,leafNode.element.style.flex="1"):(leafNode.element.style.flex="1",newLeafEl.style.flex="1");const newLeaf={kind:"leaf",group:newGroup,element:newLeafEl},splitNode={kind:"split",direction:splitDir,first:existingFirst?leafNode:newLeaf,second:existingFirst?newLeaf:leafNode,element:splitEl,separator};return separator.addEventListener("pointerdown",e=>this.onSeparatorDrag(e,splitNode)),this.replaceNodeInTree(leafNode,splitNode),leafNode.element.style.display==="none"&&(separator.style.display="none",newLeafEl.dataset.savedFlex=newLeafEl.style.flex,newLeafEl.style.flex="1",leafNode.element.dataset.savedFlex=leafNode.element.style.flex),!0}removeGroup(group){const leafNode=this.findGroupNode(group);if(!leafNode)return;if(this.root===leafNode){leafNode.element.remove(),this.root=null;return}this.restoreMaximizedLeaf(group);const parentSplit=this.findParentSplit(leafNode);if(!parentSplit)return;const sibling=parentSplit.first===leafNode?parentSplit.second:parentSplit.first;this.restoreMaximizedSubtree(sibling),sibling.element.style.flex=parentSplit.element.style.flex||"1",parentSplit.element.parentElement.insertBefore(sibling.element,parentSplit.element),leafNode.element.remove(),parentSplit.separator.remove(),parentSplit.element.remove(),this.replaceNodeInTree(parentSplit,sibling)}findGroupNode(group){return this.findGroupNodeIn(this.root,group)}getGroupPosition(group){const leaf=this.findGroupNode(group);if(!leaf)return null;const parent=this.findParentSplit(leaf);if(!parent)return null;const isFirst=parent.first===leaf,siblingNode=isFirst?parent.second:parent.first,siblingGroup=this.firstGroupIn(siblingNode);if(!siblingGroup)return null;let direction;return parent.direction==="horizontal"?direction=isFirst?"left":"right":direction=isFirst?"top":"bottom",{siblingGroup,direction}}firstGroupIn(node){return node.kind==="leaf"?node.group:this.firstGroupIn(node.first)}collapseLeaf(group){const leaf=this.findGroupNode(group);if(!leaf)return;const parent=this.findParentSplit(leaf);if(parent){const sibling=parent.first===leaf?parent.second:parent.first;sibling.element.dataset.savedFlex=sibling.element.style.flex,sibling.element.style.flex="1",parent.separator.style.display="none"}leaf.element.dataset.savedFlex=leaf.element.style.flex,leaf.element.style.display="none"}expandLeaf(group){const leaf=this.findGroupNode(group);if(!leaf)return;leaf.element.style.display="",leaf.element.dataset.savedFlex!==void 0&&(leaf.element.style.flex=leaf.element.dataset.savedFlex,delete leaf.element.dataset.savedFlex);const parent=this.findParentSplit(leaf);if(parent){parent.separator.style.display="";const sibling=parent.first===leaf?parent.second:parent.first;sibling.element.dataset.savedFlex!==void 0&&(sibling.element.style.flex=sibling.element.dataset.savedFlex,delete sibling.element.dataset.savedFlex)}}maximizeLeaf(group){const leaf=this.findGroupNode(group);if(!leaf)return;let current=leaf,parent=this.findParentSplit(current);for(;parent;){const sibling=parent.first===current?parent.second:parent.first;sibling.element.dataset.maxSavedDisplay===void 0&&(sibling.element.dataset.maxSavedDisplay=sibling.element.style.display),sibling.element.style.display="none",parent.separator.dataset.maxSavedDisplay===void 0&&(parent.separator.dataset.maxSavedDisplay=parent.separator.style.display),parent.separator.style.display="none",current.element.dataset.maxSavedFlex===void 0&&(current.element.dataset.maxSavedFlex=current.element.style.flex),current.element.style.flex="1",current=parent,parent=this.findParentSplit(current)}}restoreMaximizedLeaf(group){const leaf=this.findGroupNode(group);if(!leaf)return;let current=leaf,parent=this.findParentSplit(current);for(;parent;){const sibling=parent.first===current?parent.second:parent.first;sibling.element.dataset.maxSavedDisplay!==void 0&&(sibling.element.style.display=sibling.element.dataset.maxSavedDisplay,delete sibling.element.dataset.maxSavedDisplay),parent.separator.dataset.maxSavedDisplay!==void 0&&(parent.separator.style.display=parent.separator.dataset.maxSavedDisplay,delete parent.separator.dataset.maxSavedDisplay),current.element.dataset.maxSavedFlex!==void 0&&(current.element.style.flex=current.element.dataset.maxSavedFlex,delete current.element.dataset.maxSavedFlex),current=parent,parent=this.findParentSplit(current)}}restoreMaximizedSubtree(node){node.element.dataset.maxSavedDisplay!==void 0&&(node.element.style.display=node.element.dataset.maxSavedDisplay,delete node.element.dataset.maxSavedDisplay),node.element.dataset.maxSavedFlex!==void 0&&(node.element.style.flex=node.element.dataset.maxSavedFlex,delete node.element.dataset.maxSavedFlex),node.kind==="split"&&(node.separator.dataset.maxSavedDisplay!==void 0&&(node.separator.style.display=node.separator.dataset.maxSavedDisplay,delete node.separator.dataset.maxSavedDisplay),this.restoreMaximizedSubtree(node.first),this.restoreMaximizedSubtree(node.second))}allGroups(){const result=[];return this.collectGroups(this.root,result),result}toJSON(){return this.serializeNode(this.root)}restoreFlexFromSerialized(serialized){!serialized||!this.root||this.restoreFlexParallel(serialized,this.root)}restoreFlexParallel(serialized,current){if(serialized.flex&&this.isValidFlex(serialized.flex)&&(current.element.style.flex=serialized.flex),serialized.kind==="split"&¤t.kind==="split")this.restoreFlexParallel(serialized.first,current.first),this.restoreFlexParallel(serialized.second,current.second);else if(serialized.kind!==current.kind){const flexMap=new Map;this.collectFlexValues(serialized,flexMap),this.applyFlexValues(current,flexMap)}}animateTransition(){const cls=node=>{node&&(node.kind==="leaf"?node.element.classList.add("mb-split-leaf--animating"):(node.element.classList.add("mb-split-container--animating"),node.separator&&node.separator.classList.add("mb-layout-separator--animating"),cls(node.first),cls(node.second)))};cls(this.root);const cleanup=()=>{this.element.querySelectorAll(".mb-split-leaf--animating, .mb-split-container--animating, .mb-layout-separator--animating").forEach(el=>{el.classList.remove("mb-split-leaf--animating","mb-split-container--animating","mb-layout-separator--animating")})},timer=setTimeout(cleanup,350);this.element.addEventListener("transitionend",()=>{clearTimeout(timer),cleanup()},{once:!0})}rebuildFromSerialized(serialized,groupLookup){const newRoot=this.buildNodeFromSerialized(serialized,groupLookup);newRoot&&(this.element.appendChild(newRoot.element),this.moveGroupsIntoTree(newRoot),this.root&&this.root.element.remove(),this.root=newRoot,this.root&&(this.root.element.style.flex="1"))}buildNodeFromSerialized(serialized,groupLookup){if(serialized.kind==="leaf"){const group=groupLookup(serialized.groupId);if(!group)return null;const leafEl=document.createElement("div");return leafEl.className="mb-split-leaf",serialized.flex&&this.isValidFlex(serialized.flex)?leafEl.style.flex=serialized.flex:leafEl.style.flex="1",{kind:"leaf",group,element:leafEl}}const first=this.buildNodeFromSerialized(serialized.first,groupLookup),second=this.buildNodeFromSerialized(serialized.second,groupLookup);if(!first&&!second)return null;if(!first)return serialized.flex&&this.isValidFlex(serialized.flex)&&(second.element.style.flex=serialized.flex),second;if(!second)return serialized.flex&&this.isValidFlex(serialized.flex)&&(first.element.style.flex=serialized.flex),first;const splitEl=document.createElement("div");splitEl.className=`mb-split-container ${serialized.direction==="horizontal"?"mb-split-horizontal":"mb-split-vertical"}`,serialized.flex&&this.isValidFlex(serialized.flex)?splitEl.style.flex=serialized.flex:splitEl.style.flex="1",splitEl.style.minWidth="0",splitEl.style.minHeight="0";const separator=document.createElement("div"),isVertical=serialized.direction==="vertical";separator.className=`mb-layout-separator ${isVertical?"mb-layout-separator-h":"mb-layout-separator-v"}`,splitEl.appendChild(first.element),splitEl.appendChild(separator),splitEl.appendChild(second.element);const splitNode={kind:"split",direction:serialized.direction,first,second,element:splitEl,separator};return separator.addEventListener("pointerdown",e=>this.onSeparatorDrag(e,splitNode)),splitNode}moveGroupsIntoTree(node){if(node.kind==="leaf"){node.element.appendChild(node.group.element);return}this.moveGroupsIntoTree(node.first),this.moveGroupsIntoTree(node.second)}dispose(){this.element.remove(),this.root=null}createLeaf(group){const leafEl=document.createElement("div");return leafEl.className="mb-split-leaf",leafEl.style.flex="1",leafEl.appendChild(group.element),{kind:"leaf",group,element:leafEl}}dockToSplitDirection(pos){return pos==="left"||pos==="right"?"horizontal":"vertical"}findGroupNodeIn(node,group){return node?node.kind==="leaf"?node.group===group?node:null:this.findGroupNodeIn(node.first,group)??this.findGroupNodeIn(node.second,group):null}collectGroups(node,result){node&&(node.kind==="leaf"?result.push(node.group):(this.collectGroups(node.first,result),this.collectGroups(node.second,result)))}findParentSplit(target){return this.findParentSplitIn(this.root,target)}findParentSplitIn(node,target){return!node||node.kind==="leaf"?null:node.first===target||node.second===target?node:this.findParentSplitIn(node.first,target)??this.findParentSplitIn(node.second,target)}replaceNodeInTree(oldNode,newNode){if(this.root===oldNode){this.root=newNode;return}const parent=this.findParentSplit(oldNode);parent&&(parent.first===oldNode?parent.first=newNode:parent.second=newNode)}collectFlexValues(node,map){node.kind==="leaf"?map.set(node.groupId,node.flex):(this.collectFlexValues(node.first,map),this.collectFlexValues(node.second,map))}applyFlexValues(node,map){if(node.kind==="leaf"){const saved=map.get(node.group.id);saved&&this.isValidFlex(saved)&&(node.element.style.flex=saved)}else this.applyFlexValues(node.first,map),this.applyFlexValues(node.second,map)}isValidFlex(value){if(value.includes("px"))return!0;const first=value.split(" ")[0],n=Number(first);return Number.isFinite(n)&&n>=0}serializeNode(node){if(!node)return null;const flex=node.element.dataset.maxSavedFlex||node.element.dataset.savedFlex||node.element.style.flex||"1";if(node.kind==="leaf")return node.group.size===0&&node.group.id!=="main-group"?null:{kind:"leaf",groupId:node.group.id,flex};const first=this.serializeNode(node.first),second=this.serializeNode(node.second);return!first&&!second?null:first?second?{kind:"split",direction:node.direction,first,second,flex}:(first.flex=flex,first):(second.flex=flex,second)}onSeparatorDrag(e,splitNode){if(e.preventDefault(),splitNode.first.element.style.display==="none"||splitNode.second.element.style.display==="none")return;const isHorizontal=splitNode.direction==="horizontal",startPos=isHorizontal?e.clientX:e.clientY,firstEl=splitNode.first.element,secondEl=splitNode.second.element;this.lockDescendantSizes(splitNode.first,isHorizontal),this.lockDescendantSizes(splitNode.second,isHorizontal);const origFirstSize=isHorizontal?firstEl.offsetWidth:firstEl.offsetHeight,origSecondSize=isHorizontal?secondEl.offsetWidth:secondEl.offsetHeight,totalSize=origFirstSize+origSecondSize;if(totalSize<=0)return;const minSize=150,iframes=disableIframePointEvents();splitNode.separator.setPointerCapture(e.pointerId),splitNode.separator.classList.add("mb-layout-separator--active");const onMove=ev=>{if(firstEl.style.display==="none"||secondEl.style.display==="none"){splitNode.separator.releasePointerCapture(ev.pointerId),onUp();return}const delta=(isHorizontal?ev.clientX:ev.clientY)-startPos;let newFirst=origFirstSize+delta,newSecond=origSecondSize-delta;newFirst<minSize&&(newFirst=minSize,newSecond=totalSize-minSize),newSecond<minSize&&(newSecond=minSize,newFirst=totalSize-minSize),firstEl.style.flex=String(newFirst/totalSize),secondEl.style.flex=String(newSecond/totalSize)},onUp=()=>{iframes.release(),splitNode.separator.classList.remove("mb-layout-separator--active"),splitNode.separator.removeEventListener("pointermove",onMove),splitNode.separator.removeEventListener("pointerup",onUp),splitNode.separator.removeEventListener("pointercancel",onUp),this.unlockDescendantSizes(splitNode.first,isHorizontal),this.unlockDescendantSizes(splitNode.second,isHorizontal),this.onSplitResized?.()};splitNode.separator.addEventListener("pointermove",onMove),splitNode.separator.addEventListener("pointerup",onUp),splitNode.separator.addEventListener("pointercancel",onUp)}lockDescendantSizes(node,isHorizontal){if(node.kind!=="split"||node.direction==="horizontal"!==isHorizontal)return;const firstSize=isHorizontal?node.first.element.getBoundingClientRect().width:node.first.element.getBoundingClientRect().height;node.first.element.style.flex=`0 0 ${firstSize}px`,node.second.element.style.flex="1",this.lockDescendantSizes(node.first,isHorizontal),this.lockDescendantSizes(node.second,isHorizontal)}unlockDescendantSizes(node,isHorizontal){if(node.kind!=="split"||node.direction==="horizontal"!==isHorizontal)return;this.unlockDescendantSizes(node.first,isHorizontal),this.unlockDescendantSizes(node.second,isHorizontal);const firstSize=isHorizontal?node.first.element.getBoundingClientRect().width:node.first.element.getBoundingClientRect().height,secondSize=isHorizontal?node.second.element.getBoundingClientRect().width:node.second.element.getBoundingClientRect().height,total=firstSize+secondSize;total>0?(node.first.element.style.flex=String(firstSize/total),node.second.element.style.flex=String(secondSize/total)):(node.first.element.style.flex="0.5",node.second.element.style.flex="0.5")}}class PanelManager{constructor(opts){this.groups=new Map;this.panels=new Map;this.floatingOverlays=new Map;this.mainGroupId=null;this.onPanelRemovedCbs=[];this.onGroupCreatedByDragCb=null;this.onLayoutChangedCb=null;this.disposed=!1;this.dragState=null;this.portals=new Map;this.portalRaf=0;this.mainGroupCollapsed=!1;this._beforeTabClose=null;this.zBase=0;this.container=opts.container,this.splitEdgeSize=opts.splitEdgeSize??100,this.layout=new SplitLayout(this.container),opts.onPanelRemoved&&this.onPanelRemovedCbs.push(opts.onPanelRemoved),this.onGroupCreatedByDragCb=opts.onGroupCreatedByDrag??null,this.onLayoutChangedCb=opts.onLayoutChanged??null,this.layout.onSplitResized=this.onLayoutChangedCb,this.zBase=this.getZBase(),this.zBase!==0&&(zIndexCounter=1e5+this.zBase),this.dropOverlay=document.createElement("div"),this.dropOverlay.className="mb-drop-overlay",this.container.appendChild(this.dropOverlay),this.portalLayer=document.createElement("div"),this.portalLayer.className="mb-iframe-portal-layer",this.container.appendChild(this.portalLayer),this.syncPortals()}getZBase(){const v=getComputedStyle(this.container).getPropertyValue("--mb-adapt-z-base").trim();return parseInt(v,10)||0}setBeforeTabClose(cb){this._beforeTabClose=cb}addPanel(opts){const panel=new Panel(opts.id,opts.element,opts.title);this.panels.set(opts.id,panel);const groupId=opts.groupId;if(groupId){const group=this.groups.get(groupId);group&&(group.addPanel(panel),this.registerPortal(panel.id,panel.element,panel.wrapper))}return panel}removePanel(panelId){const panel=this.panels.get(panelId);if(panel){if(this.unregisterPortal(panelId),panel.group){const group=panel.group;group.removePanel(panelId),group.size===0&&group.id!==this.mainGroupId&&this.removeGroup(group.id)}this.panels.delete(panelId);for(const cb of this.onPanelRemovedCbs)cb(panel)}}getPanel(panelId){return this.panels.get(panelId)}addGroup(opts){const group=new PanelGroup(opts?.id);if(this.groups.set(group.id,group),this.wireGroupCallbacks(group),opts?.floating)this.addFloatingGroup(group,opts.floating);else if(opts?.direction){const targetId=this.mainGroupId,target=targetId?this.groups.get(targetId):void 0;target&&this.layout.splitGroup(target,group,opts.direction,opts.initialSize)}return group}removeGroup(groupId){const group=this.groups.get(groupId);if(!group)return;for(const panel of[...group.panels])this.unregisterPortal(panel.id);const overlay=this.floatingOverlays.get(groupId);if(overlay)if(overlay.internalLayout){overlay.internalLayout.removeGroup(group),this.floatingOverlays.delete(groupId);const remaining=this.cleanupOrphanedFloatingLeaf(overlay);remaining.length>0?(overlay.group=remaining[0],this.floatingOverlays.set(remaining[0].id,overlay)):overlay.dispose()}else overlay.dispose(),this.floatingOverlays.delete(groupId);if(!overlay){const parentOverlay=this.findOverlayForGroup(group);if(parentOverlay?.internalLayout&&(parentOverlay.internalLayout.removeGroup(group),this.cleanupOrphanedFloatingLeaf(parentOverlay).length===0)){const ownerId=[...this.floatingOverlays.entries()].find(([,o])=>o===parentOverlay)?.[0];ownerId&&this.floatingOverlays.delete(ownerId),parentOverlay.dispose()}}this.layout.findGroupNode(group)&&this.layout.removeGroup(group),group.isInFloatingSplit=!1,group.dispose(),this.groups.delete(groupId)}getGroup(groupId){return this.groups.get(groupId)}getGroupIds(){return Array.from(this.groups.keys())}getMainGroup(){if(this.mainGroupId)return this.groups.get(this.mainGroupId)}setMainGroup(group){this.mainGroupId=group.id,this.layout.setMainGroup(group)}collapseMainGroup(){const main=this.getMainGroup();!main||this.mainGroupCollapsed||(this.layout.collapseLeaf(main),this.mainGroupCollapsed=!0)}expandMainGroup(){const main=this.getMainGroup();!main||!this.mainGroupCollapsed||(this.layout.expandLeaf(main),this.mainGroupCollapsed=!1)}isMainGroupCollapsed(){return this.mainGroupCollapsed}getDockedGroups(){return this.layout.allGroups()}collapseDockedGroup(group){this.layout.collapseLeaf(group)}expandDockedGroup(group){this.layout.expandLeaf(group)}maximizeDockedGroup(group){this.layout.maximizeLeaf(group)}restoreMaximizedDockedGroup(group){this.layout.restoreMaximizedLeaf(group)}restoreLayoutFlex(serialized){this.layout.restoreFlexFromSerialized(serialized)}rebuildDockedLayout(serialized){this.layout.rebuildFromSerialized(serialized,groupId=>this.groups.get(groupId))}rebuildFloatingInternalLayout(ownerGroupId,serialized){const overlay=this.floatingOverlays.get(ownerGroupId);if(!overlay)return;overlay.internalLayout||(overlay.internalLayout=new SplitLayout(overlay.element),overlay.internalLayout.setMainGroup(overlay.group),this.onLayoutChangedCb&&(overlay.internalLayout.onSplitResized=this.onLayoutChangedCb)),overlay.internalLayout.rebuildFromSerialized(serialized,groupId=>this.groups.get(groupId));const groups=overlay.internalLayout.allGroups(),isInSplit=groups.length>1;for(const g of groups)g.location="floating",g.isInFloatingSplit=isInSplit;overlay.updateTotalTabCount()}addFloatingGroup(group,opts){const existingOverlay=this.floatingOverlays.get(group.id);existingOverlay&&this.floatingOverlays.delete(group.id);const parentOverlay=existingOverlay?null:this.findOverlayForGroup(group),overlay=new FloatingOverlay(group,opts,this.container,this.portalLayer);if(this.onLayoutChangedCb&&(overlay.onBoundsChanged=this.onLayoutChangedCb),this.floatingOverlays.set(group.id,overlay),this.layout.findGroupNode(group)&&this.layout.removeGroup(group),existingOverlay?.internalLayout){existingOverlay.internalLayout.removeGroup(group);const remaining=this.cleanupOrphanedFloatingLeaf(existingOverlay);remaining.length>0?(existingOverlay.group=remaining[0],this.floatingOverlays.set(remaining[0].id,existingOverlay)):existingOverlay.dispose()}else existingOverlay?existingOverlay.dispose():parentOverlay?.internalLayout&&(parentOverlay.internalLayout.removeGroup(group),this.cleanupOrphanedFloatingLeaf(parentOverlay));return group.isInFloatingSplit=!1,group.location="floating",overlay.updateTotalTabCount(),overlay}dockGroup(groupId,position,initialSize){const group=this.groups.get(groupId);if(!group||groupId===this.mainGroupId)return;const mainGroup=this.mainGroupId?this.groups.get(this.mainGroupId):void 0;if(!mainGroup)return;const directOverlay=this.floatingOverlays.get(groupId),parentOverlay=directOverlay?null:this.findOverlayForGroup(group);if(this.layout.splitGroup(mainGroup,group,position,initialSize)){if(directOverlay)if(directOverlay.internalLayout){directOverlay.internalLayout.removeGroup(group),this.floatingOverlays.delete(groupId);const remaining=this.cleanupOrphanedFloatingLeaf(directOverlay);remaining.length>0?(directOverlay.group=remaining[0],this.floatingOverlays.set(remaining[0].id,directOverlay)):directOverlay.dispose()}else directOverlay.dispose(),this.floatingOverlays.delete(groupId);else parentOverlay?.internalLayout&&(parentOverlay.internalLayout.removeGroup(group),this.cleanupOrphanedFloatingLeaf(parentOverlay));group.location="grid",group.isInFloatingSplit=!1}}getFloatingOverlay(groupId){return this.floatingOverlays.get(groupId)}dockOverlay(groupId){const overlay=this.floatingOverlays.get(groupId);if(!overlay)return;const mainGroup=this.mainGroupId?this.groups.get(this.mainGroupId):void 0;if(!mainGroup)return;if(!overlay.internalLayout){this.dockGroup(groupId,"right");return}const allGroups=overlay.internalLayout.allGroups();if(allGroups.length===0)return;const first=allGroups[0];this.layout.splitGroup(mainGroup,first,"right"),first.location="grid",first.isInFloatingSplit=!1;for(let i=1;i<allGroups.length;i++){const g=allGroups[i];this.layout.splitGroup(first,g,"bottom"),g.location="grid",g.isInFloatingSplit=!1}overlay.internalLayout.dispose(),overlay.internalLayout=null,overlay.dispose(),this.floatingOverlays.delete(groupId)}findOverlayContaining(groupId){const group=this.groups.get(groupId);if(group)return this.findOverlayForGroup(group)??void 0}movePanel(panelId,targetGroupId){const panel=this.panels.get(panelId);if(!panel)return;const sourceGroup=panel.group;if(!sourceGroup||sourceGroup.id===targetGroupId)return;const targetGroup=this.groups.get(targetGroupId);targetGroup&&(sourceGroup.detachPanel(panelId),targetGroup.addPanel(panel),sourceGroup.size===0&&sourceGroup.id!==this.mainGroupId&&this.removeGroup(sourceGroup.id))}serializeLayout(){const floatingGroups=[];for(const[groupId,overlay]of this.floatingOverlays){if(overlay.getAllGroups().reduce((n,g)=>n+g.size,0)===0)continue;const entry={groupId,bounds:overlay.getBounds(),internalLayout:overlay.internalLayout?.toJSON()??null};overlay.isMaximized&&(entry.isMaximized=!0),floatingGroups.push(entry)}const panelLocations={};for(const[panelId,panel]of this.panels)panel.group&&(panelLocations[panelId]=panel.group.id);return{mainLayout:this.layout.toJSON(),floatingGroups,panelLocations}}getFloatingOverlays(){return this.floatingOverlays}cleanupEmptyGroups(){let cleaned=!1;for(const group of[...this.layout.allGroups()])group.id!==this.mainGroupId&&group.size===0&&(this.removeGroup(group.id),cleaned=!0);for(const[groupId,overlay]of[...this.floatingOverlays]){if(overlay.internalLayout)for(const group of[...overlay.internalLayout.allGroups()])group.size===0&&(this.removeGroup(group.id),cleaned=!0);const stillExists=this.floatingOverlays.get(groupId);stillExists&&stillExists.getAllGroups().reduce((n,g)=>n+g.size,0)===0&&(this.removeGroup(groupId),cleaned=!0)}cleaned&&this.layout.getRoot()&&(this.layout.getRoot().element.style.flex="1")}onDidRemovePanel(cb){return this.onPanelRemovedCbs.push(cb),{dispose:()=>{const idx=this.onPanelRemovedCbs.indexOf(cb);idx!==-1&&this.onPanelRemovedCbs.splice(idx,1)}}}dispose(){if(!this.disposed){this.disposed=!0,this.cancelTabDrag(),cancelAnimationFrame(this.portalRaf);for(const overlay of this.floatingOverlays.values())overlay.dispose();this.floatingOverlays.clear();for(const group of this.groups.values())group.dispose();this.groups.clear(),this.panels.clear(),this.onPanelRemovedCbs=[],this.dropOverlay.remove();for(const portal of this.portals.values())portal.entry.remove();this.portals.clear(),this.portalLayer.remove(),this.layout.dispose()}}wireGroupCallbacks(group){group.onTabClose(panelId=>{if(this._beforeTabClose){const result=this._beforeTabClose(panelId);result&&typeof result.then=="function"?result.then(allowed=>{allowed&&this.removePanel(panelId)}):result!==!1&&this.removePanel(panelId);return}this.removePanel(panelId)}),group.onTabPointerDown((panelId,e)=>{this.onTabPointerDown(panelId,group,e)}),group.onTabPopOut(panelId=>{this.detachPanelToFloating(panelId,window.innerWidth/2,window.innerHeight/2)})}onTabPointerDown(panelId,sourceGroup,e){if(this.dragState)return;this.dragState={panelId,sourceGroup,startX:e.clientX,startY:e.clientY,isDragging:!1,ghost:null,iframes:null,currentZone:null};const onMove=ev=>{if(!this.dragState)return;if(!this.dragState.isDragging){const dx=ev.clientX-this.dragState.startX,dy=ev.clientY-this.dragState.startY;if(dx*dx+dy*dy<25)return;this.dragState.isDragging=!0,this.dragState.iframes=disableIframePointEvents(),this.dragState.ghost=this.createDragGhost(panelId),this.container.appendChild(this.dragState.ghost)}this.dragState.ghost.style.left=`${ev.clientX+8}px`,this.dragState.ghost.style.top=`${ev.clientY+8}px`;let zone=this.hitTestDropZone(ev.clientX,ev.clientY);zone=this.applyDragConstraints(zone,this.dragState.sourceGroup),this.updateDropOverlay(zone)},onUp=ev=>{if(document.removeEventListener("pointermove",onMove),document.removeEventListener("pointerup",onUp),document.removeEventListener("pointercancel",onCancel),!this.dragState)return;if(!this.dragState.isDragging){sourceGroup.activatePanel(panelId),this.dragState=null;return}this.dragState.ghost?.remove(),this.dragState.iframes?.release(),this.updateDropOverlay(null);const zone=this.applyDragConstraints(this.hitTestDropZone(ev.clientX,ev.clientY),sourceGroup);if(zone)switch(zone.kind){case"header":case"content-center":zone.group.id===sourceGroup.id?sourceGroup.reorderPanel(panelId,ev.clientX):this.movePanel(panelId,zone.group.id);break;case"content-split":this.dockTabFromDrag(panelId,zone.direction,zone.group);break}this.cleanupEmptyGroups(),zone&&this.onLayoutChangedCb?.(),this.dragState=null},onCancel=()=>{document.removeEventListener("pointermove",onMove),document.removeEventListener("pointerup",onUp),document.removeEventListener("pointercancel",onCancel),this.cancelTabDrag()};document.addEventListener("pointermove",onMove),document.addEventListener("pointerup",onUp),document.addEventListener("pointercancel",onCancel)}cancelTabDrag(){this.dragState&&(this.dragState.ghost?.remove(),this.dragState.iframes?.release(),this.updateDropOverlay(null),this.dragState=null)}createDragGhost(panelId){const panel=this.panels.get(panelId),ghost=document.createElement("div");return ghost.className="mb-drag-ghost",ghost.innerHTML=SVG_WINDOW,ghost.title=panel?.title||panelId,ghost}hitTestDropZone(x,y,skipGroupId){const floatingEntries=[...this.floatingOverlays.entries()].filter(([,overlay])=>overlay.element.style.display!=="none").sort((a,b)=>{const zA=parseInt(a[1].element.style.zIndex||"0");return parseInt(b[1].element.style.zIndex||"0")-zA});for(const[,overlay]of floatingEntries){const overlayGroups=overlay.internalLayout?overlay.internalLayout.allGroups():[overlay.group];for(const group of overlayGroups){if(group.id===skipGroupId||group.locked==="no-drop-target")continue;if(!group.headerHidden){const hr=group.headerElement.getBoundingClientRect();if(x>=hr.left&&x<=hr.right&&y>=hr.top&&y<=hr.bottom)return{kind:"header",group}}const cr=group.contentElement.getBoundingClientRect();if(x>=cr.left&&x<=cr.right&&y>=cr.top&&y<=cr.bottom)return this.classifyContentZone(x,y,cr,group)}}for(const group of this.groups.values()){if(this.floatingOverlays.has(group.id)||this.findOverlayForGroup(group)||group.id===skipGroupId)continue;if(!group.headerHidden){const hr=group.headerElement.getBoundingClientRect();if(x>=hr.left&&x<=hr.right&&y>=hr.top&&y<=hr.bottom&&group.locked!=="no-drop-target")return{kind:"header",group}}const cr=group.contentElement.getBoundingClientRect();if(x>=cr.left&&x<=cr.right&&y>=cr.top&&y<=cr.bottom)return this.classifyContentZone(x,y,cr,group)}return null}classifyContentZone(x,y,rect,group){if(group.locked==="no-drop-target"){const dT2=y-rect.top,dB2=rect.bottom-y,dL2=x-rect.left,dR2=rect.right-x;let direction;return dT2<=dB2&&dT2<=dL2&&dT2<=dR2?direction="top":dB2<=dT2&&dB2<=dL2&&dB2<=dR2?direction="bottom":dL2<=dR2?direction="left":direction="right",{kind:"content-split",group,direction}}const edgeSize=this.splitEdgeSize,dT=y-rect.top,dB=rect.bottom-y,dL=x-rect.left,dR=rect.right-x;if(Math.min(dT,dB,dL,dR)<edgeSize){let direction;return dT<=dB&&dT<=dL&&dT<=dR?direction="top":dB<=dT&&dB<=dL&&dB<=dR?direction="bottom":dL<=dR?direction="left":direction="right",{kind:"content-split",group,direction}}return{kind:"content-center",group}}applyDragConstraints(zone,sourceGroup){if(!zone)return null;if(!("group"in zone))return zone;const targetInOverlay=this.findOverlayForGroup(zone.group)!==null,targetIsLocked=!!zone.group.locked;return(zone.kind==="content-center"||zone.kind==="header")&&targetIsLocked||sourceGroup.dockingBlocked&&!targetInOverlay?null:zone.kind==="content-split"&&targetInOverlay&&sourceGroup.dialogDockingBlocked?{kind:"content-center",group:zone.group}:zone}updateDropOverlay(zone){const prev=this.dragState?.currentZone??null;if(prev&&prev.kind==="header"&&prev.group.headerElement.classList.remove("mb-group-header--drop-target"),this.dropOverlay.style.display="none",zone===null){this.dragState&&(this.dragState.currentZone=null);return}switch(zone.kind){case"header":zone.group.headerElement.classList.add("mb-group-header--drop-target");break;case"content-center":{const rect=zone.group.contentElement.getBoundingClientRect();this.positionDropOverlay(rect.left,rect.top,rect.width,rect.height),this.dropOverlay.dataset.zone="center";break}case"content-split":this.showSplitIndicator(zone.group,zone.direction);break}this.dragState&&(this.dragState.currentZone=zone)}showSplitIndicator(group,direction){const rect=group.contentElement.getBoundingClientRect();switch(direction){case"top":this.positionDropOverlay(rect.left,rect.top,rect.width,rect.height/2);break;case"bottom":this.positionDropOverlay(rect.left,rect.top+rect.height/2,rect.width,rect.height/2);break;case"left":this.positionDropOverlay(rect.left,rect.top,rect.width/2,rect.height);break;case"right":this.positionDropOverlay(rect.left+rect.width/2,rect.top,rect.width/2,rect.height);break}this.dropOverlay.dataset.zone=direction}positionDropOverlay(left,top,width,height){const el=this.dropOverlay;el.style.display="block",el.style.left=`${left}px`,el.style.top=`${top}px`,el.style.width=`${width}px`,el.style.height=`${height}px`}detachPanelToFloating(panelId,_x,_y){const panel=this.panels.get(panelId);if(!panel||!panel.group||panel.group.id===this.mainGroupId)return;const fw=Math.min(600,window.innerWidth*.8),fh=Math.min(450,window.innerHeight*.7),newGroup=this.addGroup({floating:{x:(window.innerWidth-fw)/2,y:(window.innerHeight-fh)/2,width:fw,height:fh}});this.movePanel(panelId,newGroup.id),this.onGroupCreatedByDragCb?.(newGroup)}dockTabFromDrag(panelId,direction,targetGroup){const layoutForGroup=this.getLayoutForGroup(targetGroup);if(!layoutForGroup)return;const newGroup=new PanelGroup;if(this.groups.set(newGroup.id,newGroup),this.wireGroupCallbacks(newGroup),!layoutForGroup.splitGroup(targetGroup,newGroup,direction)){this.groups.delete(newGroup.id),newGroup.dispose(),this.movePanel(panelId,targetGroup.id);return}const overlayForTarget=this.findOverlayForGroup(targetGroup);overlayForTarget?.internalLayout&&(overlayForTarget.setDragGroup(newGroup),newGroup.location="floating",this.updateFloatingSplitState(overlayForTarget)),this.movePanel(panelId,newGroup.id),overlayForTarget&&overlayForTarget.updateTotalTabCount(),this.onGroupCreatedByDragCb?.(newGroup)}getLayoutForGroup(group){if(this.layout.findGroupNode(group))return this.layout;const overlay=this.findOverlayForGroup(group);return overlay?(overlay.internalLayout||(overlay.internalLayout=new SplitLayout(overlay.element),overlay.internalLayout.setMainGroup(overlay.group)),overlay.internalLayout):null}escapeGroupFromSplit(groupId){const group=this.groups.get(groupId);if(!group||!group.isInFloatingSplit)return;const overlay=this.findOverlayForGroup(group);if(!overlay?.internalLayout)return;const rect=overlay.element.getBoundingClientRect(),wasOwner=this.floatingOverlays.get(groupId)===overlay,newOverlay=new FloatingOverlay(group,{x:Math.min(rect.left+30,window.innerWidth-320),y:Math.min(rect.top+30,window.innerHeight-220),width:Math.max(300,rect.width*.6),height:Math.max(200,rect.height*.8)},this.container,this.portalLayer);this.floatingOverlays.set(groupId,newOverlay),overlay.internalLayout.removeGroup(group);const remaining=this.cleanupOrphanedFloatingLeaf(overlay);wasOwner&&remaining.length>0?(overlay.group=remaining[0],this.floatingOverlays.set(remaining[0].id,overlay)):remaining.length===0&&overlay.dispose(),group.isInFloatingSplit=!1,group.location==="floating"?group.fireLocationChanged():group.location="floating"}updateFloatingSplitState(overlay){if(!overlay.internalLayout)return;const groups=overlay.internalLayout.allGroups(),isInSplit=groups.length>1;for(const g of groups)g.location="floating",g.isInFloatingSplit=isInSplit;overlay.repositionToolbar()}cleanupOrphanedFloatingLeaf(overlay){if(!overlay.internalLayout)return[];const remaining=overlay.internalLayout.allGroups();return remaining.length<=1?(remaining.length===1&&(overlay.element.appendChild(remaining[0].element),remaining[0].isInFloatingSplit=!1),overlay.internalLayout.dispose(),overlay.internalLayout=null,remaining.length===1&&(overlay.setDragGroup(remaining[0]),remaining[0].location="floating",overlay.repositionToolbar())):this.updateFloatingSplitState(overlay),overlay.updateTotalTabCount(),remaining}minimizeDocked(group){const pos=this.layout.getGroupPosition(group);return this.layout.findGroupNode(group)&&this.layout.removeGroup(group),pos?{siblingGroupId:pos.siblingGroup.id,direction:pos.direction