@mochabug/adapt-web 1.0.1-rc.12 → 1.0.1-rc.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -1 +1 @@
1
- import{Code,configure,ConnectError,createAdaptClient,resetConfig}from"@mochabug/adapt-core";import{createConnectClient}from"@mochabug/adapt-core/connect";import{PanelManager}from"./panel-manager.js";import{FloatingToolbar,ForkGroupActions,IframePanelComponent,injectPanelManagerStyles,MinimizeManager}from"./panel-setup.js";import{AdaptAutomationElement}from"./AdaptAutomationElement.js";import{FloatingToolbar as FloatingToolbar2,ForkGroupActions as ForkGroupActions2,IframePanelComponent as IframePanelComponent2,MinimizeManager as MinimizeManager2}from"./panel-setup.js";import{PanelManager as PanelManager2}from"./panel-manager.js";function clearPersistedState(automationId,storage="both",persistKey){const key=`mb_adapt_${persistKey??automationId}`;(storage==="session"||storage==="both")&&sessionStorage.removeItem(key),(storage==="local"||storage==="both")&&localStorage.removeItem(key)}const MAIN_PANEL_ID="main",FORK_GROUP_ID="fork-group",DEFAULT_RESPONSIVE_BREAKPOINT=768,DEFAULT_RESPONSIVE_HYSTERESIS=40,_AdaptWebClient=class _AdaptWebClient{constructor(options){this.rootElement=null;this.panelManager=null;this.mainUrl=null;this.sessionToken=null;this.forks=new Map;this.mainRetentionPolicy="retain";this.mainCloseTimer=null;this.components=new Map;this.floatingAutoResized=new Set;this.beforeUnloadHandler=null;this.resizeObserver=null;this._autoMaximizedByResize=!1;this.destroyed=!1;this.removingPanelProgrammatically=!1;this.lastForkActive=null;this.messageHandler=null;this.minimizedTabsContainer=null;this.lastReportedContentHeight=0;this.forkPanelHeights=new Map;this.statusMessageElement=null;this.capWidgetInstance=null;this.minimizeManager=null;this.forkGroupActions=new Map;this.floatingToolbars=new Map;this.options=options;const fd=options.forkDisplay??{mode:"side-by-side"};this.forkDisplay=fd,this.darkMode=options.darkMode??!1,this.autoResizing=options.autoResizing??!1,this.allowFloating=options.allowFloating??!0,this.allowDocking=options.allowDocking??!0,this.allowDialogDocking=options.allowDialogDocking??!0,this.allowMinimize=options.allowMinimize??!0,this.allowMaximize=options.allowMaximize??!0,this.floatingAutoResize=options.floatingAutoResize??!1,this.confirmOnClose=options.confirmOnClose??!1;const container=typeof options.container=="string"?document.getElementById(options.container):options.container;if(!container)throw new Error(`Container with id '${options.container}' not found`);this.container=container;const rawClient=createConnectClient({id:options.automationId});this.client=createAdaptClient(rawClient,options.automationId),injectPanelManagerStyles(),this.createRootElement(),this.createPanelManager(),this.wireBeforeTabClose(),this.setupMessageListener(),this.init().catch(error=>{this.showErrorStatus(error)})}static registerCapWidget(cls){_AdaptWebClient._capWidgetClass=cls}get responsiveBreakpoint(){if(!this.rootElement)return DEFAULT_RESPONSIVE_BREAKPOINT;const v=getComputedStyle(this.rootElement).getPropertyValue("--mb-adapt-responsive-breakpoint").trim(),n=Number(v);return n>0?n:DEFAULT_RESPONSIVE_BREAKPOINT}get responsiveHysteresis(){if(!this.rootElement)return DEFAULT_RESPONSIVE_HYSTERESIS;const v=getComputedStyle(this.rootElement).getPropertyValue("--mb-adapt-responsive-hysteresis").trim(),n=Number(v);return n>=0?n:DEFAULT_RESPONSIVE_HYSTERESIS}onComponentCreated(panelId,component){this.components.set(panelId,component)}onComponentDisposed(panelId){this.components.delete(panelId)}getDarkMode(){return this.darkMode}getClassNames(){return this.options.classNames}createPanelManager(){if(!this.rootElement)return;this.minimizeManager=new MinimizeManager,this.minimizeManager.setAllowDocking(this.allowDocking),this.minimizedTabsContainer=document.createElement("div"),this.minimizedTabsContainer.className="mb-adapt-minimized-tabs",this.rootElement.appendChild(this.minimizedTabsContainer),this.panelManager=new PanelManager({container:this.rootElement,onGroupCreatedByDrag:group=>{group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group)}});const mainGroup=this.panelManager.addGroup({id:"main-group"});this.panelManager.setMainGroup(mainGroup),mainGroup.locked="no-drop-target",mainGroup.setHeaderHidden(!0),mainGroup.element.setAttribute("data-main-group","true");const mainComponent=new IframePanelComponent(this);mainComponent.init(MAIN_PANEL_ID,{}),this.panelManager.addPanel({id:MAIN_PANEL_ID,element:mainComponent.element,groupId:mainGroup.id}),this.minimizeManager.init(this.minimizedTabsContainer,this.panelManager),this.minimizeManager.onBarVisibilityChanged(()=>{this.reapplyAutoResizeHeight()}),this.resizeObserver=new ResizeObserver(entries=>{const cr=entries[0]?.contentRect;cr&&this.onContainerResize(cr.width,cr.height)}),this.resizeObserver.observe(this.rootElement)}getOrigin(url){try{return new URL(url).origin}catch{return null}}isValidMessageOrigin(origin){if(this.mainUrl){const mainOrigin=this.getOrigin(this.mainUrl);if(mainOrigin&&origin===mainOrigin)return!0}for(const fork of this.forks.values()){const forkOrigin=this.getOrigin(fork.url);if(forkOrigin&&origin===forkOrigin)return!0}return!1}findComponentBySource(source){for(const[panelId,component]of this.components)if(component.contentWindow===source)return{panelId,component};return null}setupMessageListener(){this.messageHandler=event=>{if(!this.isValidMessageOrigin(event.origin))return;const data=event.data;if(!data||typeof data!="object")return;const source=event.source,found=this.findComponentBySource(source);if(!found)return;const{panelId,component}=found,isMainPanel=panelId===MAIN_PANEL_ID;if(data.type==="adapt-init"&&typeof data.resize=="boolean"){component.resizeCapable=data.resize,isMainPanel&&!data.resize&&this.rootElement&&(this.rootElement.style.height="");const targetOrigin=component.getOrigin();if(targetOrigin){let effective;if(isMainPanel)effective=this.autoResizing&&data.resize;else{const group=this.panelManager?.getPanel(panelId)?.group,inFloating=group?!!this.panelManager?.findOverlayContaining(group.id):!1;effective=data.resize&&(inFloating&&this.floatingAutoResize||!inFloating&&this.autoResizing)}component.postMessage({type:"adapt-autoResizing",autoResizing:effective},targetOrigin),component.postMessage({type:"adapt-darkMode",darkMode:this.darkMode},targetOrigin)}return}if(data.type==="adapt-resize"){const{height}=data;if(typeof height!="number"||!Number.isFinite(height)||height<1)return;if(isMainPanel)this.lastReportedContentHeight=height,component.resizeCapable&&this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight();else{this.forkPanelHeights.set(panelId,height);const group=this.panelManager?.getPanel(panelId)?.group;if(group&&group.activePanel?.id===panelId){const inFloating=!!this.panelManager?.findOverlayContaining(group.id);!inFloating&&component.resizeCapable&&this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight(),inFloating&&this.floatingAutoResize&&(this.floatingAutoResized.has(group.id)||(this.floatingAutoResized.add(group.id),this.resizeOverlayForGroup(group)))}}return}},window.addEventListener("message",this.messageHandler)}postToComponent(component,message){const origin=component.getOrigin();origin&&component.postMessage(message,origin)}setDarkMode(darkMode){if(this.darkMode===darkMode)return;this.darkMode=darkMode,this.rootElement?.classList.toggle("mb-adapt--dark",darkMode),this.capWidgetInstance?.setDarkMode(darkMode);const msg={type:"adapt-darkMode",darkMode};for(const c of this.components.values())this.postToComponent(c,msg)}setAutoResizing(autoResizing){if(this.autoResizing===autoResizing)return;this.autoResizing=autoResizing;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent?.resizeCapable&&this.postToComponent(mainComponent,{type:"adapt-autoResizing",autoResizing});for(const[panelId,component]of this.components){if(panelId===MAIN_PANEL_ID||!component.resizeCapable)continue;const group=this.panelManager?.getPanel(panelId)?.group;group&&this.panelManager?.findOverlayContaining(group.id)||this.postToComponent(component,{type:"adapt-autoResizing",autoResizing})}!autoResizing&&this.rootElement&&(this.rootElement.style.height="")}setAllowFloating(allow){if(this.allowFloating!==allow){this.allowFloating=allow;for(const actions of this.forkGroupActions.values())actions.setAllowFloating(allow)}}setAllowDocking(allow){if(this.allowDocking!==allow){if(this.allowDocking=allow,this.minimizeManager?.setAllowDocking(allow),this.panelManager)for(const group of this.getAllForkGroups())group.dockingBlocked=!allow;for(const toolbar of this.floatingToolbars.values())toolbar.setAllowDocking(allow)}}setAllowDialogDocking(allow){if(this.allowDialogDocking!==allow&&(this.allowDialogDocking=allow,this.panelManager))for(const group of this.getAllForkGroups())group.dialogDockingBlocked=!allow}setAllowMinimize(allow){if(this.allowMinimize!==allow){this.allowMinimize=allow;for(const actions of this.forkGroupActions.values())actions.setAllowMinimize(allow);for(const toolbar of this.floatingToolbars.values())toolbar.setAllowMinimize(allow)}}setAllowMaximize(allow){if(this.allowMaximize!==allow){this.allowMaximize=allow;for(const actions of this.forkGroupActions.values())actions.setAllowMaximize(allow);for(const toolbar of this.floatingToolbars.values())toolbar.setAllowMaximize(allow)}}setFloatingAutoResize(enabled){if(this.floatingAutoResize!==enabled){this.floatingAutoResize=enabled,this.floatingAutoResized.clear();for(const[panelId,component]of this.components){if(panelId===MAIN_PANEL_ID||!component.resizeCapable)continue;const group=this.panelManager?.getPanel(panelId)?.group,effective=(group?!!this.panelManager?.findOverlayContaining(group.id):!1)?enabled&&component.resizeCapable:this.autoResizing&&component.resizeCapable;this.postToComponent(component,{type:"adapt-autoResizing",autoResizing:effective})}}}getAllForkGroups(){if(!this.panelManager)return[];const seen=new Set,groups=[],add=g=>{g.id==="main-group"||seen.has(g.id)||(seen.add(g.id),groups.push(g))},forkGroup=this.panelManager.getGroup(FORK_GROUP_ID);forkGroup&&add(forkGroup);for(const g of this.panelManager.getDockedGroups())add(g);for(const[,overlay]of this.panelManager.getFloatingOverlays())for(const g of overlay.getAllGroups())add(g);return groups}applyAutoResizeHeight(contentHeight){if(!this.rootElement)return;const chrome=this.minimizedTabsContainer?.offsetHeight??0;this.rootElement.style.height=`${Math.ceil(contentHeight)+chrome}px`}applyBestAutoResizeHeight(){if(!this.autoResizing||!this.rootElement)return;let maxHeight=0;if(this.components.get(MAIN_PANEL_ID)?.resizeCapable&&this.lastReportedContentHeight>0){const mainGroup=this.panelManager?.getMainGroup();if(mainGroup?mainGroup.element.offsetHeight>0:!0){const headerH=mainGroup?.headerElement?.offsetHeight??0;maxHeight=this.lastReportedContentHeight+headerH}}for(const group of this.getAllForkGroups()){if(this.panelManager?.findOverlayContaining(group.id)||group.element.offsetHeight<=0)continue;const active=group.activePanel;if(active){const h=this.forkPanelHeights.get(active.id)??0,headerH=group.headerElement?.offsetHeight??0,total=h+headerH;total>maxHeight&&(maxHeight=total)}}maxHeight>0&&this.applyAutoResizeHeight(maxHeight)}reapplyAutoResizeHeight(){this.applyBestAutoResizeHeight()}setForkDisplayMode(mode){this.forkDisplay.mode!==mode&&(mode==="side-by-side"?this.forkDisplay={mode:"side-by-side"}:this.forkDisplay={mode:"dialog"},this.forks.size>0&&this.panelManager&&this.rebuildForkGroup(),this.emitForkActive(),this.saveState())}createRootElement(){this.rootElement=document.createElement("div"),this.rootElement.className=this.options.classNames?.root||"mb-adapt",this.darkMode&&this.rootElement.classList.add("mb-adapt--dark"),this.options.styles&&Object.assign(this.rootElement.style,this.options.styles),this.container.appendChild(this.rootElement)}onUrl(msg){if(this.destroyed)return;const fork=msg.fork||"";if(msg.stopped){this.handleStoppedMessage(fork),this.saveState();return}if(msg.done){this.handleDoneMessage(fork,msg.retentionPolicy),this.saveState();return}msg.url&&(fork?this.handleForkUrl(msg.url,msg.token,fork,msg.source,msg.retentionPolicy):this.handleMainUrl(msg.url,msg.token,msg.source,msg.retentionPolicy),this.saveState())}handleMainUrl(url,token,_source,retentionPolicy){this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null);const wasMainEmpty=!this.mainUrl;if(this.removeStatusMessage(),this.mainUrl=url,this.mainToken=token,this.mainRetentionPolicy=this.resolveRetention(retentionPolicy),this.panelManager?.isMainGroupCollapsed()&&this.panelManager.expandMainGroup(),wasMainEmpty){const maximizedGroupId=this.getMaximizedDockedGroupId();if(maximizedGroupId){const actions=this.forkGroupActions.get(maximizedGroupId);actions&&actions.clearMaximized()}}const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&(mainComponent.update({url,token}),mainComponent.show())}handleForkUrl(url,token,fork,source,retentionPolicy){if(!this.panelManager)return;const resolved=this.resolveRetention(retentionPolicy),existing=this.forks.get(fork);if(existing){existing.closeTimer&&(clearTimeout(existing.closeTimer),existing.closeTimer=null),existing.url=url,existing.token=token,existing.source=source,existing.retentionPolicy=resolved,existing.completed=!1,existing.recyclable=!1;const component2=this.components.get(existing.panelId);component2&&component2.update({url,token});return}const recyclable=this.findRecyclablePanel(source);if(recyclable){const{forkKey:oldFork,state:rState}=recyclable;rState.closeTimer&&(clearTimeout(rState.closeTimer),rState.closeTimer=null),this.forks.delete(oldFork),rState.url=url,rState.token=token,rState.fork=fork,rState.source=source,rState.retentionPolicy=resolved,rState.completed=!1,rState.recyclable=!1,this.forks.set(fork,rState);const component2=this.components.get(rState.panelId);component2&&component2.update({url,token});return}const panelId=`fork-${fork}`;this.ensureForkGroup(),!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup();const component=new IframePanelComponent(this);component.init(panelId,{url,token}),this.panelManager.addPanel({id:panelId,element:component.element,groupId:FORK_GROUP_ID}),this.forks.set(fork,{panelId,url,token,fork,source,completed:!1,recyclable:!1,retentionPolicy:resolved,closeTimer:null}),this.emitForkActive()}ensureForkGroup(){if(!this.panelManager||this.panelManager.getGroup(FORK_GROUP_ID))return;const cw=this.rootElement?.offsetWidth??800,ch=this.rootElement?.offsetHeight??600;if(this.forkDisplay.mode==="dialog"){const isSmall=cw<this.responsiveBreakpoint,vw=window.innerWidth,vh=window.innerHeight,fw=isSmall?Math.min(cw*.95,Math.max(200,vw-20)):Math.min(720,cw*.9),fh=isSmall?Math.min(ch*.9,Math.max(200,vh-20)):Math.min(ch*.8,600),group=this.panelManager.addGroup({id:FORK_GROUP_ID,floating:{width:fw,height:fh,x:Math.max(0,(vw-fw)/2),y:Math.max(0,(vh-fh)/2)}});group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group),isSmall&&(this.panelManager.getFloatingOverlay(FORK_GROUP_ID)?.maximize(),this._autoMaximizedByResize=!0)}else{const split=this.forkDisplay.mode==="side-by-side"?this.forkDisplay.split:void 0,initialWidth=split!==void 0?cw*(1-split/100):void 0,group=this.panelManager.addGroup({id:FORK_GROUP_ID,direction:"right",initialSize:initialWidth});if(group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group),cw<this.responsiveBreakpoint){const fa=this.forkGroupActions.get(group.id);fa?(fa.setMaximized(),fa.setAutoMaximizedByResize(!0)):this.panelManager.maximizeDockedGroup(group),this._autoMaximizedByResize=!0}}}setupForkGroupActions(group){if(!this.panelManager||!this.minimizeManager)return;const actions=new ForkGroupActions(group,this.minimizeManager,this.panelManager,()=>{if(this.setupFloatingToolbar(group),group.location==="grid"&&!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup(),group.location==="floating"&&this.floatingAutoResized.delete(group.id),this.updateAutoResizeForGroup(group),group.location==="grid"){const currentWidth=this.rootElement?.offsetWidth??0;if(currentWidth>0&&currentWidth<this.responsiveBreakpoint){const fa=this.forkGroupActions.get(group.id);fa&&!fa.isMaximized&&(fa.setMaximized(),this._autoMaximizedByResize=!0,fa.setAutoMaximizedByResize(!0))}}},{allowFloating:this.allowFloating,allowMinimize:this.allowMinimize,allowMaximize:this.allowMaximize});actions.setBeforeClose(()=>this.confirmCloseGroup(group)),actions.onMaximizeChanged(maximized=>{maximized||(this._autoMaximizedByResize=!1,actions.setAutoMaximizedByResize(!1))}),group.setHeaderActions(actions.element),this.forkGroupActions.set(group.id,actions),group.onActivePanelChanged(()=>{this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight()}),this.setupFloatingToolbar(group)}updateAutoResizeForGroup(group){const inFloating=this.panelManager?!!this.panelManager.findOverlayContaining(group.id):!1;for(const panel of group.panels){const component=this.components.get(panel.id);if(!component?.resizeCapable)continue;const effective=inFloating?this.floatingAutoResize:this.autoResizing;this.postToComponent(component,{type:"adapt-autoResizing",autoResizing:effective})}}setupFloatingToolbar(group){if(!this.panelManager||!this.minimizeManager)return;const overlay=this.panelManager.findOverlayContaining(group.id);if(overlay)if(group.onPanelCountChanged(()=>overlay.updateTotalTabCount()),overlay.hasToolbar)overlay.repositionToolbar();else{const toolbar=new FloatingToolbar(overlay,this.panelManager,this.minimizeManager,{allowDocking:this.allowDocking,allowMinimize:this.allowMinimize,allowMaximize:this.allowMaximize});toolbar.setBeforeClose(()=>this.confirmCloseGroup(overlay.group)),overlay.setToolbar(toolbar.element),this.floatingToolbars.set(overlay.group.id,toolbar),overlay.updateTotalTabCount()}}resolveRetention(policy){return policy?policy.case:"retain"}findRecyclablePanel(source){let anyRecyclable=null;for(const[forkKey,state]of this.forks)if(state.recyclable){if(source&&state.source===source)return{forkKey,state};anyRecyclable||(anyRecyclable={forkKey,state})}return anyRecyclable}handleMainDone(retentionPolicy){switch(retentionPolicy?this.resolveRetention(retentionPolicy):this.mainRetentionPolicy){case"close":this.scheduleMainClose(retentionPolicy?.case==="close"?retentionPolicy.timeoutSeconds:void 0);break;case"recycle":break;default:break}}scheduleForkClose(fork,state,timeoutSeconds){const doClose=()=>{state.closeTimer=null,this.removingPanelProgrammatically=!0;try{if(this.panelManager){const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.delete(fork),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}this.emitForkActive(),this.saveState()};timeoutSeconds&&timeoutSeconds>0?state.closeTimer=setTimeout(doClose,timeoutSeconds*1e3):doClose()}scheduleMainClose(timeoutSeconds){const doClose=()=>{this.mainCloseTimer=null,this.mainUrl=null,this.mainToken=void 0;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup(),this.saveState()};timeoutSeconds&&timeoutSeconds>0?this.mainCloseTimer=setTimeout(doClose,timeoutSeconds*1e3):doClose()}restoreFork(pf){if(!this.panelManager)return;const panelId=`fork-${pf.fork}`;this.ensureForkGroup(),!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup();const component=new IframePanelComponent(this);component.init(panelId,{url:pf.url,token:pf.token}),this.panelManager.addPanel({id:panelId,element:component.element,groupId:FORK_GROUP_ID}),this.forks.set(pf.fork,{panelId,url:pf.url,token:pf.token,fork:pf.fork,source:pf.source,completed:!1,recyclable:pf.retentionPolicy==="recycle",retentionPolicy:pf.retentionPolicy,closeTimer:null}),this.emitForkActive()}clearAllForks(){this.removingPanelProgrammatically=!0;try{if(this.panelManager)for(const state of this.forks.values()){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.clear(),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}}rebuildForkGroup(){if(!this.panelManager)return;const group=this.panelManager.getGroup(FORK_GROUP_ID);if(group)if(this.forkDisplay.mode==="dialog"){const cw=this.rootElement?.offsetWidth??800,ch=this.rootElement?.offsetHeight??600,fw=Math.min(720,cw*.9),fh=Math.min(ch*.8,600);this.panelManager.addFloatingGroup(group,{width:fw,height:fh,x:(window.innerWidth-fw)/2,y:(window.innerHeight-fh)/2}),this.setupFloatingToolbar(group)}else{const split=this.forkDisplay.mode==="side-by-side"?this.forkDisplay.split:void 0,initialWidth=split!==void 0?(this.rootElement?.offsetWidth??800)*(1-split/100):void 0;this.panelManager.dockGroup(FORK_GROUP_ID,"right",initialWidth)}}handleSessionComplete(){this.clearAllForks(),this.emitForkActive()}handleDoneMessage(fork,retentionPolicy){if(!fork){this.handleMainDone(retentionPolicy);return}const state=this.forks.get(fork);if(!state)return;switch(retentionPolicy?this.resolveRetention(retentionPolicy):state.retentionPolicy){case"close":state.completed=!0,this.scheduleForkClose(fork,state,retentionPolicy?.case==="close"?retentionPolicy.timeoutSeconds:void 0);break;case"recycle":state.completed=!0,state.recyclable=!0;break;default:state.completed=!0;break}}handleStoppedMessage(fork){if(!fork){this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null),this.mainUrl=null,this.mainToken=void 0;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.showStatusMessage(this.options.text?.stopped??"This session has been stopped","stopped");return}const state=this.forks.get(fork);if(state){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null),this.removingPanelProgrammatically=!0;try{if(this.panelManager){const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.delete(fork),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}this.emitForkActive()}}removeForkGroupIfEmpty(){if(!this.panelManager)return;const g=this.panelManager.getGroup(FORK_GROUP_ID);g&&g.size===0&&(this.panelManager.removeGroup(FORK_GROUP_ID),this._autoMaximizedByResize=!1,this.forkGroupActions.delete(FORK_GROUP_ID),this.floatingToolbars.delete(FORK_GROUP_ID),this.floatingAutoResized.delete(FORK_GROUP_ID)),this.minimizeManager?.removeGroup(FORK_GROUP_ID)}getMaximizedDockedGroupId(){for(const[groupId,actions]of this.forkGroupActions)if(actions.isMaximized)return groupId}emitForkActive(){if(!this.options.onForkActive)return;const active=this.forks.size>0;active!==this.lastForkActive&&(this.lastForkActive=active,this.options.onForkActive(active))}parseInheritToken(){const inheritFrom=this.options.inheritFrom;return inheritFrom?"hash"in inheritFrom?this.parseFromHash(inheritFrom.hash):this.parseFromParam(inheritFrom.param):null}parseFromHash(key){const hash=window.location.hash.slice(1);if(!hash)return null;const params=new URLSearchParams(hash),token=params.get(key);if(!token)return null;params.delete(key);const newHash=params.toString(),newUrl=window.location.pathname+window.location.search+(newHash?"#"+newHash:"");return history.replaceState(null,"",newUrl),decodeURIComponent(token)}parseFromParam(key){const params=new URLSearchParams(window.location.search),token=params.get(key);if(!token)return null;params.delete(key);const newSearch=params.toString(),newUrl=window.location.pathname+(newSearch?"?"+newSearch:"")+window.location.hash;return history.replaceState(null,"",newUrl),decodeURIComponent(token)}async init(){this.panelManager?.onDidRemovePanel(panel=>{if(!this.removingPanelProgrammatically){for(const[fork,state]of this.forks)if(state.panelId===panel.id){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);const component=this.components.get(state.panelId);component&&component.dispose(),this.sessionToken&&this.client.stop(this.sessionToken,fork).catch(()=>{}),this.forks.delete(fork);break}this.emitForkActive()}});const handlers={onUrl:msg=>this.onUrl(msg),onSession:sessionJson=>{sessionJson.status==="STATUS_COMPLETED"&&!sessionJson.fork&&this.handleSessionComplete(),!sessionJson.fork&&(sessionJson.status==="STATUS_COMPLETED"||sessionJson.status==="STATUS_STOPPED"||sessionJson.status==="STATUS_ERRORED")&&(this.clearState(),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)),this.options.onSession&&this.options.onSession(sessionJson.status||"STATUS_UNSPECIFIED",sessionJson.fork)}};if(this.options.onOutput&&(handlers.onOutput=this.options.onOutput),handlers.onError=error=>{this.showErrorStatus(error),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)},!this.options.sessionToken&&this.resolvePersistOptions()&&await this.tryRestoreSession(handlers)!=="none"){this.updateBeforeUnloadGuard();return}if(this.options.sessionToken)this.sessionToken=this.options.sessionToken,await this.client.subscribe(this.sessionToken,handlers);else{const urlToken=this.parseInheritToken(),inheritToken=urlToken||this.options.inheritToken;if(this.options.inheritFrom&&!urlToken){const source="hash"in this.options.inheritFrom?`hash key '${this.options.inheritFrom.hash}'`:`param '${this.options.inheritFrom.param}'`;console.error(`[AdaptWebClient] No inherit token found in URL ${source}`);return}let challengeToken=this.options.challengeToken;if(this.options.requiresChallenge&&!challengeToken&&!inheritToken&&(challengeToken=await this.showCapWidgetAndSolve(),!challengeToken)){console.error("[AdaptWebClient] Failed to solve challenge");return}let result;if(inheritToken)result=await this.client.runInherit(inheritToken,handlers);else{const runOptions={...handlers};this.options.transmitter!==void 0&&(runOptions.transmitter=this.options.transmitter),this.options.signals!==void 0&&(runOptions.signals=this.options.signals),this.options.authToken!==void 0&&(runOptions.authToken=this.options.authToken),challengeToken!==void 0&&(runOptions.challengeToken=challengeToken),result=await this.client.run(runOptions)}this.sessionToken=result.sessionToken,this.sessionExpiresAt=result.expiresAt,this.saveState()}this.updateBeforeUnloadGuard()}async showCapWidgetAndSolve(){const CapWidgetClass=_AdaptWebClient._capWidgetClass;if(!CapWidgetClass)throw new Error("Cap.js not loaded. Import '@mochabug/adapt-web/cap' or use the full UMD bundle.");return new Promise(resolve=>{this.capWidgetInstance=new CapWidgetClass({container:this.rootElement,automationId:this.options.automationId,client:this.client.raw,...this.options.capWidgetOptions?.workerCount!==void 0&&{workerCount:this.options.capWidgetOptions.workerCount},...this.options.capWidgetOptions?.i18n&&{i18n:this.options.capWidgetOptions.i18n},onSolve:token=>{this.capWidgetInstance=null,resolve(token)},onError:error=>{console.error("[AdaptWebClient] Cap.js error:",error),this.capWidgetInstance=null,resolve(void 0)}}),this.darkMode&&this.capWidgetInstance.setDarkMode(!0)})}resolvePersistOptions(){const p=this.options.persist;if(!p)return null;if(p===!0)return{storage:"session",ttl:3600};const result={storage:p.storage??"session",ttl:p.ttl??3600};return p.key!==void 0&&(result.key=p.key),result}getStorageKey(){return`mb_adapt_${this.resolvePersistOptions()?.key??this.options.automationId}`}getStorage(){const opts=this.resolvePersistOptions();return opts?opts.storage==="local"?localStorage:sessionStorage:null}saveState(){const storage=this.getStorage();if(!storage||!this.sessionToken)return;const opts=this.resolvePersistOptions(),state={v:3,token:this.sessionToken,expiresAt:this.sessionExpiresAt?this.sessionExpiresAt.toISOString():null,savedAt:Date.now(),ttl:opts.ttl,mainUrl:this.mainUrl,mainToken:this.mainToken,forks:Array.from(this.forks.values()).filter(f=>!f.completed||f.recyclable).map(f=>{const pf={url:f.url,token:f.token,fork:f.fork,retentionPolicy:f.retentionPolicy};return f.source!==void 0&&(pf.source=f.source),pf}),forkDisplayMode:this.forkDisplay.mode,layout:this.panelManager?.serializeLayout()};if(this.minimizeManager){const minimizedIds=this.minimizeManager.getMinimizedGroupIds();minimizedIds.length>0&&(state.minimizedGroups=minimizedIds.map(gid=>{const entry=this.minimizeManager.getEntry(gid),persisted={groupId:gid,mode:entry?.mode??"floating"};return entry?.savedPosition&&(persisted.savedPosition={...entry.savedPosition}),persisted}))}const maxDockedId=this.getMaximizedDockedGroupId();maxDockedId&&(state.maximizedDockedGroupId=maxDockedId),storage.setItem(this.getStorageKey(),JSON.stringify(state))}loadState(){const storage=this.getStorage();if(!storage)return null;const raw=storage.getItem(this.getStorageKey());if(!raw)return null;let state;try{state=JSON.parse(raw)}catch{return this.clearState(),null}if(state.v!==3)return this.clearState(),null;const now=Date.now(),leeway=3e4;if(now-state.savedAt>state.ttl*1e3+leeway)return this.clearState(),null;if(state.expiresAt){const expires=new Date(state.expiresAt).getTime();if(now>expires+leeway)return this.clearState(),null}return state}clearState(){const storage=this.getStorage();storage&&storage.removeItem(this.getStorageKey())}async tryRestoreSession(handlers){const state=this.loadState();if(!state)return"none";const isStale=Date.now()-state.savedAt>1e4;if(this.sessionToken=state.token,this.sessionExpiresAt=state.expiresAt?new Date(state.expiresAt):void 0,this.mainUrl=state.mainUrl,this.mainToken=state.mainToken,state.forkDisplayMode&&(state.forkDisplayMode==="side-by-side"?this.forkDisplay={mode:"side-by-side"}:this.forkDisplay={mode:"dialog"}),this.mainUrl){const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.update({url:this.mainUrl,token:this.mainToken})}for(const pf of state.forks)this.restoreFork(pf);if(state.layout&&this.panelManager){for(const fg of state.layout.floatingGroups){const overlay=this.panelManager.getFloatingOverlay(fg.groupId);overlay&&(overlay.setFullBounds(fg.bounds),fg.isMaximized&&(overlay.maximize(),this.floatingToolbars.get(fg.groupId)?.syncMaximizedState()))}state.layout.mainLayout&&this.panelManager.restoreLayoutFlex(state.layout.mainLayout)}if(state.minimizedGroups&&this.minimizeManager&&this.panelManager)for(const mg of state.minimizedGroups){const group=this.panelManager.getGroup(mg.groupId);if(group&&!this.minimizeManager.isMinimized(mg.groupId))if(mg.mode==="floating"){const overlay=this.panelManager.getFloatingOverlay(mg.groupId);overlay&&this.minimizeManager.minimize(mg.groupId,overlay,group)}else mg.mode==="docked"&&this.minimizeManager.minimizeDocked(mg.groupId,group,this.panelManager)}if(state.maximizedDockedGroupId&&this.panelManager){const actions=this.forkGroupActions.get(state.maximizedDockedGroupId);actions&&actions.setMaximized()}isStale||handlers.onSession?.({status:"STATUS_RUNNING"}),this.saveState();let subscribeFailed=!1,errorWasSilenced=!1;const originalOnError=handlers.onError,wrappedHandlers={...handlers,onError:error=>{subscribeFailed=!0;const currentState=this.loadState();!currentState||Date.now()-currentState.savedAt>1e4?(errorWasSilenced=!0,this.clearState()):originalOnError?.(error)}};try{await this.client.subscribe(state.token,wrappedHandlers)}catch{subscribeFailed=!0,isStale?(errorWasSilenced=!0,this.clearState()):this.showErrorStatus(new Error("Session restore failed"))}return subscribeFailed?(await this.client.unsubscribe(),this.sessionToken=null,this.sessionExpiresAt=void 0,this.mainUrl=null,this.mainToken=void 0,this.clearAllForks(),this.components.get(MAIN_PANEL_ID)?.hide(),errorWasSilenced?"none":"failed"):"restored"}async newSession(){this.clearState(),this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null),this.mainRetentionPolicy="retain",this.sessionToken&&this.client.stop(this.sessionToken).catch(()=>{}),await this.client.unsubscribe(),this.mainUrl=null,this.mainToken=void 0,this.sessionToken=null,this.sessionExpiresAt=void 0,this.lastForkActive=null,this.updateBeforeUnloadGuard(),this.clearAllForks(),this.components.get(MAIN_PANEL_ID)?.hide(),this.removeStatusMessage();try{await this.init()}catch(error){this.showErrorStatus(error)}}resizeOverlayForGroup(group){if(!this.panelManager)return;const overlay=this.panelManager.findOverlayContaining(group.id);if(!overlay)return;const allGroups=overlay.getAllGroups();let maxHeight=0;for(const g of allGroups){const active=g.activePanel;if(active){const h=this.forkPanelHeights.get(active.id)??0;h>maxHeight&&(maxHeight=h)}}if(maxHeight>0){const headerHeight=group.headerElement?.offsetHeight??40,newHeight=Math.ceil(maxHeight)+headerHeight,bounds=overlay.getBounds(),newY=(window.innerHeight-newHeight)/2;overlay.setFullBounds({...bounds,height:newHeight,y:Math.max(0,newY)})}}onContainerResize(width,_height){if(!this.panelManager)return;const forkGroup=this.panelManager.getGroup(FORK_GROUP_ID);if(!forkGroup)return;const bp=this.responsiveBreakpoint,restoreThreshold=bp+this.responsiveHysteresis,forkActions=this.forkGroupActions.get(FORK_GROUP_ID),isDockedMaximized=forkActions?.isMaximized??!1;if(width<bp&&!this._autoMaximizedByResize){if(forkGroup.location==="floating"){const overlay=this.panelManager.getFloatingOverlay(FORK_GROUP_ID);overlay&&!overlay.isMaximized&&(overlay.maximize(),this._autoMaximizedByResize=!0)}else isDockedMaximized||(forkActions?(forkActions.setMaximized(),forkActions.setAutoMaximizedByResize(!0)):this.panelManager.maximizeDockedGroup(forkGroup),this._autoMaximizedByResize=!0);this.applyBestAutoResizeHeight()}width>=restoreThreshold&&this._autoMaximizedByResize&&(forkGroup.location==="floating"?this.panelManager.getFloatingOverlay(FORK_GROUP_ID)?.restore():forkActions?forkActions.clearMaximized():this.panelManager.restoreMaximizedDockedGroup(forkGroup),this._autoMaximizedByResize=!1,forkActions?.setAutoMaximizedByResize(!1))}setConfirmOnClose(enabled){this.confirmOnClose=enabled,this.updateBeforeUnloadGuard()}updateBeforeUnloadGuard(){const shouldGuard=this.confirmOnClose&&!!this.sessionToken&&!this.destroyed;shouldGuard&&!this.beforeUnloadHandler?(this.beforeUnloadHandler=e=>{e.preventDefault()},window.addEventListener("beforeunload",this.beforeUnloadHandler)):!shouldGuard&&this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)}wireBeforeTabClose(){this.panelManager&&this.panelManager.setBeforeTabClose(panelId=>{if(this.removingPanelProgrammatically||!this.confirmOnClose)return!0;for(const[,state]of this.forks)if(state.panelId===panelId)return state.completed?!0:this.showConfirmDialog("This panel hasn\u2019t finished yet. Are you sure you want to close it?");return!0})}confirmCloseGroup(group){return!this.confirmOnClose||!group.panels.some(panel=>{for(const state of this.forks.values())if(state.panelId===panel.id&&!state.completed)return!0;return!1})?!0:this.showConfirmDialog("Some panels haven\u2019t finished yet. Are you sure you want to close them?")}showConfirmDialog(message){return new Promise(resolve=>{if(!this.rootElement){resolve(!0);return}const overlay=document.createElement("div");overlay.className="mb-adapt mb-adapt__confirm-overlay",this.darkMode&&overlay.classList.add("mb-adapt--dark");const card=document.createElement("div");card.className="mb-adapt__confirm-card";const text=document.createElement("p");text.className="mb-adapt__confirm-text",text.textContent=message,card.appendChild(text);const buttons=document.createElement("div");buttons.className="mb-adapt__confirm-buttons";const cancelBtn=document.createElement("button");cancelBtn.className="mb-adapt__confirm-btn mb-adapt__confirm-btn--cancel",cancelBtn.textContent="Cancel";const confirmBtn=document.createElement("button");confirmBtn.className="mb-adapt__confirm-btn mb-adapt__confirm-btn--confirm",confirmBtn.textContent="Close",buttons.appendChild(cancelBtn),buttons.appendChild(confirmBtn),card.appendChild(buttons),overlay.appendChild(card),document.body.appendChild(overlay);const cleanup=result=>{overlay.remove(),resolve(result)};cancelBtn.addEventListener("click",()=>cleanup(!1)),confirmBtn.addEventListener("click",()=>cleanup(!0)),overlay.addEventListener("click",e=>{e.target===overlay&&cleanup(!1)})})}async destroy(){this.destroyed=!0,this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null);for(const state of this.forks.values())state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.capWidgetInstance?.destroy(),this.capWidgetInstance=null,this.minimizeManager?.dispose(),this.minimizeManager=null,this.sessionToken&&!this.loadState()&&this.client.stop(this.sessionToken).catch(()=>{}),await this.client.unsubscribe(),this.messageHandler&&window.removeEventListener("message",this.messageHandler),this.panelManager&&(this.panelManager.dispose(),this.panelManager=null),this.rootElement?.parentNode?.removeChild(this.rootElement),this.mainUrl=null,this.mainToken=void 0,this.forks.clear(),this.components.clear(),this.forkPanelHeights.clear(),this.forkGroupActions.clear(),this.floatingToolbars.clear(),this.sessionToken=null,this.lastForkActive=null,this.rootElement=null,this.messageHandler=null,this.statusMessageElement=null,this.minimizedTabsContainer=null}showStatusMessage(text,kind="error"){if(!this.rootElement)return;this.removeStatusMessage();const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.statusMessageElement=document.createElement("div"),this.statusMessageElement.className=this.options.classNames?.statusMessage??"mb-adapt__status-message";const card=document.createElement("div");card.className=this.options.classNames?.statusCard??"mb-adapt__status-card";const iconWrap=document.createElement("div");iconWrap.className=kind==="stopped"||kind==="expired"?"mb-adapt__status-icon mb-adapt__status-icon--stopped":"mb-adapt__status-icon",iconWrap.innerHTML=kind==="stopped"||kind==="expired"?'<svg viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>':'<svg viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>',card.appendChild(iconWrap);const msg=document.createElement("p");msg.className="mb-adapt__status-text",msg.textContent=text,card.appendChild(msg);const btn=document.createElement("button");btn.className="mb-adapt__status-restart",btn.textContent=this.options.text?.restartButton??"Try again",btn.addEventListener("click",()=>this.newSession()),card.appendChild(btn),this.statusMessageElement.appendChild(card),this.rootElement.appendChild(this.statusMessageElement)}removeStatusMessage(){this.statusMessageElement&&(this.statusMessageElement.remove(),this.statusMessageElement=null)}showErrorStatus(error){const kind=this.isExpiredTokenError(error)?"expired":"error";this.clearState(),this.clearAllForks(),this.showStatusMessage(this.getErrorMessage(error),kind)}isExpiredTokenError(error){return error instanceof ConnectError&&error.code===Code.PermissionDenied&&error.message.toLowerCase().includes("expired")}getErrorMessage(error){const text=this.options.text,fallback=text?.error??"Something went wrong. Please try again later.";if(error instanceof ConnectError){if(this.isExpiredTokenError(error))return text?.sessionExpired??"Your session has expired.";switch(error.code){case Code.ResourceExhausted:return text?.resourceExhausted??"This automation is currently at capacity. Please try again later.";case Code.NotFound:return error.message.toLowerCase().includes("session not found")?text?.sessionNotFound??"This session has timed out or been stopped.":text?.notFound??"This automation could not be found.";case Code.PermissionDenied:case Code.Unauthenticated:return text?.permissionDenied??"You don't have permission to run this automation.";default:return fallback}}return fallback}};_AdaptWebClient._capWidgetClass=null;let AdaptWebClient=_AdaptWebClient;export{AdaptAutomationElement,AdaptWebClient,FloatingToolbar2 as FloatingToolbar,ForkGroupActions2 as ForkGroupActions,IframePanelComponent2 as IframePanelComponent,MinimizeManager2 as MinimizeManager,PanelManager2 as PanelManager,clearPersistedState,configure,createConnectClient,resetConfig};
1
+ import{Code,configure,ConnectError,createAdaptClient,resetConfig}from"@mochabug/adapt-core";import{createConnectClient}from"@mochabug/adapt-core/connect";import{PanelManager}from"./panel-manager.js";import{FloatingToolbar,ForkGroupActions,IframePanelComponent,injectPanelManagerStyles,MinimizeManager}from"./panel-setup.js";import{resolveTheme}from"./theme.js";import{resolveTheme as resolveTheme2}from"./theme.js";import{AdaptAutomationElement}from"./AdaptAutomationElement.js";import{FloatingToolbar as FloatingToolbar2,ForkGroupActions as ForkGroupActions2,IframePanelComponent as IframePanelComponent2,MinimizeManager as MinimizeManager2}from"./panel-setup.js";import{PanelManager as PanelManager2}from"./panel-manager.js";function clearPersistedState(automationId,storage="both",persistKey){const key=`mb_adapt_${persistKey??automationId}`;(storage==="session"||storage==="both")&&sessionStorage.removeItem(key),(storage==="local"||storage==="both")&&localStorage.removeItem(key)}const MAIN_PANEL_ID="main",FORK_GROUP_ID="fork-group",DEFAULT_RESPONSIVE_BREAKPOINT=768,DEFAULT_RESPONSIVE_HYSTERESIS=40,_AdaptWebClient=class _AdaptWebClient{constructor(options){this.rootElement=null;this.panelManager=null;this.mainUrl=null;this.sessionToken=null;this.forks=new Map;this.mainRetentionPolicy="retain";this.mainCloseTimer=null;this.components=new Map;this.floatingAutoResized=new Set;this.beforeUnloadHandler=null;this.resizeObserver=null;this._autoMaximizedByResize=!1;this.destroyed=!1;this.removingPanelProgrammatically=!1;this.lastForkActive=null;this.messageHandler=null;this.minimizedTabsContainer=null;this.lastReportedContentHeight=0;this.forkPanelHeights=new Map;this.statusMessageElement=null;this.capWidgetInstance=null;this.minimizeManager=null;this.forkGroupActions=new Map;this.floatingToolbars=new Map;this.options=options;const fd=options.forkDisplay??{mode:"side-by-side"};this.forkDisplay=fd,this.darkMode=options.darkMode??!1,this.autoResizing=options.autoResizing??!1,this.allowFloating=options.allowFloating??!0,this.allowDocking=options.allowDocking??!0,this.allowDialogDocking=options.allowDialogDocking??!0,this.allowMinimize=options.allowMinimize??!0,this.allowMaximize=options.allowMaximize??!0,this.floatingAutoResize=options.floatingAutoResize??!1,this.confirmOnClose=options.confirmOnClose??!1,this.theme=options.theme;const container=typeof options.container=="string"?document.getElementById(options.container):options.container;if(!container)throw new Error(`Container with id '${options.container}' not found`);this.container=container;const rawClient=createConnectClient({id:options.automationId});this.client=createAdaptClient(rawClient,options.automationId),injectPanelManagerStyles(),this.createRootElement(),this.createPanelManager(),this.wireBeforeTabClose(),this.setupMessageListener(),this.init().catch(error=>{this.showErrorStatus(error)})}static registerCapWidget(cls){_AdaptWebClient._capWidgetClass=cls}get responsiveBreakpoint(){if(!this.rootElement)return DEFAULT_RESPONSIVE_BREAKPOINT;const v=getComputedStyle(this.rootElement).getPropertyValue("--mb-adapt-responsive-breakpoint").trim(),n=Number(v);return n>0?n:DEFAULT_RESPONSIVE_BREAKPOINT}get responsiveHysteresis(){if(!this.rootElement)return DEFAULT_RESPONSIVE_HYSTERESIS;const v=getComputedStyle(this.rootElement).getPropertyValue("--mb-adapt-responsive-hysteresis").trim(),n=Number(v);return n>=0?n:DEFAULT_RESPONSIVE_HYSTERESIS}onComponentCreated(panelId,component){this.components.set(panelId,component)}onComponentDisposed(panelId){this.components.delete(panelId)}getDarkMode(){return this.darkMode}getClassNames(){return this.options.classNames}createPanelManager(){if(!this.rootElement)return;this.minimizeManager=new MinimizeManager,this.minimizeManager.setAllowDocking(this.allowDocking),this.minimizedTabsContainer=document.createElement("div"),this.minimizedTabsContainer.className="mb-adapt-minimized-tabs",this.rootElement.appendChild(this.minimizedTabsContainer),this.panelManager=new PanelManager({container:this.rootElement,onGroupCreatedByDrag:group=>{group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group)}});const mainGroup=this.panelManager.addGroup({id:"main-group"});this.panelManager.setMainGroup(mainGroup),mainGroup.locked="no-drop-target",mainGroup.setHeaderHidden(!0),mainGroup.element.setAttribute("data-main-group","true");const mainComponent=new IframePanelComponent(this);mainComponent.init(MAIN_PANEL_ID,{}),this.panelManager.addPanel({id:MAIN_PANEL_ID,element:mainComponent.element,groupId:mainGroup.id}),this.minimizeManager.init(this.minimizedTabsContainer,this.panelManager),this.minimizeManager.onBarVisibilityChanged(()=>{this.reapplyAutoResizeHeight()}),this.resizeObserver=new ResizeObserver(entries=>{const cr=entries[0]?.contentRect;cr&&this.onContainerResize(cr.width,cr.height)}),this.resizeObserver.observe(this.rootElement)}getOrigin(url){try{return new URL(url).origin}catch{return null}}isValidMessageOrigin(origin){if(this.mainUrl){const mainOrigin=this.getOrigin(this.mainUrl);if(mainOrigin&&origin===mainOrigin)return!0}for(const fork of this.forks.values()){const forkOrigin=this.getOrigin(fork.url);if(forkOrigin&&origin===forkOrigin)return!0}return!1}findComponentBySource(source){for(const[panelId,component]of this.components)if(component.contentWindow===source)return{panelId,component};return null}setupMessageListener(){this.messageHandler=event=>{if(!this.isValidMessageOrigin(event.origin))return;const data=event.data;if(!data||typeof data!="object")return;const source=event.source,found=this.findComponentBySource(source);if(!found)return;const{panelId,component}=found,isMainPanel=panelId===MAIN_PANEL_ID;if(data.type==="adapt-init"&&typeof data.resize=="boolean"){component.resizeCapable=data.resize,isMainPanel&&!data.resize&&this.rootElement&&(this.rootElement.style.height="");const targetOrigin=component.getOrigin();if(targetOrigin){let effective;if(isMainPanel)effective=this.autoResizing&&data.resize;else{const group=this.panelManager?.getPanel(panelId)?.group,inFloating=group?!!this.panelManager?.findOverlayContaining(group.id):!1;effective=data.resize&&(inFloating&&this.floatingAutoResize||!inFloating&&this.autoResizing)}component.postMessage({type:"adapt-autoResizing",autoResizing:effective},targetOrigin),component.postMessage({type:"adapt-darkMode",darkMode:this.darkMode},targetOrigin)}return}if(data.type==="adapt-resize"){const{height}=data;if(typeof height!="number"||!Number.isFinite(height)||height<1)return;if(isMainPanel)this.lastReportedContentHeight=height,component.resizeCapable&&this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight();else{this.forkPanelHeights.set(panelId,height);const group=this.panelManager?.getPanel(panelId)?.group;if(group&&group.activePanel?.id===panelId){const inFloating=!!this.panelManager?.findOverlayContaining(group.id);!inFloating&&component.resizeCapable&&this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight(),inFloating&&this.floatingAutoResize&&(this.floatingAutoResized.has(group.id)||(this.floatingAutoResized.add(group.id),this.resizeOverlayForGroup(group)))}}return}},window.addEventListener("message",this.messageHandler)}postToComponent(component,message){const origin=component.getOrigin();origin&&component.postMessage(message,origin)}setDarkMode(darkMode){if(this.darkMode===darkMode)return;this.darkMode=darkMode,this.rootElement?.classList.toggle("mb-adapt--dark",darkMode),this.capWidgetInstance?.setDarkMode(darkMode),this.applyTheme();const msg={type:"adapt-darkMode",darkMode};for(const c of this.components.values())this.postToComponent(c,msg)}setTheme(theme){if(this.theme=theme,this.clearThemeVars(),theme?.mode!==void 0){const dark=theme.mode==="dark";if(this.darkMode!==dark){this.setDarkMode(dark);return}}this.applyTheme()}clearThemeVars(){if(!(!this.rootElement||!this._appliedThemeVars)){for(const key of this._appliedThemeVars)this.rootElement.style.removeProperty(key);this._appliedThemeVars=void 0}}applyTheme(){if(!this.rootElement||!this.theme)return;const vars=resolveTheme(this.theme);for(const[key,value]of Object.entries(vars))this.rootElement.style.setProperty(key,value);this._appliedThemeVars=Object.keys(vars)}setAutoResizing(autoResizing){if(this.autoResizing===autoResizing)return;this.autoResizing=autoResizing;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent?.resizeCapable&&this.postToComponent(mainComponent,{type:"adapt-autoResizing",autoResizing});for(const[panelId,component]of this.components){if(panelId===MAIN_PANEL_ID||!component.resizeCapable)continue;const group=this.panelManager?.getPanel(panelId)?.group;group&&this.panelManager?.findOverlayContaining(group.id)||this.postToComponent(component,{type:"adapt-autoResizing",autoResizing})}!autoResizing&&this.rootElement&&(this.rootElement.style.height="")}setAllowFloating(allow){if(this.allowFloating!==allow){this.allowFloating=allow;for(const actions of this.forkGroupActions.values())actions.setAllowFloating(allow)}}setAllowDocking(allow){if(this.allowDocking!==allow){if(this.allowDocking=allow,this.minimizeManager?.setAllowDocking(allow),this.panelManager)for(const group of this.getAllForkGroups())group.dockingBlocked=!allow;for(const toolbar of this.floatingToolbars.values())toolbar.setAllowDocking(allow)}}setAllowDialogDocking(allow){if(this.allowDialogDocking!==allow&&(this.allowDialogDocking=allow,this.panelManager))for(const group of this.getAllForkGroups())group.dialogDockingBlocked=!allow}setAllowMinimize(allow){if(this.allowMinimize!==allow){this.allowMinimize=allow;for(const actions of this.forkGroupActions.values())actions.setAllowMinimize(allow);for(const toolbar of this.floatingToolbars.values())toolbar.setAllowMinimize(allow)}}setAllowMaximize(allow){if(this.allowMaximize!==allow){this.allowMaximize=allow;for(const actions of this.forkGroupActions.values())actions.setAllowMaximize(allow);for(const toolbar of this.floatingToolbars.values())toolbar.setAllowMaximize(allow)}}setFloatingAutoResize(enabled){if(this.floatingAutoResize!==enabled){this.floatingAutoResize=enabled,this.floatingAutoResized.clear();for(const[panelId,component]of this.components){if(panelId===MAIN_PANEL_ID||!component.resizeCapable)continue;const group=this.panelManager?.getPanel(panelId)?.group,effective=(group?!!this.panelManager?.findOverlayContaining(group.id):!1)?enabled&&component.resizeCapable:this.autoResizing&&component.resizeCapable;this.postToComponent(component,{type:"adapt-autoResizing",autoResizing:effective})}}}getAllForkGroups(){if(!this.panelManager)return[];const seen=new Set,groups=[],add=g=>{g.id==="main-group"||seen.has(g.id)||(seen.add(g.id),groups.push(g))},forkGroup=this.panelManager.getGroup(FORK_GROUP_ID);forkGroup&&add(forkGroup);for(const g of this.panelManager.getDockedGroups())add(g);for(const[,overlay]of this.panelManager.getFloatingOverlays())for(const g of overlay.getAllGroups())add(g);return groups}applyAutoResizeHeight(contentHeight){if(!this.rootElement)return;const chrome=this.minimizedTabsContainer?.offsetHeight??0;this.rootElement.style.height=`${Math.ceil(contentHeight)+chrome}px`}applyBestAutoResizeHeight(){if(!this.autoResizing||!this.rootElement)return;let maxHeight=0;if(this.components.get(MAIN_PANEL_ID)?.resizeCapable&&this.lastReportedContentHeight>0){const mainGroup=this.panelManager?.getMainGroup();if(mainGroup?mainGroup.element.offsetHeight>0:!0){const headerH=mainGroup?.headerElement?.offsetHeight??0;maxHeight=this.lastReportedContentHeight+headerH}}for(const group of this.getAllForkGroups()){if(this.panelManager?.findOverlayContaining(group.id)||group.element.offsetHeight<=0)continue;const active=group.activePanel;if(active){const h=this.forkPanelHeights.get(active.id)??0,headerH=group.headerElement?.offsetHeight??0,total=h+headerH;total>maxHeight&&(maxHeight=total)}}maxHeight>0&&this.applyAutoResizeHeight(maxHeight)}reapplyAutoResizeHeight(){this.applyBestAutoResizeHeight()}setForkDisplayMode(mode){this.forkDisplay.mode!==mode&&(mode==="side-by-side"?this.forkDisplay={mode:"side-by-side"}:this.forkDisplay={mode:"dialog"},this.forks.size>0&&this.panelManager&&this.rebuildForkGroup(),this.emitForkActive(),this.saveState())}createRootElement(){this.rootElement=document.createElement("div"),this.rootElement.className=this.options.classNames?.root||"mb-adapt",this.darkMode&&this.rootElement.classList.add("mb-adapt--dark"),this.options.styles&&Object.assign(this.rootElement.style,this.options.styles),this.applyTheme(),this.container.appendChild(this.rootElement)}onUrl(msg){if(this.destroyed)return;const fork=msg.fork||"";if(msg.stopped){this.handleStoppedMessage(fork),this.saveState();return}if(msg.done){this.handleDoneMessage(fork,msg.retentionPolicy),this.saveState();return}msg.url&&(fork?this.handleForkUrl(msg.url,msg.token,fork,msg.source,msg.retentionPolicy):this.handleMainUrl(msg.url,msg.token,msg.source,msg.retentionPolicy),this.saveState())}handleMainUrl(url,token,_source,retentionPolicy){this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null);const wasMainEmpty=!this.mainUrl;if(this.removeStatusMessage(),this.mainUrl=url,this.mainToken=token,this.mainRetentionPolicy=this.resolveRetention(retentionPolicy),this.panelManager?.isMainGroupCollapsed()&&this.panelManager.expandMainGroup(),wasMainEmpty){const maximizedGroupId=this.getMaximizedDockedGroupId();if(maximizedGroupId){const actions=this.forkGroupActions.get(maximizedGroupId);actions&&actions.clearMaximized()}}const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&(mainComponent.update({url,token}),mainComponent.show())}handleForkUrl(url,token,fork,source,retentionPolicy){if(!this.panelManager)return;const resolved=this.resolveRetention(retentionPolicy),existing=this.forks.get(fork);if(existing){existing.closeTimer&&(clearTimeout(existing.closeTimer),existing.closeTimer=null),existing.url=url,existing.token=token,existing.source=source,existing.retentionPolicy=resolved,existing.completed=!1,existing.recyclable=!1;const component2=this.components.get(existing.panelId);component2&&component2.update({url,token});return}const recyclable=this.findRecyclablePanel(source);if(recyclable){const{forkKey:oldFork,state:rState}=recyclable;rState.closeTimer&&(clearTimeout(rState.closeTimer),rState.closeTimer=null),this.forks.delete(oldFork),rState.url=url,rState.token=token,rState.fork=fork,rState.source=source,rState.retentionPolicy=resolved,rState.completed=!1,rState.recyclable=!1,this.forks.set(fork,rState);const component2=this.components.get(rState.panelId);component2&&component2.update({url,token});return}const panelId=`fork-${fork}`;this.ensureForkGroup(),!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup();const component=new IframePanelComponent(this);component.init(panelId,{url,token}),this.panelManager.addPanel({id:panelId,element:component.element,groupId:FORK_GROUP_ID}),this.forks.set(fork,{panelId,url,token,fork,source,completed:!1,recyclable:!1,retentionPolicy:resolved,closeTimer:null}),this.emitForkActive()}ensureForkGroup(){if(!this.panelManager||this.panelManager.getGroup(FORK_GROUP_ID))return;const cw=this.rootElement?.offsetWidth??800,ch=this.rootElement?.offsetHeight??600;if(this.forkDisplay.mode==="dialog"){const isSmall=cw<this.responsiveBreakpoint,vw=window.innerWidth,vh=window.innerHeight,fw=isSmall?Math.min(cw*.95,Math.max(200,vw-20)):Math.min(720,cw*.9),fh=isSmall?Math.min(ch*.9,Math.max(200,vh-20)):Math.min(ch*.8,600),group=this.panelManager.addGroup({id:FORK_GROUP_ID,floating:{width:fw,height:fh,x:Math.max(0,(vw-fw)/2),y:Math.max(0,(vh-fh)/2)}});group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group),isSmall&&(this.panelManager.getFloatingOverlay(FORK_GROUP_ID)?.maximize(),this._autoMaximizedByResize=!0)}else{const split=this.forkDisplay.mode==="side-by-side"?this.forkDisplay.split:void 0,initialWidth=split!==void 0?cw*(1-split/100):void 0,group=this.panelManager.addGroup({id:FORK_GROUP_ID,direction:"right",initialSize:initialWidth});if(group.dragConstraint="merge-only",this.allowDocking||(group.dockingBlocked=!0),this.allowDialogDocking||(group.dialogDockingBlocked=!0),this.setupForkGroupActions(group),cw<this.responsiveBreakpoint){const fa=this.forkGroupActions.get(group.id);fa?(fa.setMaximized(),fa.setAutoMaximizedByResize(!0)):this.panelManager.maximizeDockedGroup(group),this._autoMaximizedByResize=!0}}}setupForkGroupActions(group){if(!this.panelManager||!this.minimizeManager)return;const actions=new ForkGroupActions(group,this.minimizeManager,this.panelManager,()=>{if(this.setupFloatingToolbar(group),group.location==="grid"&&!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup(),group.location==="floating"&&this.floatingAutoResized.delete(group.id),this.updateAutoResizeForGroup(group),group.location==="grid"){const currentWidth=this.rootElement?.offsetWidth??0;if(currentWidth>0&&currentWidth<this.responsiveBreakpoint){const fa=this.forkGroupActions.get(group.id);fa&&!fa.isMaximized&&(fa.setMaximized(),this._autoMaximizedByResize=!0,fa.setAutoMaximizedByResize(!0))}}},{allowFloating:this.allowFloating,allowMinimize:this.allowMinimize,allowMaximize:this.allowMaximize});actions.setBeforeClose(()=>this.confirmCloseGroup(group)),actions.onMaximizeChanged(maximized=>{maximized||(this._autoMaximizedByResize=!1,actions.setAutoMaximizedByResize(!1))}),group.setHeaderActions(actions.element),this.forkGroupActions.set(group.id,actions),group.onActivePanelChanged(()=>{this.autoResizing&&this.rootElement&&this.applyBestAutoResizeHeight()}),this.setupFloatingToolbar(group)}updateAutoResizeForGroup(group){const inFloating=this.panelManager?!!this.panelManager.findOverlayContaining(group.id):!1;for(const panel of group.panels){const component=this.components.get(panel.id);if(!component?.resizeCapable)continue;const effective=inFloating?this.floatingAutoResize:this.autoResizing;this.postToComponent(component,{type:"adapt-autoResizing",autoResizing:effective})}}setupFloatingToolbar(group){if(!this.panelManager||!this.minimizeManager)return;const overlay=this.panelManager.findOverlayContaining(group.id);if(overlay)if(group.onPanelCountChanged(()=>overlay.updateTotalTabCount()),overlay.hasToolbar)overlay.repositionToolbar();else{const toolbar=new FloatingToolbar(overlay,this.panelManager,this.minimizeManager,{allowDocking:this.allowDocking,allowMinimize:this.allowMinimize,allowMaximize:this.allowMaximize});toolbar.setBeforeClose(()=>this.confirmCloseGroup(overlay.group)),overlay.setToolbar(toolbar.element),this.floatingToolbars.set(overlay.group.id,toolbar),overlay.updateTotalTabCount()}}resolveRetention(policy){return policy?policy.case:"retain"}findRecyclablePanel(source){let anyRecyclable=null;for(const[forkKey,state]of this.forks)if(state.recyclable){if(source&&state.source===source)return{forkKey,state};anyRecyclable||(anyRecyclable={forkKey,state})}return anyRecyclable}handleMainDone(retentionPolicy){switch(retentionPolicy?this.resolveRetention(retentionPolicy):this.mainRetentionPolicy){case"close":this.scheduleMainClose(retentionPolicy?.case==="close"?retentionPolicy.timeoutSeconds:void 0);break;case"recycle":break;default:break}}scheduleForkClose(fork,state,timeoutSeconds){const doClose=()=>{state.closeTimer=null,this.removingPanelProgrammatically=!0;try{if(this.panelManager){const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.delete(fork),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}this.emitForkActive(),this.saveState()};timeoutSeconds&&timeoutSeconds>0?state.closeTimer=setTimeout(doClose,timeoutSeconds*1e3):doClose()}scheduleMainClose(timeoutSeconds){const doClose=()=>{this.mainCloseTimer=null,this.mainUrl=null,this.mainToken=void 0;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup(),this.saveState()};timeoutSeconds&&timeoutSeconds>0?this.mainCloseTimer=setTimeout(doClose,timeoutSeconds*1e3):doClose()}restoreFork(pf){if(!this.panelManager)return;const panelId=`fork-${pf.fork}`;this.ensureForkGroup(),!this.mainUrl&&this.panelManager&&!this.panelManager.isMainGroupCollapsed()&&this.panelManager.collapseMainGroup();const component=new IframePanelComponent(this);component.init(panelId,{url:pf.url,token:pf.token}),this.panelManager.addPanel({id:panelId,element:component.element,groupId:FORK_GROUP_ID}),this.forks.set(pf.fork,{panelId,url:pf.url,token:pf.token,fork:pf.fork,source:pf.source,completed:!1,recyclable:pf.retentionPolicy==="recycle",retentionPolicy:pf.retentionPolicy,closeTimer:null}),this.emitForkActive()}clearAllForks(){this.removingPanelProgrammatically=!0;try{if(this.panelManager)for(const state of this.forks.values()){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.clear(),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}}rebuildForkGroup(){if(!this.panelManager)return;const group=this.panelManager.getGroup(FORK_GROUP_ID);if(group)if(this.forkDisplay.mode==="dialog"){const cw=this.rootElement?.offsetWidth??800,ch=this.rootElement?.offsetHeight??600,fw=Math.min(720,cw*.9),fh=Math.min(ch*.8,600);this.panelManager.addFloatingGroup(group,{width:fw,height:fh,x:(window.innerWidth-fw)/2,y:(window.innerHeight-fh)/2}),this.setupFloatingToolbar(group)}else{const split=this.forkDisplay.mode==="side-by-side"?this.forkDisplay.split:void 0,initialWidth=split!==void 0?(this.rootElement?.offsetWidth??800)*(1-split/100):void 0;this.panelManager.dockGroup(FORK_GROUP_ID,"right",initialWidth)}}handleSessionComplete(){this.clearAllForks(),this.emitForkActive()}handleDoneMessage(fork,retentionPolicy){if(!fork){this.handleMainDone(retentionPolicy);return}const state=this.forks.get(fork);if(!state)return;switch(retentionPolicy?this.resolveRetention(retentionPolicy):state.retentionPolicy){case"close":state.completed=!0,this.scheduleForkClose(fork,state,retentionPolicy?.case==="close"?retentionPolicy.timeoutSeconds:void 0);break;case"recycle":state.completed=!0,state.recyclable=!0;break;default:state.completed=!0;break}}handleStoppedMessage(fork){if(!fork){this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null),this.mainUrl=null,this.mainToken=void 0;const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.showStatusMessage(this.options.text?.stopped??"This session has been stopped","stopped");return}const state=this.forks.get(fork);if(state){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null),this.removingPanelProgrammatically=!0;try{if(this.panelManager){const component=this.components.get(state.panelId);component&&component.dispose(),this.panelManager.removePanel(state.panelId)}this.forks.delete(fork),this.removeForkGroupIfEmpty()}finally{this.removingPanelProgrammatically=!1}this.emitForkActive()}}removeForkGroupIfEmpty(){if(!this.panelManager)return;const g=this.panelManager.getGroup(FORK_GROUP_ID);g&&g.size===0&&(this.panelManager.removeGroup(FORK_GROUP_ID),this._autoMaximizedByResize=!1,this.forkGroupActions.delete(FORK_GROUP_ID),this.floatingToolbars.delete(FORK_GROUP_ID),this.floatingAutoResized.delete(FORK_GROUP_ID)),this.minimizeManager?.removeGroup(FORK_GROUP_ID)}getMaximizedDockedGroupId(){for(const[groupId,actions]of this.forkGroupActions)if(actions.isMaximized)return groupId}emitForkActive(){if(!this.options.onForkActive)return;const active=this.forks.size>0;active!==this.lastForkActive&&(this.lastForkActive=active,this.options.onForkActive(active))}parseInheritToken(){const inheritFrom=this.options.inheritFrom;return inheritFrom?"hash"in inheritFrom?this.parseFromHash(inheritFrom.hash):this.parseFromParam(inheritFrom.param):null}parseFromHash(key){const hash=window.location.hash.slice(1);if(!hash)return null;const params=new URLSearchParams(hash),token=params.get(key);if(!token)return null;params.delete(key);const newHash=params.toString(),newUrl=window.location.pathname+window.location.search+(newHash?"#"+newHash:"");return history.replaceState(null,"",newUrl),decodeURIComponent(token)}parseFromParam(key){const params=new URLSearchParams(window.location.search),token=params.get(key);if(!token)return null;params.delete(key);const newSearch=params.toString(),newUrl=window.location.pathname+(newSearch?"?"+newSearch:"")+window.location.hash;return history.replaceState(null,"",newUrl),decodeURIComponent(token)}async init(){this.panelManager?.onDidRemovePanel(panel=>{if(!this.removingPanelProgrammatically){for(const[fork,state]of this.forks)if(state.panelId===panel.id){state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);const component=this.components.get(state.panelId);component&&component.dispose(),this.sessionToken&&this.client.stop(this.sessionToken,fork).catch(()=>{}),this.forks.delete(fork);break}this.emitForkActive()}});const handlers={onUrl:msg=>this.onUrl(msg),onSession:sessionJson=>{sessionJson.status==="STATUS_COMPLETED"&&!sessionJson.fork&&this.handleSessionComplete(),!sessionJson.fork&&(sessionJson.status==="STATUS_COMPLETED"||sessionJson.status==="STATUS_STOPPED"||sessionJson.status==="STATUS_ERRORED")&&(this.clearState(),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)),this.options.onSession&&this.options.onSession(sessionJson.status||"STATUS_UNSPECIFIED",sessionJson.fork)}};if(this.options.onOutput&&(handlers.onOutput=this.options.onOutput),handlers.onError=error=>{this.showErrorStatus(error),this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)},!this.options.sessionToken&&this.resolvePersistOptions()&&await this.tryRestoreSession(handlers)!=="none"){this.updateBeforeUnloadGuard();return}if(this.options.sessionToken)this.sessionToken=this.options.sessionToken,await this.client.subscribe(this.sessionToken,handlers);else{const urlToken=this.parseInheritToken(),inheritToken=urlToken||this.options.inheritToken;if(this.options.inheritFrom&&!urlToken){const source="hash"in this.options.inheritFrom?`hash key '${this.options.inheritFrom.hash}'`:`param '${this.options.inheritFrom.param}'`;console.error(`[AdaptWebClient] No inherit token found in URL ${source}`);return}let challengeToken=this.options.challengeToken;if(this.options.requiresChallenge&&!challengeToken&&!inheritToken&&(challengeToken=await this.showCapWidgetAndSolve(),!challengeToken)){console.error("[AdaptWebClient] Failed to solve challenge");return}let result;if(inheritToken)result=await this.client.runInherit(inheritToken,handlers);else{const runOptions={...handlers};this.options.transmitter!==void 0&&(runOptions.transmitter=this.options.transmitter),this.options.signals!==void 0&&(runOptions.signals=this.options.signals),this.options.authToken!==void 0&&(runOptions.authToken=this.options.authToken),challengeToken!==void 0&&(runOptions.challengeToken=challengeToken),result=await this.client.run(runOptions)}this.sessionToken=result.sessionToken,this.sessionExpiresAt=result.expiresAt,this.saveState()}this.updateBeforeUnloadGuard()}async showCapWidgetAndSolve(){const CapWidgetClass=_AdaptWebClient._capWidgetClass;if(!CapWidgetClass)throw new Error("Cap.js not loaded. Import '@mochabug/adapt-web/cap' or use the full UMD bundle.");return new Promise(resolve=>{this.capWidgetInstance=new CapWidgetClass({container:this.rootElement,automationId:this.options.automationId,client:this.client.raw,...this.options.capWidgetOptions?.workerCount!==void 0&&{workerCount:this.options.capWidgetOptions.workerCount},...this.options.capWidgetOptions?.i18n&&{i18n:this.options.capWidgetOptions.i18n},onSolve:token=>{this.capWidgetInstance=null,resolve(token)},onError:error=>{console.error("[AdaptWebClient] Cap.js error:",error),this.capWidgetInstance=null,resolve(void 0)}}),this.darkMode&&this.capWidgetInstance.setDarkMode(!0)})}resolvePersistOptions(){const p=this.options.persist;if(!p)return null;if(p===!0)return{storage:"session",ttl:3600};const result={storage:p.storage??"session",ttl:p.ttl??3600};return p.key!==void 0&&(result.key=p.key),result}getStorageKey(){return`mb_adapt_${this.resolvePersistOptions()?.key??this.options.automationId}`}getStorage(){const opts=this.resolvePersistOptions();return opts?opts.storage==="local"?localStorage:sessionStorage:null}saveState(){const storage=this.getStorage();if(!storage||!this.sessionToken)return;const opts=this.resolvePersistOptions(),state={v:3,token:this.sessionToken,expiresAt:this.sessionExpiresAt?this.sessionExpiresAt.toISOString():null,savedAt:Date.now(),ttl:opts.ttl,mainUrl:this.mainUrl,mainToken:this.mainToken,forks:Array.from(this.forks.values()).filter(f=>!f.completed||f.recyclable).map(f=>{const pf={url:f.url,token:f.token,fork:f.fork,retentionPolicy:f.retentionPolicy};return f.source!==void 0&&(pf.source=f.source),pf}),forkDisplayMode:this.forkDisplay.mode,layout:this.panelManager?.serializeLayout()};if(this.minimizeManager){const minimizedIds=this.minimizeManager.getMinimizedGroupIds();minimizedIds.length>0&&(state.minimizedGroups=minimizedIds.map(gid=>{const entry=this.minimizeManager.getEntry(gid),persisted={groupId:gid,mode:entry?.mode??"floating"};return entry?.savedPosition&&(persisted.savedPosition={...entry.savedPosition}),persisted}))}const maxDockedId=this.getMaximizedDockedGroupId();maxDockedId&&(state.maximizedDockedGroupId=maxDockedId),storage.setItem(this.getStorageKey(),JSON.stringify(state))}loadState(){const storage=this.getStorage();if(!storage)return null;const raw=storage.getItem(this.getStorageKey());if(!raw)return null;let state;try{state=JSON.parse(raw)}catch{return this.clearState(),null}if(state.v!==3)return this.clearState(),null;const now=Date.now(),leeway=3e4;if(now-state.savedAt>state.ttl*1e3+leeway)return this.clearState(),null;if(state.expiresAt){const expires=new Date(state.expiresAt).getTime();if(now>expires+leeway)return this.clearState(),null}return state}clearState(){const storage=this.getStorage();storage&&storage.removeItem(this.getStorageKey())}async tryRestoreSession(handlers){const state=this.loadState();if(!state)return"none";const isStale=Date.now()-state.savedAt>1e4;if(this.sessionToken=state.token,this.sessionExpiresAt=state.expiresAt?new Date(state.expiresAt):void 0,this.mainUrl=state.mainUrl,this.mainToken=state.mainToken,state.forkDisplayMode&&(state.forkDisplayMode==="side-by-side"?this.forkDisplay={mode:"side-by-side"}:this.forkDisplay={mode:"dialog"}),this.mainUrl){const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.update({url:this.mainUrl,token:this.mainToken})}for(const pf of state.forks)this.restoreFork(pf);if(state.layout&&this.panelManager){for(const fg of state.layout.floatingGroups){const overlay=this.panelManager.getFloatingOverlay(fg.groupId);overlay&&(overlay.setFullBounds(fg.bounds),fg.isMaximized&&(overlay.maximize(),this.floatingToolbars.get(fg.groupId)?.syncMaximizedState()))}state.layout.mainLayout&&this.panelManager.restoreLayoutFlex(state.layout.mainLayout)}if(state.minimizedGroups&&this.minimizeManager&&this.panelManager)for(const mg of state.minimizedGroups){const group=this.panelManager.getGroup(mg.groupId);if(group&&!this.minimizeManager.isMinimized(mg.groupId))if(mg.mode==="floating"){const overlay=this.panelManager.getFloatingOverlay(mg.groupId);overlay&&this.minimizeManager.minimize(mg.groupId,overlay,group)}else mg.mode==="docked"&&this.minimizeManager.minimizeDocked(mg.groupId,group,this.panelManager)}if(state.maximizedDockedGroupId&&this.panelManager){const actions=this.forkGroupActions.get(state.maximizedDockedGroupId);actions&&actions.setMaximized()}isStale||handlers.onSession?.({status:"STATUS_RUNNING"}),this.saveState();let subscribeFailed=!1,errorWasSilenced=!1;const originalOnError=handlers.onError,wrappedHandlers={...handlers,onError:error=>{subscribeFailed=!0;const currentState=this.loadState();!currentState||Date.now()-currentState.savedAt>1e4?(errorWasSilenced=!0,this.clearState()):originalOnError?.(error)}};try{await this.client.subscribe(state.token,wrappedHandlers)}catch{subscribeFailed=!0,isStale?(errorWasSilenced=!0,this.clearState()):this.showErrorStatus(new Error("Session restore failed"))}return subscribeFailed?(await this.client.unsubscribe(),this.sessionToken=null,this.sessionExpiresAt=void 0,this.mainUrl=null,this.mainToken=void 0,this.clearAllForks(),this.components.get(MAIN_PANEL_ID)?.hide(),errorWasSilenced?"none":"failed"):"restored"}async newSession(){this.clearState(),this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null),this.mainRetentionPolicy="retain",this.sessionToken&&this.client.stop(this.sessionToken).catch(()=>{}),await this.client.unsubscribe(),this.mainUrl=null,this.mainToken=void 0,this.sessionToken=null,this.sessionExpiresAt=void 0,this.lastForkActive=null,this.updateBeforeUnloadGuard(),this.clearAllForks(),this.components.get(MAIN_PANEL_ID)?.hide(),this.removeStatusMessage();try{await this.init()}catch(error){this.showErrorStatus(error)}}resizeOverlayForGroup(group){if(!this.panelManager)return;const overlay=this.panelManager.findOverlayContaining(group.id);if(!overlay)return;const allGroups=overlay.getAllGroups();let maxHeight=0;for(const g of allGroups){const active=g.activePanel;if(active){const h=this.forkPanelHeights.get(active.id)??0;h>maxHeight&&(maxHeight=h)}}if(maxHeight>0){const headerHeight=group.headerElement?.offsetHeight??40,newHeight=Math.ceil(maxHeight)+headerHeight,bounds=overlay.getBounds(),newY=(window.innerHeight-newHeight)/2;overlay.setFullBounds({...bounds,height:newHeight,y:Math.max(0,newY)})}}onContainerResize(width,_height){if(!this.panelManager)return;const forkGroup=this.panelManager.getGroup(FORK_GROUP_ID);if(!forkGroup)return;const bp=this.responsiveBreakpoint,restoreThreshold=bp+this.responsiveHysteresis,forkActions=this.forkGroupActions.get(FORK_GROUP_ID),isDockedMaximized=forkActions?.isMaximized??!1;if(width<bp&&!this._autoMaximizedByResize){if(forkGroup.location==="floating"){const overlay=this.panelManager.getFloatingOverlay(FORK_GROUP_ID);overlay&&!overlay.isMaximized&&(overlay.maximize(),this._autoMaximizedByResize=!0)}else isDockedMaximized||(forkActions?(forkActions.setMaximized(),forkActions.setAutoMaximizedByResize(!0)):this.panelManager.maximizeDockedGroup(forkGroup),this._autoMaximizedByResize=!0);this.applyBestAutoResizeHeight()}width>=restoreThreshold&&this._autoMaximizedByResize&&(forkGroup.location==="floating"?this.panelManager.getFloatingOverlay(FORK_GROUP_ID)?.restore():forkActions?forkActions.clearMaximized():this.panelManager.restoreMaximizedDockedGroup(forkGroup),this._autoMaximizedByResize=!1,forkActions?.setAutoMaximizedByResize(!1))}setConfirmOnClose(enabled){this.confirmOnClose=enabled,this.updateBeforeUnloadGuard()}updateBeforeUnloadGuard(){const shouldGuard=this.confirmOnClose&&!!this.sessionToken&&!this.destroyed;shouldGuard&&!this.beforeUnloadHandler?(this.beforeUnloadHandler=e=>{e.preventDefault()},window.addEventListener("beforeunload",this.beforeUnloadHandler)):!shouldGuard&&this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null)}wireBeforeTabClose(){this.panelManager&&this.panelManager.setBeforeTabClose(panelId=>{if(this.removingPanelProgrammatically||!this.confirmOnClose)return!0;for(const[,state]of this.forks)if(state.panelId===panelId)return state.completed?!0:this.showConfirmDialog("This panel hasn\u2019t finished yet. Are you sure you want to close it?");return!0})}confirmCloseGroup(group){return!this.confirmOnClose||!group.panels.some(panel=>{for(const state of this.forks.values())if(state.panelId===panel.id&&!state.completed)return!0;return!1})?!0:this.showConfirmDialog("Some panels haven\u2019t finished yet. Are you sure you want to close them?")}showConfirmDialog(message){return new Promise(resolve=>{if(!this.rootElement){resolve(!0);return}const overlay=document.createElement("div");overlay.className="mb-adapt mb-adapt__confirm-overlay",this.darkMode&&overlay.classList.add("mb-adapt--dark");const card=document.createElement("div");card.className="mb-adapt__confirm-card";const text=document.createElement("p");text.className="mb-adapt__confirm-text",text.textContent=message,card.appendChild(text);const buttons=document.createElement("div");buttons.className="mb-adapt__confirm-buttons";const cancelBtn=document.createElement("button");cancelBtn.className="mb-adapt__confirm-btn mb-adapt__confirm-btn--cancel",cancelBtn.textContent="Cancel";const confirmBtn=document.createElement("button");confirmBtn.className="mb-adapt__confirm-btn mb-adapt__confirm-btn--confirm",confirmBtn.textContent="Close",buttons.appendChild(cancelBtn),buttons.appendChild(confirmBtn),card.appendChild(buttons),overlay.appendChild(card),document.body.appendChild(overlay);const cleanup=result=>{overlay.remove(),resolve(result)};cancelBtn.addEventListener("click",()=>cleanup(!1)),confirmBtn.addEventListener("click",()=>cleanup(!0)),overlay.addEventListener("click",e=>{e.target===overlay&&cleanup(!1)})})}async destroy(){this.destroyed=!0,this.mainCloseTimer&&(clearTimeout(this.mainCloseTimer),this.mainCloseTimer=null);for(const state of this.forks.values())state.closeTimer&&(clearTimeout(state.closeTimer),state.closeTimer=null);this.beforeUnloadHandler&&(window.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.resizeObserver?.disconnect(),this.resizeObserver=null,this.capWidgetInstance?.destroy(),this.capWidgetInstance=null,this.minimizeManager?.dispose(),this.minimizeManager=null,this.sessionToken&&!this.loadState()&&this.client.stop(this.sessionToken).catch(()=>{}),await this.client.unsubscribe(),this.messageHandler&&window.removeEventListener("message",this.messageHandler),this.panelManager&&(this.panelManager.dispose(),this.panelManager=null),this.rootElement?.parentNode?.removeChild(this.rootElement),this.mainUrl=null,this.mainToken=void 0,this.forks.clear(),this.components.clear(),this.forkPanelHeights.clear(),this.forkGroupActions.clear(),this.floatingToolbars.clear(),this.sessionToken=null,this.lastForkActive=null,this.rootElement=null,this.messageHandler=null,this.statusMessageElement=null,this.minimizedTabsContainer=null}showStatusMessage(text,kind="error"){if(!this.rootElement)return;this.removeStatusMessage();const mainComponent=this.components.get(MAIN_PANEL_ID);mainComponent&&mainComponent.hide(),this.statusMessageElement=document.createElement("div"),this.statusMessageElement.className=this.options.classNames?.statusMessage??"mb-adapt__status-message";const card=document.createElement("div");card.className=this.options.classNames?.statusCard??"mb-adapt__status-card";const iconWrap=document.createElement("div");iconWrap.className=kind==="stopped"||kind==="expired"?"mb-adapt__status-icon mb-adapt__status-icon--stopped":"mb-adapt__status-icon",iconWrap.innerHTML=kind==="stopped"||kind==="expired"?'<svg viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="6" width="12" height="12" rx="2"/></svg>':'<svg viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg>',card.appendChild(iconWrap);const msg=document.createElement("p");msg.className="mb-adapt__status-text",msg.textContent=text,card.appendChild(msg);const btn=document.createElement("button");btn.className="mb-adapt__status-restart",btn.textContent=this.options.text?.restartButton??"Try again",btn.addEventListener("click",()=>this.newSession()),card.appendChild(btn),this.statusMessageElement.appendChild(card),this.rootElement.appendChild(this.statusMessageElement)}removeStatusMessage(){this.statusMessageElement&&(this.statusMessageElement.remove(),this.statusMessageElement=null)}showErrorStatus(error){const kind=this.isExpiredTokenError(error)?"expired":"error";this.clearState(),this.clearAllForks(),this.showStatusMessage(this.getErrorMessage(error),kind)}isExpiredTokenError(error){return error instanceof ConnectError&&error.code===Code.PermissionDenied&&error.message.toLowerCase().includes("expired")}getErrorMessage(error){const text=this.options.text,fallback=text?.error??"Something went wrong. Please try again later.";if(error instanceof ConnectError){if(this.isExpiredTokenError(error))return text?.sessionExpired??"Your session has expired.";switch(error.code){case Code.ResourceExhausted:return text?.resourceExhausted??"This automation is currently at capacity. Please try again later.";case Code.NotFound:return error.message.toLowerCase().includes("session not found")?text?.sessionNotFound??"This session has timed out or been stopped.":text?.notFound??"This automation could not be found.";case Code.PermissionDenied:case Code.Unauthenticated:return text?.permissionDenied??"You don't have permission to run this automation.";default:return fallback}}return fallback}};_AdaptWebClient._capWidgetClass=null;let AdaptWebClient=_AdaptWebClient;export{AdaptAutomationElement,AdaptWebClient,FloatingToolbar2 as FloatingToolbar,ForkGroupActions2 as ForkGroupActions,IframePanelComponent2 as IframePanelComponent,MinimizeManager2 as MinimizeManager,PanelManager2 as PanelManager,clearPersistedState,configure,createConnectClient,resetConfig,resolveTheme2 as resolveTheme};
@@ -0,0 +1 @@
1
+ function parseHex(hex){const m=/^#([0-9a-f]{3,8})$/i.exec(hex.trim());if(!m)return null;const h=m[1];return h.length===3?[parseInt(h[0]+h[0],16),parseInt(h[1]+h[1],16),parseInt(h[2]+h[2],16)]:h.length===6||h.length===8?[parseInt(h.slice(0,2),16),parseInt(h.slice(2,4),16),parseInt(h.slice(4,6),16)]:null}function hexToRgba(hex,alpha){const rgb=parseHex(hex);return rgb?`rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${alpha})`:hex}function resolveTheme(theme){const result={};if(theme.primary){const p=theme.primary;result["--mb-adapt-separator-active"]=hexToRgba(p,.4),result["--mb-adapt-drop-border"]=p,result["--mb-adapt-drop-header-bg"]=hexToRgba(p,.15),result["--mb-adapt-drop-center-bg"]=hexToRgba(p,.1),result["--mb-adapt-drop-split-bg"]=hexToRgba(p,.12),result["--mb-adapt-status-icon-bg"]=hexToRgba(p,.08),result["--mb-adapt-cap-spinner-color"]=p}if(theme.background&&(result["--mb-adapt-bg"]=theme.background,result["--mb-adapt-fork-tab-active-bg"]=theme.background,result["--mb-adapt-status-card-bg"]=theme.background),theme.surface&&(result["--mb-adapt-fork-bg"]=theme.surface,result["--mb-adapt-fork-tab-bg"]=theme.surface),theme.text&&(result["--mb-adapt-fork-tab-color"]=theme.text,result["--mb-adapt-status-text"]=theme.text,result["--mb-adapt-cap-color"]=theme.text),theme.textSecondary&&(result["--mb-adapt-fork-tab-inactive-color"]=theme.textSecondary),theme.border&&(result["--mb-adapt-fork-separator"]=theme.border,result["--mb-adapt-status-card-border"]=theme.border,result["--mb-adapt-cap-border-color"]=theme.border),theme.font&&(result["--mb-adapt-font"]=theme.font,result["--mb-adapt-cap-font"]=theme.font),theme.vars)for(const[key,value]of Object.entries(theme.vars))result[`--mb-adapt-${key}`]=value;return result}export{resolveTheme};