{"version":3,"file":"react.mjs","sources":["../../lib/vowel/hooks/useSyncContext.ts","../../lib/vowel/components/VowelMicrophone.tsx"],"sourcesContent":["/**\n * @fileoverview Sync Context Hook - Automatically sync context with Vowel client\n * \n * This hook automatically updates the Vowel client's context whenever the provided\n * context value changes. It's useful for keeping the AI aware of dynamic app state\n * without manually calling updateContext.\n * \n * @example\n * ```tsx\n * function ProductPage({ productId }: { productId: string }) {\n *   const product = useProduct(productId);\n *   \n *   // Automatically sync product context to Vowel\n *   useSyncContext({\n *     page: 'product',\n *     productId: product.id,\n *     productName: product.name,\n *     price: product.price,\n *   });\n *   \n *   return <div>{product.name}</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * function CheckoutPage() {\n *   const cart = useCart();\n *   \n *   // Sync cart context, clear on unmount\n *   useSyncContext({\n *     page: 'checkout',\n *     cartTotal: cart.total,\n *     itemCount: cart.items.length,\n *   });\n *   \n *   return <div>Checkout</div>;\n * }\n * ```\n * \n * @module @vowel.to/client/hooks\n * @author vowel.to\n * @license MIT\n */\n\nimport { useEffect, useRef } from 'react';\nimport { useVowel } from '../components/VowelProviderSimple';\n\n/**\n * Hook that automatically syncs context to the Vowel client\n * \n * Updates the Vowel client's context whenever the provided context value changes.\n * The context is automatically sent to the AI via session.update if a session is active.\n * \n * @param context - Context object to sync with Vowel client. Use null to clear context.\n *                  Changes are detected via deep comparison of JSON.stringify result.\n * \n * @example\n * ```tsx\n * function MyComponent() {\n *   const [user, setUser] = useState(null);\n *   \n *   // Automatically sync user context\n *   useSyncContext(user ? { userId: user.id, userName: user.name } : null);\n *   \n *   return <div>Hello {user?.name}</div>;\n * }\n * ```\n */\nexport function useSyncContext(context: Record<string, unknown> | null): void {\n  const { updateContext } = useVowel();\n  const previousContextRef = useRef<string | null>(null);\n  const updateContextRef = useRef(updateContext);\n\n  // Keep updateContext ref current\n  useEffect(() => {\n    updateContextRef.current = updateContext;\n  }, [updateContext]);\n\n  useEffect(() => {\n    // Serialize current context for comparison\n    const currentContextStr = context ? JSON.stringify(context) : null;\n\n    console.log(\"[useSyncContext] 📝 Updating context:\", context);\n\n    // Only update if context actually changed\n    if (currentContextStr !== previousContextRef.current) {\n      previousContextRef.current = currentContextStr;\n      updateContext(context);\n    }\n  }, [context, updateContext]);\n\n  // WE NEVER NEED TO CLEANUP THIS HOOK - WE ALWAYS WANT THE LAST GOOD CONTEXT\n}\n","/**\n * VowelMicrophone Component\n * Microphone button for voice interaction\n */\n\nimport { Mic, MicOff, Loader2, Wrench } from \"lucide-react\";\nimport { useVowel } from \"./VowelProviderSimple\";\nimport { cn, VOWEL_UI_SCOPE_CLASS } from \"../utils\";\nimport { useEffect } from \"react\";\nimport \"../styles/styles.css\";\n\n/**\n * Props for VowelMicrophone\n */\nexport interface VowelMicrophoneProps {\n  /** Size of the button */\n  size?: \"small\" | \"default\" | \"large\";\n\n  /** Custom className */\n  className?: string;\n\n  /** Custom color (Tailwind classes) */\n  color?: string;\n\n  /** Show status text */\n  showStatus?: boolean;\n}\n\n/**\n * Microphone button component\n * Can be placed anywhere in your app\n *\n * @example\n * ```tsx\n * import { VowelMicrophone } from '@/lib/vowel';\n *\n * function Header() {\n *   return (\n *     <header>\n *       <VowelMicrophone size=\"default\" showStatus />\n *     </header>\n *   );\n * }\n * ```\n */\nexport function VowelMicrophone({\n  size = \"default\",\n  className,\n  color,\n  showStatus = false,\n}: VowelMicrophoneProps) {\n  const { state, toggleSession, client } = useVowel();\n\n  // Don't render if client is not available yet\n  if (!client) {\n    return null;\n  }\n\n  // Log state changes for debugging\n  useEffect(() => {\n    console.log(\"🎤 [VowelMicrophone] State updated:\", {\n      isConnected: state.isConnected,\n      isConnecting: state.isConnecting,\n      isUserSpeaking: state.isUserSpeaking,\n      isAIThinking: state.isAIThinking,\n      isToolExecuting: state.isToolExecuting,\n      isAISpeaking: state.isAISpeaking,\n      status: state.status,\n    });\n  }, [\n    state.isConnected,\n    state.isConnecting,\n    state.isUserSpeaking,\n    state.isAIThinking,\n    state.isToolExecuting,\n    state.isAISpeaking,\n    state.status,\n  ]);\n\n  const sizeClasses = {\n    small: \"w-10 h-10\",\n    default: \"w-12 h-12\",\n    large: \"w-16 h-16\",\n  };\n\n  const iconSizes = {\n    small: \"w-4 h-4\",\n    default: \"w-5 h-5\",\n    large: \"w-6 h-6\",\n  };\n\n  // State-specific colors and styling\n  const getButtonColor = () => {\n    if (color) return color;\n    \n    if (state.isConnected) {\n      if (state.isAISpeaking) return \"bg-purple-500 hover:bg-purple-600\";\n      // Tool executing: slightly different shade of yellow (amber/orange-yellow)\n      if (state.isToolExecuting) return \"bg-amber-500 hover:bg-amber-600\";\n      if (state.isAIThinking) return \"bg-yellow-500 hover:bg-yellow-600\";\n      if (state.isUserSpeaking) return \"bg-blue-500 hover:bg-blue-600\";\n      console.log(\"🟢 [VowelMicrophone] Connected and idle - should be GREEN\");\n      return \"bg-green-500 hover:bg-green-600\"; // Ready/idle state - clearly visible green\n    }\n    \n    console.log(\"⚪ [VowelMicrophone] Disconnected - should be GRAY\");\n    return \"bg-base-300 hover:bg-base-content/20\"; // Disconnected\n  };\n\n  // Get ring styling for current state\n  const getRingClass = () => {\n    if (state.isAISpeaking) return \"ring-4 ring-purple-400/50\";\n    // Tool executing: amber ring\n    if (state.isToolExecuting) return \"ring-4 ring-amber-400/50\";\n    if (state.isAIThinking) return \"ring-4 ring-yellow-400/50\";\n    if (state.isUserSpeaking) return \"ring-4 ring-blue-400/50\";\n    if (state.isConnected) return \"ring-2 ring-green-400/50\";\n    return \"\";\n  };\n\n  // Get icon color for current state\n  const getIconColor = () => {\n    if (state.isAISpeaking || state.isAIThinking || state.isToolExecuting || state.isUserSpeaking) {\n      return \"text-white\";\n    }\n    if (state.isConnected) return \"text-white\"; // White icon on green background\n    return \"text-base-content\";\n  };\n\n  // Should show pulse animation\n  const shouldPulse = () => {\n    return state.isUserSpeaking || state.isAISpeaking;\n  };\n\n  // Get icon to display\n  const getIcon = () => {\n    const IconComponent = Loader2;\n    const iconSize = iconSizes[size];\n    \n    if (state.isConnecting) {\n      return <IconComponent className={cn(iconSize, getIconColor(), \"animate-spin\")} />;\n    }\n    \n    // Tool executing: show wrench icon\n    if (state.isToolExecuting) {\n      return <Wrench className={cn(iconSize, getIconColor())} />;\n    }\n    \n    // AI thinking: show mic (or could show brain icon)\n    if (state.isAIThinking) {\n      return <Mic className={cn(iconSize, getIconColor())} />;\n    }\n    \n    if (state.isConnected) {\n      return <Mic className={cn(iconSize, getIconColor())} />;\n    }\n    \n    return <MicOff className={cn(iconSize, getIconColor())} />;\n  };\n\n  // State-specific title\n  const getTitle = () => {\n    if (state.isAISpeaking) return \"AI Speaking\";\n    if (state.isToolExecuting) return \"Executing Tool\";\n    if (state.isAIThinking) return \"AI Thinking\";\n    if (state.isUserSpeaking) return \"You're Speaking\";\n    return state.status || \"Voice assistant\";\n  };\n\n  // State-specific status text\n  const getStatusText = () => {\n    if (state.isAISpeaking) return \"🔊 AI Speaking\";\n    if (state.isToolExecuting) return \"🔧 Executing Tool\";\n    if (state.isAIThinking) return \"🧠 AI Thinking\";\n    if (state.isUserSpeaking) return \"🎤 Listening\";\n    return state.status;\n  };\n\n  // Only disable when disconnected AND connecting (to prevent double-connection)\n  // Always allow clicks when connected (to allow disconnection at any state)\n  const isDisabled = !state.isConnected && state.isConnecting;\n\n  return (\n    <div className={cn(VOWEL_UI_SCOPE_CLASS, className)}>\n      <button\n        onClick={toggleSession}\n        disabled={isDisabled}\n        // style={{\n        //   width: \"40px\",\n        //   height: \"40px\",\n        // }}\n        className={cn(\n          \"rounded-full flex items-center justify-center transition-all duration-300 shadow-lg relative\",\n          sizeClasses[size],\n          getButtonColor(),\n          getRingClass(),\n          shouldPulse() && \"animate-pulse\",\n          isDisabled && \"opacity-50 cursor-not-allowed\"\n        )}\n        title={getTitle()}\n        aria-label={state.isConnected ? \"Stop voice session\" : \"Start voice session\"}\n      >\n        {/* Ping effect for speaking states only */}\n        {shouldPulse() && (\n          <span className=\"absolute inset-0 rounded-full animate-ping opacity-40 bg-current pointer-events-none\" />\n        )}\n\n        {/* Icon - shows wrench when tool executing, mic when thinking, etc. */}\n        <span className=\"relative z-10\">\n          {getIcon()}\n        </span>\n      </button>\n\n      {showStatus && (\n        <span className={cn(\n          \"text-sm font-medium transition-colors\",\n          state.isAISpeaking && \"text-purple-600 dark:text-purple-400\",\n          state.isAIThinking && !state.isAISpeaking && \"text-yellow-600 dark:text-yellow-400\",\n          state.isUserSpeaking && !state.isAIThinking && !state.isAISpeaking && \"text-blue-600 dark:text-blue-400\",\n          !state.isAISpeaking && !state.isAIThinking && !state.isUserSpeaking && \"text-gray-600 dark:text-gray-400\"\n        )}>\n          {getStatusText()}\n        </span>\n      )}\n    </div>\n  );\n}\n"],"names":["Loader2"],"mappings":"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"}