{"version":3,"sources":["../src/hooks/useUserActivity.ts"],"names":["useState","useRef","useEffect"],"mappings":"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","file":"chunk-ZYPGYFWD.cjs","sourcesContent":["/**\n * useUserActivity — tracks typing, scrolling, mouse, idle, battery, network,\n * orientation, visibility. Ported from agi-diy/context-injector.js.\n */\nimport { useEffect, useRef, useState } from 'react'\n\nexport interface UserActivityState {\n  isActive: boolean\n  isTyping: boolean\n  isScrolling: boolean\n  isMovingMouse: boolean\n  idleSeconds: number\n  mousePosition: { x: number; y: number }\n  scrollPosition: { x: number; y: number }\n  focusedElement: string | null\n  visibilityState: 'visible' | 'hidden' | 'prerender' | 'unloaded'\n  windowFocused: boolean\n  screenSize: { width: number; height: number }\n  touchDevice: boolean\n  batteryLevel: number | null\n  batteryCharging: boolean | null\n  networkType: string | null\n  networkDownlink: number | null\n  orientation: { alpha: number; beta: number; gamma: number } | null\n}\n\nconst initial: UserActivityState = {\n  isActive: true, isTyping: false, isScrolling: false, isMovingMouse: false,\n  idleSeconds: 0,\n  mousePosition: { x: 0, y: 0 },\n  scrollPosition: { x: 0, y: 0 },\n  focusedElement: null,\n  visibilityState: typeof document !== 'undefined' ? document.visibilityState : 'visible',\n  windowFocused: typeof document !== 'undefined' ? document.hasFocus() : true,\n  screenSize: typeof window !== 'undefined' ? { width: window.innerWidth, height: window.innerHeight } : { width: 0, height: 0 },\n  touchDevice: typeof window !== 'undefined' && 'ontouchstart' in window,\n  batteryLevel: null, batteryCharging: null,\n  networkType: null, networkDownlink: null,\n  orientation: null,\n}\n\nexport function useUserActivity(enabled = true): UserActivityState {\n  const [state, setState] = useState<UserActivityState>(initial)\n  const lastActivityRef = useRef(Date.now())\n  const timersRef = useRef<Record<string, ReturnType<typeof setTimeout> | undefined>>({})\n\n  useEffect(() => {\n    if (!enabled) return\n    const update = (patch: Partial<UserActivityState>) => {\n      setState(prev => ({ ...prev, ...patch }))\n    }\n    const touchActivity = () => { lastActivityRef.current = Date.now() }\n    const debounce = (key: string, fn: () => void, ms: number) => {\n      clearTimeout(timersRef.current[key])\n      timersRef.current[key] = setTimeout(fn, ms)\n    }\n\n    const onMouse = (e: MouseEvent) => {\n      update({ mousePosition: { x: e.clientX, y: e.clientY }, isMovingMouse: true })\n      touchActivity()\n      debounce('mouse', () => setState(s => ({ ...s, isMovingMouse: false })), 500)\n    }\n    const onKey = () => {\n      update({ isTyping: true })\n      touchActivity()\n      debounce('type', () => setState(s => ({ ...s, isTyping: false })), 1000)\n    }\n    const onScroll = () => {\n      update({ scrollPosition: { x: window.scrollX, y: window.scrollY }, isScrolling: true })\n      touchActivity()\n      debounce('scroll', () => setState(s => ({ ...s, isScrolling: false })), 500)\n    }\n    const onClick = () => touchActivity()\n    const onTouch = () => touchActivity()\n    const onFocusIn = (e: FocusEvent) => {\n      const target = e.target as HTMLElement\n      update({ focusedElement: target?.tagName?.toLowerCase() || null })\n      touchActivity()\n    }\n    const onVis = () => update({ visibilityState: document.visibilityState })\n    const onFocus = () => { update({ windowFocused: true }); touchActivity() }\n    const onBlur = () => update({ windowFocused: false })\n    const onResize = () => update({ screenSize: { width: window.innerWidth, height: window.innerHeight } })\n    const onOrient = (e: DeviceOrientationEvent) => {\n      update({ orientation: { alpha: Math.round(e.alpha || 0), beta: Math.round(e.beta || 0), gamma: Math.round(e.gamma || 0) } })\n    }\n\n    document.addEventListener('mousemove', onMouse, { passive: true })\n    document.addEventListener('keydown', onKey, { passive: true })\n    window.addEventListener('scroll', onScroll, { passive: true })\n    document.addEventListener('click', onClick, { passive: true })\n    document.addEventListener('touchstart', onTouch, { passive: true })\n    document.addEventListener('focusin', onFocusIn, { passive: true })\n    document.addEventListener('visibilitychange', onVis)\n    window.addEventListener('focus', onFocus)\n    window.addEventListener('blur', onBlur)\n    window.addEventListener('resize', onResize, { passive: true })\n    window.addEventListener('deviceorientation', onOrient, { passive: true })\n\n    // Battery (tracked for cleanup)\n    interface BatteryManager {\n      level: number\n      charging: boolean\n      addEventListener(event: string, cb: () => void): void\n      removeEventListener(event: string, cb: () => void): void\n    }\n    interface NetInfo {\n      effectiveType?: string\n      downlink?: number\n      addEventListener(event: string, cb: () => void): void\n      removeEventListener(event: string, cb: () => void): void\n    }\n    const nav = navigator as Navigator & {\n      getBattery?: () => Promise<BatteryManager>\n      connection?: NetInfo\n    }\n    let batteryRef: BatteryManager | null = null\n    const onBatteryLevel = () => batteryRef && update({ batteryLevel: Math.round(batteryRef.level * 100) })\n    const onBatteryCharging = () => batteryRef && update({ batteryCharging: batteryRef.charging })\n    nav.getBattery?.().then((battery: BatteryManager) => {\n      batteryRef = battery\n      update({ batteryLevel: Math.round(battery.level * 100), batteryCharging: battery.charging })\n      battery.addEventListener('levelchange', onBatteryLevel)\n      battery.addEventListener('chargingchange', onBatteryCharging)\n    }).catch(() => {})\n\n    // Network (tracked for cleanup)\n    const conn = nav.connection\n    const onConn = () => conn && update({ networkType: conn.effectiveType, networkDownlink: conn.downlink })\n    if (conn) {\n      update({ networkType: conn.effectiveType, networkDownlink: conn.downlink })\n      conn.addEventListener('change', onConn)\n    }\n\n    // Idle ticker\n    const idleInterval = setInterval(() => {\n      const idleSeconds = Math.round((Date.now() - lastActivityRef.current) / 1000)\n      setState(s => ({ ...s, idleSeconds, isActive: idleSeconds < 30 }))\n    }, 1000)\n\n    return () => {\n      document.removeEventListener('mousemove', onMouse)\n      document.removeEventListener('keydown', onKey)\n      window.removeEventListener('scroll', onScroll)\n      document.removeEventListener('click', onClick)\n      document.removeEventListener('touchstart', onTouch)\n      document.removeEventListener('focusin', onFocusIn)\n      document.removeEventListener('visibilitychange', onVis)\n      window.removeEventListener('focus', onFocus)\n      window.removeEventListener('blur', onBlur)\n      window.removeEventListener('resize', onResize)\n      window.removeEventListener('deviceorientation', onOrient)\n      clearInterval(idleInterval)\n      Object.values(timersRef.current).forEach(clearTimeout)\n      if (batteryRef) {\n        batteryRef.removeEventListener('levelchange', onBatteryLevel)\n        batteryRef.removeEventListener('chargingchange', onBatteryCharging)\n      }\n      if (conn) conn.removeEventListener('change', onConn)\n    }\n  }, [enabled])\n\n  return state\n}\n\n/** Format for system prompt injection. */\nexport function activityToContextString(s: UserActivityState): string {\n  const lines: string[] = ['### User Activity']\n  lines.push(`- Active: ${s.isActive ? 'yes' : 'no'} (idle ${s.idleSeconds}s)`)\n  const actions = [\n    s.isTyping && 'typing', s.isScrolling && 'scrolling', s.isMovingMouse && 'moving mouse',\n  ].filter(Boolean).join(', ')\n  if (actions) lines.push(`- Doing: ${actions}`)\n  lines.push(`- Window: ${s.windowFocused ? 'focused' : 'unfocused'}, ${s.visibilityState}`)\n  lines.push(`- Screen: ${s.screenSize.width}×${s.screenSize.height}${s.touchDevice ? ' (touch)' : ''}`)\n  if (s.batteryLevel !== null) lines.push(`- Battery: ${s.batteryLevel}%${s.batteryCharging ? ' charging' : ''}`)\n  if (s.networkType) lines.push(`- Network: ${s.networkType}${s.networkDownlink ? ` (${s.networkDownlink}Mbps)` : ''}`)\n  if (s.orientation) lines.push(`- Orient: α${s.orientation.alpha}° β${s.orientation.beta}° γ${s.orientation.gamma}°`)\n  return lines.join('\\n')\n}\n"]}