{"version":3,"sources":["../src/utils/MouseTracker.ts","../src/utils/CursorCoordinator.ts"],"sourcesContent":["import { isSSR } from './ssr';\n\n// Types for the mouse tracker\nexport interface MouseSubscription {\n  id: string;\n  callback: (position: { x: number; y: number }) => void;\n  throttleMs?: number;\n}\n\ninterface SubscriberState {\n  subscription: MouseSubscription;\n  lastCallTime: number;\n  timeoutId: NodeJS.Timeout | null;\n}\n\nclass MouseTracker {\n  private static instance: MouseTracker | null = null;\n  private subscribers = new Map<string, SubscriberState>();\n  private currentPosition: { x: number; y: number } | null = null;\n  private isListening = false;\n  private rafId: number | null = null;\n\n  private constructor() {\n    this.handleMouseMove = this.handleMouseMove.bind(this);\n  }\n\n  static getInstance(): MouseTracker {\n    if (!MouseTracker.instance) {\n      MouseTracker.instance = new MouseTracker();\n    }\n    return MouseTracker.instance;\n  }\n\n  subscribe(subscription: MouseSubscription): () => void {\n    // Skip subscription during SSR\n    if (isSSR()) {\n      // eslint-disable-next-line @typescript-eslint/no-empty-function\n      return () => {}; // No-op cleanup\n    }\n\n    const subscriberState: SubscriberState = {\n      subscription,\n      lastCallTime: 0,\n      timeoutId: null,\n    };\n\n    this.subscribers.set(subscription.id, subscriberState);\n    \n    // Start listening if this is the first subscriber\n    if (!this.isListening) {\n      this.startListening();\n    }\n\n    // Immediately notify new subscriber if we have a current position\n    if (this.currentPosition) {\n      // Use setTimeout to avoid synchronous callback during subscription\n      setTimeout(() => {\n        if (this.subscribers.has(subscription.id)) {\n          this.callSubscriber(subscriberState);\n        }\n      }, 0);\n    }\n\n    // Return unsubscribe function\n    return () => {\n      this.unsubscribe(subscription.id);\n    };\n  }\n\n  private unsubscribe(id: string): void {\n    const subscriberState = this.subscribers.get(id);\n    if (subscriberState?.timeoutId) {\n      clearTimeout(subscriberState.timeoutId);\n    }\n    \n    this.subscribers.delete(id);\n    \n    // Stop listening if no more subscribers\n    if (this.subscribers.size === 0) {\n      this.stopListening();\n    }\n  }\n\n  private startListening(): void {\n    if (this.isListening || isSSR()) return;\n    \n    document.addEventListener('mousemove', this.handleMouseMove);\n    this.isListening = true;\n    \n    // Store position globally for persistence across navigation\n    this.loadGlobalPosition();\n  }\n\n  private stopListening(): void {\n    if (!this.isListening) return;\n    \n    document.removeEventListener('mousemove', this.handleMouseMove);\n    this.isListening = false;\n    \n    if (this.rafId) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n  }\n\n  private handleMouseMove(event: MouseEvent): void {\n    const newPosition = { x: event.clientX, y: event.clientY };\n    \n    this.currentPosition = newPosition;\n    this.saveGlobalPosition();\n    \n    // Use RAF to batch notifications for better performance\n    if (this.rafId === null) {\n      this.rafId = requestAnimationFrame(() => {\n        this.notifySubscribers();\n        this.rafId = null;\n      });\n    }\n  }\n\n  private notifySubscribers(): void {\n    // Don't notify if we don't have a position yet\n    if (!this.currentPosition) return;\n    \n    const currentTime = Date.now();\n    \n    this.subscribers.forEach((subscriberState) => {\n      const { subscription, lastCallTime, timeoutId } = subscriberState;\n      const { throttleMs = 0 } = subscription;\n\n      // Apply throttling if specified\n      if (throttleMs > 0) {\n        const timeSinceLastCall = currentTime - lastCallTime;\n        \n        if (timeSinceLastCall >= throttleMs) {\n          // Call immediately\n          this.callSubscriber(subscriberState);\n        } else {\n          // Schedule delayed call\n          if (timeoutId) {\n            clearTimeout(timeoutId);\n          }\n          \n          subscriberState.timeoutId = setTimeout(() => {\n            this.callSubscriber(subscriberState);\n            subscriberState.timeoutId = null;\n          }, throttleMs - timeSinceLastCall);\n        }\n      } else {\n        // No throttling, call immediately\n        this.callSubscriber(subscriberState);\n      }\n    });\n  }\n\n  private callSubscriber(subscriberState: SubscriberState): void {\n    if (!this.currentPosition) return;\n    \n    subscriberState.subscription.callback(this.currentPosition);\n    subscriberState.lastCallTime = Date.now();\n  }\n\n\n\n  private loadGlobalPosition(): void {\n    // Try to load position from global storage for persistence across navigation\n    try {\n      const stored = (window as any).__mouseTrackerPosition__;\n      if (stored && typeof stored.x === 'number' && typeof stored.y === 'number') {\n        this.currentPosition = stored;\n      }\n    } catch {\n      // Ignore errors when accessing global position\n    }\n  }\n\n  private saveGlobalPosition(): void {\n    // Save current position globally for persistence\n    if (this.currentPosition) {\n      try {\n        (window as any).__mouseTrackerPosition__ = { ...this.currentPosition };\n      } catch {\n        // Ignore errors when saving global position\n      }\n    }\n  }\n\n  // Public method to get current position (useful for initial positioning)\n  getCurrentPosition(): { x: number; y: number } | null {\n    return this.currentPosition ? { ...this.currentPosition } : null;\n  }\n\n  // For testing/debugging\n  getSubscriberCount(): number {\n    return this.subscribers.size;\n  }\n\n  // Cleanup method for testing\n  static resetInstance(): void {\n    if (MouseTracker.instance) {\n      MouseTracker.instance.stopListening();\n      MouseTracker.instance = null;\n    }\n  }\n}\n\nexport { MouseTracker }; ","import { isSSR } from './ssr';\nimport { MouseTracker } from './MouseTracker';\n\nexport interface CursorSubscription {\n  id: string;\n  onPositionChange: (position: { x: number; y: number }) => void;\n  throttleMs?: number;\n}\n\ninterface CoordinatorSubscriberState {\n  subscription: CursorSubscription;\n  lastCallTime: number;\n  timeoutId: NodeJS.Timeout | null;\n}\n\nclass CursorCoordinator {\n  private static instance: CursorCoordinator | null = null;\n  private subscribers = new Map<string, CoordinatorSubscriberState>();\n  private mouseTracker: MouseTracker;\n  private currentGlobalPosition: { x: number; y: number } | null = null;\n  private isListening = false;\n  private rafId: number | null = null;\n\n  private constructor() {\n    this.mouseTracker = MouseTracker.getInstance();\n    this.handleLayoutChange = this.handleLayoutChange.bind(this);\n  }\n\n  static getInstance(): CursorCoordinator {\n    if (!CursorCoordinator.instance) {\n      CursorCoordinator.instance = new CursorCoordinator();\n    }\n    return CursorCoordinator.instance;\n  }\n\n  subscribe(subscription: CursorSubscription): () => void {\n    if (isSSR()) {\n      // eslint-disable-next-line @typescript-eslint/no-empty-function\n      return () => {};\n    }\n\n    const subscriberState: CoordinatorSubscriberState = {\n      subscription,\n      lastCallTime: 0,\n      timeoutId: null,\n    };\n\n    this.subscribers.set(subscription.id, subscriberState);\n    \n    // Start listening if this is the first subscriber\n    if (!this.isListening) {\n      this.startListening();\n    }\n\n    // Subscribe to mouse tracker for position updates\n    const mouseUnsubscribe = this.mouseTracker.subscribe({\n      id: `coordinator-${subscription.id}`,\n      callback: (position) => {\n        this.updateGlobalPosition(position);\n        this.notifyPositionChange(subscription.id, position);\n      },\n      throttleMs: subscription.throttleMs,\n    });\n\n    // Return combined unsubscribe function\n    return () => {\n      mouseUnsubscribe();\n      this.unsubscribe(subscription.id);\n    };\n  }\n\n  private unsubscribe(id: string): void {\n    const subscriberState = this.subscribers.get(id);\n    if (subscriberState?.timeoutId) {\n      clearTimeout(subscriberState.timeoutId);\n    }\n    \n    this.subscribers.delete(id);\n    \n    // Stop listening if no more subscribers\n    if (this.subscribers.size === 0) {\n      this.stopListening();\n    }\n  }\n\n  private startListening(): void {\n    if (this.isListening || isSSR()) return;\n    \n    // Listen for layout-changing events\n    document.addEventListener('scroll', this.handleLayoutChange, true);\n    window.addEventListener('resize', this.handleLayoutChange);\n    this.isListening = true;\n  }\n\n  private stopListening(): void {\n    if (!this.isListening) return;\n    \n    document.removeEventListener('scroll', this.handleLayoutChange, true);\n    window.removeEventListener('resize', this.handleLayoutChange);\n    this.isListening = false;\n    \n    if (this.rafId) {\n      cancelAnimationFrame(this.rafId);\n      this.rafId = null;\n    }\n  }\n\n  private handleLayoutChange(): void {\n    // Batch layout change notifications using RAF\n    if (this.rafId === null) {\n      this.rafId = requestAnimationFrame(() => {\n        this.notifyAllSubscribersWithCurrentPosition();\n        this.rafId = null;\n      });\n    }\n  }\n\n  private updateGlobalPosition(position: { x: number; y: number }): void {\n    this.currentGlobalPosition = position;\n  }\n\n  private notifyPositionChange(subscriptionId: string, position: { x: number; y: number }): void {\n    const subscriberState = this.subscribers.get(subscriptionId);\n    if (!subscriberState) return;\n\n    subscriberState.subscription.onPositionChange(position);\n  }\n\n  private notifyAllSubscribersWithCurrentPosition(): void {\n    if (!this.currentGlobalPosition) return;\n    \n    this.subscribers.forEach((subscriberState) => {\n      subscriberState.subscription.onPositionChange(this.currentGlobalPosition!);\n    });\n  }\n\n  getSubscriberCount(): number {\n    return this.subscribers.size;\n  }\n\n  // Cleanup method for testing\n  static resetInstance(): void {\n    if (CursorCoordinator.instance) {\n      CursorCoordinator.instance.stopListening();\n      CursorCoordinator.instance = null;\n    }\n  }\n}\n\nexport { CursorCoordinator }; "],"mappings":"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","names":[]}