/** * Route Detection Utilities * Shared logic for detecting route changes across different AI chat platforms */ import { UI_CONFIG } from "./constants"; export interface RouteDetectionConfig { platform: string; selectors: string[]; reinitCallback: () => void; checkInterval?: number; observerThrottleDelay?: number; } export interface RouteDetectionCleanup { observer: MutationObserver | null; urlCheckInterval: NodeJS.Timeout | null; observerThrottle: NodeJS.Timeout | null; } export function createRouteDetection( config: RouteDetectionConfig, cleanup: RouteDetectionCleanup, ): void { if (cleanup.observer) { cleanup.observer.disconnect(); } if (cleanup.urlCheckInterval) { clearInterval(cleanup.urlCheckInterval); } if (cleanup.observerThrottle) { clearTimeout(cleanup.observerThrottle); cleanup.observerThrottle = null; } let currentUrl = window.location.href; const checkForRouteChange = () => { if (window.location.href !== currentUrl) { currentUrl = window.location.href; console.log(`${config.platform} route changed, re-initializing`); setTimeout(config.reinitCallback, 1000); } }; cleanup.urlCheckInterval = setInterval( checkForRouteChange, config.checkInterval || UI_CONFIG.ROUTE_CHECK_INTERVAL, ); cleanup.observer = new MutationObserver((mutations) => { if (cleanup.observerThrottle) { return; } let shouldRecheck = false; mutations.forEach((mutation) => { if (mutation.type === "childList" && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { if (node.nodeType === Node.ELEMENT_NODE) { const element = node as Element; for (const selector of config.selectors) { if ( element.querySelector?.(selector) || element.matches?.(selector) ) { shouldRecheck = true; break; } } } }); } }); if (shouldRecheck) { cleanup.observerThrottle = setTimeout(() => { try { cleanup.observerThrottle = null; config.reinitCallback(); } catch (error) { console.error( `Error in ${config.platform} observer callback:`, error, ); } }, config.observerThrottleDelay || UI_CONFIG.OBSERVER_THROTTLE_DELAY); } }); try { cleanup.observer.observe(document.body, { childList: true, subtree: true, }); } catch (error) { console.error(`Failed to set up ${config.platform} route observer:`, error); if (cleanup.urlCheckInterval) { clearInterval(cleanup.urlCheckInterval); } cleanup.urlCheckInterval = setInterval(checkForRouteChange, 1000); } } export function cleanupRouteDetection(cleanup: RouteDetectionCleanup): void { if (cleanup.observer) { cleanup.observer.disconnect(); cleanup.observer = null; } if (cleanup.urlCheckInterval) { clearInterval(cleanup.urlCheckInterval); cleanup.urlCheckInterval = null; } if (cleanup.observerThrottle) { clearTimeout(cleanup.observerThrottle); cleanup.observerThrottle = null; } }