js.foresight-devtools
Version:
Visual debugging tools for ForesightJS - mouse trajectory prediction and element interaction visualization
17 lines • 54.6 kB
JavaScript
const e=e=>(e=>(e=>null!=e&&"object"==typeof e||!1)(e)&&"number"==typeof e.nodeType&&[1,2,3,4,5,6,7,8,9,10,11].some(t=>e.nodeType===t)||!1)(e)&&1===e.nodeType||!1;const t=["all","intersecting","update"],n="PositionObserver Error";var i=class{entries;static version="1.1.0";_t;_r;_cm;_w;_h;_rm;_th;_c;constructor(i,s){if("function"!=typeof i)throw new Error(`${n}: ${i} is not a function.`);this.entries=new Map,this._c=i,this._t=0;const o=e(s?.root)?s.root:document?.documentElement;this._r=o,this._rm=s?.rootMargin,this._th=s?.threshold,
/* istanbul ignore next @preserve */
this._cm=t.indexOf(s?.callbackMode||"intersecting"),this._w=o.clientWidth,this._h=o.clientHeight}observe=t=>{if(!e(t))throw new Error(`${n}: ${t} is not an instance of Element.`);
/* istanbul ignore else @preserve - a guard must be set */this._r.contains(t)&&this._n(t).then(e=>{
/* istanbul ignore else @preserve - don't allow duplicate entries */
e.boundingClientRect&&!this.getEntry(t)&&this.entries.set(t,e)
/* istanbul ignore else @preserve */,this._t||(this._t=requestAnimationFrame(this._rc))})};unobserve=e=>{
/* istanbul ignore else @preserve */
this.entries.has(e)&&this.entries.delete(e)};_rc=()=>{
/* istanbul ignore if @preserve - a guard must be set */
if(!this.entries.size)return void(this._t=0);const{clientWidth:e,clientHeight:t}=this._r,n=new Promise(n=>{const i=[];this.entries.forEach(({target:n,boundingClientRect:s,isIntersecting:o})=>{
/* istanbul ignore if @preserve - a guard must be set when target has been removed */
this._r.contains(n)&&this._n(n).then(r=>{
/* istanbul ignore if @preserve - make sure to only count visible entries */
if(!r.isIntersecting){if(1===this._cm)return;if(2===this._cm)return void(o&&(this.entries.set(n,r),i.push(r)))}const{left:a,top:l}=r.boundingClientRect;
/* istanbul ignore else @preserve - only schedule entries that changed position */s.top===l&&s.left===a&&this._w===e&&this._h===t||(this.entries.set(n,r),i.push(r))})}),this._w=e,this._h=t,n(i)});this._t=requestAnimationFrame(async()=>{const e=await n;
/* istanbul ignore else @preserve */e.length&&this._c(e,this),this._rc()})};_n=e=>new Promise(t=>{new IntersectionObserver(([e],n)=>{n.disconnect(),t(e)},{threshold:this._th,rootMargin:this._rm}).observe(e)});getEntry=e=>this.entries.get(e);disconnect=()=>{cancelAnimationFrame(this._t),this.entries.clear(),this._t=0}};const s=(e,t=2)=>{const n=" ".repeat(t);if("object"==typeof e&&null!==e&&!Array.isArray(e)){const i=Object.entries(e);if(0===i.length)return"{}";return`{\n${i.map(([e,i])=>`${n} ${e}: ${s(i,t+2)}`).join(",\n")}\n${n}}`}return"string"==typeof e?`'${e}'`:"boolean"==typeof e||"number"==typeof e?String(e):null===e?"null":void 0===e?"undefined":Array.isArray(e)?JSON.stringify(e):String(e)};function o(e,t,n){const i=document.createElement(e);return n.id&&(i.id=n.id),n.className&&(i.className=n.className),n.data&&i.setAttribute("data-value",n.data),t.appendChild(i)}function r(e,t,n){const i=document.createElement("style");return i.textContent=e,i.id=n,t.appendChild(i)}const a=e=>e?"👁️":"🚫",l=!0,c="points",d="tabs",h="ms",g='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>',p="<em>No elements registered.</em>";class u{constructor(e,t){this.elementListItemsContainer=null,this.elementCountSpan=null,this.callbackCountSpan=null,this.elementListItems=new Map,this.trajectoryEnabledCheckbox=null,this.tabEnabledCheckbox=null,this.scrollEnabledCheckbox=null,this.historySizeSlider=null,this.historyValueSpan=null,this.predictionTimeSlider=null,this.predictionValueSpan=null,this.tabOffsetSlider=null,this.tabOffsetValueSpan=null,this.scrollMarginSlider=null,this.scrollMarginValueSpan=null,this.showNameTagsCheckbox=null,this.sortOptionsPopup=null,this.sortButton=null,this.containerMinimizeButton=null,this.allSettingsSectionsContainer=null,this.debuggerElementsSection=null,this.isContainerMinimized=!1,this.isMouseSettingsMinimized=!0,this.isKeyboardSettingsMinimized=!0,this.isScrollSettingsMinimized=!0,this.isGeneralSettingsMinimized=!0,this.SESSION_STORAGE_KEY="jsforesightDebuggerSectionStates",this.copySettingsButton=null,this.minimizedElementCount=null,this.copyTimeoutId=null,this.closeSortDropdownHandler=null,this.foresightManagerInstance=e,this.debuggerInstance=t}static initialize(e,t,n,i){u.isInitiated||(u.debuggerControlPanelInstance=new u(e,t));const s=u.debuggerControlPanelInstance;return s._setupDOMAndListeners(n,i),s}_setupDOMAndListeners(e,t){this.controlsContainer||(this.shadowRoot=e,this.isContainerMinimized=t.isControlPanelDefaultMinimized,this.controlsContainer=this.createControlContainer(),this.shadowRoot.appendChild(this.controlsContainer),this.controlPanelStyleElement=r(this.getStyles(),this.shadowRoot,"debug-control-panel"),this.queryDOMElements(),this.originalSectionStates(),this.setupEventListeners(),this.updateContainerVisibilityState(),this.updateControlsState(this.foresightManagerInstance.getManagerData.globalSettings,t))}static get isInitiated(){return!!u.debuggerControlPanelInstance}loadSectionStatesFromSessionStorage(){const e=sessionStorage.getItem(this.SESSION_STORAGE_KEY);let t={};return e&&(t=JSON.parse(e)),this.isMouseSettingsMinimized=t.mouse??!0,this.isKeyboardSettingsMinimized=t.keyboard??!0,this.isScrollSettingsMinimized=t.scroll??!0,this.isGeneralSettingsMinimized=t.general??!0,t}saveSectionStatesToSessionStorage(){const e={mouse:this.isMouseSettingsMinimized,keyboard:this.isKeyboardSettingsMinimized,scroll:this.isScrollSettingsMinimized,general:this.isGeneralSettingsMinimized};try{sessionStorage.setItem(this.SESSION_STORAGE_KEY,JSON.stringify(e))}catch(e){console.error("Foresight Debugger: Could not save section states to session storage.",e)}}queryDOMElements(){this.trajectoryEnabledCheckbox=this.controlsContainer.querySelector("#trajectory-enabled"),this.tabEnabledCheckbox=this.controlsContainer.querySelector("#tab-enabled"),this.scrollEnabledCheckbox=this.controlsContainer.querySelector("#scroll-enabled"),this.historySizeSlider=this.controlsContainer.querySelector("#history-size"),this.historyValueSpan=this.controlsContainer.querySelector("#history-value"),this.predictionTimeSlider=this.controlsContainer.querySelector("#prediction-time"),this.predictionValueSpan=this.controlsContainer.querySelector("#prediction-value"),this.tabOffsetSlider=this.controlsContainer.querySelector("#tab-offset"),this.tabOffsetValueSpan=this.controlsContainer.querySelector("#tab-offset-value"),this.scrollMarginSlider=this.controlsContainer.querySelector("#scroll-margin"),this.scrollMarginValueSpan=this.controlsContainer.querySelector("#scroll-margin-value"),this.elementListItemsContainer=this.controlsContainer.querySelector("#element-list-items-container"),this.showNameTagsCheckbox=this.controlsContainer.querySelector("#toggle-name-tags"),this.sortOptionsPopup=this.controlsContainer.querySelector("#sort-options-popup"),this.sortButton=this.controlsContainer.querySelector(".sort-button"),this.elementCountSpan=this.controlsContainer.querySelector("#element-count"),this.callbackCountSpan=this.controlsContainer.querySelector("#callback-count"),this.containerMinimizeButton=this.controlsContainer.querySelector(".minimize-button"),this.allSettingsSectionsContainer=this.controlsContainer.querySelector(".all-settings-sections-container"),this.debuggerElementsSection=this.controlsContainer.querySelector(".debugger-elements"),this.copySettingsButton=this.controlsContainer.querySelector(".copy-settings-button"),this.minimizedElementCount=this.controlsContainer.querySelector(".minimized-element-count")}handleCopySettings(){var e,t;this.copySettingsButton&&navigator.clipboard.writeText((e=this.foresightManagerInstance.getManagerData.globalSettings,t="ForesightManager.initialize",`${t}({\n${Object.entries(e).filter(([e])=>"resizeScrollThrottleDelay"!==String(e)).map(([e,t])=>` ${String(e)}: ${s(t)}`).join(",\n")}\n})`)).then(()=>{this.copySettingsButton.innerHTML='<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>',this.copyTimeoutId&&clearTimeout(this.copyTimeoutId),this.copyTimeoutId=setTimeout(()=>{this.copySettingsButton&&(this.copySettingsButton.innerHTML=g),this.copyTimeoutId=null},3e3)}).catch(e=>{console.error("Foresight Debugger: Could not copy settings to clipboard",e)})}createInputEventListener(e,t,n,i){e&&t&&e.addEventListener("input",e=>{const s=parseInt(e.target.value,10);t.textContent=`${s} ${n}`,this.foresightManagerInstance.alterGlobalSettings({[i]:s})})}createChangeEventListener(e,t){if(!e)return;const n=this.debuggerInstance.getDebuggerData.settings;e.addEventListener("change",e=>{const i=e.target.checked;t in n?this.debuggerInstance.alterDebuggerSettings({[t]:i}):this.foresightManagerInstance.alterGlobalSettings({[t]:i})})}createSectionVisibilityToggleEventListener(e,t){const n=e?.querySelector(".debugger-section-header");n?.addEventListener("click",n=>{n.stopPropagation(),this.toggleMinimizeSection(e,this[t]=!this[t])})}setupEventListeners(){this.createChangeEventListener(this.trajectoryEnabledCheckbox,"enableMousePrediction"),this.createChangeEventListener(this.tabEnabledCheckbox,"enableTabPrediction"),this.createChangeEventListener(this.scrollEnabledCheckbox,"enableScrollPrediction"),this.createChangeEventListener(this.showNameTagsCheckbox,"showNameTags"),this.createInputEventListener(this.historySizeSlider,this.historyValueSpan,c,"positionHistorySize"),this.createInputEventListener(this.predictionTimeSlider,this.predictionValueSpan,h,"trajectoryPredictionTime"),this.createInputEventListener(this.tabOffsetSlider,this.tabOffsetValueSpan,d,"tabOffset"),this.createInputEventListener(this.scrollMarginSlider,this.scrollMarginValueSpan,"px","scrollMargin"),this.sortButton?.addEventListener("click",e=>{e.stopPropagation(),this.sortOptionsPopup?.classList.toggle("active")}),this.sortOptionsPopup?.addEventListener("click",e=>{const t=e.target.closest("[data-sort]");if(!t)return;const n=t.dataset.sort;this.debuggerInstance.alterDebuggerSettings({sortElementList:n}),this.sortAndReorderElements(),this.updateSortOptionUI(n),this.sortOptionsPopup?.classList.remove("active")}),this.closeSortDropdownHandler=e=>{this.sortOptionsPopup?.classList.contains("active")&&!this.sortButton?.contains(e.target)&&this.sortOptionsPopup.classList.remove("active")},document.addEventListener("click",this.closeSortDropdownHandler),this.containerMinimizeButton?.addEventListener("click",()=>{this.isContainerMinimized=!this.isContainerMinimized,this.updateContainerVisibilityState()}),this.copySettingsButton?.addEventListener("click",this.handleCopySettings.bind(this)),this.createSectionVisibilityToggleEventListener(this.controlsContainer.querySelector(".mouse-settings-section"),"isMouseSettingsMinimized"),this.createSectionVisibilityToggleEventListener(this.controlsContainer.querySelector(".keyboard-settings-section"),"isKeyboardSettingsMinimized"),this.createSectionVisibilityToggleEventListener(this.controlsContainer.querySelector(".scroll-settings-section"),"isScrollSettingsMinimized"),this.createSectionVisibilityToggleEventListener(this.controlsContainer.querySelector(".general-settings-section"),"isGeneralSettingsMinimized")}toggleMinimizeSection(e,t){if(!e)return;const n=e.querySelector(".debugger-section-content"),i=e.querySelector(".section-minimize-button");n&&i&&(t?(n.style.display="none",i.textContent="+"):(n.style.display="flex",i.textContent="-")),this.saveSectionStatesToSessionStorage()}originalSectionStates(){const e=this.loadSectionStatesFromSessionStorage();this.toggleMinimizeSection(this.controlsContainer.querySelector(".mouse-settings-section"),e.mouse??!0),this.toggleMinimizeSection(this.controlsContainer.querySelector(".keyboard-settings-section"),e.keyboard??!0),this.toggleMinimizeSection(this.controlsContainer.querySelector(".scroll-settings-section"),e.scroll??!0),this.toggleMinimizeSection(this.controlsContainer.querySelector(".general-settings-section"),e.general??!0);const t=this.debuggerElementsSection?.querySelector(".debugger-section-content");t&&(t.style.display="flex")}updateContainerVisibilityState(){this.containerMinimizeButton&&(this.isContainerMinimized?(this.controlsContainer.classList.add("minimized"),this.containerMinimizeButton.textContent="+",this.allSettingsSectionsContainer&&(this.allSettingsSectionsContainer.style.display="none"),this.debuggerElementsSection&&(this.debuggerElementsSection.style.display="none"),this.copySettingsButton&&(this.copySettingsButton.style.display="none"),this.minimizedElementCount&&(this.minimizedElementCount.style.display="")):(this.controlsContainer.classList.remove("minimized"),this.containerMinimizeButton.textContent="-",this.allSettingsSectionsContainer&&(this.allSettingsSectionsContainer.style.display=""),this.debuggerElementsSection&&(this.debuggerElementsSection.style.display=""),this.copySettingsButton&&(this.copySettingsButton.style.display=""),this.minimizedElementCount&&(this.minimizedElementCount.style.display="none")))}updateSortOptionUI(e){this.sortOptionsPopup?.querySelectorAll("[data-sort]").forEach(t=>{const n=t;n.dataset.sort===e?n.classList.add("active-sort-option"):n.classList.remove("active-sort-option")})}updateControlsState(e,t){this.trajectoryEnabledCheckbox&&(this.trajectoryEnabledCheckbox.checked=e.enableMousePrediction),this.tabEnabledCheckbox&&(this.tabEnabledCheckbox.checked=e.enableTabPrediction),this.scrollEnabledCheckbox&&(this.scrollEnabledCheckbox.checked=e.enableScrollPrediction),this.showNameTagsCheckbox&&(this.showNameTagsCheckbox.checked=t.showNameTags),this.updateSortOptionUI(t.sortElementList??"visibility"),this.historySizeSlider&&this.historyValueSpan&&(this.historySizeSlider.value=e.positionHistorySize.toString(),this.historyValueSpan.textContent=`${e.positionHistorySize} ${c}`),this.predictionTimeSlider&&this.predictionValueSpan&&(this.predictionTimeSlider.value=e.trajectoryPredictionTime.toString(),this.predictionValueSpan.textContent=`${e.trajectoryPredictionTime} ${h}`),this.tabOffsetSlider&&this.tabOffsetValueSpan&&(this.tabOffsetSlider.value=e.tabOffset.toString(),this.tabOffsetValueSpan.textContent=`${e.tabOffset} ${d}`),this.scrollMarginSlider&&this.scrollMarginValueSpan&&(this.scrollMarginSlider.value=e.scrollMargin.toString(),this.scrollMarginValueSpan.textContent=`${e.scrollMargin} px`)}refreshRegisteredElementCountDisplay(e){if(!this.elementCountSpan||!this.callbackCountSpan)return;let t=0;e.forEach(e=>{e.isIntersectingWithViewport&&t++});const n=e.size,{tab:i,mouse:s,scroll:o,total:r}=this.foresightManagerInstance.getManagerData.globalCallbackHits,a=["Element Visibility Status","━━━━━━━━━━━━━━━━━━━━━━━━━━━━",`Visible in Viewport: ${t}`,"Not in Viewport: "+(n-t),`Total Registered Elements: ${n}`,"","Note: Only elements visible in the viewport","are actively tracked by intersection observers."];this.minimizedElementCount&&(this.minimizedElementCount.textContent=`${t}/${n}`,this.minimizedElementCount.title=a.join("\n")),this.elementCountSpan.textContent=`Visible: ${t}/${n} ~ `,this.elementCountSpan.title=a.join("\n"),this.callbackCountSpan.textContent=`Mouse: ${s.hover+s.trajectory} Tab: ${i.forwards+i.reverse} Scroll: ${o.down+o.left+o.right+o.up}`,this.callbackCountSpan.title=["Callback Execution Stats","━━━━━━━━━━━━━━━━━━━━━━━━","Mouse Callbacks",` • Trajectory: ${s.trajectory}`,` • Hover: ${s.hover}`,` • Subtotal: ${s.hover+s.trajectory}`,"","Keyboard Callbacks:",` • Tab Forward: ${i.forwards}`,` • Tab Reverse: ${i.reverse}`,` • Subtotal: ${i.forwards+i.reverse}`,"","Scroll Callbacks:",` • Up: ${o.up} | Down: ${o.down}`,` • Left: ${o.left} | Right: ${o.right}`,` • Subtotal: ${o.up+o.down+o.left+o.right}`,"","Total Callbacks: "+r].join("\n")}removeElementFromList(e){if(!this.elementListItemsContainer)return;const t=this.elementListItems.get(e.element);if(t){t.remove(),this.elementListItems.delete(e.element);const n=this.foresightManagerInstance.registeredElements;this.refreshRegisteredElementCountDisplay(n),0===this.elementListItems.size&&(this.elementListItemsContainer.innerHTML=p)}}updateElementVisibilityStatus(e){if(!this.elementListItemsContainer)return;const t=this.elementListItems.get(e.element);if(!t)return void this.addElementToList(e);t.classList.toggle("not-in-viewport",!e.isIntersectingWithViewport);const n=t.querySelector(".intersecting-indicator");if(n){const t=a(e.isIntersectingWithViewport);n.textContent=t}this.refreshRegisteredElementCountDisplay(this.foresightManagerInstance.registeredElements),this.sortAndReorderElements()}sortAndReorderElements(){if(!this.elementListItemsContainer)return;const e=this.debuggerInstance.getDebuggerData.settings.sortElementList??"visibility",t=Array.from(this.foresightManagerInstance.registeredElements.values());if("insertionOrder"!==e){const n=(e,t)=>{const n=e.element.compareDocumentPosition(t.element);return n&Node.DOCUMENT_POSITION_FOLLOWING?-1:n&Node.DOCUMENT_POSITION_PRECEDING?1:0};"visibility"===e?t.sort((e,t)=>e.isIntersectingWithViewport!==t.isIntersectingWithViewport?e.isIntersectingWithViewport?-1:1:n(e,t)):"documentOrder"===e&&t.sort(n)}const n=document.createDocumentFragment();t.length&&(t.forEach(e=>{const t=this.elementListItems.get(e.element);t&&n.appendChild(t)}),this.elementListItemsContainer.innerHTML="",this.elementListItemsContainer.appendChild(n))}addElementToList(e,t=!0){if(!this.elementListItemsContainer)return;if(this.elementListItemsContainer.innerHTML===p&&(this.elementListItemsContainer.innerHTML=""),this.elementListItems.has(e.element))return;const n=document.createElement("div");n.className="element-list-item",this.updateListItemContent(n,e),this.elementListItemsContainer.appendChild(n),this.elementListItems.set(e.element,n),this.refreshRegisteredElementCountDisplay(this.foresightManagerInstance.registeredElements)}updateListItemContent(e,t){const n=a(t.isIntersectingWithViewport);e.classList.toggle("not-in-viewport",!t.isIntersectingWithViewport);let i="N/A";if(t.elementBounds.hitSlop){const{top:e,right:n,bottom:s,left:o}=t.elementBounds.hitSlop;i=`T:${e} R:${n} B:${s} L:${o}`}const s=[`${t.name||"Unnamed Element"}`,"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Viewport Status:",t.isIntersectingWithViewport?" ✓ In viewport - actively tracked by observers":" ✗ Not in viewport - not being tracked","","Hit Slop:",t.elementBounds.hitSlop?[` Top: ${t.elementBounds.hitSlop.top}px, Bottom: ${t.elementBounds.hitSlop.bottom}px `,` Right: ${t.elementBounds.hitSlop.right}px, Left: ${t.elementBounds.hitSlop.left}px`].join("\n"):" • Not defined - using element's natural boundaries",""].join("\n");e.title=s,e.innerHTML=`\n <span class="intersecting-indicator">${n}</span>\n <span class="element-name">${t.name||"Unnamed Element"}</span>\n <span class="hit-slop">${i}</span>\n `}cleanup(){this.controlsContainer?.remove(),this.controlPanelStyleElement?.remove(),this.copyTimeoutId&&(clearTimeout(this.copyTimeoutId),this.copyTimeoutId=null),this.closeSortDropdownHandler&&(document.removeEventListener("click",this.closeSortDropdownHandler),this.closeSortDropdownHandler=null),this.controlsContainer=null,this.controlPanelStyleElement=null,this.elementListItemsContainer=null,this.elementCountSpan=null,this.callbackCountSpan=null,this.elementListItems.clear(),this.containerMinimizeButton=null,this.allSettingsSectionsContainer=null,this.debuggerElementsSection=null,this.trajectoryEnabledCheckbox=null,this.tabEnabledCheckbox=null,this.scrollEnabledCheckbox=null,this.historySizeSlider=null,this.historyValueSpan=null,this.predictionTimeSlider=null,this.predictionValueSpan=null,this.tabOffsetSlider=null,this.tabOffsetValueSpan=null,this.scrollMarginSlider=null,this.scrollMarginValueSpan=null,this.showNameTagsCheckbox=null,this.sortOptionsPopup=null,this.sortButton=null,this.copySettingsButton=null}createControlContainer(){const e=document.createElement("div");return e.id="debug-controls",e.innerHTML=`\n <div class="debugger-title-container">\n <button class="minimize-button">-</button>\n <div class="title-group">\n <h2>Foresight Debugger</h2>\n <span class="info-icon" title="${["Foresight Debugger Information","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Session-Only Changes:","All adjustments made here apply only to the","current browser session and won't persist.","","Permanent Configuration:","To make lasting changes, update the initial","values in your ForesightManager.initialize().","","You can copy the current debugger settings","with the button on the right"].join("\n")}">i</span>\n </div>\n <button class="copy-settings-button" title="${["Copy Settings to Clipboard","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Copies the current configuration as a","formatted method call that you can paste","directly into your code."].join("\n")}">\n ${g}\n </button>\n <span class="minimized-element-count">\n </span>\n </div>\n\n <div class="all-settings-sections-container">\n <div class="debugger-section mouse-settings-section">\n <div class="debugger-section-header collapsible">\n <h3>Mouse Settings</h3>\n <button class="section-minimize-button">-</button>\n </div>\n <div class="debugger-section-content mouse-settings-content">\n <div class="control-row">\n <label for="trajectory-enabled">\n Enable Mouse Prediction\n <span class="info-icon" title="${["Mouse Prediction Control","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","When enabled: Predicts mouse movement","trajectory and triggers callbacks before","the cursor reaches the target element.","","When disabled: Only direct hover events","trigger actions (next to tab/scroll).","","Property: enableMousePrediction"].join("\n")}">i</span>\n </label>\n <input type="checkbox" id="trajectory-enabled">\n </div>\n <div class="control-row">\n <label for="history-size">\n History Size\n <span class="info-icon" title="${["Position History","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Controls how many past mouse positions","are stored for velocity calculations.","","Higher values:"," • More accurate trajectory predictions"," • Smoother movement detection"," • Slightly increased processing overhead","","Lower values:"," • Faster response to direction changes"," • Less memory usage"," • May be less accurate for fast movements","","Property: positionHistorySize"].join("\n")}">i</span>\n </label>\n <input type="range" id="history-size" min="2" max="30">\n <span id="history-value"></span>\n </div>\n <div class="control-row">\n <label for="prediction-time">\n Prediction Time\n <span class="info-icon" title="${["Trajectory Prediction Time","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",`How far into the future (in ${h})`,"to calculate the mouse trajectory path.","","Larger values:"," • Elements are detected sooner"," • More time for preloading/preparation"," • May trigger false positives for curved paths","","Smaller values:"," • More precise targeting"," • Reduced false positive rate"," • Less time for preparation","","Property: trajectoryPredictionTime"].join("\n")}">i</span>\n </label>\n <input type="range" id="prediction-time" min="10" max="200" step="10">\n <span id="prediction-value"></span>\n </div>\n </div>\n </div>\n\n <div class="debugger-section keyboard-settings-section">\n <div class="debugger-section-header collapsible">\n <h3>Keyboard Settings</h3>\n <button class="section-minimize-button">-</button>\n </div>\n <div class="debugger-section-content keyboard-settings-content">\n <div class="control-row">\n <label for="tab-enabled">\n Enable Tab Prediction\n <span class="info-icon" title="${["Tab Navigation Prediction","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","When enabled: Callbacks are executed when",`the user is ${this.foresightManagerInstance.getManagerData.globalSettings.tabOffset} (tabOffset) ${d} away from`,"a registered element during tab navigation.","","(works with Shift+Tab too).","","Property: enableTabPrediction"].join("\n")}">i</span>\n </label>\n <input type="checkbox" id="tab-enabled">\n </div>\n <div class="control-row">\n <label for="tab-offset">\n Tab Offset\n <span class="info-icon" title="${["Tab Offset","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Number of tabbable elements to look ahead","when predicting tab navigation targets.","","How it works:"," • Tracks the current focused element"," • Looks ahead by the specified offset"," • Triggers callbacks for registered elements"," within that range","","Property: tabOffset"].join("\n")}">i</span>\n </label>\n <input type="range" id="tab-offset" min="0" max="20" step="1">\n <span id="tab-offset-value"></span>\n </div>\n </div>\n </div>\n\n <div class="debugger-section scroll-settings-section">\n <div class="debugger-section-header collapsible">\n <h3>Scroll Settings</h3>\n <button class="section-minimize-button">-</button>\n </div>\n <div class="debugger-section-content scroll-settings-content">\n <div class="control-row">\n <label for="scroll-enabled">\n Enable Scroll Prediction\n <span class="info-icon" title="${["Scroll Prediction","━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Enables predictive scrolling based on mouse","position and scroll direction.","","When enabled, calculates scroll direction from","mouse movement and triggers callbacks for","elements that intersect the predicted path.","","Property: enableScrollPrediction"].join("\n")}">i</span>\n </label>\n <input type="checkbox" id="scroll-enabled">\n </div>\n <div class="control-row">\n <label for="scroll-margin">\n Scroll Margin\n <span class="info-icon" title="${["Scroll Margin","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Sets the pixel distance to check from the","mouse position in the scroll direction.","","Higher values check further ahead, allowing","earlier detection of elements that will come","into view during scrolling.","","Property: scrollMargin"].join("\n")}">i</span>\n </label>\n <input type="range" id="scroll-margin" min="30" max="300" step="10">\n <span id="scroll-margin-value"></span>\n </div>\n </div>\n\n <div class="debugger-section general-settings-section">\n <div class="debugger-section-header collapsible">\n <h3>General Settings</h3>\n <button class="section-minimize-button">-</button>\n </div>\n <div class="debugger-section-content general-settings-content">\n <div class="control-row">\n <label for="toggle-name-tags">\n Show Name Tags\n <span class="info-icon" title="${["Visual Debug Name Tags","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","When enabled: Displays name tags over","each registered element in debug mode.","","Property: debuggerSettings.showNameTags"].join("\n")}">i</span>\n </label>\n <input type="checkbox" id="toggle-name-tags">\n </div>\n </div>\n </div>\n </div>\n\n <div class="debugger-section debugger-elements">\n <div class="debugger-section-header elements-list-header">\n <h3>Elements <span id="element-count"></span> <span id="callback-count"></span></h3>\n <div class="header-controls">\n <div class="sort-control-container">\n <button class="sort-button" title="Change element list sort order">\n <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon></svg>\n </button>\n <div id="sort-options-popup">\n <button\n data-sort="visibility"\n title="${["Sort by Visibility","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Sorts elements by their viewport visibility","(visible elements first), with a secondary","sort by their order in the document.","","Property: debuggerSettings.sortElementList","Value: 'visibility'"].join("\n")}">\n Visibility\n </button>\n <button\n data-sort="documentOrder"\n title="${["Sort by Document Order","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Sorts elements based on their order of","appearance in the document's structure","(matching the HTML source).","","Property: debuggerSettings.sortElementList","Value: 'documentOrder'"].join("\n")}"\n >\n Document Order\n </button>\n <button\n data-sort="insertionOrder"\n title="${["Sort by Insertion Order","━━━━━━━━━━━━━━━━━━━━━━━━━━━━━","Sorts elements based on the order they","were registered with the ForesightManager.","","Property: debuggerSettings.sortElementList","Value: 'insertionOrder'"].join("\n")}"\n >\n Insertion Order\n </button>\n </div>\n </div>\n </div>\n </div>\n <div class="debugger-section-content element-list">\n <div id="element-list-items-container">\n </div>\n </div>\n </div>\n `,e}getStyles(){return'\n #debug-controls {\n position: fixed; bottom: 10px; right: 10px;\n background-color: rgba(0, 0, 0, 0.90); color: white; padding: 12px;\n border-radius: 5px; font-family: Arial, sans-serif; font-size: 13px;\n z-index: 10001; pointer-events: auto; display: flex; flex-direction: column; gap: 8px;\n width: 400px;\n transition: width 0.3s ease, height 0.3s ease;\n }\n #debug-controls.minimized {\n width: 250px;\n overflow: hidden;\n padding: 12px 0; \n }\n #debug-controls.minimized .debugger-title-container {\n padding-left: 10px; \n padding-right: 10px;\n gap: 10px; \n }\n #debug-controls.minimized .debugger-title-container h2 {\n display: inline;\n font-size: 14px;\n margin: 0;\n white-space: nowrap;\n }\n #debug-controls.minimized .info-icon {\n display: none;\n }\n\n #element-count,#callback-count {\n font-size: 12px;\n color: #9e9e9e;\n }\n\n .debugger-title-container {\n display: flex;\n align-items: center;\n justify-content: space-between; \n padding: 0 0px; \n }\n .title-group { \n display: flex;\n align-items: center;\n gap: 8px; \n\n }\n .minimize-button {\n background: none; border: none; color: white;\n font-size: 22px; cursor: pointer;\n line-height: 1;\n padding-inline: 0px;\n }\n .debugger-title-container h2 { margin: 0; font-size: 15px; }\n\n .copy-settings-button {\n background: none; border: none; color: white;\n cursor: pointer; padding: 0;\n display: flex; align-items: center; justify-content: center;\n }\n\n .copy-settings-button svg {\n width: 16px; height: 16px;\n stroke: white;\n }\n\n .minimized-element-count {\n font-size: 14px;\n min-width: 30px;\n text-align: right;\n }\n\n .all-settings-sections-container {\n display: flex;\n flex-direction: column;\n gap: 8px;\n }\n\n .debugger-section-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-top: 5px;\n margin-bottom: 2px;\n padding-bottom: 2px;\n border-bottom: 1px solid #444;\n }\n .debugger-section-header.collapsible {\n cursor: pointer;\n }\n .debugger-section-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: bold;\n color: #b0c4de;\n flex-grow: 1;\n }\n\n .section-minimize-button {\n background: none;\n border: none;\n color: white;\n font-size: 18px;\n cursor: pointer;\n padding: 0px;\n line-height: 1;\n }\n\n #debug-controls .control-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n }\n #debug-controls label {\n display: flex;\n align-items: center;\n gap: 5px;\n cursor: pointer;\n }\n #debug-controls .control-row:has(input[type="checkbox"]) label {\n flex-grow: 1;\n }\n #debug-controls .control-row input[type="checkbox"] {\n appearance: none; -webkit-appearance: none; -moz-appearance: none;\n position: relative; width: 40px; height: 18px;\n background-color: #555; border-radius: 10px; cursor: pointer;\n outline: none; transition: background-color 0.2s ease;\n vertical-align: middle; flex-shrink: 0; margin: 0;\n }\n #debug-controls .control-row input[type="checkbox"]::before {\n content: ""; position: absolute; width: 14px; height: 14px;\n border-radius: 50%; background-color: white; top: 2px; left: 2px;\n transition: transform 0.2s ease; box-shadow: 0 1px 3px rgba(0,0,0,0.4);\n }\n #debug-controls .control-row input[type="checkbox"]:checked {\n background-color: #b0c4de;\n }\n #debug-controls .control-row input[type="checkbox"]:checked::before {\n transform: translateX(22px);\n }\n #debug-controls .control-row:has(input[type="range"]) label {\n flex-basis: 170px; flex-shrink: 0;\n }\n #debug-controls input[type="range"] {\n flex-grow: 1; margin: 0; cursor: pointer; -webkit-appearance: none;\n appearance: none; background: transparent; height: 18px; vertical-align: middle;\n }\n #debug-controls input[type="range"]::-webkit-slider-runnable-track {\n height: 6px; background: #555; border-radius: 3px;\n }\n #debug-controls input[type="range"]::-moz-range-track {\n height: 6px; background: #555; border-radius: 3px;\n }\n #debug-controls input[type="range"]::-webkit-slider-thumb {\n -webkit-appearance: none; appearance: none; margin-top: -5px;\n background: #b0c4de; height: 16px; width: 16px;\n border-radius: 50%; border: 1px solid #333;\n }\n #debug-controls input[type="range"]::-moz-range-thumb {\n background: #b0c4de; height: 16px; width: 16px;\n border-radius: 50%; border: 1px solid #333; border: none;\n }\n #debug-controls .control-row:has(input[type="range"]) span:not(.info-icon) {\n width: 55px; min-width: 55px; text-align: right; flex-shrink: 0;\n }\n .info-icon {\n display: inline-flex; align-items: center; justify-content: center;\n width: 16px; height: 16px; border-radius: 50%;\n background-color: #555; color: white; font-size: 10px;\n font-style: italic; font-weight: bold; font-family: \'Georgia\', serif;\n cursor: help; user-select: none; flex-shrink: 0;\n }\n .debugger-section {\n display: flex; flex-direction: column; gap: 6px;\n }\n .debugger-section-content {\n display: none; flex-direction: column; gap: 8px;\n }\n\n /* Element List Styles */\n .elements-list-header { cursor: default; }\n .header-controls {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .sort-control-container {\n position: relative;\n }\n .sort-button {\n background: none; border: none; color: white; cursor: pointer;\n padding: 0; display: flex; align-items: center; justify-content: center;\n }\n .sort-button svg {\n width: 16px; height: 16px; stroke: #b0c4de; transition: stroke 0.2s;\n }\n .sort-button:hover svg { stroke: white; }\n \n #sort-options-popup {\n position: absolute;\n bottom: calc(100% + 5px);\n right: -5px;\n z-index: 10;\n display: none;\n flex-direction: column;\n gap: 4px;\n background-color: #3a3a3a;\n border: 1px solid #555;\n border-radius: 4px;\n padding: 3px;\n width: 200px;\n box-shadow: 0 4px 8px rgba(0,0,0,0.3);\n }\n #sort-options-popup.active {\n display: flex;\n }\n #sort-options-popup button {\n background: none; border: none; color: #ccc;\n font-size: 12px; text-align: left; padding: 5px 8px;\n cursor: pointer; border-radius: 3px;\n transition: background-color 0.2s, color 0.2s;\n display: flex;\n align-items: center;\n height: 26px;\n }\n #sort-options-popup button:hover {\n background-color: #555;\n color: white;\n }\n #sort-options-popup button.active-sort-option {\n color: #b0c4de;\n font-weight: bold;\n }\n #sort-options-popup button.active-sort-option::before {\n content: \'✓\';\n margin-right: 6px;\n width: 10px;\n }\n #sort-options-popup button::before {\n content: \'\';\n margin-right: 6px;\n width: 10px;\n }\n\n .element-list { /* Scroll container */\n min-height: 237px;\n max-height: 237px; \n overflow-y: auto;\n background-color: rgba(20, 20, 20, 0.5);\n border-radius: 3px;\n padding: 0;\n display: flex;\n }\n\n /* Modern Scrollbar Styling */\n .element-list::-webkit-scrollbar { width: 8px; }\n .element-list::-webkit-scrollbar-track { background: rgba(30, 30, 30, 0.5); border-radius: 4px; }\n .element-list::-webkit-scrollbar-thumb { background-color: rgba(176, 196, 222, 0.5); border-radius: 4px; border: 2px solid rgba(0, 0, 0, 0.2); }\n .element-list::-webkit-scrollbar-thumb:hover { background-color: rgba(176, 196, 222, 0.7); }\n .element-list { scrollbar-width: thin; scrollbar-color: rgba(176, 196, 222, 0.5) rgba(30, 30, 30, 0.5); }\n\n #element-list-items-container { \n display: flex;\n flex-wrap: wrap;\n gap: 3px;\n padding: 6px;\n min-height: 225px;\n box-sizing: border-box;\n align-content: flex-start;\n }\n #element-list-items-container > em {\n flex-basis: 100%;\n text-align: center;\n padding: 10px 0;\n font-style: italic;\n color: #ccc;\n font-size: 12px;\n }\n .element-list-item {\n flex-basis: calc((100% - (0 * 3px)) / 1);\n flex-grow: 0;\n flex-shrink: 0;\n height: 35px;\n box-sizing: border-box;\n padding: 3px 5px;\n border-radius: 2px;\n display: flex;\n align-items: center;\n gap: 5px;\n background-color: rgba(50,50,50,0.7);\n transition: background-color 0.2s ease, opacity 0.2s ease;\n font-size: 11px; \n overflow: hidden;\n }\n \n /* Viewport intersection styling */\n .element-list-item.not-in-viewport { opacity: 0.4; }\n \n .element-list-item .element-name {\n flex-grow: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 12px; \n font-weight: bold;\n }\n .element-list-item .intersecting-indicator {\n font-size: 12px;\n flex-shrink: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n }\n .element-list-item .hit-behavior,\n .element-list-item .hit-slop {\n font-size: 10px; \n color: #b0b0b0;\n padding: 2px 5px; \n border-radius: 3px; \n background-color: rgba(0,0,0,0.2);\n flex-shrink: 0;\n }\n '}}function b(e,t,n){const{expandedOverlay:i,nameLabel:s}=e,{expandedRect:o}=t.elementBounds,r=o.right-o.left,a=o.bottom-o.top;i.style.width=`${r}px`,i.style.height=`${a}px`,i.style.transform=`translate3d(${o.left}px, ${o.top}px, 0)`,i.style.display="block",s.textContent=t.name,""!==t.name&&n?(s.style.display="block",s.style.transform=`translate3d(${o.left}px, ${o.top-25}px, 0)`):s.style.display="none"}function m(e,t){return void 0!==e&&e!==t}class y{constructor(e){this.callbackAnimations=new Map,this._debuggerSettings={showDebugger:true,isControlPanelDefaultMinimized:false,showNameTags:l,sortElementList:"visibility"},this.debugElementOverlays=new Map,this.predictedMouseIndicator=null,this.mouseTrajectoryLine=null,this.scrollTrajectoryLine=null,this.managerSubscriptionsController=null,this.animationPositionObserver=null,this.handleAnimationPositionChange=e=>{for(const t of e){const e=this.callbackAnimations.get(t.target);if(e){const n=t.boundingClientRect,{hitSlop:i,overlay:s}=e,o=n.left-i.left,r=n.top-i.top,a=n.width+i.left+i.right,l=n.height+i.top+i.bottom;s.style.transform=`translate3d(${o}px, ${r}px, 0)`,s.style.width=`${a}px`,s.style.height=`${l}px`}}},this.handleElementDataUpdated=e=>{switch(e.updatedProp){case"bounds":this.createOrUpdateElementOverlay(e.elementData);break;case"visibility":e.elementData.isIntersectingWithViewport||this.removeElementOverlay(e.elementData),this.controlPanel?.updateElementVisibilityStatus(e.elementData)}},this.handleUnregisterElement=e=>{this.controlPanel?.removeElementFromList(e.elementData),this.removeElementOverlay(e.elementData)},this.handleCallbackFired=e=>{this.showCallbackAnimation(e.elementData)},this.handleRegisterElement=e=>{this.createOrUpdateElementOverlay(e.elementData),this.controlPanel.addElementToList(e.elementData)},this.handleMouseTrajectoryUpdate=e=>{if(!this.shadowRoot||!this.debugContainer)return;if(!this.predictedMouseIndicator||!this.mouseTrajectoryLine)return;this.scrollTrajectoryLine&&(this.scrollTrajectoryLine.style.display="none");const{predictedPoint:t,currentPoint:n}=e.trajectoryPositions;if(this.predictedMouseIndicator.style.transform=`translate3d(${t.x}px, ${t.y}px, 0) translate3d(-50%, -50%, 0)`,this.predictedMouseIndicator.style.display=e.predictionEnabled?"block":"none",0===t.x&&0===t.y)return void(this.predictedMouseIndicator.style.display="none");if(!e.predictionEnabled)return void(this.mouseTrajectoryLine.style.display="none");const i=t.x-n.x,s=t.y-n.y,o=Math.sqrt(i*i+s*s),r=180*Math.atan2(s,i)/Math.PI;this.mouseTrajectoryLine.style.transform=`translate3d(${n.x}px, ${n.y}px, 0) rotate(${r}deg)`,this.mouseTrajectoryLine.style.width=`${o}px`,this.mouseTrajectoryLine.style.display="block"},this.handleScrollTrajectoryUpdate=e=>{if(!this.scrollTrajectoryLine)return;const t=e.predictedPoint.x-e.currentPoint.x,n=e.predictedPoint.y-e.currentPoint.y,i=Math.sqrt(t*t+n*n),s=180*Math.atan2(n,t)/Math.PI;this.scrollTrajectoryLine.style.transform=`translate3d(${e.currentPoint.x}px, ${e.currentPoint.y}px, 0) rotate(${s}deg)`,this.scrollTrajectoryLine.style.width=`${i}px`,this.scrollTrajectoryLine.style.display="block"},this.handleSettingsChanged=e=>{this.controlPanel?.updateControlsState(e.managerData.globalSettings,this._debuggerSettings)},this.foresightManagerInstance=e}get getDebuggerData(){return{settings:this._debuggerSettings}}static initialize(e,t){if("undefined"==typeof window||"undefined"==typeof window||"ontouchstart"in window)return null;y.isInitiated||(y.debuggerInstance=new y(e));const n=y.debuggerInstance;return n.subscribeToManagerEvents(),n.alterDebuggerSettings(t),n.shadowHost||n._setupDOM(),n}static get instance(){if(!y.debuggerInstance)throw new Error("ForesightDebugger has not been initialized. Call ForesightDebugger.initialize() first.");return y.debuggerInstance}_setupDOM(){this.shadowHost||(this.shadowHost=o("div",document.body,{id:"jsforesight-debugger-shadow-host"}),this.shadowRoot=this.shadowHost.attachShadow({mode:"open"}),this.debugContainer=o("div",this.shadowRoot,{id:"jsforesight-debug-container"}),this.predictedMouseIndicator=o("div",this.debugContainer,{className:"jsforesight-mouse-predicted"}),this.mouseTrajectoryLine=o("div",this.debugContainer,{className:"jsforesight-trajectory-line"}),this.scrollTrajectoryLine=o("div",this.debugContainer,{className:"jsforesight-scroll-trajectory-line"}),this.controlPanel=u.initialize(this.foresightManagerInstance,y.debuggerInstance,this.shadowRoot,this._debuggerSettings),r(f,this.shadowRoot,"screen-visuals"),this.animationPositionObserver=new i(this.handleAnimationPositionChange))}static get isInitiated(){return!!y.debuggerInstance}alterDebuggerSettings(e){m(e?.showNameTags,this._debuggerSettings.showNameTags)&&(this._debuggerSettings.showNameTags=e.showNameTags,this.toggleNameTagVisibility()),m(e?.isControlPanelDefaultMinimized,this._debuggerSettings.isControlPanelDefaultMinimized)&&(this._debuggerSettings.isControlPanelDefaultMinimized=e.isControlPanelDefaultMinimized),m(e?.sortElementList,this._debuggerSettings.sortElementList)&&(this._debuggerSettings.sortElementList=e.sortElementList),m(e?.showDebugger,this._debuggerSettings.showDebugger)&&(this._debuggerSettings.showDebugger=e.showDebugger,this._debuggerSettings.showDebugger?y.initialize(this.foresightManagerInstance):this.cleanup())}subscribeToManagerEvents(){this.managerSubscriptionsController=new AbortController;const e=this.managerSubscriptionsController.signal,t=this.foresightManagerInstance;t.addEventListener("elementRegistered",this.handleRegisterElement,{signal:e}),t.addEventListener("elementUnregistered",this.handleUnregisterElement,{signal:e}),t.addEventListener("elementDataUpdated",this.handleElementDataUpdated,{signal:e}),t.addEventListener("mouseTrajectoryUpdate",this.handleMouseTrajectoryUpdate,{signal:e}),t.addEventListener("scrollTrajectoryUpdate",this.handleScrollTrajectoryUpdate,{signal:e}),t.addEventListener("managerSettingsChanged",this.handleSettingsChanged,{signal:e}),t.addEventListener("callbackFired",this.handleCallbackFired,{signal:e})}createElementOverlays(e){const t={expandedOverlay:o("div",this.debugContainer,{className:"jsforesight-expanded-overlay",data:e.name}),nameLabel:o("div",this.debugContainer,{className:"jsforesight-name-label"})};return this.debugElementOverlays.set(e.element,t),t}createOrUpdateElementOverlay(e){if(!this.debugContainer||!this.shadowRoot)return;let t=this.debugElementOverlays.get(e.element);t||(t=this.createElementOverlays(e)),b(t,e,this._debuggerSettings.showNameTags??l)}toggleNameTagVisibility(){this.foresightManagerInstance.registeredElements.forEach(e=>{const t=this.debugElementOverlays.get(e.element);t&&b(t,e,this._debuggerSettings.showNameTags??l)})}removeElementOverlay(e){const t=this.debugElementOverlays.get(e.element);t&&(t.expandedOverlay.remove(),t.nameLabel.remove(),this.debugElementOverlays.delete(e.element))}showCallbackAnimation(e){const{element:t,elementBounds:n}=e,i=this.callbackAnimations.get(t);i&&(clearTimeout(i.timeoutId),i.overlay.remove(),this.animationPositionObserver?.unobserve(t),this.callbackAnimations.delete(t));const s=o("div",this.debugContainer,{className:"jsforesight-callback-indicator"}),{left:r,top:a,right:l,bottom:c}=n.expandedRect,d=l-r,h=c-a;s.style.display="block",s.style.transform=`translate3d(${r}px, ${a}px, 0)`,s.style.width=`${d}px`,s.style.height=`${h}px`,s.classList.add("animate");const g=setTimeout(()=>{s.remove(),this.callbackAnimations.delete(t),this.animationPositionObserver?.unobserve(t)},500);this.callbackAnimations.set(t,{hitSlop:e.elementBounds.hitSlop,overlay:s,timeoutId:g}),this.animationPositionObserver?.observe(t)}cleanup(){this.managerSubscriptionsController?.abort(),this.controlPanel?.cleanup(),this.shadowHost?.remove(),this.debugElementOverlays.clear(),this.shadowHost=null,this.shadowRoot=null,this.debugContainer=null,this.predictedMouseIndicator=null,this.mouseTrajectoryLine=null,this.scrollTrajectoryLine=null,this.controlPanel=null}}const f='\n #jsforesight-debug-container { \n position: fixed; top: 0; left: 0; width: 100%; height: 100%;\n pointer-events: none; z-index: 9999;\n }\n\n .jsforesight-expanded-overlay, \n .jsforesight-name-label, \n .jsforesight-callback-indicator,\n .jsforesight-mouse-predicted,\n .jsforesight-scroll-trajectory-line,\n .jsforesight-trajectory-line {\n position: absolute;\n top: 0;\n left: 0;\n will-change: transform; \n }\n .jsforesight-trajectory-line{\n display: none;\n }\n .jsforesight-expanded-overlay {\n border: 1px dashed rgba(100, 116, 139, 0.4);\n background-color: rgba(100, 116, 139, 0.05);\n box-sizing: border-box;\n border-radius: 8px;\n }\n .jsforesight-mouse-predicted {\n display: none !important;\n /* transform is now set dynamically via JS for performance */\n }\n .jsforesight-trajectory-line {\n he