admesh-ui-sdk 1.0.23 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/utils/logger.ts","../src/utils/viewabilityTracker.ts","../src/hooks/useViewabilityTracker.ts","../src/components/AdMeshViewabilityTracker.tsx","../src/components/AdMeshTailAd.tsx","../../node_modules/classnames/index.js","../src/hooks/useAdMeshTracker.ts","../src/components/AdMeshLinkTracker.tsx","../src/utils/styleInjection.ts","../src/hooks/useAdMeshStyles.ts","../src/utils/disclosureUtils.ts","../src/components/AdMeshProductCard.tsx","../src/context/AdMeshContext.ts","../src/hooks/useAdMesh.ts","../src/components/AdMeshBridgeFormat.tsx","../src/components/AdMeshSummaryLayout.tsx","../src/components/AdMeshLayout.tsx","../src/components/AdMeshFollowup.tsx","../src/sdk/AdMeshTracker.ts","../src/sdk/AdMeshRenderer.tsx","../src/sdk/AdMeshSDK.ts","../src/sdk/WeaveResponseProcessor.ts","../src/context/AdMeshProvider.tsx","../src/components/AdMeshRecommendations.tsx","../src/components/WeaveFallbackRecommendations.tsx","../src/context/WeaveAdFormatContext.tsx","../src/components/AdMeshEcommerceCards.tsx","../src/components/AdMeshInlineCard.tsx","../src/components/AdMeshBadge.tsx","../src/utils/streamingEvents.ts","../src/utils/inlineExposureTracker.ts","../src/components/WeaveAdFormatContainer.tsx","../src/hooks/useWeaveAdFormat.ts","../src/index.ts"],"sourcesContent":["/**\n * Logger utility for AdMesh UI SDK\n * Disables all logs in production environment\n */\n\n// Check for production environment\n// Supports both Vite (import.meta.env) and standard Node.js (process.env)\nlet isProduction = false;\ntry {\n // Check for Vite's import.meta.env (only available in ESM modules)\n if (typeof (globalThis as any).importMeta !== 'undefined' && (globalThis as any).importMeta.env?.PROD) {\n isProduction = true;\n }\n} catch (e) {\n // import.meta not available, continue with other checks\n}\n\nif (!isProduction) {\n isProduction = \n (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') ||\n (typeof process !== 'undefined' && process.env.ADMESH_ENV === 'production');\n}\n\nexport const logger = {\n log: (...args: any[]) => {\n if (!isProduction) {\n console.log(...args);\n }\n },\n \n warn: (...args: any[]) => {\n if (!isProduction) {\n console.warn(...args);\n }\n },\n \n error: (...args: any[]) => {\n // Errors are always logged, even in production, as they're critical\n console.error(...args);\n },\n \n info: (...args: any[]) => {\n if (!isProduction) {\n console.info(...args);\n }\n },\n \n debug: (...args: any[]) => {\n if (!isProduction) {\n console.debug(...args);\n }\n },\n};\n\n","/**\n * AdMesh UI SDK - MRC Viewability Tracker Utilities\n * Implements Media Rating Council (MRC) viewability standards\n */\n\nimport type {\n MRCViewabilityStandards,\n DeviceType,\n ViewabilityContextMetrics,\n ViewabilityAnalyticsEvent\n} from '../types/analytics';\nimport { logger } from './logger';\n\n/**\n * Calculate MRC viewability standards based on ad size\n */\nexport function calculateMRCStandards(\n adWidth: number,\n adHeight: number,\n customStandards?: Partial<MRCViewabilityStandards>\n): MRCViewabilityStandards {\n const adPixels = adWidth * adHeight;\n const isLargeAd = adPixels > 242500; // MRC threshold for large ads\n\n const defaults: MRCViewabilityStandards = {\n visibilityThreshold: isLargeAd ? 0.3 : 0.5, // 30% for large, 50% for standard\n minimumDuration: 1000, // 1 second in milliseconds\n isLargeAd\n };\n\n return { ...defaults, ...customStandards };\n}\n\n/**\n * Detect device type based on viewport width\n */\nexport function detectDeviceType(viewportWidth: number): DeviceType {\n if (viewportWidth < 768) return 'mobile';\n if (viewportWidth < 1024) return 'tablet';\n return 'desktop';\n}\n\n/**\n * Calculate visibility percentage of element in viewport\n */\nexport function calculateVisibilityPercentage(element: HTMLElement): number {\n const rect = element.getBoundingClientRect();\n const viewportHeight = window.innerHeight || document.documentElement.clientHeight;\n const viewportWidth = window.innerWidth || document.documentElement.clientWidth;\n\n // Element dimensions\n const elementHeight = rect.height;\n const elementWidth = rect.width;\n\n if (elementHeight === 0 || elementWidth === 0) return 0;\n\n // Calculate visible portion\n const visibleTop = Math.max(0, rect.top);\n const visibleBottom = Math.min(viewportHeight, rect.bottom);\n const visibleLeft = Math.max(0, rect.left);\n const visibleRight = Math.min(viewportWidth, rect.right);\n\n const visibleHeight = Math.max(0, visibleBottom - visibleTop);\n const visibleWidth = Math.max(0, visibleRight - visibleLeft);\n\n const visibleArea = visibleHeight * visibleWidth;\n const totalArea = elementHeight * elementWidth;\n\n return totalArea > 0 ? (visibleArea / totalArea) : 0;\n}\n\n/**\n * Calculate current scroll depth as percentage\n */\nexport function calculateScrollDepth(): number {\n const windowHeight = window.innerHeight;\n const documentHeight = document.documentElement.scrollHeight;\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n\n const scrollableHeight = documentHeight - windowHeight;\n if (scrollableHeight <= 0) return 100;\n\n return Math.min(100, (scrollTop / scrollableHeight) * 100);\n}\n\n/**\n * Get element position on page\n */\nexport function getElementPosition(element: HTMLElement): { top: number; left: number } {\n const rect = element.getBoundingClientRect();\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;\n\n return {\n top: rect.top + scrollTop,\n left: rect.left + scrollLeft\n };\n}\n\n/**\n * Collect context metrics\n */\nexport function collectContextMetrics(element: HTMLElement): ViewabilityContextMetrics {\n const rect = element.getBoundingClientRect();\n const position = getElementPosition(element);\n const viewportWidth = window.innerWidth || document.documentElement.clientWidth;\n const viewportHeight = window.innerHeight || document.documentElement.clientHeight;\n\n // Detect dark mode\n const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n\n return {\n pageUrl: window.location.href,\n pageTitle: document.title,\n referrer: document.referrer,\n deviceType: detectDeviceType(viewportWidth),\n viewportWidth,\n viewportHeight,\n adWidth: rect.width,\n adHeight: rect.height,\n adPositionTop: position.top,\n adPositionLeft: position.left,\n isDarkMode,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone\n };\n}\n\n/**\n * Generate unique session ID for INTERNAL viewability tracking only.\n * \n * IMPORTANT: This is NOT the main sessionId used for recommendations.\n * This is only used internally by the viewability tracker for tracking\n * viewability events. The main sessionId MUST be provided by the platform\n * and passed to AdMeshProvider and SDK methods.\n */\nexport function generateSessionId(): string {\n return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Generate unique batch ID\n */\nexport function generateBatchId(): string {\n return `batch_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Check if ad meets MRC viewability threshold\n */\nexport function meetsViewabilityThreshold(\n visibilityPercentage: number,\n visibleDuration: number,\n standards: MRCViewabilityStandards\n): boolean {\n return (\n visibilityPercentage >= standards.visibilityThreshold &&\n visibleDuration >= standards.minimumDuration\n );\n}\n\n/**\n * Format timestamp to ISO 8601\n */\nexport function formatTimestamp(date: Date = new Date()): string {\n return date.toISOString();\n}\n\n/**\n * Calculate average from array of numbers\n */\nexport function calculateAverage(numbers: number[]): number {\n if (numbers.length === 0) return 0;\n const sum = numbers.reduce((acc, num) => acc + num, 0);\n return sum / numbers.length;\n}\n\n/**\n * Debounce function for performance optimization\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: NodeJS.Timeout | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Throttle function for performance optimization\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n func: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let inThrottle: boolean;\n\n return function executedFunction(...args: Parameters<T>) {\n if (!inThrottle) {\n func(...args);\n inThrottle = true;\n setTimeout(() => (inThrottle = false), limit);\n }\n };\n}\n\n/**\n * Send analytics event to API\n *\n * NOTE: If apiEndpoint is empty, the event is silently discarded (no error).\n * This allows the SDK to collect analytics without sending them to a backend.\n */\nexport async function sendAnalyticsEvent(\n event: ViewabilityAnalyticsEvent,\n apiEndpoint: string,\n retryAttempts: number = 3,\n retryDelay: number = 1000\n): Promise<boolean> {\n // If no endpoint is configured, silently skip sending\n if (!apiEndpoint || apiEndpoint.trim() === '') {\n return true;\n }\n\n for (let attempt = 0; attempt < retryAttempts; attempt++) {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(event),\n keepalive: true\n });\n\n if (response.ok) {\n return true;\n }\n\n // Log error details for debugging\n await response.text().catch(() => '');\n } catch (error) {\n // Error caught, will retry\n }\n\n // Wait before retry (exponential backoff)\n if (attempt < retryAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));\n }\n }\n\n logger.error('[AdMesh Viewability] Failed to send analytics event');\n return false;\n}\n\n/**\n * Send batched analytics events to API\n *\n * NOTE: If apiEndpoint is empty, the batch is silently discarded (no error).\n * This allows the SDK to collect analytics without sending them to a backend.\n */\nexport async function sendAnalyticsBatch(\n events: ViewabilityAnalyticsEvent[],\n sessionId: string,\n apiEndpoint: string,\n retryAttempts: number = 3,\n retryDelay: number = 1000\n): Promise<boolean> {\n if (events.length === 0) return true;\n\n // If no endpoint is configured, silently skip sending\n if (!apiEndpoint || apiEndpoint.trim() === '') {\n return true;\n }\n\n const batch = {\n batchId: generateBatchId(),\n sessionId,\n createdAt: formatTimestamp(),\n events,\n eventCount: events.length\n };\n\n for (let attempt = 0; attempt < retryAttempts; attempt++) {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(batch),\n keepalive: true\n });\n\n if (response.ok) {\n return true;\n }\n\n // Log error details for debugging\n await response.text().catch(() => '');\n } catch (error) {\n // Error caught, will retry\n }\n\n // Wait before retry (exponential backoff)\n if (attempt < retryAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));\n }\n }\n\n logger.error('[AdMesh Viewability] Failed to send analytics batch');\n return false;\n}\n\n/**\n * Sanitize URL to remove PII (query parameters, fragments)\n */\nexport function sanitizeUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n // Remove query parameters and hash\n return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;\n } catch {\n return url;\n }\n}\n\n/**\n * Check if element is in viewport\n */\nexport function isElementInViewport(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect();\n return (\n rect.top < (window.innerHeight || document.documentElement.clientHeight) &&\n rect.bottom > 0 &&\n rect.left < (window.innerWidth || document.documentElement.clientWidth) &&\n rect.right > 0\n );\n}\n","/**\n * AdMesh UI SDK - MRC Viewability Tracker Hook\n * React hook for tracking ad viewability according to MRC standards\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { logger } from '../utils/logger';\nimport type {\n ViewabilityTrackerConfig,\n ViewabilityTrackerState,\n ViewabilityAnalyticsEvent,\n ViewabilityEventType,\n MRCViewabilityStandards\n} from '../types/analytics';\nimport {\n calculateMRCStandards,\n calculateVisibilityPercentage,\n calculateScrollDepth,\n collectContextMetrics,\n generateSessionId,\n meetsViewabilityThreshold,\n formatTimestamp,\n calculateAverage,\n throttle\n} from '../utils/viewabilityTracker';\n\n// Default configuration\nconst DEFAULT_CONFIG: ViewabilityTrackerConfig = {\n enabled: true,\n // Analytics endpoint disabled - no analytics will be sent\n apiEndpoint: '', // Empty string disables analytics sending\n enableBatching: false, // Disabled since no endpoint\n batchSize: 10,\n batchTimeout: 5000, // 5 seconds\n debug: false,\n enableRetry: false,\n maxRetries: 3,\n retryDelay: 1000\n};\n\n// Global config that can be set by consuming application\nlet globalConfig: ViewabilityTrackerConfig = DEFAULT_CONFIG;\n\n// TEMPORARY: Global flag to disable all analytics sending\n// Set this to true to prevent any viewability analytics from being sent to the backend\n// This is a temporary measure and can be easily reverted by setting to false\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nlet ANALYTICS_DISABLED = false;\n\nexport const setViewabilityTrackerConfig = (config: Partial<ViewabilityTrackerConfig>) => {\n globalConfig = { ...globalConfig, ...config };\n};\n\n/**\n * TEMPORARY: Disable/enable all viewability analytics sending\n * @param disabled - Set to true to disable analytics, false to enable\n *\n * Usage:\n * disableViewabilityAnalytics(true); // Disable all analytics\n * disableViewabilityAnalytics(false); // Re-enable analytics\n */\nexport const disableViewabilityAnalytics = (disabled: boolean) => {\n ANALYTICS_DISABLED = disabled;\n if (disabled) {\n logger.warn('[AdMesh Viewability] Analytics sending is DISABLED - no data will be sent to backend');\n } else {\n logger.log('[AdMesh Viewability] Analytics sending is ENABLED');\n }\n};\n\ninterface UseViewabilityTrackerProps {\n /** Product ID */\n productId?: string;\n /** Offer ID */\n offerId?: string;\n /** Agent ID */\n agentId?: string;\n /** Recommendation ID (from recommendations collection) */\n recommendationId: string;\n /** HTML element to track */\n elementRef: React.RefObject<HTMLElement>;\n /** Custom configuration */\n config?: Partial<ViewabilityTrackerConfig>;\n}\n\nexport function useViewabilityTracker({\n productId,\n offerId,\n agentId,\n recommendationId,\n elementRef,\n config: customConfig\n}: UseViewabilityTrackerProps): ViewabilityTrackerState {\n const config = { ...globalConfig, ...customConfig };\n\n // Session ID (persists for component lifetime)\n const sessionId = useRef(generateSessionId());\n\n // State\n const [state, setState] = useState<ViewabilityTrackerState>({\n isVisible: false,\n isViewable: false,\n visibilityPercentage: 0,\n timeMetrics: {\n loadedAt: formatTimestamp(),\n totalVisibleDuration: 0,\n totalViewableDuration: 0,\n totalHoverDuration: 0,\n totalFocusDuration: 0\n },\n engagementMetrics: {\n currentScrollDepth: 0,\n viewportEnterCount: 0,\n viewportExitCount: 0,\n hoverCount: 0,\n wasClicked: false,\n maxVisibilityPercentage: 0,\n averageVisibilityPercentage: 0\n },\n isTracking: config.enabled\n });\n\n // Refs for tracking\n const mrcStandards = useRef<MRCViewabilityStandards | null>(null);\n const visibilityStartTime = useRef<number | null>(null);\n const viewableStartTime = useRef<number | null>(null);\n const hoverStartTime = useRef<number | null>(null);\n const focusStartTime = useRef<number | null>(null);\n const visibilityPercentages = useRef<number[]>([]);\n const eventBatch = useRef<ViewabilityAnalyticsEvent[]>([]);\n const batchTimeout = useRef<NodeJS.Timeout | null>(null);\n\n // Log helper\n const log = useCallback((message: string) => {\n if (config.debug) {\n logger.log(`[AdMesh Viewability] ${message}`);\n }\n }, [config.debug]);\n\n // Send event (analytics disabled - no events sent to backend)\n // Viewability tracking still works for exposure pixels (handled separately)\n const sendEvent = useCallback(async (eventType: ViewabilityEventType, additionalData?: Record<string, unknown>) => {\n if (!config.enabled || !elementRef.current || !mrcStandards.current) return;\n\n // Analytics disabled - no events sent to backend\n // Viewability tracking still works internally for exposure pixel firing\n log(`Analytics disabled - skipping event: ${eventType}`);\n \n // Call custom callback if provided (for local tracking)\n if (config.onEvent) {\n const contextMetrics = collectContextMetrics(elementRef.current);\n const event: ViewabilityAnalyticsEvent = {\n eventType,\n timestamp: formatTimestamp(),\n sessionId: sessionId.current,\n productId,\n offerId,\n agentId,\n recommendationId,\n timeMetrics: state.timeMetrics,\n engagementMetrics: state.engagementMetrics,\n contextMetrics,\n mrcStandards: mrcStandards.current,\n isViewable: state.isViewable,\n metadata: additionalData\n };\n config.onEvent(event);\n }\n }, [config, productId, offerId, agentId, recommendationId, elementRef, state, log]);\n\n // Flush event batch (disabled - analytics not sent)\n const flushBatch = useCallback(async () => {\n if (eventBatch.current.length === 0) return;\n\n // Analytics disabled - clear batch without sending\n log('Analytics disabled - clearing batch without sending');\n eventBatch.current = [];\n if (batchTimeout.current) {\n clearTimeout(batchTimeout.current);\n batchTimeout.current = null;\n }\n return;\n }, [log]);\n\n // Update visibility\n const updateVisibility = useCallback(throttle(() => {\n if (!elementRef.current) return;\n\n const visibilityPercentage = calculateVisibilityPercentage(elementRef.current);\n const now = Date.now();\n const loadTime = new Date(state.timeMetrics.loadedAt).getTime();\n\n setState(prev => {\n const newState = { ...prev };\n\n // Track visibility percentages for average calculation\n if (visibilityPercentage > 0) {\n visibilityPercentages.current.push(visibilityPercentage);\n }\n\n // Update visibility state\n const wasVisible = prev.isVisible;\n const isNowVisible = visibilityPercentage > 0;\n\n if (isNowVisible && !wasVisible) {\n // Became visible\n visibilityStartTime.current = now;\n newState.engagementMetrics.viewportEnterCount++;\n\n if (!newState.timeMetrics.timeToFirstVisible) {\n newState.timeMetrics.timeToFirstVisible = now - loadTime;\n newState.engagementMetrics.scrollDepthAtFirstVisible = calculateScrollDepth();\n sendEvent('ad_visible');\n }\n } else if (!isNowVisible && wasVisible) {\n // Became hidden\n if (visibilityStartTime.current) {\n const visibleDuration = now - visibilityStartTime.current;\n newState.timeMetrics.totalVisibleDuration += visibleDuration;\n visibilityStartTime.current = null;\n }\n newState.engagementMetrics.viewportExitCount++;\n sendEvent('ad_hidden');\n } else if (isNowVisible && wasVisible && visibilityStartTime.current) {\n // Still visible, update duration\n const visibleDuration = now - visibilityStartTime.current;\n newState.timeMetrics.totalVisibleDuration += visibleDuration;\n visibilityStartTime.current = now;\n }\n\n newState.isVisible = isNowVisible;\n newState.visibilityPercentage = visibilityPercentage;\n\n // Update max visibility\n if (visibilityPercentage > newState.engagementMetrics.maxVisibilityPercentage) {\n newState.engagementMetrics.maxVisibilityPercentage = visibilityPercentage;\n }\n\n // Update average visibility\n if (visibilityPercentages.current.length > 0) {\n newState.engagementMetrics.averageVisibilityPercentage = calculateAverage(visibilityPercentages.current);\n }\n\n // Check MRC viewability threshold\n if (mrcStandards.current) {\n const wasViewable = prev.isViewable;\n const isNowViewable = meetsViewabilityThreshold(\n visibilityPercentage,\n newState.timeMetrics.totalVisibleDuration,\n mrcStandards.current\n );\n\n if (isNowViewable && !wasViewable) {\n // Met viewability threshold\n newState.isViewable = true;\n newState.timeMetrics.timeToViewable = now - loadTime;\n viewableStartTime.current = now;\n sendEvent('ad_viewable');\n } else if (isNowViewable && wasViewable && viewableStartTime.current) {\n // Still viewable, update duration\n const viewableDuration = now - viewableStartTime.current;\n newState.timeMetrics.totalViewableDuration += viewableDuration;\n viewableStartTime.current = now;\n }\n }\n\n // Update scroll depth\n newState.engagementMetrics.currentScrollDepth = calculateScrollDepth();\n\n return newState;\n });\n }, 100), [elementRef, state.timeMetrics.loadedAt, sendEvent]);\n\n // Initialize MRC standards\n useEffect(() => {\n if (!elementRef.current) return;\n\n const rect = elementRef.current.getBoundingClientRect();\n mrcStandards.current = calculateMRCStandards(rect.width, rect.height, config.mrcStandards);\n\n log('Initialized MRC standards');\n sendEvent('ad_loaded');\n }, [elementRef, config.mrcStandards, log, sendEvent]);\n\n // Set up Intersection Observer\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach(() => {\n updateVisibility();\n });\n },\n {\n threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],\n rootMargin: '0px'\n }\n );\n\n observer.observe(elementRef.current);\n\n return () => {\n observer.disconnect();\n };\n }, [config.enabled, elementRef, updateVisibility]);\n\n // Track scroll events\n useEffect(() => {\n if (!config.enabled) return;\n\n const handleScroll = throttle(() => {\n updateVisibility();\n }, 100);\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n return () => window.removeEventListener('scroll', handleScroll);\n }, [config.enabled, updateVisibility]);\n\n // Track hover events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleMouseEnter = () => {\n hoverStartTime.current = Date.now();\n setState(prev => ({\n ...prev,\n engagementMetrics: {\n ...prev.engagementMetrics,\n hoverCount: prev.engagementMetrics.hoverCount + 1\n }\n }));\n sendEvent('ad_hover_start');\n };\n\n const handleMouseLeave = () => {\n if (hoverStartTime.current) {\n const hoverDuration = Date.now() - hoverStartTime.current;\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n totalHoverDuration: prev.timeMetrics.totalHoverDuration + hoverDuration\n }\n }));\n hoverStartTime.current = null;\n sendEvent('ad_hover_end', { hoverDuration });\n }\n };\n\n element.addEventListener('mouseenter', handleMouseEnter);\n element.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n element.removeEventListener('mouseenter', handleMouseEnter);\n element.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Track focus events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleFocus = () => {\n focusStartTime.current = Date.now();\n sendEvent('ad_focus');\n };\n\n const handleBlur = () => {\n if (focusStartTime.current) {\n const focusDuration = Date.now() - focusStartTime.current;\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n totalFocusDuration: prev.timeMetrics.totalFocusDuration + focusDuration\n }\n }));\n focusStartTime.current = null;\n sendEvent('ad_blur', { focusDuration });\n }\n };\n\n element.addEventListener('focus', handleFocus);\n element.addEventListener('blur', handleBlur);\n\n return () => {\n element.removeEventListener('focus', handleFocus);\n element.removeEventListener('blur', handleBlur);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Track click events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleClick = () => {\n setState(prev => ({\n ...prev,\n engagementMetrics: {\n ...prev.engagementMetrics,\n wasClicked: true\n }\n }));\n sendEvent('ad_click');\n };\n\n element.addEventListener('click', handleClick);\n\n return () => {\n element.removeEventListener('click', handleClick);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n // Calculate session duration\n const now = Date.now();\n const loadTime = new Date(state.timeMetrics.loadedAt).getTime();\n const sessionDuration = now - loadTime;\n\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n sessionDuration\n }\n }));\n\n // Send final event\n sendEvent('ad_unloaded', { sessionDuration });\n\n // Flush any remaining batched events\n flushBatch();\n };\n }, []);\n\n return state;\n}\n","/**\n * AdMesh Viewability Tracker Component\n * Wraps any ad component with MRC viewability tracking\n */\n\nimport React, { useRef, useEffect } from 'react';\nimport { logger } from '../utils/logger';\nimport { useViewabilityTracker } from '../hooks/useViewabilityTracker';\nimport type { ViewabilityTrackerConfig } from '../types/analytics';\n\nexport interface AdMeshViewabilityTrackerProps {\n /** Product ID */\n productId?: string;\n /** Offer ID */\n offerId?: string;\n /** Agent ID */\n agentId?: string;\n /** Recommendation ID (for exposure tracking) */\n recommendationId: string;\n /** Exposure URL (for MRC-compliant exposure pixel firing) */\n exposureUrl?: string;\n /** Session ID (for exposure tracking) */\n sessionId?: string;\n /** Children to wrap with viewability tracking */\n children: React.ReactNode;\n /** Custom viewability tracker configuration */\n config?: Partial<ViewabilityTrackerConfig>;\n /** CSS class name */\n className?: string;\n /** Inline styles */\n style?: React.CSSProperties;\n /** Callback when viewability state changes */\n onViewabilityChange?: (isViewable: boolean) => void;\n /** Callback when ad becomes visible */\n onVisible?: () => void;\n /** Callback when ad becomes viewable (meets MRC threshold) */\n onViewable?: () => void;\n /** Callback when ad is clicked */\n onClick?: () => void;\n}\n\n/**\n * AdMeshViewabilityTracker Component\n * \n * Wraps ad components with comprehensive MRC viewability tracking.\n * Automatically tracks:\n * - Viewability (50% visible for 1 second)\n * - Time metrics (time to viewable, total visible duration, etc.)\n * - Engagement metrics (hover, focus, clicks, scroll depth)\n * - Context metrics (device type, viewport size, ad position)\n * \n * @example\n * ```tsx\n * <AdMeshViewabilityTracker\n * recommendationId=\"rec_123\"\n * productId=\"prod_456\"\n * offerId=\"offer_789\"\n * onViewable={() => logger.log('Ad is viewable!')}\n * >\n * <YourAdComponent />\n * </AdMeshViewabilityTracker>\n * ```\n */\nexport const AdMeshViewabilityTracker: React.FC<AdMeshViewabilityTrackerProps> = ({\n productId,\n offerId,\n agentId,\n recommendationId,\n exposureUrl,\n sessionId,\n children,\n config,\n className,\n style,\n onViewabilityChange,\n onVisible,\n onViewable,\n onClick\n}) => {\n const elementRef = useRef<HTMLElement>(null);\n const exposureFired = useRef(false);\n\n // Use viewability tracker hook\n const viewabilityState = useViewabilityTracker({\n productId,\n offerId,\n agentId,\n recommendationId,\n elementRef: elementRef as React.RefObject<HTMLElement>,\n config\n });\n\n // Track viewability changes and fire exposure pixel\n const previousViewable = useRef(viewabilityState.isViewable);\n\n useEffect(() => {\n if (viewabilityState.isViewable !== previousViewable.current) {\n previousViewable.current = viewabilityState.isViewable;\n\n if (onViewabilityChange) {\n onViewabilityChange(viewabilityState.isViewable);\n }\n\n if (viewabilityState.isViewable && onViewable) {\n onViewable();\n }\n\n // Fire exposure pixel when ad becomes viewable (MRC-compliant)\n // Only fire if we have the required data and haven't fired yet\n if (viewabilityState.isViewable && !exposureFired.current) {\n logger.log('[AdMeshViewabilityTracker] 🎯 Ad is viewable, checking exposure pixel requirements:', {\n exposureUrl: exposureUrl ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING',\n recommendationId\n });\n\n if (exposureUrl && sessionId) {\n exposureFired.current = true;\n\n logger.log('[AdMeshViewabilityTracker] 🔥 Firing exposure pixel:', exposureUrl);\n\n // Fire the exposure pixel using fetch with keepalive\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n logger.log('[AdMesh] ✅ Exposure pixel fired successfully');\n })\n .catch((error) => {\n logger.warn('[AdMesh] ⚠️ Failed to fire exposure pixel:', error);\n // Reset flag to allow retry\n exposureFired.current = false;\n });\n } else {\n logger.warn('[AdMeshViewabilityTracker] ⚠️ Cannot fire exposure pixel - missing required data:', {\n hasExposureUrl: !!exposureUrl,\n hasSessionId: !!sessionId\n });\n }\n }\n }\n }, [viewabilityState.isViewable, onViewabilityChange, onViewable, exposureUrl, sessionId, recommendationId]);\n\n // Track visibility changes\n const previousVisible = useRef(viewabilityState.isVisible);\n \n useEffect(() => {\n if (viewabilityState.isVisible !== previousVisible.current) {\n previousVisible.current = viewabilityState.isVisible;\n \n if (viewabilityState.isVisible && onVisible) {\n onVisible();\n }\n }\n }, [viewabilityState.isVisible, onVisible]);\n\n // Handle click\n const handleClick = () => {\n if (onClick) {\n onClick();\n }\n \n // Allow event to propagate to children\n };\n\n return (\n <div\n ref={elementRef as React.RefObject<HTMLDivElement>}\n className={className}\n style={style}\n onClick={handleClick}\n data-admesh-viewability-tracker\n data-recommendation-id={recommendationId}\n data-is-viewable={viewabilityState.isViewable}\n data-is-visible={viewabilityState.isVisible}\n data-visibility-percentage={viewabilityState.visibilityPercentage.toFixed(2)}\n >\n {children}\n </div>\n );\n};\n\nAdMeshViewabilityTracker.displayName = 'AdMeshViewabilityTracker';\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshTailAdProps {\n summaryText?: string; // The tail_summary from backend response (optional, not used in new UI)\n recommendations: AdMeshRecommendation[]; // Full recommendation objects\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n sessionId?: string;\n}\n\n// Utility function to validate and normalize URLs\nconst isValidUrl = (url: string): boolean => {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n};\n\n// Helper function to get CTA label from backend\nconst getCTALabel = (ctaLabel?: string): string => {\n // Use provided CTA label from backend if available\n if (ctaLabel && ctaLabel.trim()) {\n return ctaLabel.trim();\n }\n\n // Return empty string if no CTA label provided (will hide CTA button)\n return '';\n};\n\n// Process summary text with markdown links [Product Name](click_url) and brand name links\n// NOTE: This function is kept for backward compatibility but is no longer used in the new tail ad UI\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst processSummaryText = (_summaryText: string, _recommendations: AdMeshRecommendation[]): (string | React.ReactElement)[] => {\n // This function is no longer used - kept for backward compatibility only\n return [];\n};\n\nexport const AdMeshTailAd: React.FC<AdMeshTailAdProps> = ({\n recommendations,\n theme,\n className = '',\n style = {},\n sessionId\n}) => {\n // Validate inputs - return null if empty\n if (!recommendations || recommendations.length === 0) {\n logger.log('[AdMesh Tail Ad] No recommendations provided - not rendering');\n return null;\n }\n\n // Get the first recommendation's data for CTA and tracking\n const firstRecommendation = recommendations[0];\n const productId = firstRecommendation?.product_id;\n const exposureUrl = firstRecommendation?.exposure_url;\n const recommendationId = firstRecommendation?.recommendation_id || '';\n \n // Get data from creative_input for tail format\n const creativeInput = firstRecommendation?.creative_input || {};\n const shortDescription = creativeInput.short_description || '';\n const offerSummary = creativeInput.offer_summary || '';\n const brandName = creativeInput.brand_name || firstRecommendation?.title || '';\n const productName = creativeInput.product_name || '';\n \n // Get click URL - prioritize click_url from recommendation (this is the tracking URL we need)\n const clickUrl = firstRecommendation?.click_url || \n firstRecommendation?.admesh_link || \n creativeInput.cta_url ||\n firstRecommendation?.url;\n \n // Get CTA label from backend\n const ctaLabel = getCTALabel(creativeInput.cta_label);\n\n // For tail format, we need at least brand name\n // But we still validate that we have at least one recommendation with data\n if (!brandName) {\n logger.log('[AdMesh Tail Ad] No valid recommendation data provided - not rendering');\n return null;\n }\n \n // Build headline parts with priority:\n // 1. If offer_summary exists: \"Brand — Offer Summary\"\n // 2. If product_name exists: \"Brand — Product Name\"\n // 3. Otherwise: just \"Brand\"\n let headlineText = brandName;\n let headlineSuffix = '';\n \n if (offerSummary) {\n headlineSuffix = offerSummary;\n } else if (productName) {\n headlineSuffix = productName;\n }\n \n if (headlineSuffix) {\n headlineText = `${brandName} — ${headlineSuffix}`;\n }\n\n logger.debug('[AdMeshTailAd] 📊 Rendering with tracking data:', {\n recommendationId,\n productId,\n exposureUrl: exposureUrl ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING',\n recommendationsCount: recommendations.length,\n clickUrl: clickUrl ? clickUrl : 'MISSING',\n clickUrlSource: firstRecommendation?.click_url ? 'click_url' : \n firstRecommendation?.admesh_link ? 'admesh_link' :\n creativeInput.cta_url ? 'cta_url' :\n firstRecommendation?.url ? 'url' : 'none',\n shortDescription: shortDescription ? 'present' : 'MISSING',\n offerSummary: offerSummary || 'MISSING',\n brandName,\n productName,\n headlineText\n });\n\n // Handler for tracking clicks on the entire tail ad\n const handleContainerClick = (source: string, e?: React.MouseEvent) => {\n if (e) {\n e.stopPropagation();\n }\n logger.log(`AdMesh tail ad ${source} clicked`);\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n recommendationId: firstRecommendation.recommendation_id,\n productId: firstRecommendation.product_id,\n clickUrl: clickUrl,\n source: source\n }).catch(() => {\n logger.error(`[AdMesh] Failed to track ${source} click`);\n });\n }\n };\n\n // Handler for brand name link click\n const handleBrandNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n handleContainerClick('tail_ad_brand_name');\n };\n\n // Handler for CTA link click\n const handleCTAClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n handleContainerClick('tail_ad_cta');\n };\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendationId}\n exposureUrl={exposureUrl}\n sessionId={sessionId}\n className={`admesh-tail-ad ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n <div className=\"tail-ad-container\">\n {/* Headline: Brand Name (clickable link) — Offer Summary / Product Name / Brand only */}\n {headlineText && (\n <div className=\"mb-2\">\n <h3 className=\"text-black dark:text-white font-semibold text-base\">\n {clickUrl && brandName ? (\n <>\n <a\n href={clickUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-semibold\"\n style={{\n color: '#2563eb',\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={handleBrandNameClick}\n >\n {brandName}\n </a>\n {headlineSuffix && ` — ${headlineSuffix}`}\n </>\n ) : (\n headlineText\n )}\n </h3>\n </div>\n )}\n \n {/* Description: Full short_description */}\n {shortDescription && (\n <p className=\"text-gray-700 dark:text-gray-300 text-sm mb-3 leading-relaxed\">\n {shortDescription}\n </p>\n )}\n \n \n \n {/* CTA Link and Sponsored Label */}\n <div className=\"flex items-center justify-between mt-2\">\n {/* CTA Link - Left aligned (only show if ctaLabel is provided) */}\n {ctaLabel && clickUrl && (\n <a\n href={clickUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-medium text-sm\"\n style={{\n color: '#2563eb',\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={handleCTAClick}\n >\n {ctaLabel}\n </a>\n )}\n\n {/* Sponsored Label - Right aligned */}\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\n Sponsored\n </p>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nexport default AdMeshTailAd;\n\n","/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import { useState, useCallback, useMemo } from 'react';\nimport { logger } from '../utils/logger';\nimport type { TrackingData, UseAdMeshTrackerReturn } from '../types/index';\n\n// Default tracking endpoint\nconst DEFAULT_TRACKING_URL = 'https://api.useadmesh.com/track';\n\ninterface TrackingConfig {\n enabled?: boolean;\n retryAttempts?: number;\n retryDelay?: number;\n}\n\n// Global config that can be set by the consuming application\nlet globalConfig: TrackingConfig = {\n enabled: true,\n retryAttempts: 3,\n retryDelay: 1000\n};\n\nexport const setAdMeshTrackerConfig = (config: Partial<TrackingConfig>) => {\n globalConfig = { ...globalConfig, ...config };\n};\n\nexport const useAdMeshTracker = (config?: Partial<TrackingConfig>): UseAdMeshTrackerReturn => {\n const [isTracking, setIsTracking] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const mergedConfig = useMemo(() => ({ ...globalConfig, ...config }), [config]);\n\n const sendTrackingEvent = useCallback(async (\n eventType: 'click' | 'view' | 'conversion',\n data: TrackingData\n ): Promise<void> => {\n if (!mergedConfig.enabled) {\n return;\n }\n\n if (!data.recommendationId || !data.admeshLink) {\n const errorMsg = 'Missing required tracking data: recommendationId and admeshLink are required';\n setError(errorMsg);\n return;\n }\n\n setIsTracking(true);\n setError(null);\n\n const payload = {\n event_type: eventType,\n recommendation_id: data.recommendationId,\n admesh_link: data.admeshLink,\n product_id: data.productId,\n user_id: data.userId,\n session_id: data.sessionId,\n revenue: data.revenue,\n conversion_type: data.conversionType,\n metadata: data.metadata,\n timestamp: new Date().toISOString(),\n user_agent: navigator.userAgent,\n referrer: document.referrer,\n page_url: window.location.href\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 1; attempt <= (mergedConfig.retryAttempts || 3); attempt++) {\n try {\n const response = await fetch(`${DEFAULT_TRACKING_URL}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n await response.json();\n setIsTracking(false);\n return;\n\n } catch (err) {\n lastError = err as Error;\n\n if (attempt < (mergedConfig.retryAttempts || 3)) {\n await new Promise(resolve =>\n setTimeout(resolve, (mergedConfig.retryDelay || 1000) * attempt)\n );\n }\n }\n }\n\n // All attempts failed\n const errorMsg = `Failed to track ${eventType} event after ${mergedConfig.retryAttempts} attempts: ${lastError?.message}`;\n setError(errorMsg);\n setIsTracking(false);\n }, [mergedConfig]);\n\n const trackClick = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('click', data);\n }, [sendTrackingEvent]);\n\n const trackView = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('view', data);\n }, [sendTrackingEvent]);\n\n const trackConversion = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('conversion', data);\n }, [sendTrackingEvent]);\n\n return {\n trackClick,\n trackView,\n trackConversion,\n isTracking,\n error\n };\n};\n\n// Utility function to build admesh_link with tracking parameters\nexport const buildAdMeshLink = (\n baseLink: string, \n recommendationId: string, \n additionalParams?: Record<string, string>\n): string => {\n try {\n const url = new URL(baseLink);\n url.searchParams.set('recommendation_id', recommendationId);\n url.searchParams.set('utm_source', 'admesh');\n url.searchParams.set('utm_medium', 'recommendation');\n \n if (additionalParams) {\n Object.entries(additionalParams).forEach(([key, value]) => {\n url.searchParams.set(key, value);\n });\n }\n \n return url.toString();\n } catch (err) {\n logger.warn('[AdMesh] Invalid URL provided to buildAdMeshLink');\n return baseLink;\n }\n};\n\n// Helper function to extract tracking data from recommendation\nexport const extractTrackingData = (\n recommendation: { recommendation_id: string; admesh_link: string; product_id: string },\n additionalData?: Partial<TrackingData>\n): TrackingData => {\n return {\n recommendationId: recommendation.recommendation_id,\n admeshLink: recommendation.admesh_link,\n productId: recommendation.product_id,\n ...additionalData\n };\n};\n","import React, { useCallback, useEffect, useRef } from 'react';\nimport type { AdMeshLinkTrackerProps } from '../types/index';\nimport { useAdMeshTracker } from '../hooks/useAdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport const AdMeshLinkTracker: React.FC<AdMeshLinkTrackerProps> = ({\n recommendationId,\n admeshLink,\n productId,\n children,\n trackingData,\n className,\n style\n}) => {\n const { trackClick, trackView } = useAdMeshTracker();\n const elementRef = useRef<HTMLDivElement>(null);\n const hasTrackedView = useRef(false);\n\n // Ensure all child links open in new tab\n useEffect(() => {\n if (!elementRef.current) return;\n\n // Find all <a> tags within this component and ensure they open in new tab\n const links = elementRef.current.querySelectorAll('a');\n links.forEach((link) => {\n // Only set if not already set or if it's not _blank\n if (!link.hasAttribute('target') || link.getAttribute('target') !== '_blank') {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n });\n }, [children]); // Re-run when children change\n\n // Track view when component becomes visible\n useEffect(() => {\n if (!elementRef.current || hasTrackedView.current) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting && !hasTrackedView.current) {\n hasTrackedView.current = true;\n trackView({\n recommendationId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(logger.error);\n }\n });\n },\n {\n threshold: 0.5, // Track when 50% of the element is visible\n rootMargin: '0px'\n }\n );\n\n observer.observe(elementRef.current);\n\n return () => {\n observer.disconnect();\n };\n }, [recommendationId, admeshLink, productId, trackingData, trackView]);\n\n const handleClick = useCallback((event: React.MouseEvent) => {\n // Fire tracking asynchronously WITHOUT blocking navigation\n // This ensures the link opens immediately\n trackClick({\n recommendationId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(() => {\n // Log error but don't block navigation\n logger.error('[AdMesh] Failed to track click');\n });\n\n // If the children contain a link, ensure it opens in new tab\n // Otherwise, navigate programmatically\n const target = event.target as HTMLElement;\n const link = target.closest('a');\n\n if (!link) {\n // No link found, navigate programmatically\n window.open(admeshLink, '_blank', 'noopener,noreferrer');\n } else {\n // Link found - ensure it opens in new tab\n if (!link.hasAttribute('target') || link.getAttribute('target') !== '_blank') {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n // Let the browser handle navigation naturally\n }\n }, [recommendationId, admeshLink, productId, trackingData, trackClick]);\n\n return (\n <div\n ref={elementRef}\n className={className}\n onClick={handleClick}\n style={{\n cursor: 'pointer',\n ...style\n }}\n >\n {children}\n </div>\n );\n};\n\nAdMeshLinkTracker.displayName = 'AdMeshLinkTracker';\n","/**\n * AdMesh Style Injection System\n * \n * Provides platform-agnostic, isolated styling for AdMesh components\n * that prevents interference from host platform CSS frameworks\n */\n\nconst ADMESH_STYLE_ID = 'admesh-ui-sdk-styles';\nconst ADMESH_RESET_ID = 'admesh-ui-sdk-reset';\n\n/**\n * CSS Reset for AdMesh components\n * Normalizes styles to prevent host platform CSS from affecting AdMesh components\n */\nconst ADMESH_CSS_RESET = `\n/* AdMesh UI SDK - CSS Reset & Normalization */\n.admesh-component,\n.admesh-component * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n text-decoration: none;\n background: transparent;\n color: inherit;\n}\n\n.admesh-component {\n all: revert;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n}\n\n.admesh-component button,\n.admesh-component a {\n cursor: pointer;\n border: none;\n background: none;\n padding: 0;\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n}\n\n.admesh-component a {\n text-decoration: none;\n color: inherit;\n}\n\n.admesh-component button:focus,\n.admesh-component a:focus {\n outline: none;\n}\n\n.admesh-component img {\n max-width: 100%;\n height: auto;\n display: block;\n}\n\n.admesh-component ul,\n.admesh-component ol {\n list-style: none;\n}\n\n.admesh-component table {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.admesh-component input,\n.admesh-component textarea,\n.admesh-component select {\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n}\n\n/* Prevent Tailwind and other frameworks from affecting AdMesh */\n.admesh-component .prose,\n.admesh-component .container,\n.admesh-component .grid,\n.admesh-component .flex {\n all: revert;\n}\n\n/* Ensure AdMesh components are not affected by dark mode utilities */\n.admesh-component.dark,\n.admesh-component[data-admesh-theme=\"dark\"] {\n color-scheme: dark;\n}\n\n.admesh-component.light,\n.admesh-component[data-admesh-theme=\"light\"] {\n color-scheme: light;\n}\n`;\n\n/**\n * Core AdMesh Component Styles\n * Self-contained styles that work independently of host platform\n */\nconst ADMESH_CORE_STYLES = `\n/* AdMesh Core Component Styles */\n\n.admesh-component {\n position: relative;\n display: block;\n}\n\n/* Product Card Styles */\n.admesh-product-card {\n position: relative;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n height: 100%;\n border-radius: 0.75rem;\n background: rgb(255, 255, 255);\n border: 1px solid rgb(229, 231, 235);\n box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n overflow: hidden;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] {\n background: rgb(17, 24, 39);\n border-color: rgb(31, 41, 55);\n}\n\n.admesh-product-card:hover {\n transform: translateY(-4px);\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);\n border-color: rgba(99, 102, 241, 0.2);\n}\n\n.admesh-product-card__container {\n position: relative;\n padding: 1rem;\n height: 100%;\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n z-index: 2;\n}\n\n.admesh-product-card__header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 0.5rem;\n margin-bottom: 0.25rem;\n}\n\n.admesh-product-card__title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1.2;\n color: rgb(17, 24, 39);\n letter-spacing: -0.025em;\n transition: all 0.3s ease;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] .admesh-product-card__title {\n color: rgb(243, 244, 246);\n}\n\n.admesh-product-card:hover .admesh-product-card__title {\n transform: translateX(4px);\n}\n\n.admesh-product-card__cta {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n white-space: nowrap;\n border-radius: 0.5rem;\n font-size: 1rem;\n font-weight: 600;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0.75rem 1.5rem;\n background: rgb(0, 0, 0);\n color: rgb(255, 255, 255);\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] .admesh-product-card__cta {\n background: rgb(255, 255, 255);\n color: rgb(0, 0, 0);\n}\n\n.admesh-product-card__cta:hover {\n transform: translateY(-2px);\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);\n}\n\n.admesh-product-card__cta:active {\n transform: translateY(0);\n}\n\n/* Summary Unit Styles */\n.admesh-summary-unit {\n font-size: 1rem;\n line-height: 1.6;\n color: rgb(17, 24, 39);\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] {\n color: rgb(243, 244, 246);\n}\n\n.admesh-summary-unit a {\n color: rgb(37, 99, 235);\n text-decoration: underline;\n text-decoration-color: rgb(37, 99, 235);\n text-underline-offset: 2px;\n transition: all 0.2s ease;\n font-weight: 500;\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] a {\n color: rgb(96, 165, 250);\n text-decoration-color: rgb(96, 165, 250);\n}\n\n.admesh-summary-unit a:hover {\n color: rgb(29, 78, 216);\n text-decoration-color: rgb(29, 78, 216);\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] a:hover {\n color: rgb(147, 197, 253);\n text-decoration-color: rgb(147, 197, 253);\n}\n\n/* Ecommerce Cards Styles */\n.admesh-ecommerce-container {\n position: relative;\n width: 100%;\n}\n\n.admesh-ecommerce-card {\n flex-shrink: 0;\n background: white;\n border: 1px solid rgb(229, 231, 235);\n border-radius: 0.5rem;\n overflow: hidden;\n transition: all 0.2s ease-in-out;\n cursor: pointer;\n position: relative;\n}\n\n.admesh-ecommerce-card[data-admesh-theme=\"dark\"] {\n background: rgb(31, 41, 55);\n border-color: rgb(55, 65, 81);\n color: white;\n}\n\n.admesh-ecommerce-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);\n}\n\n/* Badge Styles */\n.admesh-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n border-radius: 0.5rem;\n padding: 0.375rem 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n transition: all 0.3s ease;\n position: relative;\n overflow: hidden;\n}\n\n.admesh-badge--primary {\n background: linear-gradient(135deg, rgb(99, 102, 241), rgb(79, 70, 229));\n color: white;\n box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.3);\n}\n\n.admesh-badge--secondary {\n background: rgb(243, 244, 246);\n color: rgb(55, 65, 81);\n}\n\n.admesh-badge[data-admesh-theme=\"dark\"].admesh-badge--secondary {\n background: rgb(31, 41, 55);\n color: rgb(209, 213, 219);\n}\n\n/* Responsive */\n@media (max-width: 640px) {\n .admesh-product-card__container {\n padding: 0.75rem;\n gap: 0.5rem;\n }\n\n .admesh-product-card__title {\n font-size: 1.25rem;\n }\n\n .admesh-product-card__cta {\n padding: 0.5rem 1rem;\n font-size: 0.875rem;\n }\n}\n`;\n\n/**\n * Inject AdMesh styles into the document\n * Ensures styles are loaded only once and don't conflict with host platform\n */\nexport const injectAdMeshStyles = (): void => {\n if (typeof document === 'undefined') return;\n\n // Check if styles already injected\n if (document.getElementById(ADMESH_RESET_ID) && document.getElementById(ADMESH_STYLE_ID)) {\n return;\n }\n\n // Inject CSS Reset\n if (!document.getElementById(ADMESH_RESET_ID)) {\n const resetStyle = document.createElement('style');\n resetStyle.id = ADMESH_RESET_ID;\n resetStyle.textContent = ADMESH_CSS_RESET;\n document.head.appendChild(resetStyle);\n }\n\n // Inject Core Styles\n if (!document.getElementById(ADMESH_STYLE_ID)) {\n const coreStyle = document.createElement('style');\n coreStyle.id = ADMESH_STYLE_ID;\n coreStyle.textContent = ADMESH_CORE_STYLES;\n document.head.appendChild(coreStyle);\n }\n};\n\n/**\n * Remove AdMesh styles from the document\n * Useful for cleanup or testing\n */\nexport const removeAdMeshStyles = (): void => {\n if (typeof document === 'undefined') return;\n\n const resetStyle = document.getElementById(ADMESH_RESET_ID);\n const coreStyle = document.getElementById(ADMESH_STYLE_ID);\n\n if (resetStyle) resetStyle.remove();\n if (coreStyle) coreStyle.remove();\n};\n\n/**\n * Check if AdMesh styles are already injected\n */\nexport const areAdMeshStylesInjected = (): boolean => {\n if (typeof document === 'undefined') return false;\n return !!(document.getElementById(ADMESH_RESET_ID) && document.getElementById(ADMESH_STYLE_ID));\n};\n\n","import { useEffect } from 'react';\nimport { logger } from '../utils/logger';\nimport { injectAdMeshStyles } from '../utils/styleInjection';\n\n// Complete CSS content as a string - this will be injected automatically\nconst ADMESH_STYLES = `\n/* AdMesh UI SDK - Complete Self-Contained Styles */\n\n/* CSS Reset for AdMesh components */\n.admesh-component, .admesh-component * {\n box-sizing: border-box;\n}\n\n/* CSS Variables - Black/White Color Scheme */\n.admesh-component {\n --admesh-primary: #000000;\n --admesh-primary-hover: #333333;\n --admesh-secondary: #666666;\n --admesh-accent: #000000;\n --admesh-background: #ffffff;\n --admesh-surface: #ffffff;\n --admesh-border: #e5e7eb;\n --admesh-text: #000000;\n --admesh-text-muted: #666666;\n --admesh-text-light: #999999;\n --admesh-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --admesh-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --admesh-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n --admesh-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --admesh-radius: 0.5rem;\n --admesh-radius-sm: 0.25rem;\n --admesh-radius-lg: 0.75rem;\n --admesh-radius-xl: 1rem;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] {\n --admesh-primary: #ffffff;\n --admesh-primary-hover: #e5e7eb;\n --admesh-secondary: #9ca3af;\n --admesh-accent: #ffffff;\n --admesh-background: #000000;\n --admesh-surface: #111111;\n --admesh-border: #333333;\n --admesh-text: #ffffff;\n --admesh-text-muted: #9ca3af;\n --admesh-text-light: #666666;\n --admesh-shadow: 0 1px 3px 0 rgb(255 255 255 / 0.1), 0 1px 2px -1px rgb(255 255 255 / 0.1);\n --admesh-shadow-md: 0 4px 6px -1px rgb(255 255 255 / 0.1), 0 2px 4px -2px rgb(255 255 255 / 0.1);\n --admesh-shadow-lg: 0 10px 15px -3px rgb(255 255 255 / 0.1), 0 4px 6px -4px rgb(255 255 255 / 0.1);\n --admesh-shadow-xl: 0 20px 25px -5px rgb(255 255 255 / 0.1), 0 8px 10px -6px rgb(255 255 255 / 0.1);\n}\n\n/* Layout Styles */\n.admesh-layout {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n color: var(--admesh-text);\n background-color: var(--admesh-background);\n border-radius: var(--admesh-radius);\n padding: 1.5rem;\n box-shadow: var(--admesh-shadow);\n border: 1px solid var(--admesh-border);\n /* Consistent width: 100% for all layouts except ecommerce */\n width: 100%;\n}\n\n/* Ecommerce layout exception */\n.admesh-layout--ecommerce {\n width: auto;\n}\n\n/* Citation Unit Styles */\n.admesh-citation-unit {\n width: 100%;\n}\n\n/* Inline Recommendation Styles */\n.admesh-inline-recommendation {\n width: 100%;\n}\n\n/* Simple Ad Styles */\n.admesh-simple-ad {\n width: 100%;\n}\n\n.admesh-layout__header {\n margin-bottom: 1.5rem;\n text-align: center;\n}\n\n.admesh-layout__title {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--admesh-text);\n margin-bottom: 0.5rem;\n}\n\n.admesh-layout__subtitle {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n}\n\n.admesh-layout__cards-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 1rem;\n margin-bottom: 1.5rem;\n}\n\n.admesh-layout__more-indicator {\n text-align: center;\n padding: 1rem;\n color: var(--admesh-text-muted);\n font-size: 0.875rem;\n}\n\n.admesh-layout__empty {\n text-align: center;\n padding: 3rem 1rem;\n}\n\n.admesh-layout__empty-content h3 {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--admesh-text-muted);\n margin-bottom: 0.5rem;\n}\n\n.admesh-layout__empty-content p {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n}\n\n/* Product Card Styles */\n.admesh-product-card {\n background-color: var(--admesh-surface);\n border: 1px solid var(--admesh-border);\n border-radius: var(--admesh-radius);\n padding: 1.5rem;\n transition: all 0.2s ease-in-out;\n position: relative;\n overflow: hidden;\n /* Consistent width: 100% for product cards */\n width: 100%;\n}\n\n.admesh-product-card:hover {\n box-shadow: var(--admesh-shadow-lg);\n transform: translateY(-2px);\n border-color: var(--admesh-primary);\n}\n\n.admesh-product-card__header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__title {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--admesh-text);\n margin-bottom: 0.5rem;\n line-height: 1.4;\n}\n\n.admesh-product-card__reason {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n line-height: 1.5;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__match-score {\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__match-score-label {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.75rem;\n color: var(--admesh-text-muted);\n margin-bottom: 0.25rem;\n}\n\n.admesh-product-card__match-score-bar {\n width: 100%;\n height: 0.375rem;\n background-color: var(--admesh-border);\n border-radius: var(--admesh-radius-sm);\n overflow: hidden;\n}\n\n.admesh-product-card__match-score-fill {\n height: 100%;\n background: var(--admesh-primary);\n border-radius: var(--admesh-radius-sm);\n transition: width 0.3s ease-in-out;\n}\n\n.admesh-product-card__badges {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__badge {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n background-color: var(--admesh-primary);\n color: white;\n font-size: 0.75rem;\n font-weight: 500;\n border-radius: var(--admesh-radius-sm);\n}\n\n.admesh-product-card__badge--secondary {\n background-color: var(--admesh-secondary);\n}\n\n.admesh-product-card__keywords {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__keyword {\n padding: 0.125rem 0.375rem;\n background-color: var(--admesh-border);\n color: var(--admesh-text-muted);\n font-size: 0.75rem;\n border-radius: var(--admesh-radius-sm);\n}\n\n/* Dark mode specific enhancements */\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card__keyword {\n background-color: #4b5563;\n color: #d1d5db;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card:hover {\n border-color: var(--admesh-primary);\n background-color: #374151;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card__button:hover {\n background: var(--admesh-primary-hover);\n}\n\n.admesh-product-card__footer {\n display: flex;\n justify-content: flex-end;\n margin-top: 1.5rem;\n}\n\n/* Mobile-specific sidebar improvements */\n@media (max-width: 640px) {\n .admesh-sidebar {\n /* Ensure proper mobile viewport handling */\n height: 100vh !important;\n height: 100dvh !important; /* Dynamic viewport height for mobile browsers */\n max-height: 100vh !important;\n max-height: 100dvh !important;\n width: 100vw !important;\n max-width: 90vw !important;\n overflow: hidden !important;\n }\n\n .admesh-sidebar.relative {\n height: 100% !important;\n width: 100% !important;\n max-width: 100% !important;\n }\n\n /* Improve touch scrolling */\n .admesh-sidebar .overflow-y-auto {\n -webkit-overflow-scrolling: touch !important;\n overscroll-behavior: contain !important;\n scroll-behavior: smooth !important;\n }\n\n /* Prevent body scroll when sidebar is open */\n body:has(.admesh-sidebar[data-mobile-open=\"true\"]) {\n overflow: hidden !important;\n position: fixed !important;\n width: 100% !important;\n }\n}\n\n/* Tablet improvements */\n@media (min-width: 641px) and (max-width: 1024px) {\n .admesh-sidebar {\n max-width: 400px !important;\n }\n}\n\n/* Mobile responsiveness improvements for all components */\n@media (max-width: 640px) {\n /* Product cards mobile optimization */\n .admesh-card {\n padding: 0.75rem !important;\n margin-bottom: 0.75rem !important;\n }\n\n /* Inline recommendations mobile optimization */\n .admesh-inline-recommendation {\n padding: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n /* Conversation summary mobile optimization */\n .admesh-conversation-summary {\n padding: 1rem !important;\n }\n\n /* Percentage text mobile improvements */\n .admesh-component .text-xs {\n font-size: 0.75rem !important;\n line-height: 1rem !important;\n }\n\n .admesh-component .text-sm {\n font-size: 0.875rem !important;\n line-height: 1.25rem !important;\n }\n\n /* Button mobile improvements */\n .admesh-component button {\n padding: 0.375rem 0.75rem !important;\n font-size: 0.75rem !important;\n min-height: 2rem !important;\n touch-action: manipulation !important;\n }\n\n /* Badge mobile improvements */\n .admesh-component .rounded-full {\n padding: 0.25rem 0.5rem !important;\n font-size: 0.625rem !important;\n line-height: 1rem !important;\n }\n\n /* Progress bar mobile improvements */\n .admesh-component .bg-gray-200,\n .admesh-component .bg-slate-600 {\n height: 0.25rem !important;\n }\n\n /* Flex layout mobile improvements */\n .admesh-component .flex {\n flex-wrap: wrap !important;\n }\n\n .admesh-component .gap-2 {\n gap: 0.375rem !important;\n }\n\n .admesh-component .gap-3 {\n gap: 0.5rem !important;\n }\n}\n\n.admesh-product-card__button {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: var(--admesh-primary);\n color: var(--admesh-background);\n font-size: 0.875rem;\n font-weight: 500;\n border: none;\n border-radius: var(--admesh-radius);\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n text-decoration: none;\n}\n\n.admesh-product-card__button:hover {\n transform: translateY(-1px);\n box-shadow: var(--admesh-shadow-lg);\n}\n\n/* Utility Classes */\n.admesh-text-xs { font-size: 0.75rem; }\n.admesh-text-sm { font-size: 0.875rem; }\n.admesh-text-base { font-size: 1rem; }\n.admesh-text-lg { font-size: 1.125rem; }\n.admesh-text-xl { font-size: 1.25rem; }\n\n.admesh-font-medium { font-weight: 500; }\n.admesh-font-semibold { font-weight: 600; }\n.admesh-font-bold { font-weight: 700; }\n\n.admesh-text-muted { color: var(--admesh-text-muted); }\n\n/* Comparison Table Styles */\n.admesh-compare-table {\n width: 100%;\n border-collapse: collapse;\n background-color: var(--admesh-surface);\n border: 1px solid var(--admesh-border);\n border-radius: var(--admesh-radius);\n overflow: hidden;\n}\n\n.admesh-compare-table th,\n.admesh-compare-table td {\n padding: 0.75rem;\n text-align: left;\n border-bottom: 1px solid var(--admesh-border);\n}\n\n.admesh-compare-table th {\n background-color: var(--admesh-background);\n font-weight: 600;\n color: var(--admesh-text);\n font-size: 0.875rem;\n}\n\n.admesh-compare-table td {\n color: var(--admesh-text);\n font-size: 0.875rem;\n}\n\n.admesh-compare-table tr:hover {\n background-color: var(--admesh-border);\n}\n\n/* Dark mode table enhancements */\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-compare-table th {\n background-color: #374151;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-compare-table tr:hover {\n background-color: #4b5563;\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .admesh-layout {\n padding: 1rem;\n }\n\n .admesh-layout__cards-grid {\n grid-template-columns: 1fr;\n gap: 0.75rem;\n }\n\n .admesh-product-card {\n padding: 1rem;\n }\n\n .admesh-compare-table {\n font-size: 0.75rem;\n }\n\n .admesh-compare-table th,\n .admesh-compare-table td {\n padding: 0.5rem;\n }\n}\n\n/* Essential Utility Classes for Self-Contained SDK - High Specificity */\n.admesh-component .relative { position: relative !important; }\n.admesh-component .absolute { position: absolute !important; }\n.admesh-component .flex { display: flex !important; }\n.admesh-component .inline-flex { display: inline-flex !important; }\n.admesh-component .grid { display: grid !important; }\n.admesh-component .hidden { display: none !important; }\n.admesh-component .block { display: block !important; }\n.admesh-component .inline-block { display: inline-block !important; }\n\n/* Flexbox utilities */\n.admesh-component .flex-col { flex-direction: column !important; }\n.admesh-component .flex-row { flex-direction: row !important; }\n.admesh-component .flex-wrap { flex-wrap: wrap !important; }\n.admesh-component .items-center { align-items: center !important; }\n.admesh-component .items-start { align-items: flex-start !important; }\n.admesh-component .items-end { align-items: flex-end !important; }\n.admesh-component .justify-center { justify-content: center !important; }\n.admesh-component .justify-between { justify-content: space-between !important; }\n.admesh-component .justify-end { justify-content: flex-end !important; }\n.admesh-component .flex-1 { flex: 1 1 0% !important; }\n.admesh-component .flex-shrink-0 { flex-shrink: 0 !important; }\n\n/* Grid utilities */\n.admesh-component .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }\n.admesh-component .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.admesh-component .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n\n/* Spacing utilities */\n.admesh-component .gap-1 { gap: 0.25rem; }\n.admesh-component .gap-2 { gap: 0.5rem; }\n.admesh-component .gap-3 { gap: 0.75rem; }\n.admesh-component .gap-4 { gap: 1rem; }\n.admesh-component .gap-6 { gap: 1.5rem; }\n.admesh-component .gap-8 { gap: 2rem; }\n\n/* Padding utilities */\n.admesh-component .p-1 { padding: 0.25rem; }\n.admesh-component .p-2 { padding: 0.5rem; }\n.admesh-component .p-3 { padding: 0.75rem; }\n.admesh-component .p-4 { padding: 1rem; }\n.admesh-component .p-5 { padding: 1.25rem; }\n.admesh-component .p-6 { padding: 1.5rem; }\n.admesh-component .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }\n.admesh-component .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }\n.admesh-component .px-4 { padding-left: 1rem; padding-right: 1rem; }\n.admesh-component .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }\n.admesh-component .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }\n.admesh-component .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }\n.admesh-component .pt-2 { padding-top: 0.5rem; }\n.admesh-component .pt-3 { padding-top: 0.75rem; }\n.admesh-component .pb-2 { padding-bottom: 0.5rem; }\n.admesh-component .pb-3 { padding-bottom: 0.75rem; }\n\n/* Margin utilities */\n.admesh-component .m-0 { margin: 0; }\n.admesh-component .mb-1 { margin-bottom: 0.25rem; }\n.admesh-component .mb-2 { margin-bottom: 0.5rem; }\n.admesh-component .mb-3 { margin-bottom: 0.75rem; }\n.admesh-component .mb-4 { margin-bottom: 1rem; }\n.admesh-component .mb-6 { margin-bottom: 1.5rem; }\n.admesh-component .mt-1 { margin-top: 0.25rem; }\n.admesh-component .mt-2 { margin-top: 0.5rem; }\n.admesh-component .mt-4 { margin-top: 1rem; }\n.admesh-component .mt-6 { margin-top: 1.5rem; }\n.admesh-component .mt-auto { margin-top: auto; }\n.admesh-component .ml-1 { margin-left: 0.25rem; }\n.admesh-component .mr-1 { margin-right: 0.25rem; }\n.admesh-component .mr-2 { margin-right: 0.5rem; }\n\n/* Width and height utilities */\n.admesh-component .w-2 { width: 0.5rem; }\n.admesh-component .w-3 { width: 0.75rem; }\n.admesh-component .w-4 { width: 1rem; }\n.admesh-component .w-5 { width: 1.25rem; }\n.admesh-component .w-6 { width: 1.5rem; }\n.admesh-component .w-full { width: 100%; }\n.admesh-component .w-fit { width: fit-content; }\n.admesh-component .h-2 { height: 0.5rem; }\n.admesh-component .h-3 { height: 0.75rem; }\n.admesh-component .h-4 { height: 1rem; }\n.admesh-component .h-5 { height: 1.25rem; }\n.admesh-component .h-6 { height: 1.5rem; }\n.admesh-component .h-full { height: 100%; }\n.admesh-component .min-w-0 { min-width: 0px; }\n\n/* Border utilities */\n.admesh-component .border { border-width: 1px; }\n.admesh-component .border-t { border-top-width: 1px; }\n.admesh-component .border-gray-100 { border-color: #f3f4f6; }\n.admesh-component .border-gray-200 { border-color: #e5e7eb; }\n.admesh-component .border-gray-300 { border-color: #d1d5db; }\n.admesh-component .border-blue-200 { border-color: #bfdbfe; }\n.admesh-component .border-green-200 { border-color: #bbf7d0; }\n\n/* Border radius utilities */\n.admesh-component .rounded { border-radius: 0.25rem !important; }\n.admesh-component .rounded-md { border-radius: 0.375rem !important; }\n.admesh-component .rounded-lg { border-radius: 0.5rem !important; }\n.admesh-component .rounded-xl { border-radius: 0.75rem !important; }\n.admesh-component .rounded-full { border-radius: 9999px !important; }\n\n/* Background utilities */\n.admesh-component .bg-white { background-color: #ffffff; }\n.admesh-component .bg-gray-50 { background-color: #f9fafb; }\n.admesh-component .bg-gray-100 { background-color: #f3f4f6; }\n.admesh-component .bg-blue-50 { background-color: #eff6ff; }\n.admesh-component .bg-blue-100 { background-color: #dbeafe; }\n.admesh-component .bg-green-100 { background-color: #dcfce7; }\n.admesh-component .bg-green-500 { background-color: #22c55e; }\n.admesh-component .bg-blue-500 { background-color: #3b82f6; }\n\n/* Solid backgrounds - no gradients for minimal design */\n.admesh-component .bg-primary { background-color: var(--admesh-primary); }\n.admesh-component .bg-secondary { background-color: var(--admesh-secondary); }\n.admesh-component .bg-surface { background-color: var(--admesh-surface); }\n.admesh-component .bg-background { background-color: var(--admesh-background); }\n\n/* Text utilities */\n.admesh-component .text-xs { font-size: 0.75rem; line-height: 1rem; }\n.admesh-component .text-sm { font-size: 0.875rem; line-height: 1.25rem; }\n.admesh-component .text-base { font-size: 1rem; line-height: 1.5rem; }\n.admesh-component .text-lg { font-size: 1.125rem; line-height: 1.75rem; }\n.admesh-component .text-xl { font-size: 1.25rem; line-height: 1.75rem; }\n.admesh-component .font-medium { font-weight: 500; }\n.admesh-component .font-semibold { font-weight: 600; }\n.admesh-component .font-bold { font-weight: 700; }\n.admesh-component .leading-relaxed { line-height: 1.625; }\n\n/* Text colors */\n.admesh-component .text-white { color: #ffffff; }\n.admesh-component .text-gray-400 { color: #9ca3af; }\n.admesh-component .text-gray-500 { color: #6b7280; }\n.admesh-component .text-gray-600 { color: #4b5563; }\n.admesh-component .text-gray-700 { color: #374151; }\n.admesh-component .text-gray-800 { color: #1f2937; }\n.admesh-component .text-blue-600 { color: #2563eb; }\n.admesh-component .text-blue-700 { color: #1d4ed8; }\n.admesh-component .text-green-700 { color: #15803d; }\n\n/* Shadow utilities */\n.admesh-component .shadow-sm { box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); }\n.admesh-component .shadow { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-md { box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-lg { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-xl { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); }\n\n/* Transition utilities */\n.admesh-component .transition-all { transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }\n.admesh-component .transition-colors { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }\n.admesh-component .duration-200 { transition-duration: 200ms; }\n.admesh-component .duration-300 { transition-duration: 300ms; }\n\n/* Transform utilities */\n.admesh-component .hover\\\\:-translate-y-1:hover { transform: translateY(-0.25rem); }\n.admesh-component .hover\\\\:scale-105:hover { transform: scale(1.05); }\n\n/* Hover utilities */\n.admesh-component .hover\\\\:shadow-xl:hover { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); }\n.admesh-component .hover\\\\:bg-gray-100:hover { background-color: #f3f4f6; }\n.admesh-component .hover\\\\:text-blue-800:hover { color: #1e40af; }\n\n/* Cursor utilities */\n.admesh-component .cursor-pointer { cursor: pointer; }\n\n/* Overflow utilities */\n.admesh-component .overflow-hidden { overflow: hidden; }\n.admesh-component .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n/* Text decoration */\n.admesh-component .underline { text-decoration-line: underline; }\n\n/* Whitespace */\n.admesh-component .whitespace-nowrap { white-space: nowrap; }\n\n/* Dark mode utilities */\n@media (prefers-color-scheme: dark) {\n .admesh-component .dark\\\\:bg-slate-800 { background-color: #1e293b; }\n .admesh-component .dark\\\\:bg-slate-900 { background-color: #0f172a; }\n .admesh-component .dark\\\\:border-slate-700 { border-color: #334155; }\n .admesh-component .dark\\\\:text-white { color: #ffffff; }\n .admesh-component .dark\\\\:text-gray-200 { color: #e5e7eb; }\n .admesh-component .dark\\\\:text-gray-300 { color: #d1d5db; }\n .admesh-component .dark\\\\:text-gray-400 { color: #9ca3af; }\n .admesh-component .dark\\\\:text-blue-400 { color: #60a5fa; }\n}\n\n/* Responsive utilities */\n@media (min-width: 640px) {\n .admesh-component .sm\\\\:p-5 { padding: 1.25rem; }\n .admesh-component .sm\\\\:text-base { font-size: 1rem; line-height: 1.5rem; }\n .admesh-component .sm\\\\:text-lg { font-size: 1.125rem; line-height: 1.75rem; }\n .admesh-component .sm\\\\:flex-row { flex-direction: row; }\n .admesh-component .sm\\\\:items-center { align-items: center; }\n .admesh-component .sm\\\\:justify-between { justify-content: space-between; }\n}\n\n@media (min-width: 768px) {\n .admesh-component .md\\\\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n}\n\n@media (min-width: 1024px) {\n .admesh-component .lg\\\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n .admesh-component .lg\\\\:col-span-1 { grid-column: span 1 / span 1; }\n}\n`;\n\nlet stylesInjected = false;\n\n/**\n * Hook to inject AdMesh styles into the document\n * Ensures platform-agnostic, isolated styling that prevents\n * interference from host platform CSS frameworks\n *\n * Usage:\n * const MyComponent = () => {\n * useAdMeshStyles();\n * return <div className=\"admesh-component\">...</div>;\n * };\n */\nexport const useAdMeshStyles = () => {\n useEffect(() => {\n if (stylesInjected) return;\n\n try {\n // Use the new style injection system for better isolation\n injectAdMeshStyles();\n\n // Also inject the legacy styles for backward compatibility\n const styleElement = document.createElement('style');\n styleElement.id = 'admesh-ui-sdk-styles-legacy';\n styleElement.textContent = ADMESH_STYLES;\n\n if (!document.getElementById('admesh-ui-sdk-styles-legacy')) {\n document.head.appendChild(styleElement);\n }\n\n stylesInjected = true;\n } catch (error) {\n logger.error('[AdMesh] Failed to inject styles');\n }\n\n // Cleanup function\n return () => {\n // Note: We don't remove styles on unmount to prevent flickering\n // Styles are designed to be persistent for the lifetime of the app\n };\n }, []);\n};\n","import type { AdMeshRecommendation } from '../types/index';\n\n/**\n * Utility functions for generating compliant disclosure labels and tooltips\n */\n\nexport interface DisclosureConfig {\n showTooltips?: boolean;\n compactMode?: boolean;\n customLabels?: {\n partnerRecommendation?: string;\n partnerMatch?: string;\n promotedOption?: string;\n relatedOption?: string;\n };\n}\n\n/**\n * Generate appropriate label based on match score and recommendation quality\n * Uses preferred terminology: 'Promoted Match', 'Partner Recommendation', or 'Smart Pick'\n */\nexport const getRecommendationLabel = (\n recommendation: AdMeshRecommendation,\n config: DisclosureConfig = {}\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n const customLabels = config.customLabels || {};\n\n // High match score (>0.8)\n if (matchScore >= 0.8) {\n return customLabels.partnerRecommendation || 'Smart Pick';\n }\n\n // Medium match score (0.6-0.8)\n if (matchScore >= 0.6) {\n return customLabels.partnerMatch || 'Partner Recommendation';\n }\n\n // Lower match score (<0.6)\n if (matchScore >= 0.3) {\n return customLabels.promotedOption || 'Promoted Match';\n }\n\n // Very low match - related option\n return customLabels.relatedOption || 'Related';\n};\n\n/**\n * Generate tooltip text for recommendation labels\n */\nexport const getLabelTooltip = (\n recommendation: AdMeshRecommendation,\n _label: string\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n\n if (matchScore >= 0.8) {\n return \"This recommendation is from a partner who compensates us when you engage. We've matched it to your needs based on your query.\";\n }\n \n if (matchScore >= 0.6) {\n return \"Top-rated partner solution matched to your specific requirements. Partner compensates us for qualified referrals.\";\n }\n \n if (matchScore >= 0.3) {\n return \"This partner solution may be relevant to your needs. The partner compensates us when you take qualifying actions.\";\n }\n \n return \"This solution is somewhat related to your query. While not a perfect match, it might still be helpful. This partner compensates us for qualified referrals.\";\n};\n\n/**\n * Generate section-level disclosure text\n */\nexport const getSectionDisclosure = (\n hasHighMatches: boolean = true,\n isExpanded: boolean = false\n): string => {\n if (!hasHighMatches) {\n return \"Expanded Results: While these don't perfectly match your query, they're related solutions from our partner network. All partners compensate us for referrals.\";\n }\n \n if (isExpanded) {\n return \"These curated recommendations are from partners who compensate us for referrals.\";\n }\n \n return \"Personalized Sponsoreds: All results are from vetted partners who compensate us for qualified matches. We've ranked them based on relevance to your specific needs.\";\n};\n\n/**\n * Generate inline disclosure text for product cards\n * Uses preferred terminology consistently\n */\nexport const getInlineDisclosure = (\n recommendation: AdMeshRecommendation,\n compact: boolean = false\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n\n if (compact) {\n return \"Promoted Match\";\n }\n\n if (matchScore >= 0.8) {\n return \"Smart Pick\";\n }\n\n if (matchScore >= 0.6) {\n return \"Partner Recommendation\";\n }\n\n return \"Promoted Match\";\n};\n\n/**\n * Generate detailed tooltip for inline disclosures\n */\nexport const getInlineTooltip = (): string => {\n return \"We've partnered with trusted providers to bring you relevant solutions. These partners compensate us for qualified referrals, which helps us keep our service free.\";\n};\n\n/**\n * Generate badge text without emojis using preferred terminology\n */\nexport const getBadgeText = (badgeType: string): string => {\n const badgeMap: Record<string, string> = {\n 'Top Match': 'Smart Pick',\n 'Sponsored': 'Smart Pick',\n 'Perfect Fit': 'Smart Pick',\n 'Great Match': 'Partner Recommendation',\n 'Recommended': 'Partner Recommendation',\n 'Good Fit': 'Promoted Match',\n 'Featured': 'Promoted Match',\n 'Popular Choice': 'Popular Choice',\n 'Premium Pick': 'Premium Pick',\n 'Free Tier': 'Free Tier',\n 'AI Powered': 'AI Powered',\n 'Popular': 'Popular',\n 'New': 'New',\n 'Trial Available': 'Trial Available',\n 'Related Option': 'Related Option',\n 'Alternative Solution': 'Alternative Solution',\n 'Expanded Match': 'Expanded Match'\n };\n\n return badgeMap[badgeType] || badgeType;\n};\n\n/**\n * Generate appropriate CTA text\n */\nexport const getCtaText = (\n recommendation: AdMeshRecommendation,\n context: 'button' | 'link' = 'button'\n): string => {\n const productName = recommendation.product_title || recommendation.title || 'Product';\n\n if (context === 'link') {\n return productName;\n }\n\n return `Learn More`;\n};\n\n/**\n * Check if recommendations have high match scores\n */\nexport const hasHighQualityMatches = (recommendations: AdMeshRecommendation[]): boolean => {\n return recommendations.some(rec => (rec.contextual_relevance_score || 0) >= 0.8);\n};\n\n/**\n * Generate compliant powered-by text\n */\nexport const getPoweredByText = (compact: boolean = false): string => {\n if (compact) {\n return \"\";\n }\n \n return \"Recommendations \";\n};\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshProductCardProps } from '../types/index';\nimport { AdMeshLinkTracker } from './AdMeshLinkTracker';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\nimport { useAdMeshStyles } from '../hooks/useAdMeshStyles';\nimport {\n getRecommendationLabel,\n getLabelTooltip,\n} from '../utils/disclosureUtils';\n\nexport const AdMeshProductCard: React.FC<AdMeshProductCardProps> = ({\n recommendation,\n theme,\n variation = 'default',\n className,\n style,\n sessionId\n}) => {\n // Validate recommendation - return null if empty or invalid\n if (!recommendation || typeof recommendation !== 'object') {\n logger.log('[AdMesh Product Card] No recommendation provided - not rendering');\n return null;\n }\n \n // Additional validation: ensure recommendation has at least one identifier\n if (!recommendation.recommendation_id && !recommendation.title) {\n logger.log('[AdMesh Product Card] Invalid recommendation object (missing required fields) - not rendering');\n return null;\n }\n\n // Inject styles automatically\n useAdMeshStyles();\n\n\n\n\n // Get content based on variation - prioritize creative_input fields\n const getVariationContent = () => {\n const creativeInput = recommendation.creative_input || {};\n \n // Get title from creative_input or fallback to legacy fields\n const title = creativeInput.product_name || recommendation.title || recommendation.product_title || '';\n \n // Get description based on variation\n let description = '';\n if (variation === 'simple') {\n // Simple variation uses short description or context snippet\n description = creativeInput.short_description || \n creativeInput.context_snippet || \n recommendation.product_summary || \n recommendation.weave_summary || \n recommendation.reason || '';\n } else {\n // Default variation uses long description or short description\n description = creativeInput.long_description || \n creativeInput.short_description || \n recommendation.product_summary || \n recommendation.weave_summary || \n recommendation.reason || '';\n }\n \n // Get CTA text from creative_input or fallback\n const ctaText = creativeInput.cta_label || title;\n\n if (variation === 'simple') {\n return {\n title,\n description,\n ctaText,\n isSimple: true\n };\n } else {\n return {\n title,\n description,\n ctaText\n };\n }\n };\n\n const content = getVariationContent();\n\n const cardClasses = classNames(\n 'admesh-component',\n 'admesh-card',\n 'relative p-3 sm:p-4 rounded-xl bg-gradient-to-br from-white to-gray-50 dark:from-slate-800 dark:to-slate-900 border border-gray-200/50 dark:border-slate-700/50 shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1',\n className\n );\n\n const cardStyle = theme ? {\n '--admesh-primary': theme.primaryColor || theme.accentColor || '#3b82f6',\n '--admesh-secondary': theme.secondaryColor || '#10b981',\n '--admesh-accent': theme.accentColor || '#3b82f6',\n '--admesh-background': theme.backgroundColor,\n '--admesh-surface': theme.surfaceColor,\n '--admesh-border': theme.borderColor,\n '--admesh-text': theme.textColor,\n '--admesh-text-secondary': theme.textSecondaryColor,\n '--admesh-radius': theme.borderRadius || '12px',\n '--admesh-shadow-sm': theme.shadows?.small,\n '--admesh-shadow-md': theme.shadows?.medium,\n '--admesh-shadow-lg': theme.shadows?.large,\n '--admesh-spacing-sm': theme.spacing?.small,\n '--admesh-spacing-md': theme.spacing?.medium,\n '--admesh-spacing-lg': theme.spacing?.large,\n '--admesh-font-size-sm': theme.fontSize?.small,\n '--admesh-font-size-base': theme.fontSize?.base,\n '--admesh-font-size-lg': theme.fontSize?.large,\n '--admesh-font-size-title': theme.fontSize?.title,\n fontFamily: theme.fontFamily,\n // Ensure consistent width: 100% for all components except ecommerce\n width: theme.components?.productCard?.width || '100%'\n } as React.CSSProperties : { width: '100%' } as React.CSSProperties;\n\n // Render different layouts based on variation\n if (variation === 'simple') {\n // Simple inline ad format (replaces AdMeshSimpleAd)\n // Wrap with AdMeshViewabilityTracker for MRC-compliant exposure tracking\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={classNames(\n \"admesh-component admesh-simple-ad\",\n \"inline-block text-sm leading-relaxed\",\n className\n )}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n >\n <div\n data-admesh-theme={theme?.mode}\n >\n {/* Recommendation label */}\n <span\n style={{\n fontSize: '11px',\n fontWeight: '600',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff',\n backgroundColor: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n padding: '2px 6px',\n borderRadius: '4px',\n marginRight: '8px'\n }}\n title={getLabelTooltip(recommendation, getRecommendationLabel(recommendation))}\n >\n {getRecommendationLabel(recommendation)}\n </span>\n\n {/* Main content */}\n <span\n style={{\n color: theme?.mode === 'dark' ? '#f3f4f6' : '#374151',\n marginRight: '4px'\n }}\n >\n {content.description}{' '}\n </span>\n\n {/* CTA Link */}\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: content.title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'simple_ad_cta'\n }}\n >\n <span\n style={{\n color: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n textDecoration: 'underline',\n cursor: 'pointer',\n fontSize: 'inherit',\n fontFamily: 'inherit'\n }}\n >\n {content.ctaText}\n </span>\n </AdMeshLinkTracker>\n\n {/* Disclosure removed to prevent duplicate labels */}\n </div>\n </AdMeshViewabilityTracker>\n );\n }\n\n\n\n\n // Default full product card layout\n // Wrap with AdMeshViewabilityTracker for MRC-compliant exposure tracking\n const productId = recommendation.product_id || '';\n\n logger.debug('[AdMeshProductCard] 📊 Rendering with tracking data:', {\n recommendationId: recommendation.recommendation_id,\n productId,\n exposureUrl: recommendation.exposure_url ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING'\n });\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={cardClasses}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n >\n <div\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n data-admesh-theme={theme?.mode}\n >\n <div\n className=\"h-full flex flex-col\"\n style={cardStyle}\n >\n {/* Recommendation label at top */}\n <div className=\"mb-1.5\">\n <span\n style={{\n fontSize: '11px',\n fontWeight: '600',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff',\n backgroundColor: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n padding: '2px 6px',\n borderRadius: '4px'\n }}\n title={getLabelTooltip(recommendation, getRecommendationLabel(recommendation))}\n >\n {getRecommendationLabel(recommendation)}\n </span>\n </div>\n\n {/* Header with title and CTA button in same row */}\n <div className=\"flex justify-between items-center gap-3 mb-2\">\n <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n {(recommendation.creative_input?.assets?.logo_url || recommendation.product_logo?.url) && (\n <img\n src={recommendation.creative_input?.assets?.logo_url || recommendation.product_logo?.url}\n alt={`${content.title} logo`}\n className=\"w-6 h-6 rounded flex-shrink-0\"\n onError={(e) => {\n // Hide image if it fails to load\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n />\n )}\n <h4 className=\"font-semibold text-gray-800 dark:text-gray-200 text-sm sm:text-base truncate\">\n {content.title}\n </h4>\n </div>\n\n <div className=\"flex-shrink-0\">\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: content.title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'product_card_cta'\n }}\n >\n <button\n className=\"text-xs px-2 py-1 sm:px-3 sm:py-2 rounded-full flex items-center transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg whitespace-nowrap\"\n style={{\n backgroundColor: theme?.accentColor || '#000000',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff'\n }}\n >\n {recommendation.creative_input?.cta_label || 'Visit'}\n <svg className=\"ml-1 h-3 w-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\" />\n </svg>\n </button>\n </AdMeshLinkTracker>\n </div>\n </div>\n\n {/* Product Description/Reason */}\n <div className=\"mb-3\">\n <p className=\"text-sm text-gray-600 dark:text-gray-300 leading-snug\">\n {content.description}\n </p>\n </div>\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <div className=\"mb-3\">\n <p className=\"text-xs text-gray-500 dark:text-gray-400 leading-relaxed italic\">\n {recommendation.creative_input.offer_summary}\n </p>\n </div>\n )}\n\n {/* Value Props from creative_input */}\n {recommendation.creative_input?.value_props && recommendation.creative_input.value_props.length > 0 && (\n <div className=\"mb-3\">\n <ul className=\"space-y-1.5\">\n {recommendation.creative_input.value_props.slice(0, 3).map((prop, index) => (\n <li key={index} className=\"flex items-start gap-2 text-xs text-gray-600 dark:text-gray-400\">\n <svg className=\"w-3 h-3 mt-0.5 flex-shrink-0 text-green-500\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path fillRule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clipRule=\"evenodd\" />\n </svg>\n <span>{prop}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n {/* Footer section with disclosure */}\n <div className=\"mt-auto pt-3 border-t border-gray-100 dark:border-slate-700\">\n <div className=\"flex items-center justify-between text-xs text-gray-500 dark:text-gray-400\">\n <span>\n Sponsored\n </span>\n <span className=\"text-gray-400 dark:text-gray-500\">\n \n </span>\n </div>\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nAdMeshProductCard.displayName = 'AdMeshProductCard';\n","import React from 'react';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport type { AdMeshTheme } from '../types/index';\n\n/**\n * Context value provided by AdMeshProvider\n */\nexport interface AdMeshContextValue {\n // SDK instance\n sdk: AdMeshSDK | null;\n\n // Configuration\n apiKey: string;\n sessionId: string;\n theme?: AdMeshTheme;\n\n // UCP PlatformRequest fields (optional, passed from frontend)\n language?: string; // User language in BCP 47 format (e.g., \"en-US\")\n geo_country?: string; // User country code in ISO 3166-1 alpha-2 format (e.g., \"US\")\n userId?: string; // Anonymous hashed user ID\n model?: string; // AI model identifier (e.g., \"gpt-4o\")\n messages?: Array<{ role: string; content: string; id?: string }>; // Conversation history\n\n // Tracking state\n processedMessageIds: Set<string>;\n\n // Methods\n markMessageAsProcessed: (messageId: string) => void;\n isMessageProcessed: (messageId: string) => boolean;\n}\n\n/**\n * React Context for AdMesh SDK\n * \n * Provides SDK instance and tracking state to all child components\n */\nexport const AdMeshContext = React.createContext<AdMeshContextValue | undefined>(\n undefined\n);\n\n/**\n * Hook to access AdMesh context\n * \n * @throws Error if used outside of AdMeshProvider\n * @returns AdMeshContextValue\n */\nexport function useAdMeshContext(): AdMeshContextValue {\n const context = React.useContext(AdMeshContext);\n \n if (!context) {\n throw new Error(\n 'useAdMeshContext must be used within an <AdMeshProvider>. ' +\n 'Make sure your component is wrapped with <AdMeshProvider>.'\n );\n }\n \n return context;\n}\n\n","import { useAdMeshContext } from '../context/AdMeshContext';\n\n/**\n * Hook to access AdMesh SDK and tracking state\n *\n * Must be used within an <AdMeshProvider>\n *\n * @returns Object with SDK instance and tracking methods\n *\n * @example\n * ```tsx\n * const { sdk, sessionId, markMessageAsProcessed } = useAdMesh();\n *\n * // Use SDK to show recommendations\n * await sdk?.showRecommendations({\n * query: 'user query',\n * containerId: 'recommendations-container',\n * session_id: sessionId,\n * message_id: messageId\n * });\n *\n * // Mark message as processed to avoid duplicates\n * markMessageAsProcessed(messageId);\n * ```\n */\nexport function useAdMesh() {\n const context = useAdMeshContext();\n\n return {\n /** AdMesh SDK instance */\n sdk: context.sdk,\n\n /** API key */\n apiKey: context.apiKey,\n\n /** Session ID */\n sessionId: context.sessionId,\n\n /** Theme configuration */\n theme: context.theme,\n\n /** User language in BCP 47 format (e.g., \"en-US\") - from AdMeshProvider */\n language: context.language,\n\n /** User country code in ISO 3166-1 alpha-2 format (e.g., \"US\") - from AdMeshProvider */\n geo_country: context.geo_country,\n\n /** Anonymous hashed user ID - from AdMeshProvider */\n userId: context.userId,\n\n /** AI model identifier (e.g., \"gpt-4o\") - from AdMeshProvider */\n model: context.model,\n\n /** Conversation history - from AdMeshProvider */\n messages: context.messages,\n\n /** Set of processed message IDs (for deduplication) */\n processedMessageIds: context.processedMessageIds,\n\n /** Mark a message as processed to prevent duplicate recommendations */\n markMessageAsProcessed: context.markMessageAsProcessed,\n\n /** Check if a message has already been processed */\n isMessageProcessed: context.isMessageProcessed,\n };\n}\n\nexport default useAdMesh;\n\n","'use client';\n\nimport React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\nimport { useAdMesh } from '../hooks/useAdMesh';\n\nexport interface AdMeshBridgeFormatProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n sessionId?: string;\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n /** Callback to paste content to input field (for CTA button) */\n onPasteToInput?: (content: string) => void;\n}\n\n/**\n * AdMeshBridgeFormat - Bridge Ad Format Component\n * \n * Displays bridge format recommendations as followup sponsored recommendations with setup prompts and documentation URLs.\n * Bridge format is primarily designed for Vibe Coding Platforms and AI IDEs, and can also be shown in AI search as followup suggestions.\n * These recommendations appear after initial responses to provide setup instructions and configuration guidance.\n * \n * @example\n * ```tsx\n * <AdMeshBridgeFormat\n * recommendation={recommendation}\n * sessionId={sessionId}\n * theme={theme}\n * />\n * ```\n */\n/**\n * Extract CTA text from bridge_prompt / bridge_content\n * Generates \"Integrate [Product]\" format based on product name in the prompt\n */\nconst extractCTAText = (bridgePrompt: string, productName?: string): string => {\n if (!bridgePrompt) return 'Get Started';\n\n // Try to extract product name from prompt first\n let extractedProduct = '';\n \n // Patterns to extract product name\n const productPatterns = [\n /(?:set up|setup|integrate|add|install|use)\\s+([A-Z][a-zA-Z0-9\\s]+?)(?:\\.|$|,| by)/i,\n /(?:by|from)\\s+([A-Z][a-zA-Z0-9\\s]+?)(?:\\.|$|,)/,\n ];\n\n for (const pattern of productPatterns) {\n const match = bridgePrompt.match(pattern);\n if (match && match[1]) {\n extractedProduct = match[1].trim();\n break;\n }\n }\n\n // Use provided productName or extracted product\n const product = productName || extractedProduct;\n \n if (product) {\n // Clean up product name (remove extra words, keep main product name)\n const cleanProduct = product.split(/\\s+/).slice(0, 2).join(' '); // Take first 2 words max\n return `Integrate ${cleanProduct}`;\n }\n\n // Fallback: try to extract from \"To set up [Product]\" pattern\n const setupMatch = bridgePrompt.match(/to\\s+set\\s+up\\s+([a-zA-Z0-9\\s]+?)(?:\\s+by|\\s*[.,]|$)/i);\n if (setupMatch && setupMatch[1]) {\n const product = setupMatch[1].trim().split(/\\s+/).slice(0, 2).join(' ');\n return `Integrate ${product}`;\n }\n\n return 'Integrate';\n};\n\nexport const AdMeshBridgeFormat: React.FC<AdMeshBridgeFormatProps> = ({\n recommendation,\n theme,\n className = '',\n style = {},\n sessionId,\n onLinkClick,\n onPasteToInput,\n}) => {\n const creativeInput = recommendation.creative_input || {};\n // Extract bridge format fields\n const bridgeHeadline = (creativeInput as any).bridge_headline || '';\n const bridgeDescription = (creativeInput as any).bridge_description || '';\n const bridgePrompt =\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content ||\n '';\n const productName = creativeInput.product_name || '';\n const ctaLabel = creativeInput.cta_label || '';\n\n // If no bridge description or prompt, don't render\n if (!bridgeDescription && !bridgePrompt) {\n return null;\n }\n\n const productId = recommendation.product_id || '';\n const recommendationId = recommendation.recommendation_id || '';\n \n // Get SDK and sessionId from context for API calls\n const { sdk, sessionId: contextSessionId } = useAdMesh();\n const effectiveSessionId = sessionId || contextSessionId;\n \n // Bridge format doesn't use click_url or admesh_link - it only pastes the prompt\n // Exposure pixel is fired by AdMeshViewabilityTracker when ad becomes viewable (MRC-compliant)\n\n const handleCTAClick = async (e: React.MouseEvent) => {\n e.preventDefault();\n \n if (!bridgePrompt) return;\n\n // Track bridge engagement (sets status to \"engaged\")\n if (recommendationId && effectiveSessionId) {\n try {\n // Get API base URL from SDK or use default\n const apiBaseUrl = (sdk as any)?.apiBaseUrl || \n (typeof window !== 'undefined' && (window as any).__ADMESH_API_BASE_URL__) ||\n 'https://api.useadmesh.com';\n \n const response = await fetch(`${apiBaseUrl}/click/bridge-engagement`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n recommendation_id: recommendationId,\n session_id: effectiveSessionId,\n agent_id: recommendation.agent_id,\n user_id: (recommendation as any).user_id || undefined,\n is_test: false, // TODO: Get from config if needed\n }),\n });\n\n if (response.ok) {\n logger.log('[AdMesh Bridge] ✅ Engagement tracked successfully');\n } else {\n logger.warn('[AdMesh Bridge] ⚠️ Failed to track engagement:', response.statusText);\n }\n } catch (error) {\n logger.error('[AdMesh Bridge] ❌ Error tracking engagement:', error);\n // Don't block the user action if tracking fails\n }\n }\n\n // Call onLinkClick if provided (for tracking purposes)\n if (onLinkClick) {\n onLinkClick(recommendation);\n }\n\n // Paste prompt to input if callback provided\n if (onPasteToInput) {\n onPasteToInput(bridgePrompt);\n } else if (typeof window !== 'undefined' && (window as any).__admesh_setMessage) {\n // Fallback to window object\n (window as any).__admesh_setMessage(bridgePrompt);\n }\n };\n\n // Use cta_label from creative_input if available, otherwise extract from prompt\n const ctaText = ctaLabel || extractCTAText(bridgePrompt, productName);\n \n // Always show CTA if we have text for it\n const shouldShowCTA = !!ctaText;\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={`admesh-bridge-format ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n padding: '1rem',\n border: 'none',\n borderRadius: theme?.borderRadius || '0.5rem',\n backgroundColor: 'transparent',\n color: theme?.textColor || 'inherit',\n ...style\n }}\n >\n <div data-admesh-theme={theme?.mode || 'light'}>\n {/* Bridge Headline */}\n {bridgeHeadline && (\n <div\n className=\"admesh-bridge-headline\"\n style={{\n marginBottom: bridgeDescription ? '0.75rem' : shouldShowCTA ? '1rem' : '0',\n fontSize: theme?.fontSize?.large || '1rem',\n fontWeight: 600,\n color: theme?.textColor || 'inherit',\n lineHeight: '1.4',\n }}\n >\n {bridgeHeadline}\n </div>\n )}\n\n {/* Brand Description (1 sentence, short) - shown initially */}\n {bridgeDescription && (\n <div\n className=\"admesh-bridge-description\"\n style={{\n marginBottom: (shouldShowCTA || recommendation.creative_input?.offer_summary) ? '1rem' : '0',\n lineHeight: '1.6',\n fontSize: theme?.fontSize?.base || '0.875rem',\n color: theme?.textColor || 'inherit',\n }}\n >\n {bridgeDescription}\n </div>\n )}\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <div\n className=\"admesh-bridge-offer-summary\"\n style={{\n marginBottom: shouldShowCTA ? '1rem' : '0',\n lineHeight: '1.5',\n fontSize: theme?.fontSize?.small || '0.75rem',\n color: theme?.textSecondaryColor || theme?.mode === 'dark' ? '#9ca3af' : '#6b7280',\n fontStyle: 'italic',\n }}\n >\n {recommendation.creative_input.offer_summary}\n </div>\n )}\n\n {/* CTA Button - Left aligned, just below description */}\n {shouldShowCTA && (\n <div style={{ marginBottom: '0.75rem' }}>\n <button\n onClick={handleCTAClick}\n className=\"admesh-bridge-cta-button\"\n style={{\n padding: '0.5rem 1rem',\n backgroundColor: '#000000',\n color: '#ffffff',\n fontSize: theme?.fontSize?.small || '0.875rem',\n fontWeight: 500,\n borderRadius: theme?.borderRadius || '0.5rem',\n border: 'none',\n cursor: 'pointer',\n transition: 'background-color 0.2s, opacity 0.2s',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = '#1a1a1a';\n e.currentTarget.style.opacity = '0.9';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = '#000000';\n e.currentTarget.style.opacity = '1';\n }}\n >\n {ctaText}\n </button>\n </div>\n )}\n\n {/* Sponsored Label - Right aligned */}\n <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '0.5rem' }}>\n <div\n className=\"admesh-bridge-label\"\n style={{\n fontSize: theme?.fontSize?.small || '0.75rem',\n color: theme?.textSecondaryColor || theme?.mode === 'dark' ? '#9ca3af' : '#6b7280',\n fontStyle: 'italic',\n }}\n >\n Sponsored\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nexport default AdMeshBridgeFormat;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshTailAd } from './AdMeshTailAd';\nimport { AdMeshProductCard } from './AdMeshProductCard';\nimport { AdMeshBridgeFormat } from './AdMeshBridgeFormat';\n\nexport interface AdMeshSummaryLayoutProps {\n // Backend response data (finalized minimal schema)\n recommendations: AdMeshRecommendation[];\n summaryText?: string;\n\n // Styling\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n\n // Behavior\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n /** Callback to paste content to input field (for bridge format CTA) */\n onPasteToInput?: (content: string) => void;\n\n // Exposure tracking\n sessionId?: string;\n\n // Legacy props for backward compatibility (deprecated)\n response?: {\n layout_type?: string;\n tail_summary?: string;\n recommendations?: AdMeshRecommendation[];\n requires_summary?: boolean;\n };\n}\n\nexport const AdMeshSummaryLayout: React.FC<AdMeshSummaryLayoutProps> = ({\n recommendations,\n summaryText,\n theme,\n className = '',\n style = {},\n onLinkClick,\n onPasteToInput,\n sessionId,\n response // Legacy prop for backward compatibility\n}) => {\n // Support both new and legacy props\n const recs = recommendations || response?.recommendations || [];\n \n // Filter out any null/undefined/invalid recommendations\n const validRecs = recs.filter(rec => rec && typeof rec === 'object' && rec.recommendation_id);\n \n // If no valid recommendations, silently return null\n if (!validRecs || validRecs.length === 0) {\n logger.log('[AdMesh Summary Layout] Empty or invalid recommendations array - not rendering anything');\n return null;\n }\n \n // Get summary text from multiple sources, prioritizing creative_input\n let summary = summaryText || response?.tail_summary;\n \n // If no summary provided, try to get it from first recommendation's creative_input\n if (!summary && validRecs.length > 0 && validRecs[0]?.creative_input) {\n const creativeInput = validRecs[0].creative_input;\n summary = creativeInput.long_description || \n creativeInput.context_snippet || \n creativeInput.short_description || \n undefined;\n }\n\n logger.log(`[AdMesh Summary Layout] Rendering with ${validRecs.length} valid recommendations`);\n\n // Render based on layout type (default to tail)\n const renderContent = () => {\n // Check if first recommendation has bridge format\n // Priority: 1) bridge_content in creative_input, 2) format field, 3) preferred_format field\n if (validRecs.length > 0) {\n const firstRec = validRecs[0];\n const creativeInput = firstRec?.creative_input || {};\n const hasBridgePrompt = !!(creativeInput as any).bridge_prompt || !!creativeInput.bridge_content;\n const format = (firstRec as any)?.format || (creativeInput as any)?.format;\n const preferredFormat = (firstRec as any)?.preferred_format;\n const hasBridgeFormat = format === 'bridge' || preferredFormat === 'bridge';\n \n logger.log('[AdMesh Summary Layout] 🔍 Checking format:', {\n hasBridgePrompt,\n bridgePrompt: (creativeInput as any).bridge_prompt || creativeInput.bridge_content ? ((creativeInput as any).bridge_prompt || creativeInput.bridge_content || '').substring(0, 50) + '...' : undefined,\n format,\n preferredFormat,\n creativeInputKeys: Object.keys(creativeInput),\n recommendationKeys: Object.keys(firstRec || {})\n });\n \n if (hasBridgePrompt || hasBridgeFormat) {\n logger.log('[AdMesh Summary Layout] 🎯 ✅ Rendering bridge format');\n return (\n <AdMeshBridgeFormat\n recommendation={firstRec}\n theme={theme}\n sessionId={sessionId}\n onLinkClick={onLinkClick}\n onPasteToInput={onPasteToInput}\n />\n );\n } else {\n logger.log('[AdMesh Summary Layout] ⚠️ Bridge format NOT detected, falling back to other formats');\n }\n }\n\n // Show summary if available (tail format)\n if (summary) {\n return (\n <AdMeshTailAd\n summaryText={summary}\n recommendations={validRecs}\n theme={theme}\n onLinkClick={onLinkClick}\n sessionId={sessionId}\n />\n );\n }\n // Fallback to first recommendation if no summary - but only if we have a valid recommendation\n if (validRecs.length > 0 && validRecs[0]) {\n return (\n <div className=\"fallback-citation\">\n <AdMeshProductCard\n recommendation={validRecs[0]}\n theme={theme}\n sessionId={sessionId}\n />\n </div>\n );\n }\n // If no summary and no valid recommendation, don't render anything\n return null;\n };\n\n return (\n <div\n className={`admesh-summary-layout ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n {renderContent()}\n </div>\n );\n};\n\nexport default AdMeshSummaryLayout;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshLayoutProps } from '../types/index';\nimport { AdMeshSummaryLayout } from './AdMeshSummaryLayout';\n\nexport const AdMeshLayout: React.FC<AdMeshLayoutProps> = ({\n // New props (finalized minimal schema)\n recommendations,\n summaryText,\n\n // Styling\n theme,\n className,\n style,\n\n // Behavior\n onLinkClick,\n onPasteToInput,\n\n // Exposure tracking\n sessionId,\n\n // Legacy props (deprecated)\n response\n}) => {\n // Support both new and legacy props for backward compatibility\n const recs = recommendations || response?.recommendations || [];\n const summary = summaryText || response?.tail_summary;\n\n // Filter out any null/undefined/invalid recommendations\n const validRecs = recs.filter(rec => rec && typeof rec === 'object' && rec.recommendation_id);\n\n // Validate that valid recommendations are provided\n if (!validRecs || validRecs.length === 0) {\n logger.log('[AdMeshLayout] Empty or invalid recommendations array - not rendering anything');\n return null;\n }\n\n return (\n <AdMeshSummaryLayout\n recommendations={validRecs}\n summaryText={summary}\n theme={theme}\n className={className}\n style={style}\n onLinkClick={onLinkClick}\n onPasteToInput={onPasteToInput}\n sessionId={sessionId}\n />\n );\n};\n\nexport default AdMeshLayout;\n","'use client';\n\nimport React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshFollowupProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n sdk: AdMeshSDK;\n sessionId: string;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\nconst PlusIcon = ({ className, size = 20 }: { className?: string; size?: number }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M5 12h14\" />\n <path d=\"M12 5v14\" />\n </svg>\n);\n\n/**\n * AdMeshFollowup - Sponsored Follow-up Component\n * \n * Displays sponsored follow-up suggestions as optional fields on any recommendation.\n * Follow-ups use the same recommendation_id as the primary ad and are rendered separately\n * in a followups_container_id. This component handles all exposure and engagement tracking\n * internally - platforms only need to provide an onExecuteQuery hook for query execution.\n * \n * @example\n * ```tsx\n * <AdMeshFollowup\n * recommendation={recommendation}\n * sdk={sdk}\n * sessionId={sessionId}\n * theme={theme}\n * onExecuteQuery={(query) => {\n * // Platform's query execution logic\n * executeQuery(query);\n * }}\n * />\n * ```\n */\nexport const AdMeshFollowup: React.FC<AdMeshFollowupProps> = ({\n recommendation,\n theme,\n sdk,\n sessionId,\n onExecuteQuery,\n}) => {\n const followupQuery = recommendation.followup_query;\n const followupEngagementUrl = recommendation.followup_engagement_url;\n const followupExposureUrl = recommendation.followup_exposure_url; // Dedicated followup exposure URL\n const recommendationId = recommendation.recommendation_id;\n\n // Validate required fields\n if (!followupQuery || !followupEngagementUrl || !followupExposureUrl) {\n logger.log('[AdMeshFollowup] Missing followup_query, followup_engagement_url, or followup_exposure_url - not rendering');\n return null;\n }\n\n // Handle follow-up click/selection\n const handleFollowupClick = async () => {\n try {\n // Fire engagement tracking (SDK handles this automatically)\n if (followupEngagementUrl && recommendationId) {\n logger.log('[AdMeshFollowup] 🎯 Firing follow-up engagement tracking');\n await sdk.fireFollowupEngagement(followupEngagementUrl, recommendationId, sessionId);\n }\n\n // Execute query via platform hook\n if (onExecuteQuery && followupQuery) {\n logger.log(`[AdMeshFollowup] 🔍 Executing query: ${followupQuery}`);\n await onExecuteQuery(followupQuery);\n } else {\n logger.warn('[AdMeshFollowup] ⚠️ onExecuteQuery not provided - cannot execute query');\n }\n } catch (error) {\n logger.error('[AdMeshFollowup] ❌ Error handling follow-up click:', error);\n }\n };\n\n // Get theme colors\n const mode = theme?.mode || 'light';\n\n // Platform-native styling (matching Perplexica's \"Related\" section)\n // We use inline styles that can be overridden, but default to blending in\n\n return (\n <AdMeshViewabilityTracker\n productId={recommendation.product_id || ''}\n recommendationId={recommendationId || ''}\n exposureUrl={followupExposureUrl}\n sessionId={sessionId}\n className=\"admesh-followup-container\"\n style={{\n width: '100%',\n }}\n >\n <div className=\"flex flex-col space-y-3 text-sm admesh-followup-wrapper\">\n {/* Divider matching platform style */}\n <div className=\"h-px w-full bg-[#E5E5E5] dark:bg-[#262626] admesh-divider\" />\n\n <div\n onClick={handleFollowupClick}\n className=\"cursor-pointer flex flex-row justify-between font-medium space-x-2 items-center group\"\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n handleFollowupClick();\n }\n }}\n aria-label={`Sponsored follow-up: ${followupQuery}`}\n >\n <p className=\"transition duration-200 text-[#000] dark:text-[#FFF] hover:text-[#24A0ED] admesh-followup-text\">\n {followupQuery}\n </p>\n <div className=\"flex flex-row items-center space-x-2\">\n <span className=\"text-xs text-gray-500 dark:text-gray-400 italic admesh-ad-label\">\n Ad\n </span>\n <PlusIcon\n size={20}\n className=\"text-[#24A0ED] flex-shrink-0 admesh-plus-icon\"\n />\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n","/**\n * AdMesh Tracker\n *\n * Handles MRC-compliant exposure tracking for recommendations\n *\n * MRC Viewability Standards:\n * - Display Ads: 50% of pixels visible for at least 1 continuous second\n * - Large Display Ads (>242,500 pixels): 30% of pixels visible for at least 1 continuous second\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface TrackerConfig {\n apiKey: string;\n debug?: boolean;\n}\n\n/**\n * MRC Viewability threshold configuration\n */\ninterface MRCThreshold {\n visibilityPercentage: number; // 50% for standard ads, 30% for large ads\n minimumDurationMs: number; // 1000ms (1 second)\n}\n\nexport class AdMeshTracker {\n private firedExposures: Set<string> = new Set();\n private debug: boolean = false;\n private mrcThreshold: MRCThreshold = {\n visibilityPercentage: 50,\n minimumDurationMs: 1000\n };\n\n constructor(config: TrackerConfig) {\n this.debug = config.debug || false;\n }\n\n /**\n * Fire an exposure tracking pixel with MRC viewability compliance\n *\n * This method should be called when an ad element becomes viewable according to MRC standards.\n * The caller is responsible for ensuring the ad meets the MRC threshold before calling this method.\n *\n * Prevents duplicate firing for the same ad in the same session.\n *\n * @param exposureUrl - The tracking pixel URL to fire\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireExposure(exposureUrl: string, recommendationId: string, sessionId: string): void {\n const key = `${sessionId}_${recommendationId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n logger.log('[Tracker] Exposure already fired');\n }\n return;\n }\n\n this.firedExposures.add(key);\n\n try {\n // Fire the exposure pixel\n // Note: The caller should ensure MRC compliance before calling this method\n fetch(exposureUrl, { method: 'GET', keepalive: true }).catch(() => {\n if (this.debug) {\n logger.warn('[Tracker] Failed to fire exposure');\n }\n });\n\n if (this.debug) {\n logger.log('[Tracker] Fired MRC-compliant exposure');\n }\n } catch (error) {\n if (this.debug) {\n logger.error('[Tracker] Error firing exposure');\n }\n }\n }\n\n /**\n * Fire an exposure pixel with MRC viewability verification\n *\n * This method monitors an element for MRC viewability compliance before firing the exposure pixel.\n * It uses Intersection Observer API to track visibility and ensures the ad meets the threshold\n * (50% visible for 1 continuous second) before firing.\n *\n * @param exposureUrl - The tracking pixel URL to fire\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n * @param element - The DOM element to monitor for viewability\n * @returns Promise that resolves when exposure is fired or timeout occurs\n */\n async fireExposureWithMRCCompliance(\n exposureUrl: string,\n recommendationId: string,\n sessionId: string,\n element: HTMLElement\n ): Promise<void> {\n const key = `${sessionId}_${recommendationId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n logger.log('[Tracker] MRC exposure already fired');\n }\n return;\n }\n\n return new Promise((resolve) => {\n let viewableStartTime: number | null = null;\n let timeoutId: NodeJS.Timeout | null = null;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const visibilityPercentage = (entry.intersectionRatio * 100);\n\n if (visibilityPercentage >= this.mrcThreshold.visibilityPercentage) {\n // Ad is visible enough\n if (viewableStartTime === null) {\n // Start tracking viewable duration\n viewableStartTime = Date.now();\n\n if (this.debug) {\n logger.log('[Tracker] Ad reached MRC visibility threshold');\n }\n\n // Set timeout to fire exposure after minimum duration\n timeoutId = setTimeout(() => {\n // Fire the exposure pixel\n this.fireExposure(exposureUrl, recommendationId, sessionId);\n observer.disconnect();\n resolve();\n }, this.mrcThreshold.minimumDurationMs);\n }\n } else {\n // Ad visibility dropped below threshold\n if (viewableStartTime !== null) {\n // Reset tracking\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n viewableStartTime = null;\n\n if (this.debug) {\n logger.log('[Tracker] Ad visibility dropped below MRC threshold');\n }\n }\n }\n });\n },\n {\n threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],\n rootMargin: '0px'\n }\n );\n\n observer.observe(element);\n\n // Cleanup on unmount or after reasonable timeout\n const maxWaitTime = 30000; // 30 seconds max wait\n const cleanupTimeout = setTimeout(() => {\n observer.disconnect();\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n resolve();\n }, maxWaitTime);\n\n // Store cleanup function for manual cleanup if needed\n (element as any).__admeshTrackerCleanup = () => {\n observer.disconnect();\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n clearTimeout(cleanupTimeout);\n };\n });\n }\n\n /**\n * Clear fired exposures (useful for testing or session reset)\n */\n clearFiredExposures(): void {\n this.firedExposures.clear();\n }\n\n /**\n * Get MRC threshold configuration\n */\n getMRCThreshold(): MRCThreshold {\n return { ...this.mrcThreshold };\n }\n\n /**\n * Set custom MRC threshold (for testing or special cases)\n */\n setMRCThreshold(threshold: Partial<MRCThreshold>): void {\n this.mrcThreshold = {\n ...this.mrcThreshold,\n ...threshold\n };\n }\n\n /**\n * Fire exposure pixel for sponsored followup\n * Uses same logic as regular exposure tracking\n * \n * @param exposureUrl - The tracking pixel URL to fire (can use regular exposure_url)\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireFollowupExposure(\n exposureUrl: string,\n recommendationId: string,\n sessionId: string\n ): void {\n // Reuse existing fireExposure() method\n this.fireExposure(exposureUrl, recommendationId, sessionId);\n }\n\n /**\n * Fire engagement tracking for sponsored followup\n * \n * @param engagementUrl - The engagement tracking URL to fire\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID (sent in POST body for analytics joining)\n * @returns Promise that resolves when engagement is fired\n */\n fireFollowupEngagement(\n engagementUrl: string,\n recommendationId: string,\n sessionId: string\n ): Promise<void> {\n // Use fetch with keepalive and POST method\n // Always resolve the promise even on error to prevent unhandled rejections\n return fetch(engagementUrl, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ session_id: sessionId })\n }).catch((error) => {\n // Log error silently (only in debug mode) - network errors are expected and shouldn't break the flow\n if (this.debug) {\n logger.warn('[Tracker] Failed to fire followup engagement (non-critical):', error);\n }\n // Return undefined to resolve the promise successfully\n // This prevents the error from propagating to callers\n return undefined;\n }).then(() => {\n // Ensure promise always resolves with void (no return value)\n // This makes the return type consistent: Promise<void>\n });\n }\n}\n","/**\n * AdMesh Renderer\n * \n * Handles rendering of recommendations in the specified container\n */\n\nimport ReactDOM from 'react-dom/client';\nimport type { AgentRecommendationResponse, AdMeshTheme, AdMeshRecommendation } from '../types/index';\nimport { AdMeshLayout } from '../components/AdMeshLayout';\nimport { AdMeshFollowup } from '../components/AdMeshFollowup';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport interface RenderOptions {\n containerId: string;\n followups_container_id?: string;\n response: AgentRecommendationResponse;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\n onPasteToInput?: (content: string) => void;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\nexport class AdMeshRenderer {\n private roots: Map<string, ReactDOM.Root> = new Map();\n\n constructor() {\n // No configuration needed\n }\n\n /**\n * Render recommendations in the specified container\n */\n async render(options: RenderOptions): Promise<void> {\n try {\n logger.log('[AdMeshRenderer] 🎨 Attempting to render recommendations');\n\n // Check if we have any recommendations to render\n const recommendations = options.response.recommendations || [];\n if (recommendations.length === 0) {\n logger.log('[AdMeshRenderer] ⚠️ No recommendations to render - skipping render to avoid occupying space');\n return;\n }\n\n const container = document.getElementById(options.containerId);\n\n if (!container) {\n logger.error('[AdMeshRenderer] ❌ Container not found');\n throw new Error(`Container with ID \"${options.containerId}\" not found`);\n }\n\n logger.log('[AdMeshRenderer] ✅ Container ready');\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n logger.log('[AdMeshRenderer] 🧹 Cleaning up existing root');\n existingRoot.unmount();\n this.roots.delete(options.containerId);\n }\n\n // Clear the container's innerHTML to ensure React can create a fresh root\n container.innerHTML = '';\n\n // Create a new root\n const root = ReactDOM.createRoot(container);\n\n // Render the layout component\n // Use tail_summary from first recommendation if available\n const tailSummary = recommendations[0]?.tail_summary || '';\n\n logger.log('[AdMeshRenderer] 📊 Rendering recommendations');\n\n // Get onPasteToInput from options or window object (for backward compatibility)\n const onPasteToInput = options.onPasteToInput || \n (typeof window !== 'undefined' ? (window as any).__admesh_onPasteToInput : undefined);\n\n root.render(\n <AdMeshLayout\n recommendations={recommendations}\n summaryText={tailSummary}\n theme={options.theme}\n sessionId={options.sessionId}\n onPasteToInput={onPasteToInput}\n />\n );\n\n // Show the container now that content is rendered (prevents empty space when no ads)\n container.style.display = 'block';\n\n logger.log('[AdMeshRenderer] ✅ Recommendations rendered successfully');\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n\n // After rendering primary format, check for follow-ups\n if (options.followups_container_id) {\n const recommendation = recommendations[0];\n if (recommendation?.followup_query && recommendation?.followup_engagement_url) {\n // Render follow-up in followups_container_id\n await this.renderFollowup({\n containerId: options.followups_container_id,\n recommendation,\n theme: options.theme,\n tracker: options.tracker,\n sessionId: options.sessionId,\n onExecuteQuery: options.onExecuteQuery\n });\n }\n }\n } catch (error) {\n logger.error('[AdMeshRenderer] ❌ Error rendering recommendations');\n throw error;\n }\n }\n\n /**\n * Render follow-up in the specified container\n */\n private async renderFollowup(options: {\n containerId: string;\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n }): Promise<void> {\n try {\n logger.log(`[AdMeshRenderer] 🎯 Rendering follow-up in container: ${options.containerId}`);\n\n const container = document.getElementById(options.containerId);\n if (!container) {\n logger.warn(`[AdMeshRenderer] ⚠️ Followups container not found: ${options.containerId}`);\n return;\n }\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n logger.log('[AdMeshRenderer] 🧹 Cleaning up existing followup root');\n existingRoot.unmount();\n this.roots.delete(options.containerId);\n }\n\n // Clear container\n container.innerHTML = '';\n\n // Create a new root\n const root = ReactDOM.createRoot(container);\n\n root.render(\n <AdMeshFollowup\n recommendation={options.recommendation}\n theme={options.theme}\n tracker={options.tracker}\n sessionId={options.sessionId}\n onExecuteQuery={options.onExecuteQuery}\n />\n );\n\n // Show the container\n container.style.display = 'block';\n\n logger.log('[AdMeshRenderer] ✅ Follow-up rendered successfully');\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n } catch (error) {\n logger.error('[AdMeshRenderer] ❌ Error rendering follow-up');\n // Don't throw - follow-up rendering failure shouldn't break primary ad\n }\n }\n\n /**\n * Unmount a rendered component\n */\n unmount(containerId: string): void {\n const root = this.roots.get(containerId);\n if (root) {\n root.unmount();\n this.roots.delete(containerId);\n \n // Hide the container again to prevent empty space\n const container = document.getElementById(containerId);\n if (container) {\n container.style.display = 'none';\n }\n }\n }\n\n /**\n * Unmount all rendered components\n */\n unmountAll(): void {\n for (const [, root] of this.roots.entries()) {\n root.unmount();\n }\n this.roots.clear();\n }\n}\n","/**\n * AdMesh UI SDK - Zero-Code Integration\n * \n * Provides a simple, zero-code integration experience for platforms.\n * Handles all recommendation fetching, rendering, and tracking automatically.\n */\n\nimport type { AdMeshTheme, AgentRecommendationResponse, AdMeshRecommendation, PlatformRequest, AIPContextResponse } from '../types/index';\nimport { AdMeshRenderer } from './AdMeshRenderer';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport interface AdMeshSDKConfig {\n apiKey: string;\n theme?: AdMeshTheme;\n apiBaseUrl?: string;\n}\n\nexport interface ShowRecommendationsOptions {\n query: string;\n containerId: string;\n theme?: AdMeshTheme;\n\n // Optional follow-ups container (SDK will render follow-ups here if provided and followup_query is present)\n followups_container_id?: string;\n\n // Session tracking (required)\n session_id: string;\n\n // Message tracking (required)\n // messageId MUST be provided by the platform - SDK never generates it\n messageId: string;\n\n // Platform information\n // platformId is now extracted from API key by the backend\n platformSurface?: string;\n model?: string;\n messages?: Array<{ role: string; content: string }>;\n locale?: string;\n geo?: string;\n userId?: string;\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config (configured during onboarding)\n \n /** Callback to paste content to input field (for bridge format CTA) */\n onPasteToInput?: (content: string) => void;\n \n /** Callback to execute query when follow-up is selected (required for follow-up functionality) */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\n/**\n * Main AdMesh SDK class for zero-code integration\n *\n * The SDK is stateless regarding session management. Developers must provide\n * session_id when calling showRecommendations().\n *\n * @example\n * ```typescript\n * import { AdMeshSDK } from '@admesh/ui-sdk';\n *\n * const admesh = new AdMeshSDK({ apiKey: 'your-api-key' });\n *\n * // Generate session ID on your platform\n * const sessionId = AdMeshSDK.createSession();\n *\n * await admesh.showRecommendations({\n * query: 'best CRM for small business',\n * containerId: 'admesh-recommendations',\n * session_id: sessionId\n * });\n * ```\n */\nexport class AdMeshSDK {\n private config: AdMeshSDKConfig;\n private apiBaseUrl: string;\n\n // OPTIMIZATION: Lazy-initialized managers (only created when needed)\n private renderer: AdMeshRenderer | null = null;\n private tracker: AdMeshTracker | null = null;\n\n constructor(config: AdMeshSDKConfig) {\n if (!config.apiKey) {\n throw new Error('AdMeshSDK: apiKey is required');\n }\n\n this.config = {\n apiKey: config.apiKey,\n theme: config.theme,\n apiBaseUrl: config.apiBaseUrl\n };\n\n // Set API base URL with priority: config > environment variable > production default\n this.apiBaseUrl = config.apiBaseUrl ||\n (typeof window !== 'undefined' && (window as any).__ADMESH_API_BASE_URL__) ||\n 'https://api.useadmesh.com';\n }\n\n /**\n * PLATFORM UTILITY: Generate a unique session ID for tracking recommendations\n * \n * IMPORTANT: This is a utility method for PLATFORMS to use. The SDK itself\n * NEVER calls this method automatically. Platforms must:\n * 1. Call this method (or generate their own sessionId)\n * 2. Store the sessionId in their own storage\n * 3. Pass sessionId to AdMeshProvider and SDK methods\n * \n * The SDK will throw an error if sessionId is not provided - it will never\n * auto-generate one.\n *\n * @returns A unique session ID (platform must store and manage this)\n */\n static createSession(): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 15);\n return `session_${timestamp}_${random}`;\n }\n\n /**\n * PLATFORM UTILITY: Generate a unique message ID for tracking recommendations per message\n * \n * IMPORTANT: This is a utility method for PLATFORMS to use. The SDK itself\n * NEVER calls this method automatically. Platforms must:\n * 1. Call this method (or generate their own messageId) for each user message\n * 2. Pass messageId to SDK methods (showRecommendations, fetchRecommendationFromAIPContext)\n * \n * The SDK will throw an error if messageId is not provided - it will never\n * auto-generate one.\n *\n * @param sessionId Optional session ID to include in the message ID\n * @returns A unique message ID (platform must provide this to SDK methods)\n */\n static createMessageId(sessionId?: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n if (sessionId) {\n return `msg_${sessionId}_${timestamp}_${random}`;\n }\n return `msg_${timestamp}_${random}`;\n }\n\n\n /**\n * OPTIMIZATION: Lazy initialize renderer on first use\n */\n private getRenderer(): AdMeshRenderer {\n if (!this.renderer) {\n this.renderer = new AdMeshRenderer();\n }\n return this.renderer;\n }\n\n /**\n * OPTIMIZATION: Lazy initialize tracker on first use\n */\n private getTracker(): AdMeshTracker {\n if (!this.tracker) {\n this.tracker = new AdMeshTracker({\n apiKey: this.config.apiKey\n });\n }\n return this.tracker;\n }\n\n\n\n\n /**\n * Fetch and render recommendations automatically using /aip/context endpoint\n *\n * IMPORTANT: Both session_id and messageId MUST be provided by the platform.\n * The SDK NEVER generates these automatically. Use AdMeshSDK.createSession()\n * and AdMeshSDK.createMessageId() on your platform, or generate your own IDs.\n */\n async showRecommendations(options: ShowRecommendationsOptions): Promise<void> {\n try {\n // CRITICAL: session_id MUST be provided by platform - SDK never generates it\n if (!options.session_id || options.session_id.trim() === '') {\n throw new Error('session_id is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n }\n \n // CRITICAL: messageId MUST be provided by the platform - SDK never generates it\n if (!options.messageId || options.messageId.trim() === '') {\n throw new Error('messageId is required and must be provided by the platform. The SDK never generates messageId automatically.');\n }\n \n // Fetch recommendation from /aip/context endpoint\n // platformId is extracted from API key by the backend\n const aipResponse = await this.fetchRecommendationFromAIPContext({\n query: options.query,\n sessionId: options.session_id,\n messageId: options.messageId, // Pass messageId from platform\n platformSurface: options.platformSurface,\n model: options.model,\n messages: options.messages,\n language: options.locale, // locale maps to language\n geo_country: options.geo, // geo maps to geo_country\n userId: options.userId,\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config automatically\n });\n\n // Convert AIP response to AgentRecommendationResponse format for rendering\n const recommendation = this.convertAIPResponseToRecommendation(aipResponse);\n const response: AgentRecommendationResponse = {\n session_id: aipResponse.session_id,\n message_id: `msg_${aipResponse.recommendation_id}`,\n recommendations: [recommendation]\n };\n\n // Standard rendering\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n followups_container_id: options.followups_container_id,\n response,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: options.session_id,\n onPasteToInput: options.onPasteToInput,\n onExecuteQuery: options.onExecuteQuery\n });\n\n // NOTE: Exposure pixels are now fired by AdMeshViewabilityTracker component\n // when ads meet MRC viewability standards (50% visible for 1 second).\n // This ensures MRC-compliant exposure tracking and proper CPX billing.\n } catch (error) {\n logger.error('[AdMeshSDK] Error showing recommendations');\n throw error;\n }\n }\n\n /**\n * Fetch recommendation from the /aip/context endpoint (new auction-based endpoint)\n * \n * Public method for fetching recommendation data without rendering.\n * Useful for format detection and custom rendering logic.\n * \n * IMPORTANT: Both sessionId and messageId MUST be provided by the platform.\n * The SDK NEVER generates these automatically.\n */\n async fetchRecommendationFromAIPContext(params: {\n query: string;\n sessionId: string;\n messageId?: string;\n // platformId is now extracted from API key by the backend\n platformSurface?: string;\n model?: string;\n messages?: Array<{ role: string; content: string; id?: string }>;\n language?: string;\n geo_country?: string;\n userId?: string;\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config (configured during onboarding)\n }): Promise<AIPContextResponse> {\n const url = `${this.apiBaseUrl}/aip/context`;\n\n logger.log('[AdMeshSDK] 📥 fetchRecommendationFromAIPContext called');\n\n // CRITICAL: sessionId MUST be provided by platform - SDK never generates it\n if (!params.sessionId || params.sessionId.trim() === '') {\n const error = new Error('sessionId is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n logger.error('[AdMeshSDK] ❌ sessionId not provided by platform - cannot process request');\n throw error;\n }\n\n // CRITICAL: messageId MUST be provided by the platform - SDK never generates it\n // NEVER derive from messages array - platform must pass it explicitly\n const messageId = params.messageId;\n if (!messageId || messageId.trim() === '') {\n const error = new Error('messageId is required and must be provided by the platform. The SDK never generates messageId automatically.');\n logger.error('[AdMeshSDK] ❌ messageId not provided by platform - cannot process request');\n throw error;\n }\n \n // Calculate turn_index from messages length\n const turnIndex = params.messages ? params.messages.length : 0;\n \n // Detect device platform (web by default, could be enhanced with user agent detection)\n const devicePlatform = typeof window !== 'undefined' && window.navigator ? 'web' : 'web';\n \n // Detect form factor (could be enhanced with screen size detection)\n const formFactor = typeof window !== 'undefined' && window.innerWidth \n ? (window.innerWidth < 768 ? 'mobile' : window.innerWidth < 1024 ? 'tablet' : 'desktop')\n : 'desktop';\n\n // Build PlatformRequest payload in UCP structure\n // Note: producer.agent_id will be extracted from API key by the backend\n // \n // The operator receives this PlatformRequest and converts it to ContextRequest:\n // - PlatformRequest uses extensions.aip (with query_text, messages) - this is correct\n // - ContextRequest is flat (no extensions, intent.summary replaces query_text)\n // - Brand agents receive decision context only (no raw queries, no identity fields)\n const payload: PlatformRequest = {\n spec_version: '1.0.0',\n message_id: messageId,\n timestamp: new Date().toISOString(),\n producer: {\n agent_id: 'placeholder', // Will be overridden by backend from API key\n agent_role: 'publisher',\n software: 'admesh_ui_sdk',\n software_version: params.model || '1.0.0'\n },\n context: {\n // context_id removed - operator will set it as message_id when creating ContextRequest for brand agents\n language: params.language || 'en-US',\n publisher: 'placeholder', // Will be overridden by backend from API key\n placement: {\n ad_unit: params.platformSurface || 'web'\n },\n device: {\n platform: devicePlatform,\n form_factor: formFactor\n },\n geography: {\n country: params.geo_country || 'US'\n }\n },\n identity: {\n namespace: 'platform_user',\n value_hash: params.userId || '',\n confidence: 1.0\n },\n extensions: {\n aip: {\n session_id: params.sessionId,\n turn_index: turnIndex,\n query_text: params.query,\n messages: params.messages || [],\n // latency_budget_ms removed - now fetched from agent profile by backend\n cpx_floor: 0.0\n }\n }\n };\n\n // Validate query before sending\n if (!payload.extensions.aip.query_text || !payload.extensions.aip.query_text.trim()) {\n logger.warn('[AdMeshSDK] ⚠️ Warning: Sending request with empty query_text');\n }\n\n const jsonBody = JSON.stringify(payload);\n logger.log('[AdMeshSDK] 📤 Sending request to /aip/context');\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`\n },\n body: jsonBody\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const errorMessage = errorData.detail || `HTTP ${response.status}`;\n throw new Error(`Failed to fetch recommendation from /aip/context: ${errorMessage}`);\n }\n\n const data: any = await response.json();\n \n // Log the raw response structure for debugging\n logger.log('[AdMeshSDK] 📥 Raw response from /aip/context:', {\n hasCreative: !!data.creative,\n creativeFormat: data.creative?.format,\n creativeBridgeContent: data.creative?.bridge_content ? data.creative.bridge_content.substring(0, 50) + '...' : undefined,\n hasWinningBid: !!data.winning_bid,\n winningBidPreferredFormat: data.winning_bid?.preferred_format,\n topLevelKeys: Object.keys(data)\n });\n \n return data as AIPContextResponse;\n }\n\n\n /**\n * Convert AIP context response to AdMeshRecommendation format for compatibility\n */\n private convertAIPResponseToRecommendation(aipResponse: AIPContextResponse): AdMeshRecommendation {\n const responseAny = aipResponse as any;\n let creativeInput = aipResponse.creative_input || {};\n \n // Extract format and bridge prompt/content from creative object if present\n // The response may have: creative: { format: 'bridge', bridge_prompt: '...', bridge_content: '...' }\n // Check multiple locations for the creative object\n const creative = responseAny.creative || {};\n const format = creative.format || \n responseAny.format || \n responseAny.winning_bid?.preferred_format;\n \n // Extract headline from creative if present (for tail and product_card formats)\n const headlineFromCreative = creative.headline;\n \n // Extract bridge format fields from creative if present\n const bridgeHeadlineFromCreative = creative.bridge_headline;\n const bridgeDescriptionFromCreative = creative.bridge_description;\n const bridgePromptFromCreative =\n creative.bridge_prompt || creative.bridge_content;\n const bridgePrompt =\n bridgePromptFromCreative ||\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content;\n \n // Extract cta_label from creative if present (for bridge format)\n const ctaLabelFromCreative = creative.cta_label;\n const ctaLabel =\n ctaLabelFromCreative ||\n (creativeInput as any).cta_label;\n \n // Extract bridge_headline and bridge_description\n const bridgeHeadline =\n bridgeHeadlineFromCreative ||\n (creativeInput as any).bridge_headline;\n const bridgeDescription =\n bridgeDescriptionFromCreative ||\n (creativeInput as any).bridge_description;\n \n logger.log('[AdMeshSDK] 🔍 Extracting from response:', {\n creativeObject: creative,\n creativeFormat: creative.format,\n creativeBridgeHeadline: bridgeHeadlineFromCreative,\n creativeBridgeDescription: bridgeDescriptionFromCreative\n ? String(bridgeDescriptionFromCreative).substring(0, 50) + '...'\n : undefined,\n creativeBridgePrompt: bridgePromptFromCreative\n ? String(bridgePromptFromCreative).substring(0, 50) + '...'\n : undefined,\n creativeCtaLabel: ctaLabelFromCreative,\n extractedFormat: format,\n extractedBridgeHeadline: bridgeHeadline,\n extractedBridgeDescription: bridgeDescription\n ? String(bridgeDescription).substring(0, 50) + '...'\n : undefined,\n extractedBridgePrompt: bridgePrompt\n ? String(bridgePrompt).substring(0, 50) + '...'\n : undefined,\n extractedCtaLabel: ctaLabel,\n creativeInputHasBridgeHeadline: !!(creativeInput as any).bridge_headline,\n creativeInputHasBridgeDescription: !!(creativeInput as any).bridge_description,\n creativeInputHasBridgePrompt: !!(creativeInput as any).bridge_prompt,\n creativeInputHasBridgeContent: !!(creativeInput as any).bridge_content,\n creativeInputHasCtaLabel: !!(creativeInput as any).cta_label\n });\n \n // ALWAYS merge creative fields into creative_input for format detection\n // This ensures headline, bridge_headline, bridge_description, bridge_prompt and cta_label are available\n creativeInput = {\n ...creativeInput,\n // Prioritize creative object fields over existing creative_input\n ...(headlineFromCreative && { headline: headlineFromCreative }), // For tail and product_card formats\n ...(bridgeHeadline && { bridge_headline: bridgeHeadline }),\n ...(bridgeDescription && { bridge_description: bridgeDescription }),\n ...(bridgePrompt && { bridge_prompt: bridgePrompt }),\n // Keep bridge_content for backward compatibility\n ...(bridgePrompt && { bridge_content: bridgePrompt }),\n ...(ctaLabel && { cta_label: ctaLabel }),\n ...(format && { format: format })\n };\n \n // Ensure recommendation_id is set (use from response or generate fallback)\n const recommendationId = aipResponse.recommendation_id || \n (responseAny as any).recommendation_id ||\n (responseAny as any).bid_id || // Fallback for backward compatibility\n '';\n \n // Ensure admesh_link is set (use click_url if not provided)\n const admeshLink = aipResponse.click_url || \n (aipResponse as any).admesh_link || \n '';\n \n // Build legacy fields from creative_input for backward compatibility\n const recommendation: any = {\n ...aipResponse,\n // Ensure recommendation_id is present\n recommendation_id: recommendationId,\n // Ensure admesh_link is present (use click_url as fallback)\n admesh_link: admeshLink || aipResponse.click_url || '',\n // Remove any ad_id or bid_id fields\n ad_id: undefined,\n bid_id: undefined,\n // Update creative_input with merged fields (CRITICAL: this must include bridge_content)\n creative_input: creativeInput,\n // Legacy field mappings\n product_title: aipResponse.title,\n tail_summary: creativeInput.long_description || '',\n product_summary: creativeInput.short_description || '',\n weave_summary: creativeInput.context_snippet || '',\n product_logo: creativeInput.assets?.logo_url ? {\n url: creativeInput.assets.logo_url\n } : undefined,\n categories: creativeInput.categories || []\n };\n \n // Remove ad_id and bid_id if they exist\n delete recommendation.ad_id;\n delete recommendation.bid_id;\n \n // Preserve format field for bridge format detection\n if (format) {\n recommendation.format = format;\n }\n \n logger.log('[AdMeshSDK] 🔄 Converted recommendation:', {\n hasFormat: !!format,\n format: format,\n hasBridgeHeadline: !!bridgeHeadline,\n bridgeHeadline: bridgeHeadline,\n hasBridgeDescription: !!bridgeDescription,\n bridgeDescriptionPreview: bridgeDescription\n ? String(bridgeDescription).substring(0, 50) + '...'\n : undefined,\n hasBridgePrompt: !!bridgePrompt,\n hasCtaLabel: !!ctaLabel,\n ctaLabel: ctaLabel,\n bridgeHeadlineInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_headline,\n bridgeDescriptionInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_description,\n bridgePromptInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_prompt,\n bridgeContentInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_content,\n ctaLabelInCreativeInput: !!(recommendation.creative_input as any)\n ?.cta_label,\n bridgePromptPreview: bridgePrompt\n ? String(bridgePrompt).substring(0, 50) + '...'\n : undefined,\n creativeInputKeys: Object.keys(recommendation.creative_input || {}),\n recommendationFormat: recommendation.format\n });\n \n return recommendation as AdMeshRecommendation;\n }\n\n /**\n * Fire exposure for sponsored followup\n * \n * @param exposureUrl - The exposure URL to fire (can use regular exposure_url)\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID\n */\n fireFollowupExposure(exposureUrl: string, recommendationId: string, sessionId: string): void {\n const tracker = this.getTracker();\n tracker.fireFollowupExposure(exposureUrl, recommendationId, sessionId);\n }\n\n /**\n * Fire engagement for sponsored followup\n * \n * @param engagementUrl - The engagement URL to fire\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID\n * @returns Promise that resolves when engagement is fired\n */\n async fireFollowupEngagement(engagementUrl: string, recommendationId: string, sessionId: string): Promise<void> {\n const tracker = this.getTracker();\n return tracker.fireFollowupEngagement(engagementUrl, recommendationId, sessionId);\n }\n}\n\nexport default AdMeshSDK;\n","/**\n * WeaveResponseProcessor\n * \n * Automatically detects and enhances AdMesh recommendation links in organic LLM responses.\n * Handles:\n * - Automatic link detection (AdMesh tracking URLs)\n * - Label enhancement (<sub>[Ad]</sub> subscript labels)\n * - Exposure pixel triggering\n * - Streaming response support (MutationObserver)\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface DetectedLink {\n element: HTMLAnchorElement;\n href: string;\n text: string;\n hasAdLabel: boolean;\n matchedRecommendation?: {\n recommendation_id: string;\n click_url: string;\n exposure_url?: string;\n };\n}\n\nexport interface ProcessorConfig {\n autoAddLabels?: boolean;\n fireExposurePixels?: boolean;\n labelStyle?: {\n fontSize?: string;\n fontWeight?: string;\n color?: string;\n marginLeft?: string;\n };\n}\n\nexport interface ExposurePixelTarget {\n exposureUrl?: string;\n recommendationId?: string;\n linkElement: HTMLAnchorElement;\n}\n\nexport class WeaveResponseProcessor {\n private autoAddLabels: boolean;\n private fireExposurePixels: boolean;\n private labelStyle: Record<string, string>;\n private processedLinks: Set<string> = new Set();\n private mutationObserver: MutationObserver | null = null;\n\n constructor(config: ProcessorConfig = {}) {\n this.autoAddLabels = config.autoAddLabels !== false; // Default: true\n this.fireExposurePixels = config.fireExposurePixels !== false; // Default: true\n this.labelStyle = {\n fontSize: config.labelStyle?.fontSize || '0.75em',\n fontWeight: config.labelStyle?.fontWeight || 'bold',\n color: config.labelStyle?.color || '#666',\n marginLeft: config.labelStyle?.marginLeft || '2px'\n };\n }\n\n /**\n * Get links to process with CSS selector optimization\n *\n * Optimized approach:\n * 1. Use CSS selector to find AdMesh links (97% faster)\n * - Matches: api.useadmesh.com/click/*, *.useadmesh.com/click/*\n * 2. Fallback to scanning all links if selector finds nothing\n * 3. Recommendation validation ensures accuracy\n */\n private getLinksToProcess(container: HTMLElement): HTMLAnchorElement[] {\n // Try optimized selector first for AdMesh links\n // Matches both api.useadmesh.com and *.useadmesh.com domains\n const optimizedSelector = 'a[href*=\"/click/\"][href*=\"admesh.com\"], a[href*=\"/click/\"][href*=\"useadmesh.com\"]';\n const optimizedLinks = container.querySelectorAll(optimizedSelector);\n\n if (optimizedLinks.length > 0) {\n return Array.from(optimizedLinks) as HTMLAnchorElement[];\n }\n\n // Fallback: scan all links\n return Array.from(container.querySelectorAll('a')) as HTMLAnchorElement[];\n }\n\n /**\n * Scan container for ALL links and process only AdMesh recommendation links\n *\n * This method:\n * 1. Scans container for all <a> tags (or uses optimized selector)\n * 2. If recommendations provided: Filters to only process links matching recommendation click_url values\n * 3. If NO recommendations: Detects links by URL pattern (api.useadmesh.com/click/*)\n * 4. Ignores non-AdMesh links (external links, documentation, etc.)\n * 5. Adds [Ad] labels to matching links\n * 6. Fires exposure pixels for each match\n *\n * NOTE: When recommendations array is empty, this method detects AdMesh links by URL pattern.\n * This is important for Weave Ad Format where the backend embeds links in the LLM response\n * and the frontend hook doesn't have access to the recommendations data.\n */\n scanAndProcessLinks(\n container: HTMLElement,\n recommendations: any[],\n onExposurePixel?: (target: ExposurePixelTarget) => void\n ): DetectedLink[] {\n if (!container) {\n return [];\n }\n\n const detectedLinks: DetectedLink[] = [];\n\n // If recommendations provided, use them for matching\n if (recommendations.length > 0) {\n // Get links to process (with optional optimization)\n const links = this.getLinksToProcess(container);\n\n // Build map of AdMesh click URLs for fast lookup\n const clickUrlMap = new Map(\n recommendations\n .filter(r => r.click_url)\n .map(r => [r.click_url, r])\n );\n\n // Build map of brand URLs (redirect_url, url) for matching direct brand links\n const brandUrlMap = new Map<string, any>();\n recommendations.forEach((r: any) => {\n // Match by redirect_url or url (original brand URLs)\n const redirectUrl = r.redirect_url || r.url || (r.creative_input as any)?.cta_url;\n if (redirectUrl && typeof redirectUrl === 'string') {\n // Normalize URL (remove trailing slashes, query params for matching)\n const normalizedUrl = redirectUrl.trim().replace(/\\/$/, '');\n brandUrlMap.set(normalizedUrl, r);\n // Also match with trailing slash\n brandUrlMap.set(`${normalizedUrl}/`, r);\n }\n });\n\n links.forEach((link: HTMLAnchorElement) => {\n const href = link.getAttribute('href') || '';\n const linkKey = `${href}`;\n\n // Skip if already processed\n if (this.processedLinks.has(linkKey)) {\n return;\n }\n\n // First, check if link matches AdMesh click URL\n let recommendation = clickUrlMap.get(href);\n let actualHref = href;\n \n // If not found, check if link matches brand URL (redirect_url or url)\n if (!recommendation) {\n const normalizedHref = href.trim().replace(/\\/$/, '');\n recommendation = brandUrlMap.get(normalizedHref) || brandUrlMap.get(`${normalizedHref}/`);\n \n // If brand URL matched, replace it with click_url\n if (recommendation && recommendation.click_url) {\n logger.log('[WeaveResponseProcessor] 🔄 Found brand URL match, replacing with click_url:', href);\n link.setAttribute('href', recommendation.click_url);\n actualHref = recommendation.click_url;\n }\n }\n \n // Process link if it matches a recommendation (either by click_url or brand URL)\n if (recommendation) {\n this.processedLinks.add(linkKey);\n\n // ALWAYS set target=\"_blank\" and rel=\"noopener noreferrer\" for AdMesh tracking links\n // This ensures all click_url links open in a new tab\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n\n const detectedLink: DetectedLink = {\n element: link,\n href: actualHref, // Use actual href (may be updated click_url)\n text: link.textContent || '',\n hasAdLabel: this.hasAdLabel(link),\n matchedRecommendation: {\n recommendation_id: recommendation.recommendation_id || '',\n click_url: recommendation.click_url,\n exposure_url: recommendation.exposure_url\n }\n };\n\n // Add label if not present\n if (this.autoAddLabels && !detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] 🏷️ Adding [Ad] label to matched recommendation link:', actualHref);\n this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n } else if (!this.autoAddLabels) {\n logger.log('[WeaveResponseProcessor] ℹ️ autoAddLabels is disabled, skipping label');\n } else if (detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has label, skipping');\n }\n\n // Fire exposure pixel\n if (this.fireExposurePixels && recommendation.exposure_url && onExposurePixel && detectedLink.matchedRecommendation) {\n onExposurePixel({\n exposureUrl: recommendation.exposure_url,\n recommendationId: detectedLink.matchedRecommendation.recommendation_id,\n linkElement: link\n });\n }\n\n detectedLinks.push(detectedLink);\n }\n });\n } else {\n // No recommendations provided - detect AdMesh links by URL pattern\n // This is used for Weave Ad Format where backend embeds links in LLM response\n // Links can be in formats like:\n // - https://api.useadmesh.com/click/...\n // - https://api.admesh.com/click/...\n // - https://useadmesh.com/click/...\n // - https://admesh.com/click/...\n // Get ALL links in container and filter programmatically\n const allLinks = Array.from(container.querySelectorAll('a')) as HTMLAnchorElement[];\n\n \n\n allLinks.forEach((link: HTMLAnchorElement) => {\n const href = link.getAttribute('href') || '';\n const linkKey = `${href}`;\n\n // Skip if already processed\n if (this.processedLinks.has(linkKey)) {\n return;\n }\n\n // Check if this is an AdMesh link by examining the URL\n const isAdMeshLink = this.isAdMeshLink(href);\n\n if (isAdMeshLink) {\n\n this.processedLinks.add(linkKey);\n\n const detectedLink: DetectedLink = {\n element: link,\n href,\n text: link.textContent || '',\n hasAdLabel: this.hasAdLabel(link),\n matchedRecommendation: undefined\n };\n\n // Set target=\"_blank\" and rel=\"noopener noreferrer\" for security\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n\n // Add label if not present\n if (this.autoAddLabels && !detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] 🏷️ Adding [Ad] label to pattern-matched AdMesh link:', href);\n this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n } else if (!this.autoAddLabels) {\n logger.log('[WeaveResponseProcessor] ℹ️ autoAddLabels is disabled, skipping label');\n } else if (detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has label, skipping');\n }\n\n // Fire exposure tracking with converted exposure URL\n // For Weave Ad Format: LLM embeds click URLs in response text, so we convert to exposure URL\n if (this.fireExposurePixels && onExposurePixel) {\n const exposureUrl = this.convertClickUrlToExposureUrl(href);\n onExposurePixel({\n exposureUrl,\n recommendationId: this.extractRecommendationIdFromUrl(href),\n linkElement: link\n });\n }\n\n detectedLinks.push(detectedLink);\n }\n });\n\n \n }\n\n return detectedLinks;\n }\n\n /**\n * Check if link already has [Ad] label\n *\n * This method checks for [Ad] labels on BOTH sides of the link:\n * - Previous sibling (left side): [Ad] link text\n * - Next sibling (right side): link text [Ad]\n * - Inside the link itself (as a child element)\n *\n * This prevents duplicate label rendering when the LLM response\n * already contains [Ad] labels in any position.\n */\n private hasAdLabel(link: HTMLAnchorElement): boolean {\n // First, check if link itself contains [Ad] in its text content or as a child\n const linkText = link.textContent || '';\n if (linkText.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link text contains [Ad] label');\n return true;\n }\n\n // Check for [Ad] label as a direct child element (e.g., <a>text<sub>[Ad]</sub></a>)\n const childElements = link.querySelectorAll('sub, span');\n for (const child of Array.from(childElements)) {\n if (child.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link has [Ad] label as child element');\n return true;\n }\n }\n\n // Check PREVIOUS sibling (left side) for [Ad] label\n let prevNode = link.previousSibling;\n\n // Skip text nodes that are just whitespace\n while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {\n const text = prevNode.textContent || '';\n if (text.trim() === '') {\n prevNode = prevNode.previousSibling;\n continue;\n }\n // If we find non-whitespace text, check if it contains [Ad]\n if (text.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in previous text sibling');\n return true;\n }\n break;\n }\n\n // Check if previous element node is a <sub> or <span> with [Ad] label\n if (prevNode && prevNode.nodeType === Node.ELEMENT_NODE) {\n const element = prevNode as HTMLElement;\n const tagName = element.tagName.toUpperCase();\n if ((tagName === 'SUB' || tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in previous element sibling:', tagName);\n return true;\n }\n }\n\n // Check NEXT sibling (right side) for [Ad] label\n let nextNode = link.nextSibling;\n\n // Skip text nodes that are just whitespace\n while (nextNode && nextNode.nodeType === Node.TEXT_NODE) {\n const text = nextNode.textContent || '';\n if (text.trim() === '') {\n nextNode = nextNode.nextSibling;\n continue;\n }\n // If we find non-whitespace text, check if it contains [Ad]\n if (text.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in next text sibling');\n return true;\n }\n break;\n }\n\n // Check if next element node is a <sub> or <span> with [Ad] label\n if (nextNode && nextNode.nodeType === Node.ELEMENT_NODE) {\n const element = nextNode as HTMLElement;\n const tagName = element.tagName.toUpperCase();\n if ((tagName === 'SUB' || tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in next element sibling:', tagName);\n return true;\n }\n }\n\n logger.log('[WeaveResponseProcessor] ℹ️ No existing [Ad] label found');\n return false;\n }\n\n /**\n * Add [Ad] label as subscript after link with \"Why this ad?\" tooltip\n *\n * This method:\n * 1. Removes any [Ad] label on the LEFT side (previous sibling)\n * 2. Creates a <sub> element with [Ad] text\n * 3. Positions it immediately after the link (to the RIGHT)\n * 4. Adds tooltip styling with default cursor (not help cursor)\n * 5. Adds click handler for tooltip interaction\n * 6. Ensures only ONE label per link, always on the RIGHT side\n */\n private addAdLabel(link: HTMLAnchorElement): void {\n logger.log('[WeaveResponseProcessor] 🏷️ Attempting to add [Ad] label to link:', link.href);\n \n // Verify link is still in the DOM\n if (!link.isConnected) {\n logger.warn('[WeaveResponseProcessor] ⚠️ Link is not in DOM, cannot add label');\n return;\n }\n\n // Double-check that [Ad] label doesn't already exist to prevent duplicates\n if (this.hasAdLabel(link)) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has [Ad] label, skipping');\n return;\n }\n\n // Verify parent node exists before proceeding\n const parentNode = link.parentNode;\n if (!parentNode) {\n logger.error('[WeaveResponseProcessor] ❌ Link has no parent node, cannot add label');\n return;\n }\n\n // Remove any [Ad] label on the LEFT side (previous sibling)\n this.removeLeftAdLabel(link);\n\n // Create the [Ad] label as a <sub> element\n const subLabel = document.createElement('sub');\n subLabel.textContent = '[Ad]';\n subLabel.style.fontSize = this.labelStyle.fontSize;\n subLabel.style.fontWeight = this.labelStyle.fontWeight;\n subLabel.style.color = this.labelStyle.color;\n subLabel.style.marginLeft = this.labelStyle.marginLeft;\n\n // Add tooltip styling with default cursor (not help cursor)\n subLabel.style.cursor = 'pointer';\n subLabel.style.borderBottom = `1px dotted ${this.labelStyle.color}`;\n subLabel.style.whiteSpace = 'nowrap';\n subLabel.title = 'Why this ad? This is a sponsored recommendation based on your search query.';\n\n // Track tooltip state for click interactions\n let isTooltipVisible = false;\n\n // Add hover effect for visual feedback\n subLabel.addEventListener('mouseenter', () => {\n subLabel.style.opacity = '0.7';\n });\n\n subLabel.addEventListener('mouseleave', () => {\n subLabel.style.opacity = '1';\n // Hide tooltip on mouse leave if it was shown by click\n if (isTooltipVisible) {\n isTooltipVisible = false;\n }\n });\n\n // Add click handler to toggle tooltip visibility\n subLabel.addEventListener('click', (event: Event) => {\n event.stopPropagation();\n isTooltipVisible = !isTooltipVisible;\n\n if (isTooltipVisible) {\n // Show tooltip by adding visual indicator\n subLabel.style.textDecoration = 'underline';\n subLabel.style.opacity = '0.7';\n } else {\n // Hide tooltip visual indicator\n subLabel.style.textDecoration = 'none';\n subLabel.style.opacity = '1';\n }\n });\n\n // Close tooltip when clicking elsewhere on the page\n const closeTooltipOnClickOutside = (event: Event) => {\n if (isTooltipVisible && event.target !== subLabel) {\n isTooltipVisible = false;\n subLabel.style.textDecoration = 'none';\n subLabel.style.opacity = '1';\n }\n };\n\n document.addEventListener('click', closeTooltipOnClickOutside);\n\n // Insert the label immediately after the link (to the right)\n // Handle edge cases: if nextSibling is null, appendChild will insert at the end\n try {\n const nextSibling = link.nextSibling;\n if (nextSibling) {\n parentNode.insertBefore(subLabel, nextSibling);\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label inserted before next sibling');\n } else {\n // If no next sibling, append to parent (inserts after link)\n parentNode.appendChild(subLabel);\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label appended to parent (no next sibling)');\n }\n \n // Verify label was successfully inserted\n if (subLabel.isConnected && subLabel.parentNode === parentNode) {\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label successfully added to link:', link.href);\n } else {\n logger.error('[WeaveResponseProcessor] ❌ [Ad] label insertion failed - label not in DOM');\n }\n } catch (error) {\n logger.error('[WeaveResponseProcessor] ❌ Error inserting [Ad] label:', error);\n }\n }\n\n /**\n * Remove [Ad] label from the LEFT side (previous sibling) of a link\n *\n * This ensures that only ONE [Ad] label appears on the RIGHT side,\n * removing any duplicate labels that might be on the left.\n */\n private removeLeftAdLabel(link: HTMLAnchorElement): void {\n let prevNode = link.previousSibling;\n\n // Skip text nodes that are just whitespace\n while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {\n const text = prevNode.textContent || '';\n if (text.trim() === '') {\n prevNode = prevNode.previousSibling;\n continue;\n }\n // If we find non-whitespace text containing [Ad], remove it\n if (text.includes('[Ad]')) {\n prevNode.parentNode?.removeChild(prevNode);\n return;\n }\n break;\n }\n\n // Check if previous element node is a <sub> or <span> with [Ad] label\n if (prevNode && prevNode.nodeType === Node.ELEMENT_NODE) {\n const element = prevNode as HTMLElement;\n if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n element.parentNode?.removeChild(element);\n }\n }\n }\n\n /**\n * Check if a URL is an AdMesh link\n *\n * Detects AdMesh links by checking if the URL contains /click/ and matches AdMesh patterns:\n * - https://api.useadmesh.com/click/...\n * - https://api.admesh.com/click/...\n * - http://localhost:8000/click/... (local development)\n * - Any URL with /click/ that looks like an AdMesh tracking URL\n */\n private isAdMeshLink(href: string): boolean {\n if (!href) {\n return false;\n }\n\n // Check if URL contains /click/ (AdMesh tracking pattern)\n if (!href.includes('/click/')) {\n return false;\n }\n\n // Check for known AdMesh domains\n const admeshDomains = [\n 'useadmesh.com',\n 'admesh.com',\n 'api.useadmesh.com',\n 'api.admesh.com',\n 'localhost:8000', // Local development\n 'localhost:3000', // Local development (if proxied)\n ];\n\n const isKnownDomain = admeshDomains.some(domain => href.includes(domain));\n if (isKnownDomain) {\n return true;\n }\n\n // Fallback: if URL has /click/ and looks like a tracking URL, treat it as AdMesh\n // This handles cases where the domain might be different but the pattern matches\n try {\n const url = new URL(href);\n const pathname = url.pathname;\n\n // Check if pathname starts with /click/ (AdMesh pattern)\n if (pathname.startsWith('/click/')) {\n return true;\n }\n } catch {\n // If URL parsing fails, check string pattern\n if (href.match(/\\/click\\/[a-zA-Z0-9\\-_]+/)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Extract recommendation ID from AdMesh click URL\n *\n * URL format: https://api.useadmesh.com/click/{recommendation_id}?...\n * Returns the recommendation_id portion or empty string if extraction fails\n */\n private extractRecommendationIdFromUrl(url: string): string {\n try {\n // Try to extract from /click/{id} pattern\n const match = url.match(/\\/click\\/([^/?]+)/);\n if (match && match[1]) {\n return match[1];\n }\n // Fallback to empty string (recommendation_id should be in URL)\n return '';\n } catch {\n return url;\n }\n }\n\n /**\n * Convert AdMesh click URL to exposure URL (Weave Ad Format only)\n *\n * This is ONLY used for Weave Ad Format where the LLM embeds click URLs in the response text\n * and we need to derive the exposure URL for tracking.\n *\n * Click URL format: https://api.useadmesh.com/click/r/{aid}?aid={aid}&rid={rid}&nonce={nonce}&exp={exp}&sig={sig}\n * Exposure URL format: https://api.useadmesh.com/exposure?aid={aid}&rid={rid}&nonce={nonce}&exp={exp}&sig={sig}&cpx={cpx}\n *\n * This method:\n * 1. Parses the click URL to extract the base domain and query parameters\n * 2. Replaces the /click/r/{aid} path with /exposure\n * 3. Preserves all tracking parameters (aid, rid, nonce, exp, sig)\n * 4. Adds cpx=0 as default (actual CPX value is set server-side)\n *\n * @param clickUrl - The AdMesh click tracking URL\n * @returns The corresponding exposure tracking URL, or the original URL if conversion fails\n */\n private convertClickUrlToExposureUrl(clickUrl: string): string {\n try {\n const url = new URL(clickUrl);\n\n // Extract the base URL (protocol + host)\n const baseUrl = `${url.protocol}//${url.host}`;\n\n // Build exposure endpoint URL\n const exposureUrl = `${baseUrl}/exposure`;\n\n // Preserve all existing query parameters from click URL\n // These include: aid, rid, nonce, exp, sig\n const params = new URLSearchParams(url.search);\n\n // Add cpx parameter if not present (default to 0, actual value is set server-side)\n if (!params.has('cpx')) {\n params.set('cpx', '0');\n }\n\n // Construct final exposure URL with all parameters\n return `${exposureUrl}?${params.toString()}`;\n } catch (error) {\n // If URL parsing fails, log warning and return original URL\n logger.warn('[WeaveResponseProcessor] Failed to convert click URL to exposure URL');\n return clickUrl;\n }\n }\n\n /**\n * Watch container for new links (streaming support)\n */\n watchForNewLinks(\n container: HTMLElement,\n recommendations: any[],\n onExposurePixel?: (target: ExposurePixelTarget) => void\n ): void {\n if (!container) {\n return;\n }\n\n // Stop existing observer\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n\n // Create observer for new nodes\n this.mutationObserver = new MutationObserver(() => {\n this.scanAndProcessLinks(container, recommendations, onExposurePixel);\n });\n\n this.mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n }\n\n /**\n * Stop watching for new links\n */\n stopWatching(): void {\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n }\n\n /**\n * Clear processed links cache\n */\n clearCache(): void {\n this.processedLinks.clear();\n }\n}\n\nexport default WeaveResponseProcessor;\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { logger } from '../utils/logger';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport { AdMeshContext, type AdMeshContextValue } from './AdMeshContext';\nimport type { AdMeshTheme } from '../types/index';\n\nexport interface AdMeshProviderProps {\n /** AdMesh API key (required) */\n apiKey: string;\n\n /** Session ID (required) */\n sessionId: string;\n\n /** Optional theme configuration */\n theme?: AdMeshTheme;\n\n /** Optional API base URL (defaults to production) */\n apiBaseUrl?: string;\n\n /** Optional user language in BCP 47 format (e.g., \"en-US\") */\n language?: string;\n\n /** Optional user country code in ISO 3166-1 alpha-2 format (e.g., \"US\") */\n geo_country?: string;\n\n /** Optional anonymous hashed user ID */\n userId?: string;\n\n /** Optional AI model identifier (e.g., \"gpt-4o\") - used for producer.software_version in UCP PlatformRequest */\n model?: string;\n\n /** Optional conversation history - used for extensions.aip.messages in UCP PlatformRequest */\n messages?: Array<{ role: string; content: string; id?: string }>;\n\n /** Child components */\n children: React.ReactNode;\n}\n\n/**\n * AdMeshProvider - Simplified SDK integration for React applications\n *\n * Handles:\n * - SDK initialization and lifecycle management\n * - Message deduplication tracking\n * - Session and message ID management\n * - Error handling and logging\n *\n * @example\n * ```tsx\n * <AdMeshProvider\n * apiKey={process.env.NEXT_PUBLIC_ADMESH_API_KEY}\n * sessionId={sessionId}\n * >\n * <Chat messages={messages} />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshProvider: React.FC<AdMeshProviderProps> = ({\n apiKey,\n sessionId,\n theme,\n apiBaseUrl,\n language,\n geo_country,\n userId,\n model,\n messages,\n children,\n}) => {\n const sdkRef = useRef<AdMeshSDK | null>(null);\n const [processedMessageIds, setProcessedMessageIds] = useState<Set<string>>(\n new Set()\n );\n\n // CRITICAL: Validate that sessionId is provided by platform\n // The SDK/provider NEVER generates sessionId automatically\n useEffect(() => {\n if (!sessionId || sessionId.trim() === '') {\n logger.error('[AdMeshProvider] ❌ sessionId is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n return;\n }\n }, [sessionId]);\n\n // Initialize SDK once on mount\n useEffect(() => {\n if (!apiKey) {\n logger.warn('[AdMeshProvider] ⚠️ AdMesh API key not configured');\n return;\n }\n\n if (!sessionId || sessionId.trim() === '') {\n logger.error('[AdMeshProvider] ❌ Cannot initialize SDK: sessionId is required and must be provided by the platform');\n return;\n }\n\n try {\n sdkRef.current = new AdMeshSDK({\n apiKey,\n theme,\n apiBaseUrl,\n });\n logger.log('[AdMeshProvider] ✅ AdMesh SDK initialized');\n if (apiBaseUrl) {\n logger.log('[AdMeshProvider] 📍 Using custom API base URL');\n }\n } catch (error) {\n logger.error('[AdMeshProvider] ❌ Failed to initialize AdMesh SDK');\n }\n\n // Cleanup on unmount\n return () => {\n logger.log('[AdMeshProvider] 🧹 Provider unmounted');\n };\n }, [apiKey, theme, apiBaseUrl]);\n\n // Create context value\n const contextValue: AdMeshContextValue = {\n sdk: sdkRef.current,\n apiKey,\n sessionId,\n theme,\n language,\n geo_country,\n userId,\n model,\n messages,\n processedMessageIds,\n \n markMessageAsProcessed: (messageId: string) => {\n setProcessedMessageIds((prev) => {\n const updated = new Set(prev);\n updated.add(messageId);\n return updated;\n });\n },\n \n isMessageProcessed: (messageId: string) => {\n return processedMessageIds.has(messageId);\n },\n };\n\n return (\n <AdMeshContext.Provider value={contextValue}>\n {children}\n </AdMeshContext.Provider>\n );\n};\n\nexport default AdMeshProvider;\n","'use client';\n\nimport { useEffect, useState, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { logger } from '../utils/logger';\nimport { AdMeshTailAd } from './AdMeshTailAd';\nimport { AdMeshFollowup } from './AdMeshFollowup';\nimport { AdMeshProductCard } from './AdMeshProductCard';\nimport { AdMeshBridgeFormat } from './AdMeshBridgeFormat';\nimport type { AdMeshRecommendation } from '../types/index';\n\nexport interface AdMeshRecommendationsProps {\n /** Optional callback when recommendations are shown */\n onRecommendationsShown?: (messageId: string) => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /**\n * Message ID for per-message recommendations.\n *\n * This is the primary identifier for fetching recommendations for a specific message.\n */\n messageId?: string;\n\n /**\n * User query text for generating recommendations.\n *\n * This is the query that will be used to fetch recommendations from the backend.\n * Typically the user's question or search query.\n *\n * IMPORTANT: This should be a non-empty string. If not provided or empty,\n * the component will skip rendering to prevent 400 Bad Request errors.\n * The backend requires a valid query parameter.\n */\n query?: string;\n\n /**\n * Callback to paste content to input field (for bridge format CTA button).\n * When provided, the bridge format will show a CTA button that pastes\n * the bridge_content into the input field when clicked.\n */\n onPasteToInput?: (content: string) => void;\n\n /**\n * Optional container ID for follow-up suggestions.\n * When provided and the recommendation includes a followup_query,\n * the SDK will automatically render the follow-up in this container.\n */\n followups_container_id?: string;\n\n /**\n * Callback to execute query when follow-up is selected (required for follow-up functionality).\n * When a user clicks on a follow-up suggestion, this callback is invoked with the followup_query.\n * This allows the platform to continue the conversation with the sponsored follow-up query.\n */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n\n /**\n * Callback when a sponsored followup is detected.\n * This allows third-party applications to integrate the sponsored followup query into their own\n * followup suggestions UI (e.g., adding to a suggestions list, related questions section, etc.).\n * \n * When a user clicks the followup suggestion, the application should:\n * 1. Fire engagement tracking by calling the followupEngagementUrl\n * 2. Execute the followupQuery (e.g., submit it as a new user query)\n * \n * @param followupQuery - The sponsored followup query text to display to the user\n * @param followupEngagementUrl - The engagement tracking URL to call when user clicks the followup\n * @param recommendationId - The recommendation ID for tracking and correlation\n * \n * @example\n * ```tsx\n * <AdMeshRecommendations\n * onFollowupDetected={(query, engagementUrl, recId) => {\n * // Add to your suggestions list\n * setSuggestions(prev => [...prev, {\n * text: query,\n * sponsored: true,\n * engagementUrl,\n * recommendationId: recId\n * }]);\n * }}\n * />\n * ```\n */\n\n onFollowupDetected?: (followupQuery: string, followupEngagementUrl: string, recommendationId: string) => void;\n\n /**\n * Signal indicating if the followup container is ready in the DOM.\n * \n * Useful for scenarios where the container is rendered conditionally or after a delay (e.g. streaming).\n * If provided, the component will wait until this is true before attempting to attach the portal.\n */\n isContainerReady?: boolean;\n}\n\n/**\n * AdMeshRecommendations - Citation/Product Format Recommendation Display\n *\n * Displays recommendations as a separate UI component. Handles all the complexity:\n * - Uses provided messageId directly\n * - Generates container IDs for recommendations\n * - Calls SDK's showRecommendations()\n *\n * For Weave Ad Format (where AdMesh links are embedded in LLM response),\n * use WeaveAdFormatContainer component instead.\n *\n * @example\n * ```tsx\n * // Per-message recommendations with messageId\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * {messages.map((msg) => (\n * <div key={msg.messageId}>\n * {msg.content}\n * {msg.role === 'assistant' && (\n * <AdMeshRecommendations\n * messageId={msg.messageId}\n * query={msg.userQuery}\n * />\n * )}\n * </div>\n * ))}\n * </AdMeshProvider>\n * ```\n *\n * @example\n * ```tsx\n * // Format is auto-detected from brand agent's preferred_format\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <Chat messages={messages} />\n * <AdMeshRecommendations\n * messageId={lastMessageId}\n * query=\"best CRM for small business\"\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshRecommendations = ({\n onRecommendationsShown,\n onError,\n messageId,\n query,\n onPasteToInput,\n followups_container_id: _followups_container_id,\n onExecuteQuery: _onExecuteQuery,\n\n onFollowupDetected,\n isContainerReady,\n}: AdMeshRecommendationsProps) => {\n const { sdk, sessionId, language, geo_country, userId, model, messages, theme } = useAdMesh();\n \n const [recommendation, setRecommendation] = useState<AdMeshRecommendation | null>(null);\n const [detectedFormat, setDetectedFormat] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Track fetched messageId to prevent duplicate fetches\n const fetchedMessageIdRef = useRef<string | null>(null);\n const isFetchingRef = useRef<boolean>(false);\n \n // Use refs for callbacks to avoid dependency issues\n const onRecommendationsShownRef = useRef(onRecommendationsShown);\n const onErrorRef = useRef(onError);\n const onFollowupDetectedRef = useRef(onFollowupDetected);\n \n // Update refs when callbacks change\n useEffect(() => {\n onRecommendationsShownRef.current = onRecommendationsShown;\n onErrorRef.current = onError;\n onFollowupDetectedRef.current = onFollowupDetected;\n }, [onRecommendationsShown, onError, onFollowupDetected]);\n\n // Helper function to convert AIPContextResponse to AdMeshRecommendation format\n const convertAIPResponseToRecommendation = (aipResponse: any): AdMeshRecommendation => {\n const responseAny = aipResponse as any;\n let creativeInput = aipResponse.creative_input || {};\n \n // Extract format and bridge prompt/content from creative object if present\n const creative = responseAny.creative || {};\n const formatFromResponse = creative.format || \n responseAny.format || \n responseAny.winning_bid?.preferred_format ||\n (aipResponse.creative_input as any)?.preferred_format;\n \n // Extract headline from creative if present (for tail and product_card formats)\n const headlineFromCreative = creative.headline;\n \n // Extract bridge format fields from creative if present\n const bridgeHeadlineFromCreative = creative.bridge_headline;\n const bridgeDescriptionFromCreative = creative.bridge_description;\n const bridgePromptFromCreative = creative.bridge_prompt || creative.bridge_content;\n const bridgePrompt = bridgePromptFromCreative ||\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content;\n \n // Extract cta_label from creative if present (for bridge format)\n const ctaLabelFromCreative = creative.cta_label;\n const ctaLabel = ctaLabelFromCreative || (creativeInput as any).cta_label;\n \n // Extract bridge_headline and bridge_description\n const bridgeHeadline = bridgeHeadlineFromCreative || (creativeInput as any).bridge_headline;\n const bridgeDescription = bridgeDescriptionFromCreative || (creativeInput as any).bridge_description;\n \n // Merge creative fields into creative_input\n creativeInput = {\n ...creativeInput,\n ...(headlineFromCreative && { headline: headlineFromCreative }),\n ...(bridgeHeadline && { bridge_headline: bridgeHeadline }),\n ...(bridgeDescription && { bridge_description: bridgeDescription }),\n ...(bridgePrompt && { bridge_prompt: bridgePrompt }),\n ...(bridgePrompt && { bridge_content: bridgePrompt }), // Backward compatibility\n ...(ctaLabel && { cta_label: ctaLabel }),\n ...(formatFromResponse && { format: formatFromResponse })\n };\n \n // Ensure recommendation_id is set\n const recommendationId = aipResponse.recommendation_id || \n (aipResponse as any).bid_id || // Fallback for backward compatibility\n '';\n \n // Ensure admesh_link is set (use click_url if not provided)\n const admeshLink = aipResponse.click_url || \n (aipResponse as any).admesh_link || \n '';\n \n // Build recommendation object\n const recommendation: any = {\n ...aipResponse,\n // Ensure recommendation_id is present\n recommendation_id: recommendationId,\n // Ensure admesh_link is present (use click_url as fallback)\n admesh_link: admeshLink || aipResponse.click_url || '',\n creative_input: creativeInput,\n // Remove any ad_id or bid_id fields\n ad_id: undefined,\n bid_id: undefined,\n ...(formatFromResponse && { format: formatFromResponse })\n };\n \n // Remove ad_id and bid_id if they exist\n delete recommendation.ad_id;\n delete recommendation.bid_id;\n \n // Explicitly preserve followup fields if present in aipResponse (check both top-level and creative_input)\n // Followup fields can be at top-level (from operator response) or in creative_input (from Firestore)\n const followupQueryTopLevel = (aipResponse as any).followup_query;\n const followupQueryInCreative = (aipResponse.creative_input as any)?.followup_query || (creativeInput as any)?.followup_query;\n const followupQuery = followupQueryTopLevel || followupQueryInCreative;\n \n const followupEngagementUrlTopLevel = (aipResponse as any).followup_engagement_url;\n const followupEngagementUrlInCreative = (aipResponse.creative_input as any)?.followup_engagement_url || (creativeInput as any)?.followup_engagement_url;\n const followupEngagementUrl = followupEngagementUrlTopLevel || followupEngagementUrlInCreative;\n \n const followupExposureUrlTopLevel = (aipResponse as any).followup_exposure_url;\n const followupExposureUrlInCreative = (aipResponse.creative_input as any)?.followup_exposure_url || (creativeInput as any)?.followup_exposure_url;\n const followupExposureUrl = followupExposureUrlTopLevel || followupExposureUrlInCreative;\n \n if (followupQuery) {\n recommendation.followup_query = followupQuery;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_query:', followupQuery.substring(0, 50) + '...');\n }\n if (followupEngagementUrl) {\n recommendation.followup_engagement_url = followupEngagementUrl;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_engagement_url');\n }\n if (followupExposureUrl) {\n recommendation.followup_exposure_url = followupExposureUrl;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_exposure_url');\n }\n \n return recommendation as AdMeshRecommendation;\n };\n\n // Reset fetched ref when messageId changes\n useEffect(() => {\n if (fetchedMessageIdRef.current !== messageId) {\n fetchedMessageIdRef.current = null;\n setRecommendation(null);\n setDetectedFormat(null);\n }\n }, [messageId]);\n\n // Find container for followups when recommendation is available\n const [followupContainer, setFollowupContainer] = useState<Element | null>(null);\n\n useEffect(() => {\n // If we don't have a query or container ID, we can't do anything\n if (!recommendation?.followup_query || !_followups_container_id) {\n setFollowupContainer(null);\n return;\n }\n\n // If isContainerReady is explicitly provided and false, wait\n if (isContainerReady === false) {\n logger.debug(`[AdMeshRecommendations] ⏳ Waiting for container signal...`);\n return;\n }\n\n // Try to find the container\n let attempts = 0;\n const maxAttempts = 5; // Reduced from 30 since we now have a signal - just need to handle React render lag\n\n const checkForContainer = () => {\n const container = document.getElementById(_followups_container_id);\n if (container) {\n logger.debug(`[AdMeshRecommendations] ✅ Found followup container: ${_followups_container_id} `);\n setFollowupContainer(container);\n return true;\n }\n return false;\n };\n\n // Check immediately\n if (checkForContainer()) return;\n\n // Short poll to account for React rendering\n const interval = setInterval(() => {\n attempts++;\n if (checkForContainer() || attempts >= maxAttempts) {\n clearInterval(interval);\n if (attempts >= maxAttempts) {\n logger.warn(`[AdMeshRecommendations] ⚠️ Followup container not found after signal: ${_followups_container_id} `);\n }\n }\n }, 100);\n\n return () => clearInterval(interval);\n }, [recommendation, _followups_container_id, isContainerReady]);\n\n // Single fetch effect - fetch recommendation once and detect format\n useEffect(() => {\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n logger.log('[AdMeshRecommendations] ❌ Validation failed - missing required parameters');\n setIsLoading(false);\n return;\n }\n\n if (!sdk?.fetchRecommendationFromAIPContext) {\n logger.log('[AdMeshRecommendations] SDK fetchRecommendationFromAIPContext not available');\n setIsLoading(false);\n return;\n }\n\n // Prevent duplicate fetches for the same messageId\n if (fetchedMessageIdRef.current === messageId) {\n logger.log('[AdMeshRecommendations] ⏭️ Already fetched for this messageId, skipping duplicate fetch');\n return;\n }\n\n // Prevent concurrent fetches\n if (isFetchingRef.current) {\n logger.log('[AdMeshRecommendations] ⏭️ Fetch already in progress, skipping duplicate fetch');\n return;\n }\n\n logger.log('[AdMeshRecommendations] 📤 Fetching recommendation from /aip/context (single fetch)');\n\n const fetchRecommendations = async () => {\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n\n // Single fetch call\n // Note: messages is optional - only pass if available\n const aipResponse = await sdk.fetchRecommendationFromAIPContext({\n query: query.trim(),\n sessionId: sessionId,\n messageId: messageId, // REQUIRED: messageId is mandatory\n language: language,\n geo_country: geo_country,\n userId: userId,\n model: model,\n ...(messages && messages.length > 0 && { messages }), // Optional: only pass if messages exist\n });\n\n // Convert response to AdMeshRecommendation format\n const convertedRecommendation = convertAIPResponseToRecommendation(aipResponse);\n \n // Log followup fields for debugging and call onFollowupDetected if present\n const followupQuery = (aipResponse as any).followup_query || (convertedRecommendation as any).followup_query;\n const followupEngagementUrl = (aipResponse as any).followup_engagement_url || (convertedRecommendation as any).followup_engagement_url;\n const recommendationId = convertedRecommendation.recommendation_id || '';\n \n // Only require followupQuery to trigger the callback - other fields are optional\n if (followupQuery) {\n logger.debug('[AdMeshRecommendations] ✅ Followup query detected:', followupQuery);\n logger.debug('[AdMeshRecommendations] ✅ Followup engagement URL:', followupEngagementUrl ? 'present' : 'missing');\n logger.debug('[AdMeshRecommendations] ✅ Recommendation ID:', recommendationId ? recommendationId : 'missing');\n\n // Log when optional fields are missing\n if (!followupEngagementUrl) {\n logger.debug('[AdMeshRecommendations] ⚠️ Followup engagement URL is missing (optional)');\n }\n if (!recommendationId) {\n logger.debug('[AdMeshRecommendations] ⚠️ Recommendation ID is missing (optional)');\n }\n\n // SDK-managed rendering: If followups_container_id is provided, use portal rendering\n // Only use onFollowupDetected callback if container ID is NOT provided (legacy/fallback mode)\n if (_followups_container_id) {\n logger.debug('[AdMeshRecommendations] ✅ Using SDK-managed portal rendering (followups_container_id provided)');\n } else if (onFollowupDetectedRef.current) {\n // Legacy/fallback: Notify third-party application via callback\n logger.debug('[AdMeshRecommendations] 🔔 Using legacy callback mode (followups_container_id not provided)');\n onFollowupDetectedRef.current(followupQuery, followupEngagementUrl || '', recommendationId || '');\n } else {\n logger.debug('[AdMeshRecommendations] ⚠️ Followup detected but no rendering method provided (neither followups_container_id nor onFollowupDetected callback)');\n }\n }\n \n setRecommendation(convertedRecommendation);\n\n // Extract format information for detection\n const responseAny = aipResponse as any;\n const preferredFormat = responseAny.creative?.preferred_format || \n responseAny.winning_bid?.preferred_format ||\n responseAny.preferred_format ||\n (aipResponse.creative_input as any)?.preferred_format;\n\n // Format selection logic - auto-detect from brand agent's preferred_format\n let selectedFormat: string;\n if (preferredFormat) {\n // Brand agent's preferred format (already validated by backend)\n // If weave format is returned, fall back to tail (weave should use WeaveAdFormatContainer)\n selectedFormat = preferredFormat === 'weave' ? 'tail' : preferredFormat;\n } else {\n // Fallback to default\n selectedFormat = 'tail';\n }\n\n setDetectedFormat(selectedFormat);\n fetchedMessageIdRef.current = messageId; // Mark as fetched\n logger.log('[AdMeshRecommendations] 📊 Format detected:', selectedFormat);\n \n setIsLoading(false);\n isFetchingRef.current = false;\n onRecommendationsShownRef.current?.(messageId);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n // Log error but don't break the UI - network errors are expected in some scenarios\n if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {\n logger.warn(`[AdMeshRecommendations] ⚠️ Network error fetching recommendations(non - critical): ${error.message} `);\n } else {\n logger.error(`[AdMeshRecommendations] ❌ Error fetching recommendations: ${error.message} `);\n }\n setError(error);\n setIsLoading(false);\n isFetchingRef.current = false;\n onErrorRef.current?.(error);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, messageId, query, language, geo_country, userId, model]); // Removed messages, onRecommendationsShown, onError from dependencies\n\n // Helper to render followup portal\n const renderFollowupPortal = () => {\n if (followupContainer && recommendation?.followup_query && sdk) {\n logger.debug(`[AdMeshRecommendations] 🌀 Rendering followup portal into: ${_followups_container_id} `);\n return createPortal(\n <AdMeshFollowup\n recommendation={recommendation}\n theme={theme}\n sdk={sdk}\n sessionId={sessionId}\n onExecuteQuery={_onExecuteQuery}\n />,\n followupContainer\n );\n } else {\n logger.debug(`[AdMeshRecommendations] ❌ Skipping followup portal.Container: ${!!followupContainer}, Query: ${!!recommendation?.followup_query}, SDK: ${!!sdk} `);\n }\n return null;\n };\n\n // Don't render anything if validation fails\n if (!messageId || !query || query.trim() === '') {\n return null;\n }\n\n // Show loading state (optional - can be removed if not needed)\n if (isLoading) {\n return null; // Or return a loading spinner if desired\n }\n\n // Show error state (optional - can be removed if not needed)\n if (error) {\n return null; // Error is already handled by onError callback\n }\n\n // Don't render if no recommendation\n if (!recommendation || !detectedFormat) {\n return null;\n }\n\n // Render appropriate component based on detected format\n const creativeInput = recommendation.creative_input || {};\n const hasBridgePrompt = !!(creativeInput as any).bridge_prompt || !!creativeInput.bridge_content;\n const formatFromRec = (recommendation as any)?.format || (creativeInput as any)?.format;\n const preferredFormatFromRec = (recommendation as any)?.preferred_format;\n const hasBridgeFormat = formatFromRec === 'bridge' || preferredFormatFromRec === 'bridge' || detectedFormat === 'bridge';\n\n // Bridge format detection and rendering\n if (hasBridgePrompt || hasBridgeFormat) {\n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshBridgeFormat\n recommendation={recommendation}\n theme={theme}\n sessionId={sessionId}\n onPasteToInput={onPasteToInput}\n />\n {renderFollowupPortal()}\n </div>\n );\n }\n\n // Product card format\n if (detectedFormat === 'product' || detectedFormat === 'product_card') {\n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshProductCard\n recommendation={recommendation}\n theme={theme}\n sessionId={sessionId}\n />\n {renderFollowupPortal()}\n </div>\n );\n }\n\n // Tail format (default)\n const summaryText = creativeInput.long_description || \n creativeInput.context_snippet || \n creativeInput.short_description || \n '';\n \n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshTailAd\n summaryText={summaryText}\n recommendations={[recommendation]}\n theme={theme}\n sessionId={sessionId}\n />\n {renderFollowupPortal()}\n </div>\n );\n};\n\nexport default AdMeshRecommendations;\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { logger } from '../utils/logger';\n\nexport interface WeaveFallbackRecommendationsProps {\n /** Recommendation format for fallback display\n *\n * - 'product': Display as product cards\n * - 'tail': Display as tail format (default)\n */\n format?: 'product' | 'tail';\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /**\n * Message ID for per-message recommendations.\n *\n * This is the primary identifier for fetching recommendations for a specific message.\n */\n messageId: string;\n\n /**\n * User query text for generating recommendations.\n *\n * This is the query that will be used to fetch recommendations from the backend.\n * Typically the user's question or search query.\n *\n * IMPORTANT: This should be a non-empty string. If not provided or empty,\n * the component will skip rendering to prevent 400 Bad Request errors.\n * The backend requires a valid query parameter.\n */\n query?: string;\n\n /**\n * Fallback state - controls whether to show recommendations\n * When true, recommendations will be fetched and displayed\n * When false or undefined, component will not render\n */\n fallback?: boolean;\n\n /**\n * Previously fetched recommendations from WeaveAdFormatContainer.\n * If provided and not empty, these will be rendered directly without making a new API call.\n * Only makes a new API call if this is empty/null/undefined.\n */\n previousRecommendations?: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any\n}\n\n/**\n * WeaveFallbackRecommendations - Weave Format Fallback Component\n *\n * Displays recommendations as a fallback UI when no AdMesh links are detected\n * in the LLM response. Works independently without requiring WeaveAdFormatContext.\n *\n * This component will:\n * - Accept messageId, query, and fallback state as props\n * - Only render when fallback prop is true\n * - If previousRecommendations are provided and not empty: renders them directly (no API call)\n * - If previousRecommendations are empty/null: calls SDK's showRecommendations() to fetch recommendations\n * - Display recommendations in the specified format (tail or product)\n *\n * IMPORTANT: Pass previousRecommendations from WeaveAdFormatContainer to avoid duplicate API calls.\n *\n * @example\n * ```tsx\n * const [fallback, setFallback] = useState(false);\n *\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <WeaveAdFormatContainer\n * messageId={message.id}\n * query={userQuery}\n * onFallbackChange={setFallback}\n * >\n * {llmResponseContent}\n * </WeaveAdFormatContainer>\n * <WeaveFallbackRecommendations\n * messageId={message.id}\n * query={userQuery}\n * format=\"tail\"\n * fallback={fallback}\n * previousRecommendations={recommendations} // Pass from WeaveAdFormatContainer\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const WeaveFallbackRecommendations: React.FC<WeaveFallbackRecommendationsProps> = ({\n format = 'tail',\n onError,\n messageId,\n query,\n fallback,\n previousRecommendations,\n}) => {\n const { sdk, sessionId, theme } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerId, setContainerId] = useState<string>('');\n\n // Generate container ID based on message ID\n useEffect(() => {\n if (messageId) {\n setContainerId(`admesh-weave-fallback-${messageId}`);\n }\n }, [messageId]);\n\n // Log component render\n logger.log('[WeaveFallbackRecommendations] 🎨 Component render');\n\n useEffect(() => {\n // Log what we're receiving\n logger.log('[WeaveFallbackRecommendations] 🔄 useEffect triggered', {\n fallback,\n hasPreviousRecommendations: previousRecommendations && previousRecommendations.length > 0,\n previousRecommendationsCount: previousRecommendations?.length || 0\n });\n\n // Skip if fallback is not true\n if (!fallback) {\n logger.log('[WeaveFallbackRecommendations] ⏭️ Skipping - fallback is FALSE, not rendering recommendations');\n return;\n }\n\n logger.log('[WeaveFallbackRecommendations] ✅ fallback is TRUE, proceeding with recommendations');\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n logger.log('[WeaveFallbackRecommendations] ❌ Validation failed - returning early');\n return;\n }\n\n logger.log('[WeaveFallbackRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n logger.log('[WeaveFallbackRecommendations] SDK or containerId not ready');\n return;\n }\n\n // Check if we have previous recommendations to reuse\n const hasPreviousRecs = previousRecommendations && previousRecommendations.length > 0;\n \n logger.log('[WeaveFallbackRecommendations] 📊 Checking previous recommendations:', {\n hasPreviousRecs,\n count: previousRecommendations?.length || 0,\n fallback,\n messageId\n });\n \n if (hasPreviousRecs) {\n // Use previous recommendations - render directly without API call\n logger.log(`[WeaveFallbackRecommendations] ♻️ Reusing ${previousRecommendations.length} previous recommendation(s), skipping API call`);\n \n const renderRecommendations = async () => {\n try {\n const aipResponse = previousRecommendations[0];\n \n logger.log('[WeaveFallbackRecommendations] 📦 Processing AIP response:', {\n hasAipResponse: !!aipResponse,\n recommendationId: aipResponse?.recommendation_id,\n hasCreativeInput: !!aipResponse?.creative_input\n });\n \n // Development logging: Log full recommendation details\n if (aipResponse && process.env.NODE_ENV === 'development') {\n logger.log('[WeaveFallbackRecommendations] 📋 Recommendation received (DEV):', {\n recommendation_id: aipResponse.recommendation_id,\n product_id: aipResponse.product_id,\n brand_id: aipResponse.brand_id,\n title: aipResponse.title,\n click_url: aipResponse.click_url,\n contextual_relevance_score: aipResponse.contextual_relevance_score,\n preferred_format: aipResponse.creative_input?.preferred_format,\n resolved_format: aipResponse.format_resolution?.resolved_format,\n creative_input: {\n product_name: aipResponse.creative_input?.product_name,\n brand_name: aipResponse.creative_input?.brand_name,\n cta_url: aipResponse.creative_input?.cta_url,\n short_description: aipResponse.creative_input?.short_description,\n allowed_formats: aipResponse.creative_input?.allowed_formats,\n fallback_formats: aipResponse.creative_input?.fallback_formats,\n },\n format_resolution: aipResponse.format_resolution,\n full_response: aipResponse\n });\n }\n \n if (!aipResponse) {\n logger.warn('[WeaveFallbackRecommendations] ⚠️ Previous recommendation is empty, falling back to API call');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n return;\n }\n\n // Try to access SDK's private methods via type casting\n // These are used internally by showRecommendations\n const sdkAny = sdk as any;\n const convertMethod = sdkAny.convertAIPResponseToRecommendation;\n const getRendererMethod = sdkAny.getRenderer;\n const getTrackerMethod = sdkAny.getTracker;\n const sdkTheme = sdkAny.config?.theme;\n\n if (convertMethod && getRendererMethod && getTrackerMethod) {\n // Convert AIP response to recommendation format (same as showRecommendations does)\n const recommendation = convertMethod.call(sdk, aipResponse);\n \n if (!recommendation) {\n logger.warn('[WeaveFallbackRecommendations] ⚠️ Conversion returned empty, falling back to API call');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n return;\n }\n\n const response = {\n session_id: aipResponse.session_id || sessionId,\n message_id: `msg_${aipResponse.recommendation_id || messageId}`,\n recommendations: [recommendation]\n };\n\n // Get renderer and tracker\n const renderer = getRendererMethod.call(sdk);\n const tracker = getTrackerMethod.call(sdk);\n \n // Render directly\n await renderer.render({\n containerId,\n response,\n theme: theme || sdkTheme,\n tracker: tracker,\n sessionId: sessionId,\n });\n \n logger.log('[WeaveFallbackRecommendations] ✅ Recommendations rendered from previous response (no API call)');\n } else {\n // SDK methods not accessible - fall back to showRecommendations\n // This will use the cached result from auction coordination, so it's still fast\n logger.warn('[WeaveFallbackRecommendations] ⚠️ SDK internal methods not accessible, using showRecommendations (will use cached result)');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.error(`[WeaveFallbackRecommendations] ❌ Error rendering previous recommendations: ${err.message}`);\n // Fallback to API call on error (will use cached result)\n try {\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n } catch (fallbackError) {\n onError?.(err);\n }\n }\n };\n\n renderRecommendations();\n } else {\n // No previous recommendations - make new API call\n logger.log('[WeaveFallbackRecommendations] 📤 No previous recommendations, calling sdk.showRecommendations');\n \n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n logger.log('[WeaveFallbackRecommendations] SDK showRecommendations not available');\n return;\n }\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId, // REQUIRED: messageId must be provided by platform\n });\n\n logger.log('[WeaveFallbackRecommendations] ✅ Recommendations displayed successfully');\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.log(`[WeaveFallbackRecommendations] ❌ Error: ${err.message}`);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }\n }, [sdk, sessionId, containerId, format, messageId, query, fallback, onError, previousRecommendations, theme]);\n\n // Don't render anything if fallback is not active or validation fails\n if (!fallback || !messageId || !query || query.trim() === '') {\n return null;\n }\n\n // Render the container where fallback recommendations will be displayed\n // Use display: 'none' initially to prevent taking up space until recommendations are loaded\n // The SDK will set display: 'block' when it renders content\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-weave-fallback-recommendations-container\"\n style={{\n marginTop: '1rem',\n display: 'none', // Hidden by default, SDK will show when recommendations are rendered\n }}\n />\n );\n};\n\nexport default WeaveFallbackRecommendations;\n","'use client';\n\nimport React, { createContext, useContext } from 'react';\n\nexport interface WeaveAdFormatContextType {\n /** Whether the fallback UI should be rendered (no AdMesh links detected) */\n shouldRenderFallback: boolean;\n /** The message ID for this Weave Ad Format container (used for deduplication) */\n messageId: string;\n /** The session ID for this Weave Ad Format container (used for tracking) */\n sessionId?: string;\n /** The query for this message (used for fallback API calls) */\n query?: string;\n}\n\nconst WeaveAdFormatContext = createContext<WeaveAdFormatContextType | undefined>(undefined);\n\nexport const WeaveAdFormatProvider: React.FC<{\n children: React.ReactNode;\n shouldRenderFallback: boolean;\n messageId: string;\n sessionId?: string;\n query?: string;\n}> = ({ children, shouldRenderFallback, messageId, sessionId, query }) => {\n return (\n <WeaveAdFormatContext.Provider value={{ shouldRenderFallback, messageId, sessionId, query }}>\n {children}\n </WeaveAdFormatContext.Provider>\n );\n};\n\n/**\n * Hook to access WeaveAdFormatContext\n * \n * Returns the context value if inside a WeaveAdFormatContainer,\n * or undefined if not inside one.\n * \n * @example\n * ```tsx\n * const weaveContext = useWeaveAdFormatContext();\n * if (weaveContext) {\n * // Inside a WeaveAdFormatContainer\n * const { shouldRenderFallback, messageId } = weaveContext;\n * }\n * ```\n */\nexport const useWeaveAdFormatContext = (): WeaveAdFormatContextType | undefined => {\n return useContext(WeaveAdFormatContext);\n};\n\nexport default WeaveAdFormatContext;\n\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshRecommendation, EcommerceProduct } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshEcommerceCardsProps {\n recommendations: AdMeshRecommendation[]; // Required: unified schema recommendations\n title?: string;\n showTitle?: boolean;\n className?: string;\n cardClassName?: string;\n onProductClick?: (product: AdMeshRecommendation) => void;\n showPricing?: boolean;\n showRatings?: boolean;\n showBrand?: boolean;\n showSource?: boolean;\n showShipping?: boolean;\n maxCards?: number;\n cardWidth?: 'sm' | 'md' | 'lg';\n theme?: 'light' | 'dark' | 'auto';\n borderRadius?: 'none' | 'sm' | 'md' | 'lg';\n shadow?: 'none' | 'sm' | 'md' | 'lg';\n}\n\nexport const AdMeshEcommerceCards: React.FC<AdMeshEcommerceCardsProps> = ({\n recommendations,\n title = \"Product Recommendations\",\n showTitle = true,\n className = \"\",\n cardClassName = \"\",\n onProductClick,\n maxCards = 10,\n cardWidth = 'md',\n theme = 'auto',\n borderRadius = 'md',\n shadow = 'sm'\n}) => {\n // Validate recommendations - silently return null if empty\n if (!recommendations || recommendations.length === 0) {\n logger.log('[AdMesh Ecommerce Cards] Empty recommendations - not rendering anything');\n return null;\n }\n\n const displayItems: AdMeshRecommendation[] = recommendations.slice(0, maxCards);\n\n\n\n const getCardWidthClass = () => {\n switch (cardWidth) {\n case 'sm': return 'w-48 min-w-48';\n case 'md': return 'w-64 min-w-64';\n case 'lg': return 'w-80 min-w-80';\n default: return 'w-64 min-w-64';\n }\n };\n\n const getBorderRadiusClass = () => {\n switch (borderRadius) {\n case 'none': return 'rounded-none';\n case 'sm': return 'rounded-sm';\n case 'md': return 'rounded-lg';\n case 'lg': return 'rounded-xl';\n default: return 'rounded-lg';\n }\n };\n\n const getShadowClass = () => {\n switch (shadow) {\n case 'none': return '';\n case 'sm': return 'shadow-sm hover:shadow-md';\n case 'md': return 'shadow-md hover:shadow-lg';\n case 'lg': return 'shadow-lg hover:shadow-xl';\n default: return 'shadow-sm hover:shadow-md';\n }\n };\n\n const getThemeClasses = () => {\n if (theme === 'dark') {\n return 'bg-gray-900 text-white';\n } else if (theme === 'light') {\n return 'bg-white text-gray-900';\n }\n return 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white';\n };\n\n\n\n const handleProductClick = (item: EcommerceProduct | AdMeshRecommendation) => {\n if (onProductClick) {\n // Cast to AdMeshRecommendation for callback\n const rec = item as AdMeshRecommendation;\n onProductClick(rec);\n } else {\n // Default behavior: open product link - prioritize click_url, then admesh_link, then url\n const link = (item as any).click_url || \n (item as any).admesh_link || \n (item as any).url ||\n (item as any).creative_input?.cta_url;\n if (link) {\n window.open(link, '_blank', 'noopener,noreferrer');\n }\n }\n };\n\n // Check if we have any data to display\n if (!recommendations || recommendations.length === 0) {\n return null;\n }\n\n return (\n <div className={classNames('w-full', className)}>\n {showTitle && (\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-white\">\n {title}\n </h3>\n <div className=\"mt-1 h-0.5 w-12 bg-blue-500\"></div>\n </div>\n )}\n \n <div className=\"relative\">\n {displayItems.length === 0 ? (\n <div className=\"text-center py-8 text-gray-500\">\n No products to display\n </div>\n ) : (\n <div className=\"flex gap-4 overflow-x-auto pb-4 scrollbar-hide\">\n {displayItems.map((item) => {\n // Get the appropriate ID for the key and tracking\n const itemId = (item as any).recommendation_id || (item as any).product_id || (item as any).id || '';\n const recommendationId = (item as any).recommendation_id || (item as any).product_id || '';\n const productId = (item as any).product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n key={itemId}\n recommendationId={recommendationId}\n productId={productId}\n className={classNames(\n getCardWidthClass(),\n getBorderRadiusClass(),\n getShadowClass(),\n getThemeClasses(),\n 'flex-shrink-0 border border-gray-200 dark:border-gray-700 transition-all duration-200 cursor-pointer hover:scale-[1.02]',\n cardClassName\n )}\n onClick={() => handleProductClick(item)}\n >\n <div>\n {/* Product Image */}\n <div className=\"relative aspect-square w-full overflow-hidden\">\n {(item.creative_input?.assets?.logo_url || \n item.creative_input?.assets?.image_urls?.[0] || \n item.product_logo?.url) ? (\n <img\n src={item.creative_input?.assets?.image_urls?.[0] || \n item.creative_input?.assets?.logo_url || \n item.product_logo?.url}\n alt={item.creative_input?.product_name || item.title || item.product_title || 'Product'}\n className=\"h-full w-full object-cover transition-transform duration-200 hover:scale-105\"\n loading=\"lazy\"\n onError={(e) => {\n // Hide image if it fails to load\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gray-100 dark:bg-gray-800\">\n <svg className=\"h-12 w-12 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"p-3\">\n {/* Title */}\n <h4 className=\"text-sm font-medium text-gray-900 dark:text-white line-clamp-2 mb-2 leading-tight\">\n {item.creative_input?.product_name || item.title || item.product_title || ''}\n </h4>\n\n {/* Summary - prioritize creative_input fields */}\n {(item.creative_input?.short_description || \n item.creative_input?.context_snippet || \n item.weave_summary) && (\n <p className=\"text-xs text-gray-600 dark:text-gray-400 line-clamp-2 mb-2\">\n {item.creative_input?.short_description || \n item.creative_input?.context_snippet || \n item.weave_summary}\n </p>\n )}\n\n {/* Offer Summary: Display below short_description */}\n {item.creative_input?.offer_summary && (\n <p className=\"text-xs text-gray-500 dark:text-gray-500 line-clamp-2 mb-2 italic\">\n {item.creative_input.offer_summary}\n </p>\n )}\n\n {/* Value Props from creative_input */}\n {item.creative_input?.value_props && item.creative_input.value_props.length > 0 && (\n <div className=\"mb-2\">\n <ul className=\"space-y-1\">\n {item.creative_input.value_props.slice(0, 2).map((prop, idx) => (\n <li key={idx} className=\"flex items-start gap-1.5 text-xs text-gray-600 dark:text-gray-400\">\n <svg className=\"w-3 h-3 mt-0.5 flex-shrink-0 text-green-500\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path fillRule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clipRule=\"evenodd\" />\n </svg>\n <span className=\"line-clamp-1\">{prop}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Categories - fallback if no value props */}\n {(!item.creative_input?.value_props || item.creative_input.value_props.length === 0) && \n item.categories && item.categories.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {item.categories.slice(0, 2).map((cat, idx) => (\n <span key={idx} className=\"text-xs bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1 rounded\">\n {cat}\n </span>\n ))}\n </div>\n )}\n\n {/* Trust Score */}\n {item.trust_score !== undefined && (\n <div className=\"text-xs text-gray-600 dark:text-gray-400\">\n Trust Score: {(item.trust_score * 100).toFixed(0)}%\n </div>\n )}\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n })}\n </div>\n )}\n\n {/* Scroll Indicators - only show when there are products */}\n {displayItems.length > 0 && (\n <>\n <div className=\"absolute top-1/2 -left-2 transform -translate-y-1/2 bg-white dark:bg-gray-800 rounded-full shadow-lg p-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n <svg className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n </div>\n <div className=\"absolute top-1/2 -right-2 transform -translate-y-1/2 bg-white dark:bg-gray-800 rounded-full shadow-lg p-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n <svg className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </div>\n </>\n )}\n </div>\n\n {/* Disclosure */}\n <div className=\"flex justify-between items-center mt-3 pt-2 border-t border-gray-200 dark:border-gray-700\">\n <span className=\"text-xs text-gray-500 dark:text-gray-400\">\n Sponsored\n </span>\n <span className=\"text-xs text-gray-400 dark:text-gray-500\">\n \n </span>\n </div>\n\n {/* Custom scrollbar styles */}\n <style dangerouslySetInnerHTML={{\n __html: `\n .scrollbar-hide {\n -ms-overflow-style: none;\n scrollbar-width: none;\n }\n .scrollbar-hide::-webkit-scrollbar {\n display: none;\n }\n .line-clamp-2 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n `\n }} />\n </div>\n );\n};\n\nexport default AdMeshEcommerceCards;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshLinkTracker } from './AdMeshLinkTracker';\n\nexport interface AdMeshInlineCardProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n variation?: 'default' | 'statement' | 'question';\n expandable?: boolean; // For question and statement variations, default: false\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * AdMeshInlineCard\n * Compact inline card that uses the same card style as AdMeshProductCard,\n * but with a smaller content footprint suitable for inline placements.\n */\nexport const AdMeshInlineCard: React.FC<AdMeshInlineCardProps> = ({\n recommendation,\n theme,\n variation = 'default',\n expandable = false,\n className,\n style,\n}) => {\n // Validate recommendation - return null if empty\n if (!recommendation) {\n logger.log('[AdMesh Inline Card] No recommendation provided - not rendering');\n return null;\n }\n\n // Get content based on variation\n const getVariationContent = () => {\n // Note: content_variations is not in the finalized minimal schema\n // Using product_title and weave_summary instead\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.weave_summary || recommendation.reason || '',\n ctaText: recommendation.product_title || recommendation.title\n };\n };\n\n const content = getVariationContent();\n\n const cardClasses = classNames(\n 'rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-4 bg-white dark:bg-slate-900',\n 'hover:shadow-md transition-shadow duration-200',\n {\n 'cursor-pointer': expandable && (variation === 'question' || variation === 'statement')\n },\n className\n );\n\n return (\n <div\n className={cardClasses}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n // Ensure consistent width: 100% for inline cards\n width: theme?.components?.inlineRecommendation?.width || '100%',\n ...theme?.components?.productCard,\n ...style,\n }}\n data-admesh-theme={theme?.mode}\n >\n <div className=\"h-full flex flex-col\">\n {/* Header with title and CTA button in same row */}\n <div className=\"flex justify-between items-center gap-3 mb-2\">\n <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n {recommendation.product_logo?.url && (\n <img\n src={recommendation.product_logo.url}\n alt={`${recommendation.product_title} logo`}\n className=\"w-5 h-5 rounded flex-shrink-0\"\n onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n )}\n <h4 className=\"font-semibold text-gray-800 dark:text-gray-200 text-sm truncate\">\n {content.title}\n </h4>\n </div>\n\n <div className=\"flex-shrink-0\">\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'inline_card_cta',\n }}\n >\n <button className=\"text-xs px-2 py-1 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 flex items-center transition-all duration-200 transform hover:scale-105 shadow-sm whitespace-nowrap\">\n {variation === 'question' ? 'Try' : 'Visit'}\n <svg className=\"ml-1 h-3 w-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\" />\n </svg>\n </button>\n </AdMeshLinkTracker>\n </div>\n </div>\n\n {/* Brief description (single line clamp) */}\n {content.description && (\n <p className=\"text-xs text-gray-600 dark:text-gray-300 mb-2 line-clamp-2\">\n {content.description}\n </p>\n )}\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <p className=\"text-xs text-gray-500 dark:text-gray-400 mb-2 line-clamp-2 italic\">\n {recommendation.creative_input.offer_summary}\n </p>\n )}\n\n {/* Footer with disclosures */}\n <div className=\"mt-auto pt-2 border-t border-gray-100 dark:border-slate-700\">\n <div className=\"flex items-center justify-between text-[11px] text-gray-500 dark:text-gray-400\">\n <span>Sponsored</span>\n <span className=\"text-gray-400 dark:text-gray-500\"></span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nAdMeshInlineCard.displayName = 'AdMeshInlineCard';\n\n","import React from 'react';\nimport classNames from 'classnames';\nimport type { AdMeshBadgeProps, BadgeType } from '../types/index';\n\n// Badge type to variant mapping\nconst badgeTypeVariants: Record<BadgeType, string> = {\n 'Top Match': 'primary',\n 'Free Tier': 'success',\n 'AI Powered': 'secondary',\n 'Popular': 'warning',\n 'New': 'primary',\n 'Trial Available': 'success'\n};\n\n// Badge type to icon mapping (using clean Unicode symbols)\nconst badgeTypeIcons: Partial<Record<BadgeType, string>> = {\n 'Top Match': '★',\n 'Free Tier': '◆',\n 'AI Powered': '◉',\n 'Popular': '▲',\n 'New': '●',\n 'Trial Available': '◈'\n};\n\nexport const AdMeshBadge: React.FC<AdMeshBadgeProps> = ({\n type,\n variant,\n size = 'md',\n className,\n style\n}) => {\n const effectiveVariant = variant || badgeTypeVariants[type] || 'secondary';\n const icon = badgeTypeIcons[type];\n\n const badgeClasses = classNames(\n 'admesh-component',\n 'admesh-badge',\n `admesh-badge--${effectiveVariant}`,\n `admesh-badge--${size}`,\n className\n );\n\n return (\n <span\n className={badgeClasses}\n style={style}\n >\n {icon && <span className=\"admesh-badge__icon\">{icon}</span>}\n <span className=\"admesh-badge__text\">{type}</span>\n </span>\n );\n};\n\nAdMeshBadge.displayName = 'AdMeshBadge';\n","/**\n * Streaming Events Utilities\n * \n * Custom event system for communicating LLM streaming lifecycle events\n * between the chat frontend and AdMesh WeaveAdFormatContainer.\n * \n * This allows WeaveAdFormatContainer to trigger final link detection\n * when streaming completes, rather than using arbitrary timeouts.\n */\n\nimport { logger } from './logger';\n\nexport const STREAMING_START_EVENT = 'admesh:streamingStart';\nexport const STREAMING_COMPLETE_EVENT = 'admesh:streamingComplete';\n\nexport interface StreamingStartEventDetail {\n messageId: string;\n sessionId: string;\n timestamp: number;\n}\n\nexport interface StreamingCompleteEventDetail {\n messageId: string;\n sessionId: string;\n timestamp: number;\n metadata?: {\n hasRecommendations?: boolean;\n recommendationCount?: number;\n };\n}\n\n/**\n * Dispatch a streaming start event\n * \n * Call this when the backend starts streaming the LLM response.\n * This signals to WeaveAdFormatContainer that content is being received.\n * \n * @param messageId - Unique identifier for the message\n * @param sessionId - Session/chat identifier\n */\nexport function dispatchStreamingStartEvent(\n messageId: string,\n sessionId: string\n): void {\n const detail: StreamingStartEventDetail = {\n messageId,\n sessionId,\n timestamp: Date.now(),\n };\n\n const event = new CustomEvent(STREAMING_START_EVENT, { detail });\n window.dispatchEvent(event);\n\n logger.log('[StreamingEvents] 📢 Dispatched streamingStart event', {\n messageId,\n sessionId\n });\n}\n\n/**\n * Dispatch a streaming complete event\n * \n * Call this when the backend finishes streaming the LLM response.\n * This triggers WeaveAdFormatContainer to perform final link detection.\n * \n * @param messageId - Unique identifier for the message\n * @param sessionId - Session/chat identifier\n * @param metadata - Optional metadata about recommendations\n */\nexport function dispatchStreamingCompleteEvent(\n messageId: string,\n sessionId: string,\n metadata?: {\n hasRecommendations?: boolean;\n recommendationCount?: number;\n }\n): void {\n const detail: StreamingCompleteEventDetail = {\n messageId,\n sessionId,\n timestamp: Date.now(),\n metadata,\n };\n\n const event = new CustomEvent(STREAMING_COMPLETE_EVENT, { detail });\n window.dispatchEvent(event);\n\n logger.log('[StreamingEvents] 📢 Dispatched streamingComplete event', {\n messageId,\n sessionId,\n metadata\n });\n}\n\n/**\n * Listen for streaming start events\n * \n * @param messageId - Filter events by this messageId\n * @param sessionId - Filter events by this sessionId\n * @param callback - Called when matching event is received\n * @returns Cleanup function to remove the event listener\n */\nexport function onStreamingStart(\n messageId: string,\n sessionId: string,\n callback: (detail: StreamingStartEventDetail) => void\n): () => void {\n const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingStartEventDetail>;\n \n // Only trigger callback if this is the message we're waiting for\n if (\n customEvent.detail.messageId === messageId &&\n customEvent.detail.sessionId === sessionId\n ) {\n logger.log('[StreamingEvents] 📨 Received streamingStart event');\n callback(customEvent.detail);\n }\n };\n\n window.addEventListener(STREAMING_START_EVENT, handler);\n\n // Return cleanup function\n return () => {\n window.removeEventListener(STREAMING_START_EVENT, handler);\n };\n}\n\n/**\n * Listen for streaming complete events\n * \n * @param messageId - Filter events by this messageId\n * @param sessionId - Filter events by this sessionId\n * @param callback - Called when matching event is received\n * @returns Cleanup function to remove the event listener\n */\nexport function onStreamingComplete(\n messageId: string,\n sessionId: string,\n callback: (detail: StreamingCompleteEventDetail) => void\n): () => void {\n logger.log('[StreamingEvents] 👂 Setting up streamingComplete listener', {\n expectedMessageId: messageId,\n expectedSessionId: sessionId\n });\n\n const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingCompleteEventDetail>;\n \n logger.log('[StreamingEvents] 📨 Received streamingComplete event (checking match)', {\n receivedMessageId: customEvent.detail.messageId,\n receivedSessionId: customEvent.detail.sessionId,\n expectedMessageId: messageId,\n expectedSessionId: sessionId,\n messageIdMatch: customEvent.detail.messageId === messageId,\n sessionIdMatch: customEvent.detail.sessionId === sessionId\n });\n \n // Only trigger callback if this is the message we're waiting for\n if (\n customEvent.detail.messageId === messageId &&\n customEvent.detail.sessionId === sessionId\n ) {\n logger.log('[StreamingEvents] ✅ Event matched! Calling callback');\n callback(customEvent.detail);\n } else {\n logger.log('[StreamingEvents] ⚠️ Event did not match - ignoring');\n }\n };\n\n window.addEventListener(STREAMING_COMPLETE_EVENT, handler);\n\n // Return cleanup function\n return () => {\n logger.log('[StreamingEvents] 🧹 Removing streamingComplete listener');\n window.removeEventListener(STREAMING_COMPLETE_EVENT, handler);\n };\n}\n","import { logger } from './logger';\n\ninterface InlineExposureTrackerState {\n observer: IntersectionObserver;\n timeoutId: ReturnType<typeof setTimeout> | null;\n viewableStart: number | null;\n}\n\nexport interface InlineExposureTrackingParams {\n exposureUrl?: string;\n recommendationId?: string;\n linkElement?: HTMLElement | null;\n sessionId?: string;\n logPrefix?: string;\n}\n\nexport interface InlineExposureTracker {\n startTracking: (params: InlineExposureTrackingParams) => void;\n cleanup: () => void;\n}\n\n/**\n * Creates an inline exposure tracker that fires AdMesh exposure pixels when\n * detected links meet the MRC viewability threshold (50% visible for 1 second).\n */\nexport const createInlineExposureTracker = (): InlineExposureTracker => {\n const firedKeys = new Set<string>();\n const activeTrackers = new Map<string, InlineExposureTrackerState>();\n\n const cleanupTracker = (key: string) => {\n const tracker = activeTrackers.get(key);\n if (!tracker) {\n return;\n }\n tracker.observer.disconnect();\n if (tracker.timeoutId) {\n clearTimeout(tracker.timeoutId);\n }\n activeTrackers.delete(key);\n };\n\n const startTracking = ({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix = '[AdMesh Exposure]'\n }: InlineExposureTrackingParams) => {\n if (typeof window === 'undefined') {\n return;\n }\n\n if (!exposureUrl || !linkElement) {\n if (logPrefix) {\n logger.warn(`${logPrefix} ⚠️ Missing exposure tracking data`);\n }\n return;\n }\n\n const dedupeKey = `${sessionId || 'anonymous'}::${recommendationId || exposureUrl}`;\n\n if (firedKeys.has(dedupeKey) || activeTrackers.has(dedupeKey)) {\n return;\n }\n\n const trackerState: InlineExposureTrackerState = {\n observer: undefined as unknown as IntersectionObserver,\n timeoutId: null,\n viewableStart: null\n };\n\n const fireExposurePixel = () => {\n cleanupTracker(dedupeKey);\n firedKeys.add(dedupeKey);\n\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n if (logPrefix) {\n logger.log(`${logPrefix} ✅ Exposure pixel fired`);\n }\n })\n .catch(() => {\n if (logPrefix) {\n logger.warn(`${logPrefix} ⚠️ Failed to fire exposure pixel`);\n }\n firedKeys.delete(dedupeKey);\n });\n };\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const visibility = entry.intersectionRatio;\n\n if (visibility >= 0.5) {\n if (trackerState.viewableStart === null) {\n trackerState.viewableStart = performance.now();\n trackerState.timeoutId = setTimeout(fireExposurePixel, 1000);\n }\n } else {\n trackerState.viewableStart = null;\n if (trackerState.timeoutId) {\n clearTimeout(trackerState.timeoutId);\n trackerState.timeoutId = null;\n }\n }\n });\n },\n {\n threshold: [0, 0.25, 0.5, 0.75, 1],\n rootMargin: '0px'\n }\n );\n\n trackerState.observer = observer;\n observer.observe(linkElement as Element);\n activeTrackers.set(dedupeKey, trackerState);\n };\n\n const cleanup = () => {\n Array.from(activeTrackers.keys()).forEach(cleanupTracker);\n };\n\n return {\n startTracking,\n cleanup\n };\n};\n","'use client';\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { WeaveFallbackRecommendations } from './WeaveFallbackRecommendations';\nimport { AdMeshFollowup } from './AdMeshFollowup';\nimport { onStreamingComplete } from '../utils/streamingEvents';\nimport {\n createInlineExposureTracker,\n type InlineExposureTrackingParams\n} from '../utils/inlineExposureTracker';\nimport { WeaveResponseProcessor } from '../sdk/WeaveResponseProcessor';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation } from '../types/index';\n\n/**\n * FinalLinkDetectionCheck - Event-driven link detection component\n *\n * This component waits for the 'admesh:streamingComplete' event from ChatWindow\n * before performing final link detection. This eliminates race conditions where\n * timeout-based detection would trigger before streaming completes.\n */\ninterface FinalLinkDetectionCheckProps {\n containerId: string;\n messageId: string;\n sessionId: string;\n sdk: any; // eslint-disable-line @typescript-eslint/no-explicit-any\n query?: string;\n recommendations?: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any\n onLinksFound: (count: number) => void;\n onNoLinksFound: () => void;\n children: React.ReactNode;\n}\n\nconst FinalLinkDetectionCheck: React.FC<FinalLinkDetectionCheckProps> = ({\n containerId,\n messageId,\n sessionId,\n sdk,\n query,\n recommendations = [],\n onLinksFound,\n onNoLinksFound,\n children\n}) => {\n const [checkComplete, setCheckComplete] = useState(false);\n const [waitingForStreamEnd, setWaitingForStreamEnd] = useState(true);\n const [linksFound, setLinksFound] = useState(false);\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n\n // Use refs to avoid recreating the callback on every render\n const onLinksFoundRef = useRef(onLinksFound);\n const onNoLinksFoundRef = useRef(onNoLinksFound);\n const containerIdRef = useRef(containerId);\n const sdkRef = useRef(sdk);\n const sessionIdRef = useRef(sessionId);\n const queryRef = useRef(query);\n\n // Update refs when values change\n useEffect(() => {\n onLinksFoundRef.current = onLinksFound;\n onNoLinksFoundRef.current = onNoLinksFound;\n containerIdRef.current = containerId;\n sdkRef.current = sdk;\n sessionIdRef.current = sessionId;\n queryRef.current = query;\n }, [onLinksFound, onNoLinksFound, containerId, sdk, sessionId, query]);\n\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n const trackExposurePixel = useCallback(\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId: sessionIdRef.current,\n logPrefix: '[FinalLinkDetectionCheck]'\n });\n },\n []\n );\n\n const performFinalCheck = useCallback(() => {\n logger.log('[FinalLinkDetectionCheck] 🔍 Performing final link detection...');\n\n try {\n const container = document.getElementById(containerIdRef.current);\n if (!container) {\n logger.warn('[FinalLinkDetectionCheck] ❌ Container not found');\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Create WeaveResponseProcessor instance\n const weaveProcessor = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n\n // Use fetched recommendations if available, otherwise fall back to pattern-based detection\n const recsToUse = recommendations.length > 0 ? recommendations : [];\n logger.log(`[FinalLinkDetectionCheck] 📋 Using ${recsToUse.length} recommendations for link detection`);\n \n // Development logging: Log recommendation details used for link detection\n if (recsToUse.length > 0 && process.env.NODE_ENV === 'development') {\n recsToUse.forEach((rec, index) => {\n logger.log(`[FinalLinkDetectionCheck] 📋 Recommendation ${index + 1} (DEV):`, {\n recommendation_id: rec.recommendation_id,\n product_id: rec.product_id,\n title: rec.title,\n click_url: rec.click_url,\n preferred_format: rec.creative_input?.preferred_format,\n resolved_format: rec.format_resolution?.resolved_format,\n creative_input: {\n product_name: rec.creative_input?.product_name,\n brand_name: rec.creative_input?.brand_name,\n cta_url: rec.creative_input?.cta_url,\n }\n });\n });\n }\n\n // Scan for AdMesh links one final time\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recsToUse, // Use fetched recommendations for brand URL matching\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) =>\n trackExposurePixel({ exposureUrl, recommendationId, linkElement })\n );\n\n logger.log(`[FinalLinkDetectionCheck] 📊 Final check result: ${detectedLinks.length} links`);\n\n if (detectedLinks.length > 0) {\n // Links found! Cancel fallback\n logger.log('[FinalLinkDetectionCheck] ✅ Links detected - canceling fallback (no API call)');\n setLinksFound(true);\n onLinksFoundRef.current(detectedLinks.length);\n } else {\n // No links found, proceed with fallback\n logger.log(`[FinalLinkDetectionCheck] ⚠️ No links found - rendering fallback (will use ${recsToUse.length} previous recommendation(s) if available)`);\n setLinksFound(false);\n onNoLinksFoundRef.current();\n }\n\n setCheckComplete(true);\n } catch (error) {\n logger.error('[FinalLinkDetectionCheck] ❌ Error during final check');\n setCheckComplete(true);\n onNoLinksFoundRef.current();\n }\n }, [trackExposurePixel, recommendations]);\n\n useEffect(() => {\n logger.log('[FinalLinkDetectionCheck] ⏳ Setting up listener', {\n messageId,\n sessionId,\n recommendationsCount: recommendations.length\n });\n\n let timeoutId: NodeJS.Timeout | null = null;\n let eventReceived = false;\n\n // Listen for the streamingComplete event from ChatWindow\n const cleanup = onStreamingComplete(messageId, sessionId, (detail) => {\n logger.log('[FinalLinkDetectionCheck] 🎯 Received streamingComplete event', {\n messageId: detail.messageId,\n sessionId: detail.sessionId,\n timestamp: detail.timestamp\n });\n eventReceived = true;\n \n // Clear timeout since event was received\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n \n setWaitingForStreamEnd(false);\n\n // Small delay to ensure DOM is fully updated\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n });\n\n // Fallback timeout: If streamingComplete doesn't fire within 5 seconds, trigger check anyway\n // This ensures fallback recommendations display even if event system fails\n timeoutId = setTimeout(() => {\n if (!eventReceived) {\n logger.warn('[FinalLinkDetectionCheck] ⏱️ Timeout: streamingComplete event not received, triggering fallback check anyway');\n setWaitingForStreamEnd(false);\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n }\n }, 5000); // 5 second timeout\n\n return () => {\n logger.log('[FinalLinkDetectionCheck] 🧹 Cleaning up listener');\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n cleanup();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messageId, sessionId]); // performFinalCheck depends on recommendations prop, but recommendations should be stable for a given messageId\n\n // Show loading state while waiting for stream to complete\n if (waitingForStreamEnd) {\n return <></>;\n }\n\n // Show checking state while performing final detection\n if (!checkComplete) {\n return <></>;\n }\n\n // Only render children (fallback) if NO links were found\n if (linksFound) {\n logger.log('[FinalLinkDetectionCheck] 🚫 Links found - NOT rendering fallback');\n return <></>;\n }\n\n logger.log('[FinalLinkDetectionCheck] ✅ No links found - rendering fallback');\n return <>{children}</>;\n};\n\nexport interface WeaveAdFormatContainerProps {\n /** Unique ID for this message container */\n messageId: string;\n\n /** The LLM response content (may contain AdMesh links) */\n children: React.ReactNode;\n\n /** Fallback format if no links detected (default: 'tail') */\n fallbackFormat?: 'product' | 'tail';\n\n /** Optional callback when AdMesh links are detected */\n onLinksDetected?: (count: number) => void;\n\n /** Optional callback when no links are detected (fallback should be rendered) */\n onNoLinksDetected?: () => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /** Optional CSS class for the container */\n className?: string;\n\n /** Optional query text for fallback API calls (used when no links are detected) */\n query?: string;\n\n /** Optional callback to notify parent when fallback state changes */\n onFallbackChange?: (shouldFallback: boolean) => void;\n\n /** Optional callback when weave injection is attempted */\n onWeaveAttempt?: (messageId: string) => void;\n\n /** Optional callback when weave injection outcome is determined */\n onWeaveOutcome?: (messageId: string, success: boolean, reason?: string) => void;\n\n /** Optional container ID for follow-up suggestions */\n followups_container_id?: string;\n\n /** Callback to execute query when follow-up is selected (required for follow-up functionality) */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n\n /** Optional callback when a sponsored followup is detected */\n onFollowupDetected?: (followupQuery: string, followupEngagementUrl: string, recommendationId: string) => void;\n\n /** Signal indicating if the followup container is ready in the DOM */\n isContainerReady?: boolean;\n}\n\n/**\n * WeaveAdFormatContainer - Automatic Weave Ad Format handling component\n *\n * Wraps LLM response content and automatically:\n * - Detects AdMesh links in the content\n * - Fires exposure tracking for detected links\n * - Adds [Ad] labels to links\n * - Shows \"Why this ad?\" tooltip on hover\n * - Provides context for WeaveFallbackRecommendations component\n *\n * @example\n * ```tsx\n * <WeaveAdFormatContainer\n * messageId={message.id}\n * query={userQuery}\n * onLinksDetected={(count) => console.log(`Found ${count} ads`)}\n * >\n * {llmResponseContent}\n * </WeaveAdFormatContainer>\n * ```\n */\nexport const WeaveAdFormatContainer: React.FC<WeaveAdFormatContainerProps> = ({\n messageId,\n children,\n fallbackFormat = 'tail',\n onLinksDetected,\n onNoLinksDetected,\n onError,\n className,\n query,\n onFallbackChange,\n onWeaveAttempt,\n onWeaveOutcome,\n followups_container_id: _followups_container_id,\n onExecuteQuery: _onExecuteQuery,\n onFollowupDetected,\n isContainerReady\n}) => {\n const { sessionId, sdk, language, geo_country, userId, model, messages, theme } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const containerId = `weave-ad-container-${messageId}`;\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n const scannedRef = useRef(false);\n const weaveProcessorRef = useRef<WeaveResponseProcessor | null>(null);\n const [recommendations, setRecommendations] = useState<any[]>([]);\n const recommendationsRef = useRef<any[]>([]);\n const [recommendationWithFollowup, setRecommendationWithFollowup] = useState<AdMeshRecommendation | null>(null);\n const [followupContainer, setFollowupContainer] = useState<Element | null>(null);\n\n // Use refs for callbacks to avoid dependency issues\n const onFollowupDetectedRef = useRef(onFollowupDetected);\n \n // Update refs when callbacks change\n useEffect(() => {\n onFollowupDetectedRef.current = onFollowupDetected;\n }, [onFollowupDetected]);\n\n // Initialize WeaveResponseProcessor once\n useEffect(() => {\n if (!weaveProcessorRef.current) {\n weaveProcessorRef.current = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n }\n return () => {\n if (weaveProcessorRef.current) {\n weaveProcessorRef.current.stopWatching();\n weaveProcessorRef.current.clearCache();\n }\n };\n }, []);\n\n // Cleanup exposure tracker on unmount\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n // Fetch recommendations from SDK cache if available\n useEffect(() => {\n const fetchRecommendations = async () => {\n if (!sdk || !query?.trim() || !messageId) {\n logger.log('[WeaveAdFormatContainer] ⚠️ Missing SDK, query, or messageId - using pattern-based detection');\n return;\n }\n\n try {\n logger.log('[WeaveAdFormatContainer] 📤 Fetching recommendations from SDK for brand URL matching');\n // Use context values (from AdMeshProvider) for UCP PlatformRequest fields\n const aipResponse = await sdk.fetchRecommendationFromAIPContext({\n query: query.trim(),\n sessionId: sessionId,\n messageId: messageId,\n language: language, // From context (AdMeshProvider) - maps to context.language in UCP\n geo_country: geo_country, // From context (AdMeshProvider) - maps to context.geography.country in UCP\n userId: userId, // From context (AdMeshProvider)\n model: model, // From context (AdMeshProvider)\n messages: messages, // From context (AdMeshProvider)\n });\n\n // Wrap single recommendation in array for WeaveResponseProcessor\n const recs = aipResponse ? [aipResponse] : [];\n setRecommendations(recs);\n recommendationsRef.current = recs; // Update ref to avoid dependency issues\n logger.log('[WeaveAdFormatContainer] ✅ Fetched recommendations:', recs.length);\n \n // Extract follow-up data from response (similar to AdMeshRecommendations)\n if (aipResponse) {\n const followupQueryTopLevel = (aipResponse as any).followup_query;\n const followupQueryInCreative = (aipResponse.creative_input as any)?.followup_query;\n const followupQuery = followupQueryTopLevel || followupQueryInCreative;\n \n const followupEngagementUrlTopLevel = (aipResponse as any).followup_engagement_url;\n const followupEngagementUrlInCreative = (aipResponse.creative_input as any)?.followup_engagement_url;\n const followupEngagementUrl = followupEngagementUrlTopLevel || followupEngagementUrlInCreative;\n \n const followupExposureUrlTopLevel = (aipResponse as any).followup_exposure_url;\n const followupExposureUrlInCreative = (aipResponse.creative_input as any)?.followup_exposure_url;\n const followupExposureUrl = followupExposureUrlTopLevel || followupExposureUrlInCreative;\n \n const recommendationId = (aipResponse as any).recommendation_id || '';\n \n // If followup query exists, create recommendation object with follow-up data\n if (followupQuery) {\n logger.debug('[WeaveAdFormatContainer] ✅ Followup query detected:', followupQuery);\n logger.debug('[WeaveAdFormatContainer] ✅ Followup engagement URL:', followupEngagementUrl ? 'present' : 'missing');\n logger.debug('[WeaveAdFormatContainer] ✅ Recommendation ID:', recommendationId ? recommendationId : 'missing');\n \n // SDK-managed rendering: If followups_container_id is provided, use portal rendering\n // Only use onFollowupDetected callback if container ID is NOT provided (legacy/fallback mode)\n if (_followups_container_id) {\n logger.debug('[WeaveAdFormatContainer] ✅ Using SDK-managed portal rendering (followups_container_id provided)');\n // Store recommendation with follow-up data for portal rendering\n const recommendationWithFollowupData: any = {\n ...aipResponse,\n followup_query: followupQuery,\n followup_engagement_url: followupEngagementUrl,\n followup_exposure_url: followupExposureUrl,\n recommendation_id: recommendationId || (aipResponse as any).recommendation_id\n };\n setRecommendationWithFollowup(recommendationWithFollowupData as AdMeshRecommendation);\n } else if (onFollowupDetectedRef.current) {\n // Legacy/fallback: Notify third-party application via callback\n logger.debug('[WeaveAdFormatContainer] 🔔 Using legacy callback mode (followups_container_id not provided)');\n onFollowupDetectedRef.current(followupQuery, followupEngagementUrl || '', recommendationId || '');\n } else {\n logger.debug('[WeaveAdFormatContainer] ⚠️ Followup detected but no rendering method provided (neither followups_container_id nor onFollowupDetected callback)');\n }\n } else {\n setRecommendationWithFollowup(null);\n }\n }\n \n // Development logging: Log full recommendation details\n if (aipResponse && process.env.NODE_ENV === 'development') {\n logger.log('[WeaveAdFormatContainer] 📋 Recommendation received (DEV):', {\n recommendation_id: aipResponse.recommendation_id,\n product_id: aipResponse.product_id,\n brand_id: aipResponse.brand_id,\n title: aipResponse.title,\n click_url: aipResponse.click_url,\n contextual_relevance_score: aipResponse.contextual_relevance_score,\n preferred_format: aipResponse.creative_input?.preferred_format,\n resolved_format: aipResponse.format_resolution?.resolved_format,\n creative_input: {\n product_name: aipResponse.creative_input?.product_name,\n brand_name: aipResponse.creative_input?.brand_name,\n cta_url: aipResponse.creative_input?.cta_url,\n short_description: aipResponse.creative_input?.short_description,\n allowed_formats: aipResponse.creative_input?.allowed_formats,\n fallback_formats: aipResponse.creative_input?.fallback_formats,\n },\n format_resolution: aipResponse.format_resolution,\n full_response: aipResponse\n });\n }\n } catch (error) {\n logger.warn('[WeaveAdFormatContainer] ⚠️ Failed to fetch recommendations, falling back to pattern-based detection:', error);\n setRecommendations([]);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, messageId, query, _followups_container_id]);\n\n // Find container for followups when recommendation with follow-up is available\n useEffect(() => {\n // If we don't have a query or container ID, we can't do anything\n if (!recommendationWithFollowup?.followup_query || !_followups_container_id) {\n setFollowupContainer(null);\n return;\n }\n\n // If isContainerReady is explicitly provided and false, wait\n if (isContainerReady === false) {\n logger.debug(`[WeaveAdFormatContainer] ⏳ Waiting for container signal...`);\n return;\n }\n\n // Try to find the container\n let attempts = 0;\n const maxAttempts = 5; // Reduced from 30 since we now have a signal - just need to handle React render lag\n\n const checkForContainer = () => {\n const container = document.getElementById(_followups_container_id);\n if (container) {\n logger.debug(`[WeaveAdFormatContainer] ✅ Found followup container: ${_followups_container_id} `);\n setFollowupContainer(container);\n return true;\n }\n return false;\n };\n\n // Check immediately\n if (checkForContainer()) return;\n\n // Short poll to account for React rendering\n const interval = setInterval(() => {\n attempts++;\n if (checkForContainer() || attempts >= maxAttempts) {\n clearInterval(interval);\n if (attempts >= maxAttempts) {\n logger.warn(`[WeaveAdFormatContainer] ⚠️ Followup container not found after signal: ${_followups_container_id} `);\n }\n }\n }, 100);\n\n return () => clearInterval(interval);\n }, [recommendationWithFollowup, _followups_container_id, isContainerReady]);\n\n // Scan for links immediately when container is ready and on children changes\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !weaveProcessorRef.current) {\n return;\n }\n\n const weaveProcessor = weaveProcessorRef.current;\n\n const trackExposurePixel = ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix: '[WeaveAdFormatContainer]'\n });\n };\n\n // Scan immediately\n const scanLinks = () => {\n // Emit weave attempt signal\n logger.log('[WeaveAdFormatContainer] 🔍 Weave injection attempt started');\n onWeaveAttempt?.(messageId);\n\n // Use fetched recommendations if available, otherwise fall back to pattern-based detection\n // Use ref to avoid triggering re-runs when recommendations change\n const recsToUse = recommendationsRef.current.length > 0 ? recommendationsRef.current : [];\n logger.log(`[WeaveAdFormatContainer] 📋 Using ${recsToUse.length} recommendations for link detection`);\n\n try {\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recsToUse, // Use fetched recommendations for brand URL matching\n trackExposurePixel\n );\n\n if (detectedLinks.length > 0 && !scannedRef.current) {\n scannedRef.current = true;\n logger.log(`[WeaveAdFormatContainer] ✅ Weave injection succeeded: ${detectedLinks.length} links detected`);\n onWeaveOutcome?.(messageId, true, `Found ${detectedLinks.length} links`);\n onLinksDetected?.(detectedLinks.length);\n onFallbackChange?.(false);\n } else if (!scannedRef.current) {\n // No links found on first scan - will be handled by FinalLinkDetectionCheck\n // But we emit the outcome signal here for immediate feedback\n logger.log('[WeaveAdFormatContainer] ⚠️ Weave injection: no links found on initial scan');\n // Don't emit outcome yet - wait for FinalLinkDetectionCheck to confirm\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`[WeaveAdFormatContainer] ❌ Weave injection failed: ${errorMessage}`);\n onWeaveOutcome?.(messageId, false, errorMessage);\n // Immediately trigger fallback on error\n onNoLinksDetected?.();\n onFallbackChange?.(true);\n }\n };\n\n // Initial scan after a small delay to ensure DOM is ready\n const timeoutId = setTimeout(scanLinks, 100);\n\n // Watch for new links (streaming support)\n // Use ref to avoid triggering re-runs when recommendations change\n weaveProcessor.watchForNewLinks(container, recommendationsRef.current, trackExposurePixel);\n\n return () => {\n clearTimeout(timeoutId);\n weaveProcessor.stopWatching();\n };\n }, [children, sessionId, messageId, onLinksDetected, onFallbackChange, onWeaveAttempt, onWeaveOutcome]); // Removed recommendations from deps to prevent infinite loop\n\n // Check if format is \"weave\" - if so, don't show fallback recommendations\n const resolvedFormat = recommendations[0]?.format_resolution?.resolved_format;\n const preferredFormat = recommendations[0]?.creative_input?.preferred_format;\n const isWeaveFormat = resolvedFormat === 'weave' || preferredFormat === 'weave';\n\n // Log when format is weave (for debugging)\n useEffect(() => {\n if (query && isWeaveFormat) {\n logger.log(`[WeaveAdFormatContainer] 🎯 Format is \"weave\" (resolved: ${resolvedFormat}, preferred: ${preferredFormat}) - skipping fallback recommendations`);\n }\n }, [query, isWeaveFormat, resolvedFormat, preferredFormat]);\n\n // Helper to render followup portal\n const renderFollowupPortal = () => {\n if (followupContainer && recommendationWithFollowup?.followup_query && sdk) {\n logger.debug(`[WeaveAdFormatContainer] 🌀 Rendering followup portal into: ${_followups_container_id} `);\n return createPortal(\n <AdMeshFollowup\n recommendation={recommendationWithFollowup}\n theme={theme}\n sdk={sdk}\n sessionId={sessionId}\n onExecuteQuery={_onExecuteQuery}\n />,\n followupContainer\n );\n } else {\n logger.debug(`[WeaveAdFormatContainer] ❌ Skipping followup portal. Container: ${!!followupContainer}, Query: ${!!recommendationWithFollowup?.followup_query}, SDK: ${!!sdk} `);\n }\n return null;\n };\n\n return (\n <>\n <div\n ref={containerRef}\n id={containerId}\n className={className}\n data-weave-ad-format=\"true\"\n data-message-id={messageId}\n >\n {children}\n </div>\n\n {/* Event-driven link detection and fallback rendering - only show if format is NOT weave */}\n {query && !isWeaveFormat && (\n <FinalLinkDetectionCheck\n containerId={containerId}\n messageId={messageId}\n sessionId={sessionId}\n sdk={sdk}\n query={query}\n recommendations={recommendations}\n onLinksFound={(count) => {\n logger.log(`[WeaveAdFormatContainer] ✅ Links found (${count}) - no fallback needed`);\n // Emit weave outcome success if not already emitted\n onWeaveOutcome?.(messageId, true, `Found ${count} links in final check`);\n onLinksDetected?.(count);\n onFallbackChange?.(false);\n }}\n onNoLinksFound={() => {\n logger.log(`[WeaveAdFormatContainer] ⚠️ No links found - rendering fallback`);\n logger.log(`[WeaveAdFormatContainer] 📋 Passing ${recommendations.length} recommendation(s) to fallback component`);\n // Emit weave outcome failure\n onWeaveOutcome?.(messageId, false, 'No links detected in final check');\n onNoLinksDetected?.();\n onFallbackChange?.(true);\n }}\n >\n <WeaveFallbackRecommendations\n format={fallbackFormat}\n messageId={messageId}\n query={query}\n fallback={true}\n onError={onError}\n previousRecommendations={recommendations.length > 0 ? recommendations : undefined}\n />\n </FinalLinkDetectionCheck>\n )}\n\n {/* Render follow-up portal if follow-up exists */}\n {renderFollowupPortal()}\n </>\n );\n};\n\nexport default WeaveAdFormatContainer;\n","'use client';\n\nimport { useEffect, useRef, useCallback } from 'react';\nimport { useAdMesh } from './useAdMesh';\nimport {\n createInlineExposureTracker,\n type InlineExposureTrackingParams\n} from '../utils/inlineExposureTracker';\n\nexport interface UseWeaveAdFormatOptions {\n /** Container ID for the LLM output (where AdMesh links will be detected) */\n llmOutputContainerId: string;\n\n /** Timeout for link detection (default: 900ms) */\n timeoutMs?: number;\n\n /** Fallback format if no links detected (default: 'tail') */\n fallbackFormat?: 'product' | 'tail';\n\n /** Optional callback when AdMesh links are detected */\n onLinksDetected?: (count: number) => void;\n\n /** Optional callback when no links are detected (fallback should be rendered) */\n onNoLinksDetected?: () => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /** Optional query for fallback API calls (used when no links are detected) */\n query?: string;\n\n /** Optional message ID for fallback API calls (used when no links are detected) */\n messageId?: string;\n}\n\n/**\n * useWeaveAdFormat - Hook for automatic Weave Ad Format handling\n * \n * Automatically:\n * - Detects AdMesh links in LLM output\n * - Fires exposure tracking for detected links\n * - Adds [Ad] labels to links\n * - Shows \"Why this ad?\" tooltip on hover\n * - Renders fallback UI if no links detected\n * \n * @example\n * ```tsx\n * const { isProcessing, detectedLinksCount } = useWeaveAdFormat({\n * llmOutputContainerId: 'llm-output-123',\n * timeoutMs: 1500,\n * fallbackFormat: 'tail'\n * });\n * ```\n */\nexport const useWeaveAdFormat = (options: UseWeaveAdFormatOptions) => {\n const { sdk, sessionId } = useAdMesh();\n const processingRef = useRef(false);\n const detectedLinksRef = useRef(0);\n const linksFoundRef = useRef(false);\n const callbackFiredRef = useRef(false);\n const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n const trackInlineExposure = useCallback(\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix: '[useWeaveAdFormat]'\n });\n },\n [sessionId]\n );\n\n // Phase 1: Enhanced Mutation Silence Detection\n // Track when last mutation occurred for silence detection\n const lastMutationTimeRef = useRef<number>(Date.now());\n // Count consecutive silence checks (requires 2 for confirmation)\n const mutationSilenceCountRef = useRef<number>(0);\n // Store interval timer for cleanup\n const silenceCheckIntervalRef = useRef<NodeJS.Timeout | null>(null);\n\n // Phase 2: Adaptive Debounce Detection\n // Track timestamps of recent mutations to calculate adaptive timeout\n const mutationTimestampsRef = useRef<number[]>([]);\n // Store the calculated adaptive timeout value\n const adaptiveTimeoutRef = useRef<number>(300); // Default to 300ms\n\n const processWeaveFormat = useCallback(async () => {\n if (!sdk || !sessionId || processingRef.current) {\n return;\n }\n\n processingRef.current = true;\n detectedLinksRef.current = 0;\n\n try {\n const container = document.getElementById(options.llmOutputContainerId);\n if (!container) {\n \n return;\n }\n\n // Get the WeaveResponseProcessor from SDK\n const weaveProcessor = (sdk as any).getWeaveProcessor?.(); // eslint-disable-line @typescript-eslint/no-explicit-any\n if (!weaveProcessor) {\n return;\n }\n\n // Scan and process AdMesh links in the container\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n [], // Recommendations will be fetched from SDK cache\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) =>\n trackInlineExposure({ exposureUrl, recommendationId, linkElement })\n );\n\n detectedLinksRef.current = detectedLinks.length;\n const hasLinks = detectedLinks.length > 0;\n\n // Update the flag indicating whether links were found\n if (hasLinks && !linksFoundRef.current) {\n // Links detected (first time or after streaming)\n linksFoundRef.current = true;\n options.onLinksDetected?.(detectedLinks.length);\n callbackFiredRef.current = true;\n } else if (!hasLinks && linksFoundRef.current) {\n // Links were found before but now they're gone (shouldn't happen in normal flow)\n linksFoundRef.current = false;\n callbackFiredRef.current = false;\n } else if (!hasLinks && !callbackFiredRef.current) {\n // No links found and callback hasn't been fired yet\n // Use enhanced mutation silence detection to wait for streaming to complete\n // This prevents firing the callback too early during streaming\n\n // Clear existing debounce timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n\n // Clear existing silence check interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n }\n\n // Reset silence counter when starting new detection\n mutationSilenceCountRef.current = 0;\n lastMutationTimeRef.current = Date.now();\n\n // Phase 1 + Phase 2: Enhanced Mutation Silence Detection with Adaptive Timeout\n // Check for mutation silence at adaptive intervals\n // Require 2 consecutive silence checks before declaring streaming complete\n const silenceCheckInterval = Math.max(100, adaptiveTimeoutRef.current / 3); // Check 3x per adaptive timeout\n\n silenceCheckIntervalRef.current = setInterval(() => {\n const timeSinceLastMutation = Date.now() - lastMutationTimeRef.current;\n const SILENCE_THRESHOLD = adaptiveTimeoutRef.current; // Use adaptive timeout as silence threshold\n\n if (timeSinceLastMutation >= SILENCE_THRESHOLD) {\n // Mutation silence detected\n mutationSilenceCountRef.current++;\n\n // Require 2 consecutive silence checks for confirmation\n if (mutationSilenceCountRef.current >= 2) {\n // Clear the interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n silenceCheckIntervalRef.current = null;\n }\n\n // Fire callback if no links were found\n if (!linksFoundRef.current && !callbackFiredRef.current) {\n options.onNoLinksDetected?.();\n callbackFiredRef.current = true;\n }\n }\n } else {\n // Mutations still happening, reset counter\n mutationSilenceCountRef.current = 0;\n }\n }, silenceCheckInterval);\n\n // Phase 2: Fallback timeout using adaptive timeout\n // Set a maximum timeout to ensure we eventually fire the callback\n // This handles edge cases where mutations might be very infrequent\n const maxFallbackTimeout = Math.max(1500, adaptiveTimeoutRef.current * 5); // At least 1500ms, or 5x adaptive timeout\n\n debounceTimerRef.current = setTimeout(() => {\n // Clear the silence check interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n silenceCheckIntervalRef.current = null;\n }\n\n // Fire callback if no links were found\n if (!linksFoundRef.current && !callbackFiredRef.current) {\n options.onNoLinksDetected?.();\n callbackFiredRef.current = true;\n }\n }, maxFallbackTimeout);\n } else if (hasLinks && callbackFiredRef.current && !linksFoundRef.current) {\n // Links detected after we initially said \"no links found\"\n // This happens when links arrive during streaming\n\n\n // Clear debounce timer since we found links\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n\n linksFoundRef.current = true;\n options.onLinksDetected?.(detectedLinks.length);\n callbackFiredRef.current = true;\n } else {\n \n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n options.onError?.(err);\n } finally {\n processingRef.current = false;\n }\n }, [sdk, sessionId, options, trackInlineExposure]);\n\n // Phase 2: Calculate adaptive debounce timeout based on mutation frequency\n const calculateAdaptiveTimeout = useCallback(() => {\n const timestamps = mutationTimestampsRef.current;\n\n // Need at least 2 timestamps to calculate interval\n if (timestamps.length < 2) {\n adaptiveTimeoutRef.current = 300; // Default to 300ms\n return;\n }\n\n // Calculate intervals between consecutive mutations\n const intervals: number[] = [];\n for (let i = 1; i < timestamps.length; i++) {\n intervals.push(timestamps[i] - timestamps[i - 1]);\n }\n\n // Calculate average interval\n const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;\n\n // Adaptive timeout: 3x the average interval, min 100ms, max 1000ms\n const adaptiveTimeout = Math.max(100, Math.min(avgInterval * 3, 1000));\n\n adaptiveTimeoutRef.current = adaptiveTimeout;\n }, []);\n\n // Watch for changes in the LLM output container\n useEffect(() => {\n const container = document.getElementById(options.llmOutputContainerId);\n if (!container) {\n return;\n }\n\n // Initial scan\n processWeaveFormat();\n\n // Set up MutationObserver for streaming responses\n const observer = new MutationObserver(() => {\n const now = Date.now();\n\n // Phase 1: Update last mutation time for silence detection\n lastMutationTimeRef.current = now;\n // Reset silence counter since we detected a mutation\n mutationSilenceCountRef.current = 0;\n\n // Phase 2: Track mutation timestamp for adaptive debounce calculation\n mutationTimestampsRef.current.push(now);\n // Keep only last 10 timestamps to avoid memory bloat\n if (mutationTimestampsRef.current.length > 10) {\n mutationTimestampsRef.current.shift();\n }\n // Recalculate adaptive timeout based on recent mutation pattern\n calculateAdaptiveTimeout();\n\n processWeaveFormat();\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n\n return () => {\n observer.disconnect();\n // Clean up debounce timer on unmount\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n // Phase 1: Clean up silence check interval on unmount\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n }\n };\n }, [options.llmOutputContainerId, processWeaveFormat, calculateAdaptiveTimeout]);\n\n return {\n isProcessing: processingRef.current,\n detectedLinksCount: detectedLinksRef.current,\n linksFound: linksFoundRef.current,\n shouldRenderFallback: !linksFoundRef.current && callbackFiredRef.current,\n processWeaveFormat\n };\n};\n\nexport default useWeaveAdFormat;\n","/**\n * AdMesh UI SDK - Main Entry Point\n * \n * Zero-code integration for displaying AdMesh recommendations\n */\n\n// Core SDK\nexport { AdMeshSDK } from './sdk/AdMeshSDK';\nexport type { AdMeshSDKConfig, ShowRecommendationsOptions } from './sdk/AdMeshSDK';\n\n// Tracking\nexport { AdMeshTracker } from './sdk/AdMeshTracker';\nexport type { TrackerConfig } from './sdk/AdMeshTracker';\n\n// Rendering\nexport { AdMeshRenderer } from './sdk/AdMeshRenderer';\nexport type { RenderOptions } from './sdk/AdMeshRenderer';\n\n// Weave Response Processing\nexport { WeaveResponseProcessor } from './sdk/WeaveResponseProcessor';\nexport type { DetectedLink, ProcessorConfig } from './sdk/WeaveResponseProcessor';\n\n// Weave format is now integrated into AdMeshSDK\n// Use format: 'weave' option in showRecommendations() method\n\n// Provider Pattern (Simplified Integration)\nexport { AdMeshProvider } from './context/AdMeshProvider';\nexport type { AdMeshProviderProps } from './context/AdMeshProvider';\n\n// Components\nexport { AdMeshRecommendations } from './components/AdMeshRecommendations';\nexport type { AdMeshRecommendationsProps } from './components/AdMeshRecommendations';\nexport { WeaveFallbackRecommendations } from './components/WeaveFallbackRecommendations';\nexport type { WeaveFallbackRecommendationsProps } from './components/WeaveFallbackRecommendations';\n\n// Context\nexport { AdMeshContext, useAdMeshContext } from './context/AdMeshContext';\nexport type { AdMeshContextValue } from './context/AdMeshContext';\nexport { WeaveAdFormatProvider, useWeaveAdFormatContext } from './context/WeaveAdFormatContext';\nexport type { WeaveAdFormatContextType } from './context/WeaveAdFormatContext';\n\n// Components\nexport { AdMeshEcommerceCards } from './components/AdMeshEcommerceCards';\nexport { AdMeshProductCard } from './components/AdMeshProductCard';\nexport { AdMeshInlineCard } from './components/AdMeshInlineCard';\nexport { AdMeshLayout } from './components/AdMeshLayout';\nexport { AdMeshSummaryLayout } from './components/AdMeshSummaryLayout';\nexport { AdMeshTailAd } from './components/AdMeshTailAd';\nexport type { AdMeshTailAdProps } from './components/AdMeshTailAd';\nexport { AdMeshBridgeFormat } from './components/AdMeshBridgeFormat';\nexport type { AdMeshBridgeFormatProps } from './components/AdMeshBridgeFormat';\nexport { AdMeshFollowup } from './components/AdMeshFollowup';\nexport type { AdMeshFollowupProps } from './components/AdMeshFollowup';\n// Backward compatibility export\nexport { AdMeshTailAd as AdMeshSummaryUnit } from './components/AdMeshTailAd';\nexport type { AdMeshTailAdProps as AdMeshSummaryUnitProps } from './components/AdMeshTailAd';\nexport { AdMeshViewabilityTracker } from './components/AdMeshViewabilityTracker';\nexport { AdMeshLinkTracker } from './components/AdMeshLinkTracker';\nexport { AdMeshBadge } from './components/AdMeshBadge';\nexport { WeaveAdFormatContainer } from './components/WeaveAdFormatContainer';\nexport type { WeaveAdFormatContainerProps } from './components/WeaveAdFormatContainer';\n\n// Hooks\nexport { useAdMesh } from './hooks/useAdMesh';\nexport { useAdMeshStyles } from './hooks/useAdMeshStyles';\nexport { useAdMeshTracker } from './hooks/useAdMeshTracker';\nexport { useViewabilityTracker } from './hooks/useViewabilityTracker';\nexport { useWeaveAdFormat } from './hooks/useWeaveAdFormat';\nexport type { UseWeaveAdFormatOptions } from './hooks/useWeaveAdFormat';\n\n// Types\nexport type { AdMeshTheme } from './types/index';\n\n// Streaming Events (for event-driven link detection)\nexport {\n dispatchStreamingStartEvent,\n dispatchStreamingCompleteEvent,\n onStreamingStart,\n onStreamingComplete,\n STREAMING_START_EVENT,\n STREAMING_COMPLETE_EVENT\n} from './utils/streamingEvents';\nexport type {\n StreamingStartEventDetail,\n StreamingCompleteEventDetail\n} from './utils/streamingEvents';\n\n// Inline exposure tracking helper\nexport {\n createInlineExposureTracker\n} from './utils/inlineExposureTracker';\nexport type {\n InlineExposureTracker,\n InlineExposureTrackingParams\n} from './utils/inlineExposureTracker';\n\n// Version\nexport const VERSION = '1.0.10';\n"],"names":["isProduction","_a","logger","args","calculateMRCStandards","adWidth","adHeight","customStandards","isLargeAd","detectDeviceType","viewportWidth","calculateVisibilityPercentage","element","rect","viewportHeight","elementHeight","elementWidth","visibleTop","visibleBottom","visibleLeft","visibleRight","visibleHeight","visibleWidth","visibleArea","totalArea","calculateScrollDepth","windowHeight","documentHeight","scrollTop","scrollableHeight","getElementPosition","scrollLeft","collectContextMetrics","position","isDarkMode","generateSessionId","meetsViewabilityThreshold","visibilityPercentage","visibleDuration","standards","formatTimestamp","date","calculateAverage","numbers","acc","num","throttle","func","limit","inThrottle","DEFAULT_CONFIG","globalConfig","useViewabilityTracker","productId","offerId","agentId","recommendationId","elementRef","customConfig","config","sessionId","useRef","state","setState","useState","mrcStandards","visibilityStartTime","viewableStartTime","hoverStartTime","focusStartTime","visibilityPercentages","eventBatch","batchTimeout","log","useCallback","message","sendEvent","eventType","additionalData","contextMetrics","event","flushBatch","updateVisibility","now","loadTime","prev","newState","wasVisible","isNowVisible","wasViewable","isNowViewable","viewableDuration","useEffect","observer","entries","handleScroll","handleMouseEnter","handleMouseLeave","hoverDuration","handleFocus","handleBlur","focusDuration","handleClick","sessionDuration","AdMeshViewabilityTracker","exposureUrl","children","className","style","onViewabilityChange","onVisible","onViewable","onClick","exposureFired","viewabilityState","previousViewable","error","previousVisible","jsx","getCTALabel","ctaLabel","AdMeshTailAd","recommendations","theme","firstRecommendation","creativeInput","shortDescription","offerSummary","brandName","productName","clickUrl","headlineText","headlineSuffix","handleContainerClick","source","e","handleBrandNameClick","handleCTAClick","jsxs","Fragment","hasOwn","classNames","classes","i","arg","appendClass","parseValue","key","value","newClass","module","DEFAULT_TRACKING_URL","useAdMeshTracker","isTracking","setIsTracking","setError","mergedConfig","useMemo","sendTrackingEvent","data","payload","lastError","attempt","response","err","resolve","errorMsg","trackClick","trackView","trackConversion","AdMeshLinkTracker","admeshLink","trackingData","hasTrackedView","link","entry","ADMESH_STYLE_ID","ADMESH_RESET_ID","ADMESH_CSS_RESET","ADMESH_CORE_STYLES","injectAdMeshStyles","resetStyle","coreStyle","ADMESH_STYLES","stylesInjected","useAdMeshStyles","styleElement","getRecommendationLabel","recommendation","matchScore","customLabels","getLabelTooltip","_label","AdMeshProductCard","variation","content","title","description","ctaText","cardClasses","cardStyle","_b","_c","_d","_e","_f","_g","_h","_i","_j","_l","_k","_m","_n","_o","_p","_r","_q","_s","_u","_t","_v","_w","_x","_y","_z","prop","index","AdMeshContext","React","useAdMeshContext","context","useAdMesh","extractCTAText","bridgePrompt","extractedProduct","productPatterns","pattern","match","product","setupMatch","AdMeshBridgeFormat","onLinkClick","onPasteToInput","bridgeHeadline","bridgeDescription","sdk","contextSessionId","effectiveSessionId","apiBaseUrl","shouldShowCTA","AdMeshSummaryLayout","summaryText","validRecs","rec","summary","renderContent","firstRec","hasBridgePrompt","format","preferredFormat","hasBridgeFormat","AdMeshLayout","recs","PlusIcon","size","AdMeshFollowup","onExecuteQuery","followupQuery","followupEngagementUrl","followupExposureUrl","handleFollowupClick","AdMeshTracker","__publicField","timeoutId","cleanupTimeout","threshold","engagementUrl","AdMeshRenderer","options","container","existingRoot","root","ReactDOM","tailSummary","containerId","AdMeshSDK","timestamp","random","aipResponse","renderer","tracker","params","url","messageId","turnIndex","devicePlatform","formFactor","jsonBody","errorMessage","responseAny","creative","headlineFromCreative","bridgeHeadlineFromCreative","bridgeDescriptionFromCreative","bridgePromptFromCreative","ctaLabelFromCreative","WeaveResponseProcessor","optimizedLinks","onExposurePixel","detectedLinks","links","clickUrlMap","r","brandUrlMap","redirectUrl","normalizedUrl","href","linkKey","actualHref","normalizedHref","detectedLink","childElements","child","prevNode","text","tagName","nextNode","parentNode","subLabel","isTooltipVisible","closeTooltipOnClickOutside","nextSibling","domain","AdMeshProvider","apiKey","language","geo_country","userId","model","messages","sdkRef","processedMessageIds","setProcessedMessageIds","contextValue","updated","AdMeshRecommendations","onRecommendationsShown","onError","query","_followups_container_id","_onExecuteQuery","onFollowupDetected","isContainerReady","setRecommendation","detectedFormat","setDetectedFormat","isLoading","setIsLoading","fetchedMessageIdRef","isFetchingRef","onRecommendationsShownRef","onErrorRef","onFollowupDetectedRef","convertAIPResponseToRecommendation","formatFromResponse","followupQueryTopLevel","followupQueryInCreative","followupEngagementUrlTopLevel","followupEngagementUrlInCreative","followupExposureUrlTopLevel","followupExposureUrlInCreative","followupContainer","setFollowupContainer","attempts","maxAttempts","checkForContainer","interval","convertedRecommendation","selectedFormat","renderFollowupPortal","createPortal","formatFromRec","preferredFormatFromRec","WeaveFallbackRecommendations","fallback","previousRecommendations","containerRef","setContainerId","hasPreviousRecs","sdkAny","convertMethod","getRendererMethod","getTrackerMethod","sdkTheme","WeaveAdFormatContext","createContext","WeaveAdFormatProvider","shouldRenderFallback","useWeaveAdFormatContext","useContext","AdMeshEcommerceCards","showTitle","cardClassName","onProductClick","maxCards","cardWidth","borderRadius","shadow","displayItems","getCardWidthClass","getBorderRadiusClass","getShadowClass","getThemeClasses","handleProductClick","item","itemId","idx","cat","AdMeshInlineCard","expandable","badgeTypeVariants","badgeTypeIcons","AdMeshBadge","type","variant","effectiveVariant","icon","badgeClasses","STREAMING_START_EVENT","STREAMING_COMPLETE_EVENT","dispatchStreamingStartEvent","detail","dispatchStreamingCompleteEvent","metadata","onStreamingStart","callback","handler","customEvent","onStreamingComplete","createInlineExposureTracker","firedKeys","activeTrackers","cleanupTracker","linkElement","logPrefix","dedupeKey","trackerState","fireExposurePixel","FinalLinkDetectionCheck","onLinksFound","onNoLinksFound","checkComplete","setCheckComplete","waitingForStreamEnd","setWaitingForStreamEnd","linksFound","setLinksFound","exposureTrackerRef","onLinksFoundRef","onNoLinksFoundRef","containerIdRef","sessionIdRef","queryRef","trackExposurePixel","performFinalCheck","weaveProcessor","recsToUse","eventReceived","cleanup","WeaveAdFormatContainer","fallbackFormat","onLinksDetected","onNoLinksDetected","onFallbackChange","onWeaveAttempt","onWeaveOutcome","scannedRef","weaveProcessorRef","setRecommendations","recommendationsRef","recommendationWithFollowup","setRecommendationWithFollowup","recommendationWithFollowupData","resolvedFormat","isWeaveFormat","count","useWeaveAdFormat","processingRef","detectedLinksRef","linksFoundRef","callbackFiredRef","debounceTimerRef","trackInlineExposure","lastMutationTimeRef","mutationSilenceCountRef","silenceCheckIntervalRef","mutationTimestampsRef","adaptiveTimeoutRef","processWeaveFormat","hasLinks","silenceCheckInterval","timeSinceLastMutation","SILENCE_THRESHOLD","maxFallbackTimeout","calculateAdaptiveTimeout","timestamps","intervals","avgInterval","a","b","adaptiveTimeout","VERSION"],"mappings":"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,KAAK,YAAYG,CAAS,CACrC,EACF,ECxVMC,GAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","x_google_ignoreList":[5]}
1
+ {"version":3,"file":"index.js","sources":["../src/utils/logger.ts","../src/utils/viewabilityTracker.ts","../src/hooks/useViewabilityTracker.ts","../src/components/AdMeshViewabilityTracker.tsx","../src/components/AdMeshTailAd.tsx","../../node_modules/classnames/index.js","../src/hooks/useAdMeshTracker.ts","../src/components/AdMeshLinkTracker.tsx","../src/utils/styleInjection.ts","../src/hooks/useAdMeshStyles.ts","../src/utils/disclosureUtils.ts","../src/components/AdMeshProductCard.tsx","../src/context/AdMeshContext.ts","../src/hooks/useAdMesh.ts","../src/components/AdMeshBridgeFormat.tsx","../src/components/AdMeshSummaryLayout.tsx","../src/components/AdMeshLayout.tsx","../src/components/AdMeshFollowup.tsx","../src/sdk/AdMeshTracker.ts","../src/sdk/AdMeshRenderer.tsx","../src/sdk/AdMeshSDK.ts","../src/sdk/WeaveResponseProcessor.ts","../src/context/AdMeshProvider.tsx","../src/components/AdMeshRecommendations.tsx","../src/components/WeaveFallbackRecommendations.tsx","../src/context/WeaveAdFormatContext.tsx","../src/components/AdMeshEcommerceCards.tsx","../src/components/AdMeshInlineCard.tsx","../src/components/AdMeshBadge.tsx","../src/utils/streamingEvents.ts","../src/utils/inlineExposureTracker.ts","../src/components/WeaveAdFormatContainer.tsx","../src/hooks/useWeaveAdFormat.ts","../src/index.ts"],"sourcesContent":["/**\n * Logger utility for AdMesh UI SDK\n * Disables all logs in production environment\n */\n\n// Check for production environment\n// Supports both Vite (import.meta.env) and standard Node.js (process.env)\nlet isProduction = false;\ntry {\n // Check for Vite's import.meta.env (only available in ESM modules)\n if (typeof (globalThis as any).importMeta !== 'undefined' && (globalThis as any).importMeta.env?.PROD) {\n isProduction = true;\n }\n} catch (e) {\n // import.meta not available, continue with other checks\n}\n\nif (!isProduction) {\n isProduction = \n (typeof process !== 'undefined' && process.env.NODE_ENV === 'production') ||\n (typeof process !== 'undefined' && process.env.ADMESH_ENV === 'production');\n}\n\nexport const logger = {\n log: (...args: any[]) => {\n if (!isProduction) {\n console.log(...args);\n }\n },\n \n warn: (...args: any[]) => {\n if (!isProduction) {\n console.warn(...args);\n }\n },\n \n error: (...args: any[]) => {\n // Errors are always logged, even in production, as they're critical\n console.error(...args);\n },\n \n info: (...args: any[]) => {\n if (!isProduction) {\n console.info(...args);\n }\n },\n \n debug: (...args: any[]) => {\n if (!isProduction) {\n console.debug(...args);\n }\n },\n};\n\n","/**\n * AdMesh UI SDK - MRC Viewability Tracker Utilities\n * Implements Media Rating Council (MRC) viewability standards\n */\n\nimport type {\n MRCViewabilityStandards,\n DeviceType,\n ViewabilityContextMetrics,\n ViewabilityAnalyticsEvent\n} from '../types/analytics';\nimport { logger } from './logger';\n\n/**\n * Calculate MRC viewability standards based on ad size\n */\nexport function calculateMRCStandards(\n adWidth: number,\n adHeight: number,\n customStandards?: Partial<MRCViewabilityStandards>\n): MRCViewabilityStandards {\n const adPixels = adWidth * adHeight;\n const isLargeAd = adPixels > 242500; // MRC threshold for large ads\n\n const defaults: MRCViewabilityStandards = {\n visibilityThreshold: isLargeAd ? 0.3 : 0.5, // 30% for large, 50% for standard\n minimumDuration: 1000, // 1 second in milliseconds\n isLargeAd\n };\n\n return { ...defaults, ...customStandards };\n}\n\n/**\n * Detect device type based on viewport width\n */\nexport function detectDeviceType(viewportWidth: number): DeviceType {\n if (viewportWidth < 768) return 'mobile';\n if (viewportWidth < 1024) return 'tablet';\n return 'desktop';\n}\n\n/**\n * Calculate visibility percentage of element in viewport\n */\nexport function calculateVisibilityPercentage(element: HTMLElement): number {\n const rect = element.getBoundingClientRect();\n const viewportHeight = window.innerHeight || document.documentElement.clientHeight;\n const viewportWidth = window.innerWidth || document.documentElement.clientWidth;\n\n // Element dimensions\n const elementHeight = rect.height;\n const elementWidth = rect.width;\n\n if (elementHeight === 0 || elementWidth === 0) return 0;\n\n // Calculate visible portion\n const visibleTop = Math.max(0, rect.top);\n const visibleBottom = Math.min(viewportHeight, rect.bottom);\n const visibleLeft = Math.max(0, rect.left);\n const visibleRight = Math.min(viewportWidth, rect.right);\n\n const visibleHeight = Math.max(0, visibleBottom - visibleTop);\n const visibleWidth = Math.max(0, visibleRight - visibleLeft);\n\n const visibleArea = visibleHeight * visibleWidth;\n const totalArea = elementHeight * elementWidth;\n\n return totalArea > 0 ? (visibleArea / totalArea) : 0;\n}\n\n/**\n * Calculate current scroll depth as percentage\n */\nexport function calculateScrollDepth(): number {\n const windowHeight = window.innerHeight;\n const documentHeight = document.documentElement.scrollHeight;\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n\n const scrollableHeight = documentHeight - windowHeight;\n if (scrollableHeight <= 0) return 100;\n\n return Math.min(100, (scrollTop / scrollableHeight) * 100);\n}\n\n/**\n * Get element position on page\n */\nexport function getElementPosition(element: HTMLElement): { top: number; left: number } {\n const rect = element.getBoundingClientRect();\n const scrollTop = window.pageYOffset || document.documentElement.scrollTop;\n const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;\n\n return {\n top: rect.top + scrollTop,\n left: rect.left + scrollLeft\n };\n}\n\n/**\n * Collect context metrics\n */\nexport function collectContextMetrics(element: HTMLElement): ViewabilityContextMetrics {\n const rect = element.getBoundingClientRect();\n const position = getElementPosition(element);\n const viewportWidth = window.innerWidth || document.documentElement.clientWidth;\n const viewportHeight = window.innerHeight || document.documentElement.clientHeight;\n\n // Detect dark mode\n const isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;\n\n return {\n pageUrl: window.location.href,\n pageTitle: document.title,\n referrer: document.referrer,\n deviceType: detectDeviceType(viewportWidth),\n viewportWidth,\n viewportHeight,\n adWidth: rect.width,\n adHeight: rect.height,\n adPositionTop: position.top,\n adPositionLeft: position.left,\n isDarkMode,\n language: navigator.language,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone\n };\n}\n\n/**\n * Generate unique session ID for INTERNAL viewability tracking only.\n * \n * IMPORTANT: This is NOT the main sessionId used for recommendations.\n * This is only used internally by the viewability tracker for tracking\n * viewability events. The main sessionId MUST be provided by the platform\n * and passed to AdMeshProvider and SDK methods.\n */\nexport function generateSessionId(): string {\n return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Generate unique batch ID\n */\nexport function generateBatchId(): string {\n return `batch_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n}\n\n/**\n * Check if ad meets MRC viewability threshold\n */\nexport function meetsViewabilityThreshold(\n visibilityPercentage: number,\n visibleDuration: number,\n standards: MRCViewabilityStandards\n): boolean {\n return (\n visibilityPercentage >= standards.visibilityThreshold &&\n visibleDuration >= standards.minimumDuration\n );\n}\n\n/**\n * Format timestamp to ISO 8601\n */\nexport function formatTimestamp(date: Date = new Date()): string {\n return date.toISOString();\n}\n\n/**\n * Calculate average from array of numbers\n */\nexport function calculateAverage(numbers: number[]): number {\n if (numbers.length === 0) return 0;\n const sum = numbers.reduce((acc, num) => acc + num, 0);\n return sum / numbers.length;\n}\n\n/**\n * Debounce function for performance optimization\n */\nexport function debounce<T extends (...args: unknown[]) => unknown>(\n func: T,\n wait: number\n): (...args: Parameters<T>) => void {\n let timeout: NodeJS.Timeout | null = null;\n\n return function executedFunction(...args: Parameters<T>) {\n const later = () => {\n timeout = null;\n func(...args);\n };\n\n if (timeout) clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n}\n\n/**\n * Throttle function for performance optimization\n */\nexport function throttle<T extends (...args: unknown[]) => unknown>(\n func: T,\n limit: number\n): (...args: Parameters<T>) => void {\n let inThrottle: boolean;\n\n return function executedFunction(...args: Parameters<T>) {\n if (!inThrottle) {\n func(...args);\n inThrottle = true;\n setTimeout(() => (inThrottle = false), limit);\n }\n };\n}\n\n/**\n * Send analytics event to API\n *\n * NOTE: If apiEndpoint is empty, the event is silently discarded (no error).\n * This allows the SDK to collect analytics without sending them to a backend.\n */\nexport async function sendAnalyticsEvent(\n event: ViewabilityAnalyticsEvent,\n apiEndpoint: string,\n retryAttempts: number = 3,\n retryDelay: number = 1000\n): Promise<boolean> {\n // If no endpoint is configured, silently skip sending\n if (!apiEndpoint || apiEndpoint.trim() === '') {\n return true;\n }\n\n for (let attempt = 0; attempt < retryAttempts; attempt++) {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(event),\n keepalive: true\n });\n\n if (response.ok) {\n return true;\n }\n\n // Log error details for debugging\n await response.text().catch(() => '');\n } catch (error) {\n // Error caught, will retry\n }\n\n // Wait before retry (exponential backoff)\n if (attempt < retryAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));\n }\n }\n\n logger.error('[AdMesh Viewability] Failed to send analytics event');\n return false;\n}\n\n/**\n * Send batched analytics events to API\n *\n * NOTE: If apiEndpoint is empty, the batch is silently discarded (no error).\n * This allows the SDK to collect analytics without sending them to a backend.\n */\nexport async function sendAnalyticsBatch(\n events: ViewabilityAnalyticsEvent[],\n sessionId: string,\n apiEndpoint: string,\n retryAttempts: number = 3,\n retryDelay: number = 1000\n): Promise<boolean> {\n if (events.length === 0) return true;\n\n // If no endpoint is configured, silently skip sending\n if (!apiEndpoint || apiEndpoint.trim() === '') {\n return true;\n }\n\n const batch = {\n batchId: generateBatchId(),\n sessionId,\n createdAt: formatTimestamp(),\n events,\n eventCount: events.length\n };\n\n for (let attempt = 0; attempt < retryAttempts; attempt++) {\n try {\n const response = await fetch(apiEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify(batch),\n keepalive: true\n });\n\n if (response.ok) {\n return true;\n }\n\n // Log error details for debugging\n await response.text().catch(() => '');\n } catch (error) {\n // Error caught, will retry\n }\n\n // Wait before retry (exponential backoff)\n if (attempt < retryAttempts - 1) {\n await new Promise(resolve => setTimeout(resolve, retryDelay * Math.pow(2, attempt)));\n }\n }\n\n logger.error('[AdMesh Viewability] Failed to send analytics batch');\n return false;\n}\n\n/**\n * Sanitize URL to remove PII (query parameters, fragments)\n */\nexport function sanitizeUrl(url: string): string {\n try {\n const urlObj = new URL(url);\n // Remove query parameters and hash\n return `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`;\n } catch {\n return url;\n }\n}\n\n/**\n * Check if element is in viewport\n */\nexport function isElementInViewport(element: HTMLElement): boolean {\n const rect = element.getBoundingClientRect();\n return (\n rect.top < (window.innerHeight || document.documentElement.clientHeight) &&\n rect.bottom > 0 &&\n rect.left < (window.innerWidth || document.documentElement.clientWidth) &&\n rect.right > 0\n );\n}\n","/**\n * AdMesh UI SDK - MRC Viewability Tracker Hook\n * React hook for tracking ad viewability according to MRC standards\n */\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\nimport { logger } from '../utils/logger';\nimport type {\n ViewabilityTrackerConfig,\n ViewabilityTrackerState,\n ViewabilityAnalyticsEvent,\n ViewabilityEventType,\n MRCViewabilityStandards\n} from '../types/analytics';\nimport {\n calculateMRCStandards,\n calculateVisibilityPercentage,\n calculateScrollDepth,\n collectContextMetrics,\n generateSessionId,\n meetsViewabilityThreshold,\n formatTimestamp,\n calculateAverage,\n throttle\n} from '../utils/viewabilityTracker';\n\n// Default configuration\nconst DEFAULT_CONFIG: ViewabilityTrackerConfig = {\n enabled: true,\n // Analytics endpoint disabled - no analytics will be sent\n apiEndpoint: '', // Empty string disables analytics sending\n enableBatching: false, // Disabled since no endpoint\n batchSize: 10,\n batchTimeout: 5000, // 5 seconds\n debug: false,\n enableRetry: false,\n maxRetries: 3,\n retryDelay: 1000\n};\n\n// Global config that can be set by consuming application\nlet globalConfig: ViewabilityTrackerConfig = DEFAULT_CONFIG;\n\n// TEMPORARY: Global flag to disable all analytics sending\n// Set this to true to prevent any viewability analytics from being sent to the backend\n// This is a temporary measure and can be easily reverted by setting to false\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nlet ANALYTICS_DISABLED = false;\n\nexport const setViewabilityTrackerConfig = (config: Partial<ViewabilityTrackerConfig>) => {\n globalConfig = { ...globalConfig, ...config };\n};\n\n/**\n * TEMPORARY: Disable/enable all viewability analytics sending\n * @param disabled - Set to true to disable analytics, false to enable\n *\n * Usage:\n * disableViewabilityAnalytics(true); // Disable all analytics\n * disableViewabilityAnalytics(false); // Re-enable analytics\n */\nexport const disableViewabilityAnalytics = (disabled: boolean) => {\n ANALYTICS_DISABLED = disabled;\n if (disabled) {\n logger.warn('[AdMesh Viewability] Analytics sending is DISABLED - no data will be sent to backend');\n } else {\n logger.log('[AdMesh Viewability] Analytics sending is ENABLED');\n }\n};\n\ninterface UseViewabilityTrackerProps {\n /** Product ID */\n productId?: string;\n /** Offer ID */\n offerId?: string;\n /** Agent ID */\n agentId?: string;\n /** Recommendation ID (from recommendations collection) */\n recommendationId: string;\n /** HTML element to track */\n elementRef: React.RefObject<HTMLElement>;\n /** Custom configuration */\n config?: Partial<ViewabilityTrackerConfig>;\n}\n\nexport function useViewabilityTracker({\n productId,\n offerId,\n agentId,\n recommendationId,\n elementRef,\n config: customConfig\n}: UseViewabilityTrackerProps): ViewabilityTrackerState {\n const config = { ...globalConfig, ...customConfig };\n\n // Session ID (persists for component lifetime)\n const sessionId = useRef(generateSessionId());\n\n // State\n const [state, setState] = useState<ViewabilityTrackerState>({\n isVisible: false,\n isViewable: false,\n visibilityPercentage: 0,\n timeMetrics: {\n loadedAt: formatTimestamp(),\n totalVisibleDuration: 0,\n totalViewableDuration: 0,\n totalHoverDuration: 0,\n totalFocusDuration: 0\n },\n engagementMetrics: {\n currentScrollDepth: 0,\n viewportEnterCount: 0,\n viewportExitCount: 0,\n hoverCount: 0,\n wasClicked: false,\n maxVisibilityPercentage: 0,\n averageVisibilityPercentage: 0\n },\n isTracking: config.enabled\n });\n\n // Refs for tracking\n const mrcStandards = useRef<MRCViewabilityStandards | null>(null);\n const visibilityStartTime = useRef<number | null>(null);\n const viewableStartTime = useRef<number | null>(null);\n const hoverStartTime = useRef<number | null>(null);\n const focusStartTime = useRef<number | null>(null);\n const visibilityPercentages = useRef<number[]>([]);\n const eventBatch = useRef<ViewabilityAnalyticsEvent[]>([]);\n const batchTimeout = useRef<NodeJS.Timeout | null>(null);\n\n // Log helper\n const log = useCallback((message: string) => {\n if (config.debug) {\n logger.log(`[AdMesh Viewability] ${message}`);\n }\n }, [config.debug]);\n\n // Send event (analytics disabled - no events sent to backend)\n // Viewability tracking still works for exposure pixels (handled separately)\n const sendEvent = useCallback(async (eventType: ViewabilityEventType, additionalData?: Record<string, unknown>) => {\n if (!config.enabled || !elementRef.current || !mrcStandards.current) return;\n\n // Analytics disabled - no events sent to backend\n // Viewability tracking still works internally for exposure pixel firing\n log(`Analytics disabled - skipping event: ${eventType}`);\n \n // Call custom callback if provided (for local tracking)\n if (config.onEvent) {\n const contextMetrics = collectContextMetrics(elementRef.current);\n const event: ViewabilityAnalyticsEvent = {\n eventType,\n timestamp: formatTimestamp(),\n sessionId: sessionId.current,\n productId,\n offerId,\n agentId,\n recommendationId,\n timeMetrics: state.timeMetrics,\n engagementMetrics: state.engagementMetrics,\n contextMetrics,\n mrcStandards: mrcStandards.current,\n isViewable: state.isViewable,\n metadata: additionalData\n };\n config.onEvent(event);\n }\n }, [config, productId, offerId, agentId, recommendationId, elementRef, state, log]);\n\n // Flush event batch (disabled - analytics not sent)\n const flushBatch = useCallback(async () => {\n if (eventBatch.current.length === 0) return;\n\n // Analytics disabled - clear batch without sending\n log('Analytics disabled - clearing batch without sending');\n eventBatch.current = [];\n if (batchTimeout.current) {\n clearTimeout(batchTimeout.current);\n batchTimeout.current = null;\n }\n return;\n }, [log]);\n\n // Update visibility\n const updateVisibility = useCallback(throttle(() => {\n if (!elementRef.current) return;\n\n const visibilityPercentage = calculateVisibilityPercentage(elementRef.current);\n const now = Date.now();\n const loadTime = new Date(state.timeMetrics.loadedAt).getTime();\n\n setState(prev => {\n const newState = { ...prev };\n\n // Track visibility percentages for average calculation\n if (visibilityPercentage > 0) {\n visibilityPercentages.current.push(visibilityPercentage);\n }\n\n // Update visibility state\n const wasVisible = prev.isVisible;\n const isNowVisible = visibilityPercentage > 0;\n\n if (isNowVisible && !wasVisible) {\n // Became visible\n visibilityStartTime.current = now;\n newState.engagementMetrics.viewportEnterCount++;\n\n if (!newState.timeMetrics.timeToFirstVisible) {\n newState.timeMetrics.timeToFirstVisible = now - loadTime;\n newState.engagementMetrics.scrollDepthAtFirstVisible = calculateScrollDepth();\n sendEvent('ad_visible');\n }\n } else if (!isNowVisible && wasVisible) {\n // Became hidden\n if (visibilityStartTime.current) {\n const visibleDuration = now - visibilityStartTime.current;\n newState.timeMetrics.totalVisibleDuration += visibleDuration;\n visibilityStartTime.current = null;\n }\n newState.engagementMetrics.viewportExitCount++;\n sendEvent('ad_hidden');\n } else if (isNowVisible && wasVisible && visibilityStartTime.current) {\n // Still visible, update duration\n const visibleDuration = now - visibilityStartTime.current;\n newState.timeMetrics.totalVisibleDuration += visibleDuration;\n visibilityStartTime.current = now;\n }\n\n newState.isVisible = isNowVisible;\n newState.visibilityPercentage = visibilityPercentage;\n\n // Update max visibility\n if (visibilityPercentage > newState.engagementMetrics.maxVisibilityPercentage) {\n newState.engagementMetrics.maxVisibilityPercentage = visibilityPercentage;\n }\n\n // Update average visibility\n if (visibilityPercentages.current.length > 0) {\n newState.engagementMetrics.averageVisibilityPercentage = calculateAverage(visibilityPercentages.current);\n }\n\n // Check MRC viewability threshold\n if (mrcStandards.current) {\n const wasViewable = prev.isViewable;\n const isNowViewable = meetsViewabilityThreshold(\n visibilityPercentage,\n newState.timeMetrics.totalVisibleDuration,\n mrcStandards.current\n );\n\n if (isNowViewable && !wasViewable) {\n // Met viewability threshold\n newState.isViewable = true;\n newState.timeMetrics.timeToViewable = now - loadTime;\n viewableStartTime.current = now;\n sendEvent('ad_viewable');\n } else if (isNowViewable && wasViewable && viewableStartTime.current) {\n // Still viewable, update duration\n const viewableDuration = now - viewableStartTime.current;\n newState.timeMetrics.totalViewableDuration += viewableDuration;\n viewableStartTime.current = now;\n }\n }\n\n // Update scroll depth\n newState.engagementMetrics.currentScrollDepth = calculateScrollDepth();\n\n return newState;\n });\n }, 100), [elementRef, state.timeMetrics.loadedAt, sendEvent]);\n\n // Initialize MRC standards\n useEffect(() => {\n if (!elementRef.current) return;\n\n const rect = elementRef.current.getBoundingClientRect();\n mrcStandards.current = calculateMRCStandards(rect.width, rect.height, config.mrcStandards);\n\n log('Initialized MRC standards');\n sendEvent('ad_loaded');\n }, [elementRef, config.mrcStandards, log, sendEvent]);\n\n // Set up Intersection Observer\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach(() => {\n updateVisibility();\n });\n },\n {\n threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],\n rootMargin: '0px'\n }\n );\n\n observer.observe(elementRef.current);\n\n return () => {\n observer.disconnect();\n };\n }, [config.enabled, elementRef, updateVisibility]);\n\n // Track scroll events\n useEffect(() => {\n if (!config.enabled) return;\n\n const handleScroll = throttle(() => {\n updateVisibility();\n }, 100);\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n return () => window.removeEventListener('scroll', handleScroll);\n }, [config.enabled, updateVisibility]);\n\n // Track hover events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleMouseEnter = () => {\n hoverStartTime.current = Date.now();\n setState(prev => ({\n ...prev,\n engagementMetrics: {\n ...prev.engagementMetrics,\n hoverCount: prev.engagementMetrics.hoverCount + 1\n }\n }));\n sendEvent('ad_hover_start');\n };\n\n const handleMouseLeave = () => {\n if (hoverStartTime.current) {\n const hoverDuration = Date.now() - hoverStartTime.current;\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n totalHoverDuration: prev.timeMetrics.totalHoverDuration + hoverDuration\n }\n }));\n hoverStartTime.current = null;\n sendEvent('ad_hover_end', { hoverDuration });\n }\n };\n\n element.addEventListener('mouseenter', handleMouseEnter);\n element.addEventListener('mouseleave', handleMouseLeave);\n\n return () => {\n element.removeEventListener('mouseenter', handleMouseEnter);\n element.removeEventListener('mouseleave', handleMouseLeave);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Track focus events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleFocus = () => {\n focusStartTime.current = Date.now();\n sendEvent('ad_focus');\n };\n\n const handleBlur = () => {\n if (focusStartTime.current) {\n const focusDuration = Date.now() - focusStartTime.current;\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n totalFocusDuration: prev.timeMetrics.totalFocusDuration + focusDuration\n }\n }));\n focusStartTime.current = null;\n sendEvent('ad_blur', { focusDuration });\n }\n };\n\n element.addEventListener('focus', handleFocus);\n element.addEventListener('blur', handleBlur);\n\n return () => {\n element.removeEventListener('focus', handleFocus);\n element.removeEventListener('blur', handleBlur);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Track click events\n useEffect(() => {\n if (!config.enabled || !elementRef.current) return;\n\n const element = elementRef.current;\n\n const handleClick = () => {\n setState(prev => ({\n ...prev,\n engagementMetrics: {\n ...prev.engagementMetrics,\n wasClicked: true\n }\n }));\n sendEvent('ad_click');\n };\n\n element.addEventListener('click', handleClick);\n\n return () => {\n element.removeEventListener('click', handleClick);\n };\n }, [config.enabled, elementRef, sendEvent]);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n // Calculate session duration\n const now = Date.now();\n const loadTime = new Date(state.timeMetrics.loadedAt).getTime();\n const sessionDuration = now - loadTime;\n\n setState(prev => ({\n ...prev,\n timeMetrics: {\n ...prev.timeMetrics,\n sessionDuration\n }\n }));\n\n // Send final event\n sendEvent('ad_unloaded', { sessionDuration });\n\n // Flush any remaining batched events\n flushBatch();\n };\n }, []);\n\n return state;\n}\n","/**\n * AdMesh Viewability Tracker Component\n * Wraps any ad component with MRC viewability tracking\n */\n\nimport React, { useRef, useEffect } from 'react';\nimport { logger } from '../utils/logger';\nimport { useViewabilityTracker } from '../hooks/useViewabilityTracker';\nimport type { ViewabilityTrackerConfig } from '../types/analytics';\n\nexport interface AdMeshViewabilityTrackerProps {\n /** Product ID */\n productId?: string;\n /** Offer ID */\n offerId?: string;\n /** Agent ID */\n agentId?: string;\n /** Recommendation ID (for exposure tracking) */\n recommendationId: string;\n /** Exposure URL (for MRC-compliant exposure pixel firing) */\n exposureUrl?: string;\n /** Session ID (for exposure tracking) */\n sessionId?: string;\n /** Children to wrap with viewability tracking */\n children: React.ReactNode;\n /** Custom viewability tracker configuration */\n config?: Partial<ViewabilityTrackerConfig>;\n /** CSS class name */\n className?: string;\n /** Inline styles */\n style?: React.CSSProperties;\n /** Callback when viewability state changes */\n onViewabilityChange?: (isViewable: boolean) => void;\n /** Callback when ad becomes visible */\n onVisible?: () => void;\n /** Callback when ad becomes viewable (meets MRC threshold) */\n onViewable?: () => void;\n /** Callback when ad is clicked */\n onClick?: () => void;\n}\n\n/**\n * AdMeshViewabilityTracker Component\n * \n * Wraps ad components with comprehensive MRC viewability tracking.\n * Automatically tracks:\n * - Viewability (50% visible for 1 second)\n * - Time metrics (time to viewable, total visible duration, etc.)\n * - Engagement metrics (hover, focus, clicks, scroll depth)\n * - Context metrics (device type, viewport size, ad position)\n * \n * @example\n * ```tsx\n * <AdMeshViewabilityTracker\n * recommendationId=\"rec_123\"\n * productId=\"prod_456\"\n * offerId=\"offer_789\"\n * onViewable={() => logger.log('Ad is viewable!')}\n * >\n * <YourAdComponent />\n * </AdMeshViewabilityTracker>\n * ```\n */\nexport const AdMeshViewabilityTracker: React.FC<AdMeshViewabilityTrackerProps> = ({\n productId,\n offerId,\n agentId,\n recommendationId,\n exposureUrl,\n sessionId,\n children,\n config,\n className,\n style,\n onViewabilityChange,\n onVisible,\n onViewable,\n onClick\n}) => {\n const elementRef = useRef<HTMLElement>(null);\n const exposureFired = useRef(false);\n\n // Use viewability tracker hook\n const viewabilityState = useViewabilityTracker({\n productId,\n offerId,\n agentId,\n recommendationId,\n elementRef: elementRef as React.RefObject<HTMLElement>,\n config\n });\n\n // Track viewability changes and fire exposure pixel\n const previousViewable = useRef(viewabilityState.isViewable);\n\n useEffect(() => {\n if (viewabilityState.isViewable !== previousViewable.current) {\n previousViewable.current = viewabilityState.isViewable;\n\n if (onViewabilityChange) {\n onViewabilityChange(viewabilityState.isViewable);\n }\n\n if (viewabilityState.isViewable && onViewable) {\n onViewable();\n }\n\n // Fire exposure pixel when ad becomes viewable (MRC-compliant)\n // Only fire if we have the required data and haven't fired yet\n if (viewabilityState.isViewable && !exposureFired.current) {\n logger.log('[AdMeshViewabilityTracker] 🎯 Ad is viewable, checking exposure pixel requirements:', {\n exposureUrl: exposureUrl ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING',\n recommendationId\n });\n\n if (exposureUrl && sessionId) {\n exposureFired.current = true;\n\n logger.log('[AdMeshViewabilityTracker] 🔥 Firing exposure pixel:', exposureUrl);\n\n // Fire the exposure pixel using fetch with keepalive\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n logger.log('[AdMesh] ✅ Exposure pixel fired successfully');\n })\n .catch((error) => {\n logger.warn('[AdMesh] ⚠️ Failed to fire exposure pixel:', error);\n // Reset flag to allow retry\n exposureFired.current = false;\n });\n } else {\n logger.warn('[AdMeshViewabilityTracker] ⚠️ Cannot fire exposure pixel - missing required data:', {\n hasExposureUrl: !!exposureUrl,\n hasSessionId: !!sessionId\n });\n }\n }\n }\n }, [viewabilityState.isViewable, onViewabilityChange, onViewable, exposureUrl, sessionId, recommendationId]);\n\n // Track visibility changes\n const previousVisible = useRef(viewabilityState.isVisible);\n \n useEffect(() => {\n if (viewabilityState.isVisible !== previousVisible.current) {\n previousVisible.current = viewabilityState.isVisible;\n \n if (viewabilityState.isVisible && onVisible) {\n onVisible();\n }\n }\n }, [viewabilityState.isVisible, onVisible]);\n\n // Handle click\n const handleClick = () => {\n if (onClick) {\n onClick();\n }\n \n // Allow event to propagate to children\n };\n\n return (\n <div\n ref={elementRef as React.RefObject<HTMLDivElement>}\n className={className}\n style={style}\n onClick={handleClick}\n data-admesh-viewability-tracker\n data-recommendation-id={recommendationId}\n data-is-viewable={viewabilityState.isViewable}\n data-is-visible={viewabilityState.isVisible}\n data-visibility-percentage={viewabilityState.visibilityPercentage.toFixed(2)}\n >\n {children}\n </div>\n );\n};\n\nAdMeshViewabilityTracker.displayName = 'AdMeshViewabilityTracker';\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshTailAdProps {\n summaryText?: string; // The tail_summary from backend response (optional, not used in new UI)\n recommendations: AdMeshRecommendation[]; // Full recommendation objects\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n sessionId?: string;\n}\n\n// Utility function to validate and normalize URLs\nconst isValidUrl = (url: string): boolean => {\n try {\n new URL(url);\n return true;\n } catch {\n return false;\n }\n};\n\n// Helper function to get CTA label from backend\nconst getCTALabel = (ctaLabel?: string): string => {\n // Use provided CTA label from backend if available\n if (ctaLabel && ctaLabel.trim()) {\n return ctaLabel.trim();\n }\n\n // Return empty string if no CTA label provided (will hide CTA button)\n return '';\n};\n\n// Process summary text with markdown links [Product Name](click_url) and brand name links\n// NOTE: This function is kept for backward compatibility but is no longer used in the new tail ad UI\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nconst processSummaryText = (_summaryText: string, _recommendations: AdMeshRecommendation[]): (string | React.ReactElement)[] => {\n // This function is no longer used - kept for backward compatibility only\n return [];\n};\n\nexport const AdMeshTailAd: React.FC<AdMeshTailAdProps> = ({\n recommendations,\n theme,\n className = '',\n style = {},\n sessionId\n}) => {\n // Validate inputs - return null if empty\n if (!recommendations || recommendations.length === 0) {\n logger.log('[AdMesh Tail Ad] No recommendations provided - not rendering');\n return null;\n }\n\n // Get the first recommendation's data for CTA and tracking\n const firstRecommendation = recommendations[0];\n const productId = firstRecommendation?.product_id;\n const exposureUrl = firstRecommendation?.exposure_url;\n const recommendationId = firstRecommendation?.recommendation_id || '';\n \n // Get data from creative_input for tail format\n const creativeInput = firstRecommendation?.creative_input || {};\n const shortDescription = creativeInput.short_description || '';\n const offerSummary = creativeInput.offer_summary || '';\n const brandName = creativeInput.brand_name || firstRecommendation?.title || '';\n const productName = creativeInput.product_name || '';\n \n // Get logo_url from assets\n const assets = creativeInput.assets || {};\n const logoUrl = assets.logo_url || '';\n \n // Get click URL - prioritize click_url from recommendation (this is the tracking URL we need)\n const clickUrl = firstRecommendation?.click_url || \n firstRecommendation?.admesh_link || \n creativeInput.cta_url ||\n firstRecommendation?.url;\n \n // Get CTA label from backend\n const ctaLabel = getCTALabel(creativeInput.cta_label);\n\n // For tail format, we need at least brand name\n // But we still validate that we have at least one recommendation with data\n if (!brandName) {\n logger.log('[AdMesh Tail Ad] No valid recommendation data provided - not rendering', {\n reason: 'brandName is missing',\n brandName,\n hasFirstRecommendation: !!firstRecommendation,\n hasCreativeInput: !!creativeInput,\n creativeInputBrandName: creativeInput.brand_name,\n recommendationTitle: firstRecommendation?.title,\n recommendationId,\n recommendationKeys: firstRecommendation ? Object.keys(firstRecommendation) : []\n });\n return null;\n }\n \n // Build headline parts with priority:\n // 1. If offer_summary exists: \"Brand — Offer Summary\"\n // 2. If product_name exists: \"Brand — Product Name\"\n // 3. Otherwise: just \"Brand\"\n let headlineText = brandName;\n let headlineSuffix = '';\n \n if (offerSummary) {\n headlineSuffix = offerSummary;\n } else if (productName) {\n headlineSuffix = productName;\n }\n \n if (headlineSuffix) {\n headlineText = `${brandName} — ${headlineSuffix}`;\n }\n\n logger.debug('[AdMeshTailAd] 📊 Rendering with tracking data:', {\n recommendationId,\n productId,\n exposureUrl: exposureUrl ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING',\n recommendationsCount: recommendations.length,\n clickUrl: clickUrl ? clickUrl : 'MISSING',\n clickUrlSource: firstRecommendation?.click_url ? 'click_url' : \n firstRecommendation?.admesh_link ? 'admesh_link' :\n creativeInput.cta_url ? 'cta_url' :\n firstRecommendation?.url ? 'url' : 'none',\n shortDescription: shortDescription ? 'present' : 'MISSING',\n offerSummary: offerSummary || 'MISSING',\n brandName,\n productName,\n headlineText\n });\n\n // Handler for tracking clicks on the entire tail ad\n const handleContainerClick = (source: string, e?: React.MouseEvent) => {\n if (e) {\n e.stopPropagation();\n }\n logger.log(`AdMesh tail ad ${source} clicked`);\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n recommendationId: firstRecommendation.recommendation_id,\n productId: firstRecommendation.product_id,\n clickUrl: clickUrl,\n source: source\n }).catch(() => {\n logger.error(`[AdMesh] Failed to track ${source} click`);\n });\n }\n };\n\n // Handler for brand name link click\n const handleBrandNameClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n handleContainerClick('tail_ad_brand_name');\n };\n\n // Handler for CTA link click\n const handleCTAClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n handleContainerClick('tail_ad_cta');\n };\n\n // Handler for logo click\n const handleLogoClick = (e: React.MouseEvent) => {\n e.stopPropagation();\n handleContainerClick('tail_ad_logo');\n };\n\n // State for logo load error\n const [logoError, setLogoError] = React.useState(false);\n\n // Get first letter of brand name for fallback\n const brandInitial = brandName ? brandName.charAt(0).toUpperCase() : 'B';\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendationId}\n exposureUrl={exposureUrl}\n sessionId={sessionId}\n className={`admesh-tail-ad ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n <div \n className=\"tail-ad-container flex flex-row gap-3\"\n >\n {/* Left Section: Logo Area (10% width, stacks on mobile) */}\n {logoUrl && (\n <div \n className=\"flex-shrink-0 flex items-center justify-center pr-2\"\n style={{\n width: '10%',\n minWidth: '48px'\n }}\n >\n {!logoError && isValidUrl(logoUrl) ? (\n <a\n href={clickUrl || '#'}\n target={clickUrl ? \"_blank\" : undefined}\n rel={clickUrl ? \"noopener noreferrer\" : undefined}\n onClick={clickUrl ? handleLogoClick : undefined}\n className=\"block\"\n style={{ \n cursor: clickUrl ? 'pointer' : 'default',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n width: '100%',\n maxWidth: '64px'\n }}\n >\n <img\n src={logoUrl}\n alt={`${brandName} logo`}\n className=\"object-cover border border-gray-200 dark:border-gray-700\"\n style={{\n width: '100%',\n height: 'auto',\n maxWidth: '64px',\n maxHeight: '64px',\n objectFit: 'contain',\n borderRadius: '8px'\n }}\n onError={() => {\n setLogoError(true);\n logger.debug('[AdMesh Tail Ad] Logo failed to load, showing fallback');\n }}\n />\n </a>\n ) : (\n <div\n className=\"flex items-center justify-center text-lg font-semibold text-gray-600 dark:text-gray-300 bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700\"\n style={{\n width: '100%',\n maxWidth: '64px',\n aspectRatio: '1',\n borderRadius: '8px',\n minHeight: '48px'\n }}\n >\n {brandInitial}\n </div>\n )}\n </div>\n )}\n\n {/* Right Section: Content Area (90% width) */}\n <div \n className=\"flex-1\"\n style={{\n width: logoUrl ? '90%' : '100%',\n minWidth: 0 // Allow flex item to shrink below content size\n }}\n >\n {/* Headline: Brand Name (clickable link) — Offer Summary / Product Name / Brand only */}\n {headlineText && (\n <div className=\"mb-2\">\n <h3 className=\"text-black dark:text-white font-semibold text-base\">\n {clickUrl && brandName ? (\n <>\n <a\n href={clickUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-semibold\"\n style={{\n color: '#2563eb',\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={handleBrandNameClick}\n >\n {brandName}\n </a>\n {headlineSuffix && ` — ${headlineSuffix}`}\n </>\n ) : (\n headlineText\n )}\n </h3>\n </div>\n )}\n \n {/* Description: Full short_description */}\n {shortDescription && (\n <p className=\"text-gray-700 dark:text-gray-300 text-sm mb-3 leading-relaxed\">\n {shortDescription}\n </p>\n )}\n \n {/* CTA Link and Sponsored Label */}\n <div className=\"flex items-center justify-between mt-2\">\n {/* CTA Link - Left aligned (only show if ctaLabel is provided) */}\n {ctaLabel && clickUrl && (\n <a\n href={clickUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-medium text-sm\"\n style={{\n color: '#2563eb',\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={handleCTAClick}\n >\n {ctaLabel}\n </a>\n )}\n\n {/* Sponsored Label - Right aligned */}\n <p className=\"text-xs text-gray-500 dark:text-gray-400\">\n Sponsored\n </p>\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nexport default AdMeshTailAd;\n\n","/*!\n\tCopyright (c) 2018 Jed Watson.\n\tLicensed under the MIT License (MIT), see\n\thttp://jedwatson.github.io/classnames\n*/\n/* global define */\n\n(function () {\n\t'use strict';\n\n\tvar hasOwn = {}.hasOwnProperty;\n\n\tfunction classNames () {\n\t\tvar classes = '';\n\n\t\tfor (var i = 0; i < arguments.length; i++) {\n\t\t\tvar arg = arguments[i];\n\t\t\tif (arg) {\n\t\t\t\tclasses = appendClass(classes, parseValue(arg));\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction parseValue (arg) {\n\t\tif (typeof arg === 'string' || typeof arg === 'number') {\n\t\t\treturn arg;\n\t\t}\n\n\t\tif (typeof arg !== 'object') {\n\t\t\treturn '';\n\t\t}\n\n\t\tif (Array.isArray(arg)) {\n\t\t\treturn classNames.apply(null, arg);\n\t\t}\n\n\t\tif (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {\n\t\t\treturn arg.toString();\n\t\t}\n\n\t\tvar classes = '';\n\n\t\tfor (var key in arg) {\n\t\t\tif (hasOwn.call(arg, key) && arg[key]) {\n\t\t\t\tclasses = appendClass(classes, key);\n\t\t\t}\n\t\t}\n\n\t\treturn classes;\n\t}\n\n\tfunction appendClass (value, newClass) {\n\t\tif (!newClass) {\n\t\t\treturn value;\n\t\t}\n\t\n\t\tif (value) {\n\t\t\treturn value + ' ' + newClass;\n\t\t}\n\t\n\t\treturn value + newClass;\n\t}\n\n\tif (typeof module !== 'undefined' && module.exports) {\n\t\tclassNames.default = classNames;\n\t\tmodule.exports = classNames;\n\t} else if (typeof define === 'function' && typeof define.amd === 'object' && define.amd) {\n\t\t// register as 'classnames', consistent with npm package name\n\t\tdefine('classnames', [], function () {\n\t\t\treturn classNames;\n\t\t});\n\t} else {\n\t\twindow.classNames = classNames;\n\t}\n}());\n","import { useState, useCallback, useMemo } from 'react';\nimport { logger } from '../utils/logger';\nimport type { TrackingData, UseAdMeshTrackerReturn } from '../types/index';\n\n// Default tracking endpoint\nconst DEFAULT_TRACKING_URL = 'https://api.useadmesh.com/track';\n\ninterface TrackingConfig {\n enabled?: boolean;\n retryAttempts?: number;\n retryDelay?: number;\n}\n\n// Global config that can be set by the consuming application\nlet globalConfig: TrackingConfig = {\n enabled: true,\n retryAttempts: 3,\n retryDelay: 1000\n};\n\nexport const setAdMeshTrackerConfig = (config: Partial<TrackingConfig>) => {\n globalConfig = { ...globalConfig, ...config };\n};\n\nexport const useAdMeshTracker = (config?: Partial<TrackingConfig>): UseAdMeshTrackerReturn => {\n const [isTracking, setIsTracking] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const mergedConfig = useMemo(() => ({ ...globalConfig, ...config }), [config]);\n\n const sendTrackingEvent = useCallback(async (\n eventType: 'click' | 'view' | 'conversion',\n data: TrackingData\n ): Promise<void> => {\n if (!mergedConfig.enabled) {\n return;\n }\n\n if (!data.recommendationId || !data.admeshLink) {\n const errorMsg = 'Missing required tracking data: recommendationId and admeshLink are required';\n setError(errorMsg);\n return;\n }\n\n setIsTracking(true);\n setError(null);\n\n const payload = {\n event_type: eventType,\n recommendation_id: data.recommendationId,\n admesh_link: data.admeshLink,\n product_id: data.productId,\n user_id: data.userId,\n session_id: data.sessionId,\n revenue: data.revenue,\n conversion_type: data.conversionType,\n metadata: data.metadata,\n timestamp: new Date().toISOString(),\n user_agent: navigator.userAgent,\n referrer: document.referrer,\n page_url: window.location.href\n };\n\n let lastError: Error | null = null;\n\n for (let attempt = 1; attempt <= (mergedConfig.retryAttempts || 3); attempt++) {\n try {\n const response = await fetch(`${DEFAULT_TRACKING_URL}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n await response.json();\n setIsTracking(false);\n return;\n\n } catch (err) {\n lastError = err as Error;\n\n if (attempt < (mergedConfig.retryAttempts || 3)) {\n await new Promise(resolve =>\n setTimeout(resolve, (mergedConfig.retryDelay || 1000) * attempt)\n );\n }\n }\n }\n\n // All attempts failed\n const errorMsg = `Failed to track ${eventType} event after ${mergedConfig.retryAttempts} attempts: ${lastError?.message}`;\n setError(errorMsg);\n setIsTracking(false);\n }, [mergedConfig]);\n\n const trackClick = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('click', data);\n }, [sendTrackingEvent]);\n\n const trackView = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('view', data);\n }, [sendTrackingEvent]);\n\n const trackConversion = useCallback(async (data: TrackingData): Promise<void> => {\n return sendTrackingEvent('conversion', data);\n }, [sendTrackingEvent]);\n\n return {\n trackClick,\n trackView,\n trackConversion,\n isTracking,\n error\n };\n};\n\n// Utility function to build admesh_link with tracking parameters\nexport const buildAdMeshLink = (\n baseLink: string, \n recommendationId: string, \n additionalParams?: Record<string, string>\n): string => {\n try {\n const url = new URL(baseLink);\n url.searchParams.set('recommendation_id', recommendationId);\n url.searchParams.set('utm_source', 'admesh');\n url.searchParams.set('utm_medium', 'recommendation');\n \n if (additionalParams) {\n Object.entries(additionalParams).forEach(([key, value]) => {\n url.searchParams.set(key, value);\n });\n }\n \n return url.toString();\n } catch (err) {\n logger.warn('[AdMesh] Invalid URL provided to buildAdMeshLink');\n return baseLink;\n }\n};\n\n// Helper function to extract tracking data from recommendation\nexport const extractTrackingData = (\n recommendation: { recommendation_id: string; admesh_link: string; product_id: string },\n additionalData?: Partial<TrackingData>\n): TrackingData => {\n return {\n recommendationId: recommendation.recommendation_id,\n admeshLink: recommendation.admesh_link,\n productId: recommendation.product_id,\n ...additionalData\n };\n};\n","import React, { useCallback, useEffect, useRef } from 'react';\nimport type { AdMeshLinkTrackerProps } from '../types/index';\nimport { useAdMeshTracker } from '../hooks/useAdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport const AdMeshLinkTracker: React.FC<AdMeshLinkTrackerProps> = ({\n recommendationId,\n admeshLink,\n productId,\n children,\n trackingData,\n className,\n style\n}) => {\n const { trackClick, trackView } = useAdMeshTracker();\n const elementRef = useRef<HTMLDivElement>(null);\n const hasTrackedView = useRef(false);\n\n // Ensure all child links open in new tab\n useEffect(() => {\n if (!elementRef.current) return;\n\n // Find all <a> tags within this component and ensure they open in new tab\n const links = elementRef.current.querySelectorAll('a');\n links.forEach((link) => {\n // Only set if not already set or if it's not _blank\n if (!link.hasAttribute('target') || link.getAttribute('target') !== '_blank') {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n });\n }, [children]); // Re-run when children change\n\n // Track view when component becomes visible\n useEffect(() => {\n if (!elementRef.current || hasTrackedView.current) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n if (entry.isIntersecting && !hasTrackedView.current) {\n hasTrackedView.current = true;\n trackView({\n recommendationId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(logger.error);\n }\n });\n },\n {\n threshold: 0.5, // Track when 50% of the element is visible\n rootMargin: '0px'\n }\n );\n\n observer.observe(elementRef.current);\n\n return () => {\n observer.disconnect();\n };\n }, [recommendationId, admeshLink, productId, trackingData, trackView]);\n\n const handleClick = useCallback((event: React.MouseEvent) => {\n // Fire tracking asynchronously WITHOUT blocking navigation\n // This ensures the link opens immediately\n trackClick({\n recommendationId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(() => {\n // Log error but don't block navigation\n logger.error('[AdMesh] Failed to track click');\n });\n\n // If the children contain a link, ensure it opens in new tab\n // Otherwise, navigate programmatically\n const target = event.target as HTMLElement;\n const link = target.closest('a');\n\n if (!link) {\n // No link found, navigate programmatically\n window.open(admeshLink, '_blank', 'noopener,noreferrer');\n } else {\n // Link found - ensure it opens in new tab\n if (!link.hasAttribute('target') || link.getAttribute('target') !== '_blank') {\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n }\n // Let the browser handle navigation naturally\n }\n }, [recommendationId, admeshLink, productId, trackingData, trackClick]);\n\n return (\n <div\n ref={elementRef}\n className={className}\n onClick={handleClick}\n style={{\n cursor: 'pointer',\n ...style\n }}\n >\n {children}\n </div>\n );\n};\n\nAdMeshLinkTracker.displayName = 'AdMeshLinkTracker';\n","/**\n * AdMesh Style Injection System\n * \n * Provides platform-agnostic, isolated styling for AdMesh components\n * that prevents interference from host platform CSS frameworks\n */\n\nconst ADMESH_STYLE_ID = 'admesh-ui-sdk-styles';\nconst ADMESH_RESET_ID = 'admesh-ui-sdk-reset';\n\n/**\n * CSS Reset for AdMesh components\n * Normalizes styles to prevent host platform CSS from affecting AdMesh components\n */\nconst ADMESH_CSS_RESET = `\n/* AdMesh UI SDK - CSS Reset & Normalization */\n.admesh-component,\n.admesh-component * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font-weight: normal;\n font-style: normal;\n line-height: 1.5;\n text-decoration: none;\n background: transparent;\n color: inherit;\n}\n\n.admesh-component {\n all: revert;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n text-rendering: optimizeLegibility;\n}\n\n.admesh-component button,\n.admesh-component a {\n cursor: pointer;\n border: none;\n background: none;\n padding: 0;\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n}\n\n.admesh-component a {\n text-decoration: none;\n color: inherit;\n}\n\n.admesh-component button:focus,\n.admesh-component a:focus {\n outline: none;\n}\n\n.admesh-component img {\n max-width: 100%;\n height: auto;\n display: block;\n}\n\n.admesh-component ul,\n.admesh-component ol {\n list-style: none;\n}\n\n.admesh-component table {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n.admesh-component input,\n.admesh-component textarea,\n.admesh-component select {\n font-family: inherit;\n font-size: inherit;\n color: inherit;\n}\n\n/* Prevent Tailwind and other frameworks from affecting AdMesh */\n.admesh-component .prose,\n.admesh-component .container,\n.admesh-component .grid,\n.admesh-component .flex {\n all: revert;\n}\n\n/* Ensure AdMesh components are not affected by dark mode utilities */\n.admesh-component.dark,\n.admesh-component[data-admesh-theme=\"dark\"] {\n color-scheme: dark;\n}\n\n.admesh-component.light,\n.admesh-component[data-admesh-theme=\"light\"] {\n color-scheme: light;\n}\n`;\n\n/**\n * Core AdMesh Component Styles\n * Self-contained styles that work independently of host platform\n */\nconst ADMESH_CORE_STYLES = `\n/* AdMesh Core Component Styles */\n\n.admesh-component {\n position: relative;\n display: block;\n}\n\n/* Product Card Styles */\n.admesh-product-card {\n position: relative;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n height: 100%;\n border-radius: 0.75rem;\n background: rgb(255, 255, 255);\n border: 1px solid rgb(229, 231, 235);\n box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\n overflow: hidden;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] {\n background: rgb(17, 24, 39);\n border-color: rgb(31, 41, 55);\n}\n\n.admesh-product-card:hover {\n transform: translateY(-4px);\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);\n border-color: rgba(99, 102, 241, 0.2);\n}\n\n.admesh-product-card__container {\n position: relative;\n padding: 1rem;\n height: 100%;\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n z-index: 2;\n}\n\n.admesh-product-card__header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 0.5rem;\n margin-bottom: 0.25rem;\n}\n\n.admesh-product-card__title {\n margin: 0;\n font-size: 1.5rem;\n font-weight: 700;\n line-height: 1.2;\n color: rgb(17, 24, 39);\n letter-spacing: -0.025em;\n transition: all 0.3s ease;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] .admesh-product-card__title {\n color: rgb(243, 244, 246);\n}\n\n.admesh-product-card:hover .admesh-product-card__title {\n transform: translateX(4px);\n}\n\n.admesh-product-card__cta {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n white-space: nowrap;\n border-radius: 0.5rem;\n font-size: 1rem;\n font-weight: 600;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0.75rem 1.5rem;\n background: rgb(0, 0, 0);\n color: rgb(255, 255, 255);\n border: none;\n cursor: pointer;\n box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n}\n\n.admesh-product-card[data-admesh-theme=\"dark\"] .admesh-product-card__cta {\n background: rgb(255, 255, 255);\n color: rgb(0, 0, 0);\n}\n\n.admesh-product-card__cta:hover {\n transform: translateY(-2px);\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);\n}\n\n.admesh-product-card__cta:active {\n transform: translateY(0);\n}\n\n/* Summary Unit Styles */\n.admesh-summary-unit {\n font-size: 1rem;\n line-height: 1.6;\n color: rgb(17, 24, 39);\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] {\n color: rgb(243, 244, 246);\n}\n\n.admesh-summary-unit a {\n color: rgb(37, 99, 235);\n text-decoration: underline;\n text-decoration-color: rgb(37, 99, 235);\n text-underline-offset: 2px;\n transition: all 0.2s ease;\n font-weight: 500;\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] a {\n color: rgb(96, 165, 250);\n text-decoration-color: rgb(96, 165, 250);\n}\n\n.admesh-summary-unit a:hover {\n color: rgb(29, 78, 216);\n text-decoration-color: rgb(29, 78, 216);\n}\n\n.admesh-summary-unit[data-admesh-theme=\"dark\"] a:hover {\n color: rgb(147, 197, 253);\n text-decoration-color: rgb(147, 197, 253);\n}\n\n/* Ecommerce Cards Styles */\n.admesh-ecommerce-container {\n position: relative;\n width: 100%;\n}\n\n.admesh-ecommerce-card {\n flex-shrink: 0;\n background: white;\n border: 1px solid rgb(229, 231, 235);\n border-radius: 0.5rem;\n overflow: hidden;\n transition: all 0.2s ease-in-out;\n cursor: pointer;\n position: relative;\n}\n\n.admesh-ecommerce-card[data-admesh-theme=\"dark\"] {\n background: rgb(31, 41, 55);\n border-color: rgb(55, 65, 81);\n color: white;\n}\n\n.admesh-ecommerce-card:hover {\n transform: translateY(-2px);\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);\n}\n\n/* Badge Styles */\n.admesh-badge {\n display: inline-flex;\n align-items: center;\n gap: 0.375rem;\n border-radius: 0.5rem;\n padding: 0.375rem 0.75rem;\n font-size: 0.875rem;\n font-weight: 600;\n transition: all 0.3s ease;\n position: relative;\n overflow: hidden;\n}\n\n.admesh-badge--primary {\n background: linear-gradient(135deg, rgb(99, 102, 241), rgb(79, 70, 229));\n color: white;\n box-shadow: 0 4px 6px -1px rgba(99, 102, 241, 0.3);\n}\n\n.admesh-badge--secondary {\n background: rgb(243, 244, 246);\n color: rgb(55, 65, 81);\n}\n\n.admesh-badge[data-admesh-theme=\"dark\"].admesh-badge--secondary {\n background: rgb(31, 41, 55);\n color: rgb(209, 213, 219);\n}\n\n/* Responsive */\n@media (max-width: 640px) {\n .admesh-product-card__container {\n padding: 0.75rem;\n gap: 0.5rem;\n }\n\n .admesh-product-card__title {\n font-size: 1.25rem;\n }\n\n .admesh-product-card__cta {\n padding: 0.5rem 1rem;\n font-size: 0.875rem;\n }\n}\n`;\n\n/**\n * Inject AdMesh styles into the document\n * Ensures styles are loaded only once and don't conflict with host platform\n */\nexport const injectAdMeshStyles = (): void => {\n if (typeof document === 'undefined') return;\n\n // Check if styles already injected\n if (document.getElementById(ADMESH_RESET_ID) && document.getElementById(ADMESH_STYLE_ID)) {\n return;\n }\n\n // Inject CSS Reset\n if (!document.getElementById(ADMESH_RESET_ID)) {\n const resetStyle = document.createElement('style');\n resetStyle.id = ADMESH_RESET_ID;\n resetStyle.textContent = ADMESH_CSS_RESET;\n document.head.appendChild(resetStyle);\n }\n\n // Inject Core Styles\n if (!document.getElementById(ADMESH_STYLE_ID)) {\n const coreStyle = document.createElement('style');\n coreStyle.id = ADMESH_STYLE_ID;\n coreStyle.textContent = ADMESH_CORE_STYLES;\n document.head.appendChild(coreStyle);\n }\n};\n\n/**\n * Remove AdMesh styles from the document\n * Useful for cleanup or testing\n */\nexport const removeAdMeshStyles = (): void => {\n if (typeof document === 'undefined') return;\n\n const resetStyle = document.getElementById(ADMESH_RESET_ID);\n const coreStyle = document.getElementById(ADMESH_STYLE_ID);\n\n if (resetStyle) resetStyle.remove();\n if (coreStyle) coreStyle.remove();\n};\n\n/**\n * Check if AdMesh styles are already injected\n */\nexport const areAdMeshStylesInjected = (): boolean => {\n if (typeof document === 'undefined') return false;\n return !!(document.getElementById(ADMESH_RESET_ID) && document.getElementById(ADMESH_STYLE_ID));\n};\n\n","import { useEffect } from 'react';\nimport { logger } from '../utils/logger';\nimport { injectAdMeshStyles } from '../utils/styleInjection';\n\n// Complete CSS content as a string - this will be injected automatically\nconst ADMESH_STYLES = `\n/* AdMesh UI SDK - Complete Self-Contained Styles */\n\n/* CSS Reset for AdMesh components */\n.admesh-component, .admesh-component * {\n box-sizing: border-box;\n}\n\n/* CSS Variables - Black/White Color Scheme */\n.admesh-component {\n --admesh-primary: #000000;\n --admesh-primary-hover: #333333;\n --admesh-secondary: #666666;\n --admesh-accent: #000000;\n --admesh-background: #ffffff;\n --admesh-surface: #ffffff;\n --admesh-border: #e5e7eb;\n --admesh-text: #000000;\n --admesh-text-muted: #666666;\n --admesh-text-light: #999999;\n --admesh-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);\n --admesh-shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);\n --admesh-shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n --admesh-shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --admesh-radius: 0.5rem;\n --admesh-radius-sm: 0.25rem;\n --admesh-radius-lg: 0.75rem;\n --admesh-radius-xl: 1rem;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] {\n --admesh-primary: #ffffff;\n --admesh-primary-hover: #e5e7eb;\n --admesh-secondary: #9ca3af;\n --admesh-accent: #ffffff;\n --admesh-background: #000000;\n --admesh-surface: #111111;\n --admesh-border: #333333;\n --admesh-text: #ffffff;\n --admesh-text-muted: #9ca3af;\n --admesh-text-light: #666666;\n --admesh-shadow: 0 1px 3px 0 rgb(255 255 255 / 0.1), 0 1px 2px -1px rgb(255 255 255 / 0.1);\n --admesh-shadow-md: 0 4px 6px -1px rgb(255 255 255 / 0.1), 0 2px 4px -2px rgb(255 255 255 / 0.1);\n --admesh-shadow-lg: 0 10px 15px -3px rgb(255 255 255 / 0.1), 0 4px 6px -4px rgb(255 255 255 / 0.1);\n --admesh-shadow-xl: 0 20px 25px -5px rgb(255 255 255 / 0.1), 0 8px 10px -6px rgb(255 255 255 / 0.1);\n}\n\n/* Layout Styles */\n.admesh-layout {\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n color: var(--admesh-text);\n background-color: var(--admesh-background);\n border-radius: var(--admesh-radius);\n padding: 1.5rem;\n box-shadow: var(--admesh-shadow);\n border: 1px solid var(--admesh-border);\n /* Consistent width: 100% for all layouts except ecommerce */\n width: 100%;\n}\n\n/* Ecommerce layout exception */\n.admesh-layout--ecommerce {\n width: auto;\n}\n\n/* Citation Unit Styles */\n.admesh-citation-unit {\n width: 100%;\n}\n\n/* Inline Recommendation Styles */\n.admesh-inline-recommendation {\n width: 100%;\n}\n\n/* Simple Ad Styles */\n.admesh-simple-ad {\n width: 100%;\n}\n\n.admesh-layout__header {\n margin-bottom: 1.5rem;\n text-align: center;\n}\n\n.admesh-layout__title {\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--admesh-text);\n margin-bottom: 0.5rem;\n}\n\n.admesh-layout__subtitle {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n}\n\n.admesh-layout__cards-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));\n gap: 1rem;\n margin-bottom: 1.5rem;\n}\n\n.admesh-layout__more-indicator {\n text-align: center;\n padding: 1rem;\n color: var(--admesh-text-muted);\n font-size: 0.875rem;\n}\n\n.admesh-layout__empty {\n text-align: center;\n padding: 3rem 1rem;\n}\n\n.admesh-layout__empty-content h3 {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--admesh-text-muted);\n margin-bottom: 0.5rem;\n}\n\n.admesh-layout__empty-content p {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n}\n\n/* Product Card Styles */\n.admesh-product-card {\n background-color: var(--admesh-surface);\n border: 1px solid var(--admesh-border);\n border-radius: var(--admesh-radius);\n padding: 1.5rem;\n transition: all 0.2s ease-in-out;\n position: relative;\n overflow: hidden;\n /* Consistent width: 100% for product cards */\n width: 100%;\n}\n\n.admesh-product-card:hover {\n box-shadow: var(--admesh-shadow-lg);\n transform: translateY(-2px);\n border-color: var(--admesh-primary);\n}\n\n.admesh-product-card__header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__title {\n font-size: 1.125rem;\n font-weight: 600;\n color: var(--admesh-text);\n margin-bottom: 0.5rem;\n line-height: 1.4;\n}\n\n.admesh-product-card__reason {\n font-size: 0.875rem;\n color: var(--admesh-text-muted);\n line-height: 1.5;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__match-score {\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__match-score-label {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 0.75rem;\n color: var(--admesh-text-muted);\n margin-bottom: 0.25rem;\n}\n\n.admesh-product-card__match-score-bar {\n width: 100%;\n height: 0.375rem;\n background-color: var(--admesh-border);\n border-radius: var(--admesh-radius-sm);\n overflow: hidden;\n}\n\n.admesh-product-card__match-score-fill {\n height: 100%;\n background: var(--admesh-primary);\n border-radius: var(--admesh-radius-sm);\n transition: width 0.3s ease-in-out;\n}\n\n.admesh-product-card__badges {\n display: flex;\n flex-wrap: wrap;\n gap: 0.5rem;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__badge {\n display: inline-flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.25rem 0.5rem;\n background-color: var(--admesh-primary);\n color: white;\n font-size: 0.75rem;\n font-weight: 500;\n border-radius: var(--admesh-radius-sm);\n}\n\n.admesh-product-card__badge--secondary {\n background-color: var(--admesh-secondary);\n}\n\n.admesh-product-card__keywords {\n display: flex;\n flex-wrap: wrap;\n gap: 0.25rem;\n margin-bottom: 1rem;\n}\n\n.admesh-product-card__keyword {\n padding: 0.125rem 0.375rem;\n background-color: var(--admesh-border);\n color: var(--admesh-text-muted);\n font-size: 0.75rem;\n border-radius: var(--admesh-radius-sm);\n}\n\n/* Dark mode specific enhancements */\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card__keyword {\n background-color: #4b5563;\n color: #d1d5db;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card:hover {\n border-color: var(--admesh-primary);\n background-color: #374151;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-product-card__button:hover {\n background: var(--admesh-primary-hover);\n}\n\n.admesh-product-card__footer {\n display: flex;\n justify-content: flex-end;\n margin-top: 1.5rem;\n}\n\n/* Mobile-specific sidebar improvements */\n@media (max-width: 640px) {\n .admesh-sidebar {\n /* Ensure proper mobile viewport handling */\n height: 100vh !important;\n height: 100dvh !important; /* Dynamic viewport height for mobile browsers */\n max-height: 100vh !important;\n max-height: 100dvh !important;\n width: 100vw !important;\n max-width: 90vw !important;\n overflow: hidden !important;\n }\n\n .admesh-sidebar.relative {\n height: 100% !important;\n width: 100% !important;\n max-width: 100% !important;\n }\n\n /* Improve touch scrolling */\n .admesh-sidebar .overflow-y-auto {\n -webkit-overflow-scrolling: touch !important;\n overscroll-behavior: contain !important;\n scroll-behavior: smooth !important;\n }\n\n /* Prevent body scroll when sidebar is open */\n body:has(.admesh-sidebar[data-mobile-open=\"true\"]) {\n overflow: hidden !important;\n position: fixed !important;\n width: 100% !important;\n }\n}\n\n/* Tablet improvements */\n@media (min-width: 641px) and (max-width: 1024px) {\n .admesh-sidebar {\n max-width: 400px !important;\n }\n}\n\n/* Mobile responsiveness improvements for all components */\n@media (max-width: 640px) {\n /* Product cards mobile optimization */\n .admesh-card {\n padding: 0.75rem !important;\n margin-bottom: 0.75rem !important;\n }\n\n /* Inline recommendations mobile optimization */\n .admesh-inline-recommendation {\n padding: 0.5rem !important;\n margin-bottom: 0.5rem !important;\n }\n\n /* Conversation summary mobile optimization */\n .admesh-conversation-summary {\n padding: 1rem !important;\n }\n\n /* Percentage text mobile improvements */\n .admesh-component .text-xs {\n font-size: 0.75rem !important;\n line-height: 1rem !important;\n }\n\n .admesh-component .text-sm {\n font-size: 0.875rem !important;\n line-height: 1.25rem !important;\n }\n\n /* Button mobile improvements */\n .admesh-component button {\n padding: 0.375rem 0.75rem !important;\n font-size: 0.75rem !important;\n min-height: 2rem !important;\n touch-action: manipulation !important;\n }\n\n /* Badge mobile improvements */\n .admesh-component .rounded-full {\n padding: 0.25rem 0.5rem !important;\n font-size: 0.625rem !important;\n line-height: 1rem !important;\n }\n\n /* Progress bar mobile improvements */\n .admesh-component .bg-gray-200,\n .admesh-component .bg-slate-600 {\n height: 0.25rem !important;\n }\n\n /* Flex layout mobile improvements */\n .admesh-component .flex {\n flex-wrap: wrap !important;\n }\n\n .admesh-component .gap-2 {\n gap: 0.375rem !important;\n }\n\n .admesh-component .gap-3 {\n gap: 0.5rem !important;\n }\n}\n\n.admesh-product-card__button {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: var(--admesh-primary);\n color: var(--admesh-background);\n font-size: 0.875rem;\n font-weight: 500;\n border: none;\n border-radius: var(--admesh-radius);\n cursor: pointer;\n transition: all 0.2s ease-in-out;\n text-decoration: none;\n}\n\n.admesh-product-card__button:hover {\n transform: translateY(-1px);\n box-shadow: var(--admesh-shadow-lg);\n}\n\n/* Utility Classes */\n.admesh-text-xs { font-size: 0.75rem; }\n.admesh-text-sm { font-size: 0.875rem; }\n.admesh-text-base { font-size: 1rem; }\n.admesh-text-lg { font-size: 1.125rem; }\n.admesh-text-xl { font-size: 1.25rem; }\n\n.admesh-font-medium { font-weight: 500; }\n.admesh-font-semibold { font-weight: 600; }\n.admesh-font-bold { font-weight: 700; }\n\n.admesh-text-muted { color: var(--admesh-text-muted); }\n\n/* Comparison Table Styles */\n.admesh-compare-table {\n width: 100%;\n border-collapse: collapse;\n background-color: var(--admesh-surface);\n border: 1px solid var(--admesh-border);\n border-radius: var(--admesh-radius);\n overflow: hidden;\n}\n\n.admesh-compare-table th,\n.admesh-compare-table td {\n padding: 0.75rem;\n text-align: left;\n border-bottom: 1px solid var(--admesh-border);\n}\n\n.admesh-compare-table th {\n background-color: var(--admesh-background);\n font-weight: 600;\n color: var(--admesh-text);\n font-size: 0.875rem;\n}\n\n.admesh-compare-table td {\n color: var(--admesh-text);\n font-size: 0.875rem;\n}\n\n.admesh-compare-table tr:hover {\n background-color: var(--admesh-border);\n}\n\n/* Dark mode table enhancements */\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-compare-table th {\n background-color: #374151;\n}\n\n.admesh-component[data-admesh-theme=\"dark\"] .admesh-compare-table tr:hover {\n background-color: #4b5563;\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .admesh-layout {\n padding: 1rem;\n }\n\n .admesh-layout__cards-grid {\n grid-template-columns: 1fr;\n gap: 0.75rem;\n }\n\n .admesh-product-card {\n padding: 1rem;\n }\n\n .admesh-compare-table {\n font-size: 0.75rem;\n }\n\n .admesh-compare-table th,\n .admesh-compare-table td {\n padding: 0.5rem;\n }\n}\n\n/* Essential Utility Classes for Self-Contained SDK - High Specificity */\n.admesh-component .relative { position: relative !important; }\n.admesh-component .absolute { position: absolute !important; }\n.admesh-component .flex { display: flex !important; }\n.admesh-component .inline-flex { display: inline-flex !important; }\n.admesh-component .grid { display: grid !important; }\n.admesh-component .hidden { display: none !important; }\n.admesh-component .block { display: block !important; }\n.admesh-component .inline-block { display: inline-block !important; }\n\n/* Flexbox utilities */\n.admesh-component .flex-col { flex-direction: column !important; }\n.admesh-component .flex-row { flex-direction: row !important; }\n.admesh-component .flex-wrap { flex-wrap: wrap !important; }\n.admesh-component .items-center { align-items: center !important; }\n.admesh-component .items-start { align-items: flex-start !important; }\n.admesh-component .items-end { align-items: flex-end !important; }\n.admesh-component .justify-center { justify-content: center !important; }\n.admesh-component .justify-between { justify-content: space-between !important; }\n.admesh-component .justify-end { justify-content: flex-end !important; }\n.admesh-component .flex-1 { flex: 1 1 0% !important; }\n.admesh-component .flex-shrink-0 { flex-shrink: 0 !important; }\n\n/* Grid utilities */\n.admesh-component .grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }\n.admesh-component .grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n.admesh-component .grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n\n/* Spacing utilities */\n.admesh-component .gap-1 { gap: 0.25rem; }\n.admesh-component .gap-2 { gap: 0.5rem; }\n.admesh-component .gap-3 { gap: 0.75rem; }\n.admesh-component .gap-4 { gap: 1rem; }\n.admesh-component .gap-6 { gap: 1.5rem; }\n.admesh-component .gap-8 { gap: 2rem; }\n\n/* Padding utilities */\n.admesh-component .p-1 { padding: 0.25rem; }\n.admesh-component .p-2 { padding: 0.5rem; }\n.admesh-component .p-3 { padding: 0.75rem; }\n.admesh-component .p-4 { padding: 1rem; }\n.admesh-component .p-5 { padding: 1.25rem; }\n.admesh-component .p-6 { padding: 1.5rem; }\n.admesh-component .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }\n.admesh-component .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }\n.admesh-component .px-4 { padding-left: 1rem; padding-right: 1rem; }\n.admesh-component .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }\n.admesh-component .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }\n.admesh-component .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }\n.admesh-component .pt-2 { padding-top: 0.5rem; }\n.admesh-component .pt-3 { padding-top: 0.75rem; }\n.admesh-component .pb-2 { padding-bottom: 0.5rem; }\n.admesh-component .pb-3 { padding-bottom: 0.75rem; }\n\n/* Margin utilities */\n.admesh-component .m-0 { margin: 0; }\n.admesh-component .mb-1 { margin-bottom: 0.25rem; }\n.admesh-component .mb-2 { margin-bottom: 0.5rem; }\n.admesh-component .mb-3 { margin-bottom: 0.75rem; }\n.admesh-component .mb-4 { margin-bottom: 1rem; }\n.admesh-component .mb-6 { margin-bottom: 1.5rem; }\n.admesh-component .mt-1 { margin-top: 0.25rem; }\n.admesh-component .mt-2 { margin-top: 0.5rem; }\n.admesh-component .mt-4 { margin-top: 1rem; }\n.admesh-component .mt-6 { margin-top: 1.5rem; }\n.admesh-component .mt-auto { margin-top: auto; }\n.admesh-component .ml-1 { margin-left: 0.25rem; }\n.admesh-component .mr-1 { margin-right: 0.25rem; }\n.admesh-component .mr-2 { margin-right: 0.5rem; }\n\n/* Width and height utilities */\n.admesh-component .w-2 { width: 0.5rem; }\n.admesh-component .w-3 { width: 0.75rem; }\n.admesh-component .w-4 { width: 1rem; }\n.admesh-component .w-5 { width: 1.25rem; }\n.admesh-component .w-6 { width: 1.5rem; }\n.admesh-component .w-full { width: 100%; }\n.admesh-component .w-fit { width: fit-content; }\n.admesh-component .h-2 { height: 0.5rem; }\n.admesh-component .h-3 { height: 0.75rem; }\n.admesh-component .h-4 { height: 1rem; }\n.admesh-component .h-5 { height: 1.25rem; }\n.admesh-component .h-6 { height: 1.5rem; }\n.admesh-component .h-full { height: 100%; }\n.admesh-component .min-w-0 { min-width: 0px; }\n\n/* Border utilities */\n.admesh-component .border { border-width: 1px; }\n.admesh-component .border-t { border-top-width: 1px; }\n.admesh-component .border-gray-100 { border-color: #f3f4f6; }\n.admesh-component .border-gray-200 { border-color: #e5e7eb; }\n.admesh-component .border-gray-300 { border-color: #d1d5db; }\n.admesh-component .border-blue-200 { border-color: #bfdbfe; }\n.admesh-component .border-green-200 { border-color: #bbf7d0; }\n\n/* Border radius utilities */\n.admesh-component .rounded { border-radius: 0.25rem !important; }\n.admesh-component .rounded-md { border-radius: 0.375rem !important; }\n.admesh-component .rounded-lg { border-radius: 0.5rem !important; }\n.admesh-component .rounded-xl { border-radius: 0.75rem !important; }\n.admesh-component .rounded-full { border-radius: 9999px !important; }\n\n/* Background utilities */\n.admesh-component .bg-white { background-color: #ffffff; }\n.admesh-component .bg-gray-50 { background-color: #f9fafb; }\n.admesh-component .bg-gray-100 { background-color: #f3f4f6; }\n.admesh-component .bg-blue-50 { background-color: #eff6ff; }\n.admesh-component .bg-blue-100 { background-color: #dbeafe; }\n.admesh-component .bg-green-100 { background-color: #dcfce7; }\n.admesh-component .bg-green-500 { background-color: #22c55e; }\n.admesh-component .bg-blue-500 { background-color: #3b82f6; }\n\n/* Solid backgrounds - no gradients for minimal design */\n.admesh-component .bg-primary { background-color: var(--admesh-primary); }\n.admesh-component .bg-secondary { background-color: var(--admesh-secondary); }\n.admesh-component .bg-surface { background-color: var(--admesh-surface); }\n.admesh-component .bg-background { background-color: var(--admesh-background); }\n\n/* Text utilities */\n.admesh-component .text-xs { font-size: 0.75rem; line-height: 1rem; }\n.admesh-component .text-sm { font-size: 0.875rem; line-height: 1.25rem; }\n.admesh-component .text-base { font-size: 1rem; line-height: 1.5rem; }\n.admesh-component .text-lg { font-size: 1.125rem; line-height: 1.75rem; }\n.admesh-component .text-xl { font-size: 1.25rem; line-height: 1.75rem; }\n.admesh-component .font-medium { font-weight: 500; }\n.admesh-component .font-semibold { font-weight: 600; }\n.admesh-component .font-bold { font-weight: 700; }\n.admesh-component .leading-relaxed { line-height: 1.625; }\n\n/* Text colors */\n.admesh-component .text-white { color: #ffffff; }\n.admesh-component .text-gray-400 { color: #9ca3af; }\n.admesh-component .text-gray-500 { color: #6b7280; }\n.admesh-component .text-gray-600 { color: #4b5563; }\n.admesh-component .text-gray-700 { color: #374151; }\n.admesh-component .text-gray-800 { color: #1f2937; }\n.admesh-component .text-blue-600 { color: #2563eb; }\n.admesh-component .text-blue-700 { color: #1d4ed8; }\n.admesh-component .text-green-700 { color: #15803d; }\n\n/* Shadow utilities */\n.admesh-component .shadow-sm { box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); }\n.admesh-component .shadow { box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-md { box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-lg { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); }\n.admesh-component .shadow-xl { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); }\n\n/* Transition utilities */\n.admesh-component .transition-all { transition-property: all; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }\n.admesh-component .transition-colors { transition-property: color, background-color, border-color, text-decoration-color, fill, stroke; transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); transition-duration: 150ms; }\n.admesh-component .duration-200 { transition-duration: 200ms; }\n.admesh-component .duration-300 { transition-duration: 300ms; }\n\n/* Transform utilities */\n.admesh-component .hover\\\\:-translate-y-1:hover { transform: translateY(-0.25rem); }\n.admesh-component .hover\\\\:scale-105:hover { transform: scale(1.05); }\n\n/* Hover utilities */\n.admesh-component .hover\\\\:shadow-xl:hover { box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1); }\n.admesh-component .hover\\\\:bg-gray-100:hover { background-color: #f3f4f6; }\n.admesh-component .hover\\\\:text-blue-800:hover { color: #1e40af; }\n\n/* Cursor utilities */\n.admesh-component .cursor-pointer { cursor: pointer; }\n\n/* Overflow utilities */\n.admesh-component .overflow-hidden { overflow: hidden; }\n.admesh-component .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n\n/* Text decoration */\n.admesh-component .underline { text-decoration-line: underline; }\n\n/* Whitespace */\n.admesh-component .whitespace-nowrap { white-space: nowrap; }\n\n/* Dark mode utilities */\n@media (prefers-color-scheme: dark) {\n .admesh-component .dark\\\\:bg-slate-800 { background-color: #1e293b; }\n .admesh-component .dark\\\\:bg-slate-900 { background-color: #0f172a; }\n .admesh-component .dark\\\\:border-slate-700 { border-color: #334155; }\n .admesh-component .dark\\\\:text-white { color: #ffffff; }\n .admesh-component .dark\\\\:text-gray-200 { color: #e5e7eb; }\n .admesh-component .dark\\\\:text-gray-300 { color: #d1d5db; }\n .admesh-component .dark\\\\:text-gray-400 { color: #9ca3af; }\n .admesh-component .dark\\\\:text-blue-400 { color: #60a5fa; }\n}\n\n/* Responsive utilities */\n@media (min-width: 640px) {\n .admesh-component .sm\\\\:p-5 { padding: 1.25rem; }\n .admesh-component .sm\\\\:text-base { font-size: 1rem; line-height: 1.5rem; }\n .admesh-component .sm\\\\:text-lg { font-size: 1.125rem; line-height: 1.75rem; }\n .admesh-component .sm\\\\:flex-row { flex-direction: row; }\n .admesh-component .sm\\\\:items-center { align-items: center; }\n .admesh-component .sm\\\\:justify-between { justify-content: space-between; }\n}\n\n@media (min-width: 768px) {\n .admesh-component .md\\\\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n}\n\n@media (min-width: 1024px) {\n .admesh-component .lg\\\\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }\n .admesh-component .lg\\\\:col-span-1 { grid-column: span 1 / span 1; }\n}\n`;\n\nlet stylesInjected = false;\n\n/**\n * Hook to inject AdMesh styles into the document\n * Ensures platform-agnostic, isolated styling that prevents\n * interference from host platform CSS frameworks\n *\n * Usage:\n * const MyComponent = () => {\n * useAdMeshStyles();\n * return <div className=\"admesh-component\">...</div>;\n * };\n */\nexport const useAdMeshStyles = () => {\n useEffect(() => {\n if (stylesInjected) return;\n\n try {\n // Use the new style injection system for better isolation\n injectAdMeshStyles();\n\n // Also inject the legacy styles for backward compatibility\n const styleElement = document.createElement('style');\n styleElement.id = 'admesh-ui-sdk-styles-legacy';\n styleElement.textContent = ADMESH_STYLES;\n\n if (!document.getElementById('admesh-ui-sdk-styles-legacy')) {\n document.head.appendChild(styleElement);\n }\n\n stylesInjected = true;\n } catch (error) {\n logger.error('[AdMesh] Failed to inject styles');\n }\n\n // Cleanup function\n return () => {\n // Note: We don't remove styles on unmount to prevent flickering\n // Styles are designed to be persistent for the lifetime of the app\n };\n }, []);\n};\n","import type { AdMeshRecommendation } from '../types/index';\n\n/**\n * Utility functions for generating compliant disclosure labels and tooltips\n */\n\nexport interface DisclosureConfig {\n showTooltips?: boolean;\n compactMode?: boolean;\n customLabels?: {\n partnerRecommendation?: string;\n partnerMatch?: string;\n promotedOption?: string;\n relatedOption?: string;\n };\n}\n\n/**\n * Generate appropriate label based on match score and recommendation quality\n * Uses preferred terminology: 'Promoted Match', 'Partner Recommendation', or 'Smart Pick'\n */\nexport const getRecommendationLabel = (\n recommendation: AdMeshRecommendation,\n config: DisclosureConfig = {}\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n const customLabels = config.customLabels || {};\n\n // High match score (>0.8)\n if (matchScore >= 0.8) {\n return customLabels.partnerRecommendation || 'Smart Pick';\n }\n\n // Medium match score (0.6-0.8)\n if (matchScore >= 0.6) {\n return customLabels.partnerMatch || 'Partner Recommendation';\n }\n\n // Lower match score (<0.6)\n if (matchScore >= 0.3) {\n return customLabels.promotedOption || 'Promoted Match';\n }\n\n // Very low match - related option\n return customLabels.relatedOption || 'Related';\n};\n\n/**\n * Generate tooltip text for recommendation labels\n */\nexport const getLabelTooltip = (\n recommendation: AdMeshRecommendation,\n _label: string\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n\n if (matchScore >= 0.8) {\n return \"This recommendation is from a partner who compensates us when you engage. We've matched it to your needs based on your query.\";\n }\n \n if (matchScore >= 0.6) {\n return \"Top-rated partner solution matched to your specific requirements. Partner compensates us for qualified referrals.\";\n }\n \n if (matchScore >= 0.3) {\n return \"This partner solution may be relevant to your needs. The partner compensates us when you take qualifying actions.\";\n }\n \n return \"This solution is somewhat related to your query. While not a perfect match, it might still be helpful. This partner compensates us for qualified referrals.\";\n};\n\n/**\n * Generate section-level disclosure text\n */\nexport const getSectionDisclosure = (\n hasHighMatches: boolean = true,\n isExpanded: boolean = false\n): string => {\n if (!hasHighMatches) {\n return \"Expanded Results: While these don't perfectly match your query, they're related solutions from our partner network. All partners compensate us for referrals.\";\n }\n \n if (isExpanded) {\n return \"These curated recommendations are from partners who compensate us for referrals.\";\n }\n \n return \"Personalized Sponsoreds: All results are from vetted partners who compensate us for qualified matches. We've ranked them based on relevance to your specific needs.\";\n};\n\n/**\n * Generate inline disclosure text for product cards\n * Uses preferred terminology consistently\n */\nexport const getInlineDisclosure = (\n recommendation: AdMeshRecommendation,\n compact: boolean = false\n): string => {\n const matchScore = recommendation.intent_match_score || 0;\n\n if (compact) {\n return \"Promoted Match\";\n }\n\n if (matchScore >= 0.8) {\n return \"Smart Pick\";\n }\n\n if (matchScore >= 0.6) {\n return \"Partner Recommendation\";\n }\n\n return \"Promoted Match\";\n};\n\n/**\n * Generate detailed tooltip for inline disclosures\n */\nexport const getInlineTooltip = (): string => {\n return \"We've partnered with trusted providers to bring you relevant solutions. These partners compensate us for qualified referrals, which helps us keep our service free.\";\n};\n\n/**\n * Generate badge text without emojis using preferred terminology\n */\nexport const getBadgeText = (badgeType: string): string => {\n const badgeMap: Record<string, string> = {\n 'Top Match': 'Smart Pick',\n 'Sponsored': 'Smart Pick',\n 'Perfect Fit': 'Smart Pick',\n 'Great Match': 'Partner Recommendation',\n 'Recommended': 'Partner Recommendation',\n 'Good Fit': 'Promoted Match',\n 'Featured': 'Promoted Match',\n 'Popular Choice': 'Popular Choice',\n 'Premium Pick': 'Premium Pick',\n 'Free Tier': 'Free Tier',\n 'AI Powered': 'AI Powered',\n 'Popular': 'Popular',\n 'New': 'New',\n 'Trial Available': 'Trial Available',\n 'Related Option': 'Related Option',\n 'Alternative Solution': 'Alternative Solution',\n 'Expanded Match': 'Expanded Match'\n };\n\n return badgeMap[badgeType] || badgeType;\n};\n\n/**\n * Generate appropriate CTA text\n */\nexport const getCtaText = (\n recommendation: AdMeshRecommendation,\n context: 'button' | 'link' = 'button'\n): string => {\n const productName = recommendation.product_title || recommendation.title || 'Product';\n\n if (context === 'link') {\n return productName;\n }\n\n return `Learn More`;\n};\n\n/**\n * Check if recommendations have high match scores\n */\nexport const hasHighQualityMatches = (recommendations: AdMeshRecommendation[]): boolean => {\n return recommendations.some(rec => (rec.contextual_relevance_score || 0) >= 0.8);\n};\n\n/**\n * Generate compliant powered-by text\n */\nexport const getPoweredByText = (compact: boolean = false): string => {\n if (compact) {\n return \"\";\n }\n \n return \"Recommendations \";\n};\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshProductCardProps } from '../types/index';\nimport { AdMeshLinkTracker } from './AdMeshLinkTracker';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\nimport { useAdMeshStyles } from '../hooks/useAdMeshStyles';\nimport {\n getRecommendationLabel,\n getLabelTooltip,\n} from '../utils/disclosureUtils';\n\nexport const AdMeshProductCard: React.FC<AdMeshProductCardProps> = ({\n recommendation,\n theme,\n variation = 'default',\n className,\n style,\n sessionId\n}) => {\n // Validate recommendation - return null if empty or invalid\n if (!recommendation || typeof recommendation !== 'object') {\n logger.log('[AdMesh Product Card] No recommendation provided - not rendering');\n return null;\n }\n \n // Additional validation: ensure recommendation has at least one identifier\n if (!recommendation.recommendation_id && !recommendation.title) {\n logger.log('[AdMesh Product Card] Invalid recommendation object (missing required fields) - not rendering');\n return null;\n }\n\n // Inject styles automatically\n useAdMeshStyles();\n\n\n\n\n // Get content based on variation - prioritize creative_input fields\n const getVariationContent = () => {\n const creativeInput = recommendation.creative_input || {};\n \n // Get title from creative_input or fallback to legacy fields\n const title = creativeInput.product_name || recommendation.title || recommendation.product_title || '';\n \n // Get description based on variation\n let description = '';\n if (variation === 'simple') {\n // Simple variation uses short description or context snippet\n description = creativeInput.short_description || \n creativeInput.context_snippet || \n recommendation.product_summary || \n recommendation.weave_summary || \n recommendation.reason || '';\n } else {\n // Default variation uses long description or short description\n description = creativeInput.long_description || \n creativeInput.short_description || \n recommendation.product_summary || \n recommendation.weave_summary || \n recommendation.reason || '';\n }\n \n // Get CTA text from creative_input or fallback\n const ctaText = creativeInput.cta_label || title;\n\n if (variation === 'simple') {\n return {\n title,\n description,\n ctaText,\n isSimple: true\n };\n } else {\n return {\n title,\n description,\n ctaText\n };\n }\n };\n\n const content = getVariationContent();\n\n const cardClasses = classNames(\n 'admesh-component',\n 'admesh-card',\n 'relative p-3 sm:p-4 rounded-xl bg-gradient-to-br from-white to-gray-50 dark:from-slate-800 dark:to-slate-900 border border-gray-200/50 dark:border-slate-700/50 shadow-lg hover:shadow-xl transition-all duration-300 hover:-translate-y-1',\n className\n );\n\n const cardStyle = theme ? {\n '--admesh-primary': theme.primaryColor || theme.accentColor || '#3b82f6',\n '--admesh-secondary': theme.secondaryColor || '#10b981',\n '--admesh-accent': theme.accentColor || '#3b82f6',\n '--admesh-background': theme.backgroundColor,\n '--admesh-surface': theme.surfaceColor,\n '--admesh-border': theme.borderColor,\n '--admesh-text': theme.textColor,\n '--admesh-text-secondary': theme.textSecondaryColor,\n '--admesh-radius': theme.borderRadius || '12px',\n '--admesh-shadow-sm': theme.shadows?.small,\n '--admesh-shadow-md': theme.shadows?.medium,\n '--admesh-shadow-lg': theme.shadows?.large,\n '--admesh-spacing-sm': theme.spacing?.small,\n '--admesh-spacing-md': theme.spacing?.medium,\n '--admesh-spacing-lg': theme.spacing?.large,\n '--admesh-font-size-sm': theme.fontSize?.small,\n '--admesh-font-size-base': theme.fontSize?.base,\n '--admesh-font-size-lg': theme.fontSize?.large,\n '--admesh-font-size-title': theme.fontSize?.title,\n fontFamily: theme.fontFamily,\n // Ensure consistent width: 100% for all components except ecommerce\n width: theme.components?.productCard?.width || '100%'\n } as React.CSSProperties : { width: '100%' } as React.CSSProperties;\n\n // Render different layouts based on variation\n if (variation === 'simple') {\n // Simple inline ad format (replaces AdMeshSimpleAd)\n // Wrap with AdMeshViewabilityTracker for MRC-compliant exposure tracking\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={classNames(\n \"admesh-component admesh-simple-ad\",\n \"inline-block text-sm leading-relaxed\",\n className\n )}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n >\n <div\n data-admesh-theme={theme?.mode}\n >\n {/* Recommendation label */}\n <span\n style={{\n fontSize: '11px',\n fontWeight: '600',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff',\n backgroundColor: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n padding: '2px 6px',\n borderRadius: '4px',\n marginRight: '8px'\n }}\n title={getLabelTooltip(recommendation, getRecommendationLabel(recommendation))}\n >\n {getRecommendationLabel(recommendation)}\n </span>\n\n {/* Main content */}\n <span\n style={{\n color: theme?.mode === 'dark' ? '#f3f4f6' : '#374151',\n marginRight: '4px'\n }}\n >\n {content.description}{' '}\n </span>\n\n {/* CTA Link */}\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: content.title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'simple_ad_cta'\n }}\n >\n <span\n style={{\n color: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n textDecoration: 'underline',\n cursor: 'pointer',\n fontSize: 'inherit',\n fontFamily: 'inherit'\n }}\n >\n {content.ctaText}\n </span>\n </AdMeshLinkTracker>\n\n {/* Disclosure removed to prevent duplicate labels */}\n </div>\n </AdMeshViewabilityTracker>\n );\n }\n\n\n\n\n // Default full product card layout\n // Wrap with AdMeshViewabilityTracker for MRC-compliant exposure tracking\n const productId = recommendation.product_id || '';\n\n logger.debug('[AdMeshProductCard] 📊 Rendering with tracking data:', {\n recommendationId: recommendation.recommendation_id,\n productId,\n exposureUrl: recommendation.exposure_url ? 'present' : 'MISSING',\n sessionId: sessionId ? 'present' : 'MISSING'\n });\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={cardClasses}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n >\n <div\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...theme?.components?.productCard,\n ...style\n }}\n data-admesh-theme={theme?.mode}\n >\n <div\n className=\"h-full flex flex-col\"\n style={cardStyle}\n >\n {/* Recommendation label at top */}\n <div className=\"mb-1.5\">\n <span\n style={{\n fontSize: '11px',\n fontWeight: '600',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff',\n backgroundColor: theme?.mode === 'dark' ? '#ffffff' : '#000000',\n padding: '2px 6px',\n borderRadius: '4px'\n }}\n title={getLabelTooltip(recommendation, getRecommendationLabel(recommendation))}\n >\n {getRecommendationLabel(recommendation)}\n </span>\n </div>\n\n {/* Header with title and CTA button in same row */}\n <div className=\"flex justify-between items-center gap-3 mb-2\">\n <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n {(recommendation.creative_input?.assets?.logo_url || recommendation.product_logo?.url) && (\n <img\n src={recommendation.creative_input?.assets?.logo_url || recommendation.product_logo?.url}\n alt={`${content.title} logo`}\n className=\"w-6 h-6 rounded flex-shrink-0\"\n onError={(e) => {\n // Hide image if it fails to load\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n />\n )}\n <h4 className=\"font-semibold text-gray-800 dark:text-gray-200 text-sm sm:text-base truncate\">\n {content.title}\n </h4>\n </div>\n\n <div className=\"flex-shrink-0\">\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: content.title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'product_card_cta'\n }}\n >\n <button\n className=\"text-xs px-2 py-1 sm:px-3 sm:py-2 rounded-full flex items-center transition-all duration-200 transform hover:scale-105 shadow-md hover:shadow-lg whitespace-nowrap\"\n style={{\n backgroundColor: theme?.accentColor || '#000000',\n color: theme?.mode === 'dark' ? '#000000' : '#ffffff'\n }}\n >\n {recommendation.creative_input?.cta_label || 'Visit'}\n <svg className=\"ml-1 h-3 w-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\" />\n </svg>\n </button>\n </AdMeshLinkTracker>\n </div>\n </div>\n\n {/* Product Description/Reason */}\n <div className=\"mb-3\">\n <p className=\"text-sm text-gray-600 dark:text-gray-300 leading-snug\">\n {content.description}\n </p>\n </div>\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <div className=\"mb-3\">\n <p className=\"text-xs text-gray-500 dark:text-gray-400 leading-relaxed italic\">\n {recommendation.creative_input.offer_summary}\n </p>\n </div>\n )}\n\n {/* Value Props from creative_input */}\n {recommendation.creative_input?.value_props && recommendation.creative_input.value_props.length > 0 && (\n <div className=\"mb-3\">\n <ul className=\"space-y-1.5\">\n {recommendation.creative_input.value_props.slice(0, 3).map((prop, index) => (\n <li key={index} className=\"flex items-start gap-2 text-xs text-gray-600 dark:text-gray-400\">\n <svg className=\"w-3 h-3 mt-0.5 flex-shrink-0 text-green-500\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path fillRule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clipRule=\"evenodd\" />\n </svg>\n <span>{prop}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n\n \n\n\n\n\n\n\n\n\n\n\n\n\n {/* Footer section with disclosure */}\n <div className=\"mt-auto pt-3 border-t border-gray-100 dark:border-slate-700\">\n <div className=\"flex items-center justify-between text-xs text-gray-500 dark:text-gray-400\">\n <span>\n Sponsored\n </span>\n <span className=\"text-gray-400 dark:text-gray-500\">\n \n </span>\n </div>\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nAdMeshProductCard.displayName = 'AdMeshProductCard';\n","import React from 'react';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport type { AdMeshTheme } from '../types/index';\n\n/**\n * Context value provided by AdMeshProvider\n */\nexport interface AdMeshContextValue {\n // SDK instance\n sdk: AdMeshSDK | null;\n\n // Configuration\n apiKey: string;\n sessionId: string;\n theme?: AdMeshTheme;\n\n // UCP PlatformRequest fields (optional, passed from frontend)\n language?: string; // User language in BCP 47 format (e.g., \"en-US\")\n geo_country?: string; // User country code in ISO 3166-1 alpha-2 format (e.g., \"US\")\n userId?: string; // Anonymous hashed user ID\n model?: string; // AI model identifier (e.g., \"gpt-4o\")\n messages?: Array<{ role: string; content: string; id?: string }>; // Conversation history\n\n // Tracking state\n processedMessageIds: Set<string>;\n\n // Methods\n markMessageAsProcessed: (messageId: string) => void;\n isMessageProcessed: (messageId: string) => boolean;\n}\n\n/**\n * React Context for AdMesh SDK\n * \n * Provides SDK instance and tracking state to all child components\n */\nexport const AdMeshContext = React.createContext<AdMeshContextValue | undefined>(\n undefined\n);\n\n/**\n * Hook to access AdMesh context\n * \n * @throws Error if used outside of AdMeshProvider\n * @returns AdMeshContextValue\n */\nexport function useAdMeshContext(): AdMeshContextValue {\n const context = React.useContext(AdMeshContext);\n \n if (!context) {\n throw new Error(\n 'useAdMeshContext must be used within an <AdMeshProvider>. ' +\n 'Make sure your component is wrapped with <AdMeshProvider>.'\n );\n }\n \n return context;\n}\n\n","import { useAdMeshContext } from '../context/AdMeshContext';\n\n/**\n * Hook to access AdMesh SDK and tracking state\n *\n * Must be used within an <AdMeshProvider>\n *\n * @returns Object with SDK instance and tracking methods\n *\n * @example\n * ```tsx\n * const { sdk, sessionId, markMessageAsProcessed } = useAdMesh();\n *\n * // Use SDK to show recommendations\n * await sdk?.showRecommendations({\n * query: 'user query',\n * containerId: 'recommendations-container',\n * session_id: sessionId,\n * message_id: messageId\n * });\n *\n * // Mark message as processed to avoid duplicates\n * markMessageAsProcessed(messageId);\n * ```\n */\nexport function useAdMesh() {\n const context = useAdMeshContext();\n\n return {\n /** AdMesh SDK instance */\n sdk: context.sdk,\n\n /** API key */\n apiKey: context.apiKey,\n\n /** Session ID */\n sessionId: context.sessionId,\n\n /** Theme configuration */\n theme: context.theme,\n\n /** User language in BCP 47 format (e.g., \"en-US\") - from AdMeshProvider */\n language: context.language,\n\n /** User country code in ISO 3166-1 alpha-2 format (e.g., \"US\") - from AdMeshProvider */\n geo_country: context.geo_country,\n\n /** Anonymous hashed user ID - from AdMeshProvider */\n userId: context.userId,\n\n /** AI model identifier (e.g., \"gpt-4o\") - from AdMeshProvider */\n model: context.model,\n\n /** Conversation history - from AdMeshProvider */\n messages: context.messages,\n\n /** Set of processed message IDs (for deduplication) */\n processedMessageIds: context.processedMessageIds,\n\n /** Mark a message as processed to prevent duplicate recommendations */\n markMessageAsProcessed: context.markMessageAsProcessed,\n\n /** Check if a message has already been processed */\n isMessageProcessed: context.isMessageProcessed,\n };\n}\n\nexport default useAdMesh;\n\n","'use client';\n\nimport React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\nimport { useAdMesh } from '../hooks/useAdMesh';\n\nexport interface AdMeshBridgeFormatProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n sessionId?: string;\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n /** Callback to paste content to input field (for CTA button) */\n onPasteToInput?: (content: string) => void;\n}\n\n/**\n * AdMeshBridgeFormat - Bridge Ad Format Component\n * \n * Displays bridge format recommendations as followup sponsored recommendations with setup prompts and documentation URLs.\n * Bridge format is primarily designed for Vibe Coding Platforms and AI IDEs, and can also be shown in AI search as followup suggestions.\n * These recommendations appear after initial responses to provide setup instructions and configuration guidance.\n * \n * @example\n * ```tsx\n * <AdMeshBridgeFormat\n * recommendation={recommendation}\n * sessionId={sessionId}\n * theme={theme}\n * />\n * ```\n */\n/**\n * Extract CTA text from bridge_prompt / bridge_content\n * Generates \"Integrate [Product]\" format based on product name in the prompt\n */\nconst extractCTAText = (bridgePrompt: string, productName?: string): string => {\n if (!bridgePrompt) return 'Get Started';\n\n // Try to extract product name from prompt first\n let extractedProduct = '';\n \n // Patterns to extract product name\n const productPatterns = [\n /(?:set up|setup|integrate|add|install|use)\\s+([A-Z][a-zA-Z0-9\\s]+?)(?:\\.|$|,| by)/i,\n /(?:by|from)\\s+([A-Z][a-zA-Z0-9\\s]+?)(?:\\.|$|,)/,\n ];\n\n for (const pattern of productPatterns) {\n const match = bridgePrompt.match(pattern);\n if (match && match[1]) {\n extractedProduct = match[1].trim();\n break;\n }\n }\n\n // Use provided productName or extracted product\n const product = productName || extractedProduct;\n \n if (product) {\n // Clean up product name (remove extra words, keep main product name)\n const cleanProduct = product.split(/\\s+/).slice(0, 2).join(' '); // Take first 2 words max\n return `Integrate ${cleanProduct}`;\n }\n\n // Fallback: try to extract from \"To set up [Product]\" pattern\n const setupMatch = bridgePrompt.match(/to\\s+set\\s+up\\s+([a-zA-Z0-9\\s]+?)(?:\\s+by|\\s*[.,]|$)/i);\n if (setupMatch && setupMatch[1]) {\n const product = setupMatch[1].trim().split(/\\s+/).slice(0, 2).join(' ');\n return `Integrate ${product}`;\n }\n\n return 'Integrate';\n};\n\nexport const AdMeshBridgeFormat: React.FC<AdMeshBridgeFormatProps> = ({\n recommendation,\n theme,\n className = '',\n style = {},\n sessionId,\n onLinkClick,\n onPasteToInput,\n}) => {\n const creativeInput = recommendation.creative_input || {};\n // Extract bridge format fields\n const bridgeHeadline = (creativeInput as any).bridge_headline || '';\n const bridgeDescription = (creativeInput as any).bridge_description || '';\n const bridgePrompt =\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content ||\n '';\n const productName = creativeInput.product_name || '';\n const ctaLabel = creativeInput.cta_label || '';\n\n // If no bridge description or prompt, don't render\n if (!bridgeDescription && !bridgePrompt) {\n return null;\n }\n\n const productId = recommendation.product_id || '';\n const recommendationId = recommendation.recommendation_id || '';\n \n // Get SDK and sessionId from context for API calls\n const { sdk, sessionId: contextSessionId } = useAdMesh();\n const effectiveSessionId = sessionId || contextSessionId;\n \n // Bridge format doesn't use click_url or admesh_link - it only pastes the prompt\n // Exposure pixel is fired by AdMeshViewabilityTracker when ad becomes viewable (MRC-compliant)\n\n const handleCTAClick = async (e: React.MouseEvent) => {\n e.preventDefault();\n \n if (!bridgePrompt) return;\n\n // Track bridge engagement (sets status to \"engaged\")\n if (recommendationId && effectiveSessionId) {\n try {\n // Get API base URL from SDK or use default\n const apiBaseUrl = (sdk as any)?.apiBaseUrl || \n (typeof window !== 'undefined' && (window as any).__ADMESH_API_BASE_URL__) ||\n 'https://api.useadmesh.com';\n \n const response = await fetch(`${apiBaseUrl}/click/bridge-engagement`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n recommendation_id: recommendationId,\n session_id: effectiveSessionId,\n agent_id: recommendation.agent_id,\n user_id: (recommendation as any).user_id || undefined,\n is_test: false, // TODO: Get from config if needed\n }),\n });\n\n if (response.ok) {\n logger.log('[AdMesh Bridge] ✅ Engagement tracked successfully');\n } else {\n logger.warn('[AdMesh Bridge] ⚠️ Failed to track engagement:', response.statusText);\n }\n } catch (error) {\n logger.error('[AdMesh Bridge] ❌ Error tracking engagement:', error);\n // Don't block the user action if tracking fails\n }\n }\n\n // Call onLinkClick if provided (for tracking purposes)\n if (onLinkClick) {\n onLinkClick(recommendation);\n }\n\n // Paste prompt to input if callback provided\n if (onPasteToInput) {\n onPasteToInput(bridgePrompt);\n } else if (typeof window !== 'undefined' && (window as any).__admesh_setMessage) {\n // Fallback to window object\n (window as any).__admesh_setMessage(bridgePrompt);\n }\n };\n\n // Use cta_label from creative_input if available, otherwise extract from prompt\n const ctaText = ctaLabel || extractCTAText(bridgePrompt, productName);\n \n // Always show CTA if we have text for it\n const shouldShowCTA = !!ctaText;\n\n return (\n <AdMeshViewabilityTracker\n productId={productId}\n recommendationId={recommendation.recommendation_id || ''}\n exposureUrl={recommendation.exposure_url}\n sessionId={sessionId}\n className={`admesh-bridge-format ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n padding: '1rem',\n border: 'none',\n borderRadius: theme?.borderRadius || '0.5rem',\n backgroundColor: 'transparent',\n color: theme?.textColor || 'inherit',\n ...style\n }}\n >\n <div data-admesh-theme={theme?.mode || 'light'}>\n {/* Bridge Headline */}\n {bridgeHeadline && (\n <div\n className=\"admesh-bridge-headline\"\n style={{\n marginBottom: bridgeDescription ? '0.75rem' : shouldShowCTA ? '1rem' : '0',\n fontSize: theme?.fontSize?.large || '1rem',\n fontWeight: 600,\n color: theme?.textColor || 'inherit',\n lineHeight: '1.4',\n }}\n >\n {bridgeHeadline}\n </div>\n )}\n\n {/* Brand Description (1 sentence, short) - shown initially */}\n {bridgeDescription && (\n <div\n className=\"admesh-bridge-description\"\n style={{\n marginBottom: (shouldShowCTA || recommendation.creative_input?.offer_summary) ? '1rem' : '0',\n lineHeight: '1.6',\n fontSize: theme?.fontSize?.base || '0.875rem',\n color: theme?.textColor || 'inherit',\n }}\n >\n {bridgeDescription}\n </div>\n )}\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <div\n className=\"admesh-bridge-offer-summary\"\n style={{\n marginBottom: shouldShowCTA ? '1rem' : '0',\n lineHeight: '1.5',\n fontSize: theme?.fontSize?.small || '0.75rem',\n color: theme?.textSecondaryColor || theme?.mode === 'dark' ? '#9ca3af' : '#6b7280',\n fontStyle: 'italic',\n }}\n >\n {recommendation.creative_input.offer_summary}\n </div>\n )}\n\n {/* CTA Button - Left aligned, just below description */}\n {shouldShowCTA && (\n <div style={{ marginBottom: '0.75rem' }}>\n <button\n onClick={handleCTAClick}\n className=\"admesh-bridge-cta-button\"\n style={{\n padding: '0.5rem 1rem',\n backgroundColor: '#000000',\n color: '#ffffff',\n fontSize: theme?.fontSize?.small || '0.875rem',\n fontWeight: 500,\n borderRadius: theme?.borderRadius || '0.5rem',\n border: 'none',\n cursor: 'pointer',\n transition: 'background-color 0.2s, opacity 0.2s',\n }}\n onMouseEnter={(e) => {\n e.currentTarget.style.backgroundColor = '#1a1a1a';\n e.currentTarget.style.opacity = '0.9';\n }}\n onMouseLeave={(e) => {\n e.currentTarget.style.backgroundColor = '#000000';\n e.currentTarget.style.opacity = '1';\n }}\n >\n {ctaText}\n </button>\n </div>\n )}\n\n {/* Sponsored Label - Right aligned */}\n <div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '0.5rem' }}>\n <div\n className=\"admesh-bridge-label\"\n style={{\n fontSize: theme?.fontSize?.small || '0.75rem',\n color: theme?.textSecondaryColor || theme?.mode === 'dark' ? '#9ca3af' : '#6b7280',\n fontStyle: 'italic',\n }}\n >\n Sponsored\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n\nexport default AdMeshBridgeFormat;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshTailAd } from './AdMeshTailAd';\nimport { AdMeshProductCard } from './AdMeshProductCard';\nimport { AdMeshBridgeFormat } from './AdMeshBridgeFormat';\n\nexport interface AdMeshSummaryLayoutProps {\n // Backend response data (finalized minimal schema)\n recommendations: AdMeshRecommendation[];\n summaryText?: string;\n\n // Styling\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n\n // Behavior\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\n /** Callback to paste content to input field (for bridge format CTA) */\n onPasteToInput?: (content: string) => void;\n\n // Exposure tracking\n sessionId?: string;\n\n // Legacy props for backward compatibility (deprecated)\n response?: {\n layout_type?: string;\n tail_summary?: string;\n recommendations?: AdMeshRecommendation[];\n requires_summary?: boolean;\n };\n}\n\nexport const AdMeshSummaryLayout: React.FC<AdMeshSummaryLayoutProps> = ({\n recommendations,\n summaryText,\n theme,\n className = '',\n style = {},\n onLinkClick,\n onPasteToInput,\n sessionId,\n response // Legacy prop for backward compatibility\n}) => {\n // Support both new and legacy props\n const recs = recommendations || response?.recommendations || [];\n \n // Filter out any null/undefined/invalid recommendations\n const validRecs = recs.filter(rec => rec && typeof rec === 'object' && rec.recommendation_id);\n \n // If no valid recommendations, silently return null\n if (!validRecs || validRecs.length === 0) {\n logger.log('[AdMesh Summary Layout] Empty or invalid recommendations array - not rendering anything');\n return null;\n }\n \n // Get summary text from multiple sources, prioritizing creative_input\n let summary = summaryText || response?.tail_summary;\n \n // If no summary provided, try to get it from first recommendation's creative_input\n if (!summary && validRecs.length > 0 && validRecs[0]?.creative_input) {\n const creativeInput = validRecs[0].creative_input;\n summary = creativeInput.long_description || \n creativeInput.context_snippet || \n creativeInput.short_description || \n undefined;\n }\n\n logger.log(`[AdMesh Summary Layout] Rendering with ${validRecs.length} valid recommendations`);\n\n // Render based on layout type (default to tail)\n const renderContent = () => {\n // Check if first recommendation has bridge format\n // Priority: 1) bridge_content in creative_input, 2) format field, 3) preferred_format field\n if (validRecs.length > 0) {\n const firstRec = validRecs[0];\n const creativeInput = firstRec?.creative_input || {};\n const hasBridgePrompt = !!(creativeInput as any).bridge_prompt || !!creativeInput.bridge_content;\n const format = (firstRec as any)?.format || (creativeInput as any)?.format;\n const preferredFormat = (firstRec as any)?.preferred_format;\n const hasBridgeFormat = format === 'bridge' || preferredFormat === 'bridge';\n \n logger.log('[AdMesh Summary Layout] 🔍 Checking format:', {\n hasBridgePrompt,\n bridgePrompt: (creativeInput as any).bridge_prompt || creativeInput.bridge_content ? ((creativeInput as any).bridge_prompt || creativeInput.bridge_content || '').substring(0, 50) + '...' : undefined,\n format,\n preferredFormat,\n creativeInputKeys: Object.keys(creativeInput),\n recommendationKeys: Object.keys(firstRec || {})\n });\n \n if (hasBridgePrompt || hasBridgeFormat) {\n logger.log('[AdMesh Summary Layout] 🎯 ✅ Rendering bridge format');\n return (\n <AdMeshBridgeFormat\n recommendation={firstRec}\n theme={theme}\n sessionId={sessionId}\n onLinkClick={onLinkClick}\n onPasteToInput={onPasteToInput}\n />\n );\n } else {\n logger.log('[AdMesh Summary Layout] ⚠️ Bridge format NOT detected, falling back to other formats');\n }\n }\n\n // Show summary if available (tail format)\n if (summary) {\n return (\n <AdMeshTailAd\n summaryText={summary}\n recommendations={validRecs}\n theme={theme}\n onLinkClick={onLinkClick}\n sessionId={sessionId}\n />\n );\n }\n // Fallback to first recommendation if no summary - but only if we have a valid recommendation\n if (validRecs.length > 0 && validRecs[0]) {\n return (\n <div className=\"fallback-citation\">\n <AdMeshProductCard\n recommendation={validRecs[0]}\n theme={theme}\n sessionId={sessionId}\n />\n </div>\n );\n }\n // If no summary and no valid recommendation, don't render anything\n return null;\n };\n\n return (\n <div\n className={`admesh-summary-layout ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n {renderContent()}\n </div>\n );\n};\n\nexport default AdMeshSummaryLayout;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshLayoutProps } from '../types/index';\nimport { AdMeshSummaryLayout } from './AdMeshSummaryLayout';\n\nexport const AdMeshLayout: React.FC<AdMeshLayoutProps> = ({\n // New props (finalized minimal schema)\n recommendations,\n summaryText,\n\n // Styling\n theme,\n className,\n style,\n\n // Behavior\n onLinkClick,\n onPasteToInput,\n\n // Exposure tracking\n sessionId,\n\n // Legacy props (deprecated)\n response\n}) => {\n // Support both new and legacy props for backward compatibility\n const recs = recommendations || response?.recommendations || [];\n const summary = summaryText || response?.tail_summary;\n\n // Filter out any null/undefined/invalid recommendations\n const validRecs = recs.filter(rec => rec && typeof rec === 'object' && rec.recommendation_id);\n\n // Validate that valid recommendations are provided\n if (!validRecs || validRecs.length === 0) {\n logger.log('[AdMeshLayout] Empty or invalid recommendations array - not rendering anything');\n return null;\n }\n\n return (\n <AdMeshSummaryLayout\n recommendations={validRecs}\n summaryText={summary}\n theme={theme}\n className={className}\n style={style}\n onLinkClick={onLinkClick}\n onPasteToInput={onPasteToInput}\n sessionId={sessionId}\n />\n );\n};\n\nexport default AdMeshLayout;\n","'use client';\n\nimport React from 'react';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshFollowupProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n sdk: AdMeshSDK;\n sessionId: string;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\nconst PlusIcon = ({ className, size = 20 }: { className?: string; size?: number }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n <path d=\"M5 12h14\" />\n <path d=\"M12 5v14\" />\n </svg>\n);\n\n/**\n * AdMeshFollowup - Sponsored Follow-up Component\n * \n * Displays sponsored follow-up suggestions as optional fields on any recommendation.\n * Follow-ups use the same recommendation_id as the primary ad and are rendered separately\n * in a followups_container_id. This component handles all exposure and engagement tracking\n * internally - platforms only need to provide an onExecuteQuery hook for query execution.\n * \n * @example\n * ```tsx\n * <AdMeshFollowup\n * recommendation={recommendation}\n * sdk={sdk}\n * sessionId={sessionId}\n * theme={theme}\n * onExecuteQuery={(query) => {\n * // Platform's query execution logic\n * executeQuery(query);\n * }}\n * />\n * ```\n */\nexport const AdMeshFollowup: React.FC<AdMeshFollowupProps> = ({\n recommendation,\n theme,\n sdk,\n sessionId,\n onExecuteQuery,\n}) => {\n const followupQuery = recommendation.followup_query;\n const followupEngagementUrl = recommendation.followup_engagement_url;\n const followupExposureUrl = recommendation.followup_exposure_url; // Dedicated followup exposure URL\n const recommendationId = recommendation.recommendation_id;\n\n // Validate required fields\n if (!followupQuery || !followupEngagementUrl || !followupExposureUrl) {\n logger.log('[AdMeshFollowup] Missing followup_query, followup_engagement_url, or followup_exposure_url - not rendering');\n return null;\n }\n\n // Handle follow-up click/selection\n const handleFollowupClick = async () => {\n try {\n // Fire engagement tracking (SDK handles this automatically)\n if (followupEngagementUrl && recommendationId) {\n logger.log('[AdMeshFollowup] 🎯 Firing follow-up engagement tracking');\n await sdk.fireFollowupEngagement(followupEngagementUrl, recommendationId, sessionId);\n }\n\n // Execute query via platform hook\n if (onExecuteQuery && followupQuery) {\n logger.log(`[AdMeshFollowup] 🔍 Executing query: ${followupQuery}`);\n await onExecuteQuery(followupQuery);\n } else {\n logger.warn('[AdMeshFollowup] ⚠️ onExecuteQuery not provided - cannot execute query');\n }\n } catch (error) {\n logger.error('[AdMeshFollowup] ❌ Error handling follow-up click:', error);\n }\n };\n\n // Get theme colors\n const mode = theme?.mode || 'light';\n\n // Platform-native styling (matching Perplexica's \"Related\" section)\n // We use inline styles that can be overridden, but default to blending in\n\n return (\n <AdMeshViewabilityTracker\n productId={recommendation.product_id || ''}\n recommendationId={recommendationId || ''}\n exposureUrl={followupExposureUrl}\n sessionId={sessionId}\n className=\"admesh-followup-container\"\n style={{\n width: '100%',\n }}\n >\n <div className=\"flex flex-col space-y-3 text-sm admesh-followup-wrapper\">\n {/* Divider matching platform style */}\n <div className=\"h-px w-full bg-[#E5E5E5] dark:bg-[#262626] admesh-divider\" />\n\n <div\n onClick={handleFollowupClick}\n className=\"cursor-pointer flex flex-row justify-between font-medium space-x-2 items-center group\"\n role=\"button\"\n tabIndex={0}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n handleFollowupClick();\n }\n }}\n aria-label={`Sponsored follow-up: ${followupQuery}`}\n >\n <p className=\"transition duration-200 text-[#000] dark:text-[#FFF] hover:text-[#24A0ED] admesh-followup-text\">\n {followupQuery}\n </p>\n <div className=\"flex flex-row items-center space-x-2\">\n <span className=\"text-xs text-gray-500 dark:text-gray-400 italic admesh-ad-label\">\n Ad\n </span>\n <PlusIcon\n size={20}\n className=\"text-[#24A0ED] flex-shrink-0 admesh-plus-icon\"\n />\n </div>\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n};\n","/**\n * AdMesh Tracker\n *\n * Handles MRC-compliant exposure tracking for recommendations\n *\n * MRC Viewability Standards:\n * - Display Ads: 50% of pixels visible for at least 1 continuous second\n * - Large Display Ads (>242,500 pixels): 30% of pixels visible for at least 1 continuous second\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface TrackerConfig {\n apiKey: string;\n debug?: boolean;\n}\n\n/**\n * MRC Viewability threshold configuration\n */\ninterface MRCThreshold {\n visibilityPercentage: number; // 50% for standard ads, 30% for large ads\n minimumDurationMs: number; // 1000ms (1 second)\n}\n\nexport class AdMeshTracker {\n private firedExposures: Set<string> = new Set();\n private debug: boolean = false;\n private mrcThreshold: MRCThreshold = {\n visibilityPercentage: 50,\n minimumDurationMs: 1000\n };\n\n constructor(config: TrackerConfig) {\n this.debug = config.debug || false;\n }\n\n /**\n * Fire an exposure tracking pixel with MRC viewability compliance\n *\n * This method should be called when an ad element becomes viewable according to MRC standards.\n * The caller is responsible for ensuring the ad meets the MRC threshold before calling this method.\n *\n * Prevents duplicate firing for the same ad in the same session.\n *\n * @param exposureUrl - The tracking pixel URL to fire\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireExposure(exposureUrl: string, recommendationId: string, sessionId: string): void {\n const key = `${sessionId}_${recommendationId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n logger.log('[Tracker] Exposure already fired');\n }\n return;\n }\n\n this.firedExposures.add(key);\n\n try {\n // Fire the exposure pixel\n // Note: The caller should ensure MRC compliance before calling this method\n fetch(exposureUrl, { method: 'GET', keepalive: true }).catch(() => {\n if (this.debug) {\n logger.warn('[Tracker] Failed to fire exposure');\n }\n });\n\n if (this.debug) {\n logger.log('[Tracker] Fired MRC-compliant exposure');\n }\n } catch (error) {\n if (this.debug) {\n logger.error('[Tracker] Error firing exposure');\n }\n }\n }\n\n /**\n * Fire an exposure pixel with MRC viewability verification\n *\n * This method monitors an element for MRC viewability compliance before firing the exposure pixel.\n * It uses Intersection Observer API to track visibility and ensures the ad meets the threshold\n * (50% visible for 1 continuous second) before firing.\n *\n * @param exposureUrl - The tracking pixel URL to fire\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n * @param element - The DOM element to monitor for viewability\n * @returns Promise that resolves when exposure is fired or timeout occurs\n */\n async fireExposureWithMRCCompliance(\n exposureUrl: string,\n recommendationId: string,\n sessionId: string,\n element: HTMLElement\n ): Promise<void> {\n const key = `${sessionId}_${recommendationId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n logger.log('[Tracker] MRC exposure already fired');\n }\n return;\n }\n\n return new Promise((resolve) => {\n let viewableStartTime: number | null = null;\n let timeoutId: NodeJS.Timeout | null = null;\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const visibilityPercentage = (entry.intersectionRatio * 100);\n\n if (visibilityPercentage >= this.mrcThreshold.visibilityPercentage) {\n // Ad is visible enough\n if (viewableStartTime === null) {\n // Start tracking viewable duration\n viewableStartTime = Date.now();\n\n if (this.debug) {\n logger.log('[Tracker] Ad reached MRC visibility threshold');\n }\n\n // Set timeout to fire exposure after minimum duration\n timeoutId = setTimeout(() => {\n // Fire the exposure pixel\n this.fireExposure(exposureUrl, recommendationId, sessionId);\n observer.disconnect();\n resolve();\n }, this.mrcThreshold.minimumDurationMs);\n }\n } else {\n // Ad visibility dropped below threshold\n if (viewableStartTime !== null) {\n // Reset tracking\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n viewableStartTime = null;\n\n if (this.debug) {\n logger.log('[Tracker] Ad visibility dropped below MRC threshold');\n }\n }\n }\n });\n },\n {\n threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],\n rootMargin: '0px'\n }\n );\n\n observer.observe(element);\n\n // Cleanup on unmount or after reasonable timeout\n const maxWaitTime = 30000; // 30 seconds max wait\n const cleanupTimeout = setTimeout(() => {\n observer.disconnect();\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n resolve();\n }, maxWaitTime);\n\n // Store cleanup function for manual cleanup if needed\n (element as any).__admeshTrackerCleanup = () => {\n observer.disconnect();\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n clearTimeout(cleanupTimeout);\n };\n });\n }\n\n /**\n * Clear fired exposures (useful for testing or session reset)\n */\n clearFiredExposures(): void {\n this.firedExposures.clear();\n }\n\n /**\n * Get MRC threshold configuration\n */\n getMRCThreshold(): MRCThreshold {\n return { ...this.mrcThreshold };\n }\n\n /**\n * Set custom MRC threshold (for testing or special cases)\n */\n setMRCThreshold(threshold: Partial<MRCThreshold>): void {\n this.mrcThreshold = {\n ...this.mrcThreshold,\n ...threshold\n };\n }\n\n /**\n * Fire exposure pixel for sponsored followup\n * Uses same logic as regular exposure tracking\n * \n * @param exposureUrl - The tracking pixel URL to fire (can use regular exposure_url)\n * @param recommendationId - The recommendation ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireFollowupExposure(\n exposureUrl: string,\n recommendationId: string,\n sessionId: string\n ): void {\n // Reuse existing fireExposure() method\n this.fireExposure(exposureUrl, recommendationId, sessionId);\n }\n\n /**\n * Fire engagement tracking for sponsored followup\n * \n * @param engagementUrl - The engagement tracking URL to fire\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID (sent in POST body for analytics joining)\n * @returns Promise that resolves when engagement is fired\n */\n fireFollowupEngagement(\n engagementUrl: string,\n recommendationId: string,\n sessionId: string\n ): Promise<void> {\n // Use fetch with keepalive and POST method\n // Always resolve the promise even on error to prevent unhandled rejections\n return fetch(engagementUrl, {\n method: 'POST',\n keepalive: true,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ session_id: sessionId })\n }).catch((error) => {\n // Log error silently (only in debug mode) - network errors are expected and shouldn't break the flow\n if (this.debug) {\n logger.warn('[Tracker] Failed to fire followup engagement (non-critical):', error);\n }\n // Return undefined to resolve the promise successfully\n // This prevents the error from propagating to callers\n return undefined;\n }).then(() => {\n // Ensure promise always resolves with void (no return value)\n // This makes the return type consistent: Promise<void>\n });\n }\n}\n","/**\n * AdMesh Renderer\n * \n * Handles rendering of recommendations in the specified container\n */\n\nimport ReactDOM from 'react-dom/client';\nimport type { AgentRecommendationResponse, AdMeshTheme, AdMeshRecommendation } from '../types/index';\nimport { AdMeshLayout } from '../components/AdMeshLayout';\nimport { AdMeshFollowup } from '../components/AdMeshFollowup';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport interface RenderOptions {\n containerId: string;\n followups_container_id?: string;\n response: AgentRecommendationResponse;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\n onPasteToInput?: (content: string) => void;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\nexport class AdMeshRenderer {\n private roots: Map<string, ReactDOM.Root> = new Map();\n\n constructor() {\n // No configuration needed\n }\n\n /**\n * Render recommendations in the specified container\n */\n async render(options: RenderOptions): Promise<void> {\n try {\n logger.log('[AdMeshRenderer] 🎨 Attempting to render recommendations');\n\n // Check if we have any recommendations to render\n const recommendations = options.response.recommendations || [];\n if (recommendations.length === 0) {\n logger.log('[AdMeshRenderer] ⚠️ No recommendations to render - skipping render to avoid occupying space');\n return;\n }\n\n const container = document.getElementById(options.containerId);\n\n if (!container) {\n logger.error('[AdMeshRenderer] ❌ Container not found');\n throw new Error(`Container with ID \"${options.containerId}\" not found`);\n }\n\n logger.log('[AdMeshRenderer] ✅ Container ready');\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n logger.log('[AdMeshRenderer] 🧹 Cleaning up existing root');\n existingRoot.unmount();\n this.roots.delete(options.containerId);\n }\n\n // Clear the container's innerHTML to ensure React can create a fresh root\n container.innerHTML = '';\n\n // Create a new root\n const root = ReactDOM.createRoot(container);\n\n // Render the layout component\n // Use tail_summary from first recommendation if available\n const tailSummary = recommendations[0]?.tail_summary || '';\n\n logger.log('[AdMeshRenderer] 📊 Rendering recommendations');\n\n // Get onPasteToInput from options or window object (for backward compatibility)\n const onPasteToInput = options.onPasteToInput || \n (typeof window !== 'undefined' ? (window as any).__admesh_onPasteToInput : undefined);\n\n root.render(\n <AdMeshLayout\n recommendations={recommendations}\n summaryText={tailSummary}\n theme={options.theme}\n sessionId={options.sessionId}\n onPasteToInput={onPasteToInput}\n />\n );\n\n // Show the container now that content is rendered (prevents empty space when no ads)\n container.style.display = 'block';\n\n logger.log('[AdMeshRenderer] ✅ Recommendations rendered successfully');\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n\n // After rendering primary format, check for follow-ups\n if (options.followups_container_id) {\n const recommendation = recommendations[0];\n if (recommendation?.followup_query && recommendation?.followup_engagement_url) {\n // Render follow-up in followups_container_id\n await this.renderFollowup({\n containerId: options.followups_container_id,\n recommendation,\n theme: options.theme,\n tracker: options.tracker,\n sessionId: options.sessionId,\n onExecuteQuery: options.onExecuteQuery\n });\n }\n }\n } catch (error) {\n logger.error('[AdMeshRenderer] ❌ Error rendering recommendations');\n throw error;\n }\n }\n\n /**\n * Render follow-up in the specified container\n */\n private async renderFollowup(options: {\n containerId: string;\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\n onExecuteQuery?: (query: string) => void | Promise<void>;\n }): Promise<void> {\n try {\n logger.log(`[AdMeshRenderer] 🎯 Rendering follow-up in container: ${options.containerId}`);\n\n const container = document.getElementById(options.containerId);\n if (!container) {\n logger.warn(`[AdMeshRenderer] ⚠️ Followups container not found: ${options.containerId}`);\n return;\n }\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n logger.log('[AdMeshRenderer] 🧹 Cleaning up existing followup root');\n existingRoot.unmount();\n this.roots.delete(options.containerId);\n }\n\n // Clear container\n container.innerHTML = '';\n\n // Create a new root\n const root = ReactDOM.createRoot(container);\n\n root.render(\n <AdMeshFollowup\n recommendation={options.recommendation}\n theme={options.theme}\n tracker={options.tracker}\n sessionId={options.sessionId}\n onExecuteQuery={options.onExecuteQuery}\n />\n );\n\n // Show the container\n container.style.display = 'block';\n\n logger.log('[AdMeshRenderer] ✅ Follow-up rendered successfully');\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n } catch (error) {\n logger.error('[AdMeshRenderer] ❌ Error rendering follow-up');\n // Don't throw - follow-up rendering failure shouldn't break primary ad\n }\n }\n\n /**\n * Unmount a rendered component\n */\n unmount(containerId: string): void {\n const root = this.roots.get(containerId);\n if (root) {\n root.unmount();\n this.roots.delete(containerId);\n \n // Hide the container again to prevent empty space\n const container = document.getElementById(containerId);\n if (container) {\n container.style.display = 'none';\n }\n }\n }\n\n /**\n * Unmount all rendered components\n */\n unmountAll(): void {\n for (const [, root] of this.roots.entries()) {\n root.unmount();\n }\n this.roots.clear();\n }\n}\n","/**\n * AdMesh UI SDK - Zero-Code Integration\n * \n * Provides a simple, zero-code integration experience for platforms.\n * Handles all recommendation fetching, rendering, and tracking automatically.\n */\n\nimport type { AdMeshTheme, AgentRecommendationResponse, AdMeshRecommendation, PlatformRequest, AIPContextResponse } from '../types/index';\nimport { AdMeshRenderer } from './AdMeshRenderer';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { logger } from '../utils/logger';\n\nexport interface AdMeshSDKConfig {\n apiKey: string;\n theme?: AdMeshTheme;\n apiBaseUrl?: string;\n}\n\nexport interface ShowRecommendationsOptions {\n query: string;\n containerId: string;\n theme?: AdMeshTheme;\n\n // Optional follow-ups container (SDK will render follow-ups here if provided and followup_query is present)\n followups_container_id?: string;\n\n // Session tracking (required)\n session_id: string;\n\n // Message tracking (required)\n // messageId MUST be provided by the platform - SDK never generates it\n messageId: string;\n\n // Platform information\n // platformId is now extracted from API key by the backend\n platformSurface?: string;\n model?: string;\n messages?: Array<{ role: string; content: string }>;\n locale?: string;\n geo?: string;\n userId?: string;\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config (configured during onboarding)\n \n /** Callback to paste content to input field (for bridge format CTA) */\n onPasteToInput?: (content: string) => void;\n \n /** Callback to execute query when follow-up is selected (required for follow-up functionality) */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n}\n\n/**\n * Main AdMesh SDK class for zero-code integration\n *\n * The SDK is stateless regarding session management. Developers must provide\n * session_id when calling showRecommendations().\n *\n * @example\n * ```typescript\n * import { AdMeshSDK } from '@admesh/ui-sdk';\n *\n * const admesh = new AdMeshSDK({ apiKey: 'your-api-key' });\n *\n * // Generate session ID on your platform\n * const sessionId = AdMeshSDK.createSession();\n *\n * await admesh.showRecommendations({\n * query: 'best CRM for small business',\n * containerId: 'admesh-recommendations',\n * session_id: sessionId\n * });\n * ```\n */\nexport class AdMeshSDK {\n private config: AdMeshSDKConfig;\n private apiBaseUrl: string;\n\n // OPTIMIZATION: Lazy-initialized managers (only created when needed)\n private renderer: AdMeshRenderer | null = null;\n private tracker: AdMeshTracker | null = null;\n\n constructor(config: AdMeshSDKConfig) {\n if (!config.apiKey) {\n throw new Error('AdMeshSDK: apiKey is required');\n }\n\n this.config = {\n apiKey: config.apiKey,\n theme: config.theme,\n apiBaseUrl: config.apiBaseUrl\n };\n\n // Set API base URL with priority: config > environment variable > production default\n this.apiBaseUrl = config.apiBaseUrl ||\n (typeof window !== 'undefined' && (window as any).__ADMESH_API_BASE_URL__) ||\n 'https://api.useadmesh.com';\n }\n\n /**\n * PLATFORM UTILITY: Generate a unique session ID for tracking recommendations\n * \n * IMPORTANT: This is a utility method for PLATFORMS to use. The SDK itself\n * NEVER calls this method automatically. Platforms must:\n * 1. Call this method (or generate their own sessionId)\n * 2. Store the sessionId in their own storage\n * 3. Pass sessionId to AdMeshProvider and SDK methods\n * \n * The SDK will throw an error if sessionId is not provided - it will never\n * auto-generate one.\n *\n * @returns A unique session ID (platform must store and manage this)\n */\n static createSession(): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 15);\n return `session_${timestamp}_${random}`;\n }\n\n /**\n * PLATFORM UTILITY: Generate a unique message ID for tracking recommendations per message\n * \n * IMPORTANT: This is a utility method for PLATFORMS to use. The SDK itself\n * NEVER calls this method automatically. Platforms must:\n * 1. Call this method (or generate their own messageId) for each user message\n * 2. Pass messageId to SDK methods (showRecommendations, fetchRecommendationFromAIPContext)\n * \n * The SDK will throw an error if messageId is not provided - it will never\n * auto-generate one.\n *\n * @param sessionId Optional session ID to include in the message ID\n * @returns A unique message ID (platform must provide this to SDK methods)\n */\n static createMessageId(sessionId?: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n if (sessionId) {\n return `msg_${sessionId}_${timestamp}_${random}`;\n }\n return `msg_${timestamp}_${random}`;\n }\n\n\n /**\n * OPTIMIZATION: Lazy initialize renderer on first use\n */\n private getRenderer(): AdMeshRenderer {\n if (!this.renderer) {\n this.renderer = new AdMeshRenderer();\n }\n return this.renderer;\n }\n\n /**\n * OPTIMIZATION: Lazy initialize tracker on first use\n */\n private getTracker(): AdMeshTracker {\n if (!this.tracker) {\n this.tracker = new AdMeshTracker({\n apiKey: this.config.apiKey\n });\n }\n return this.tracker;\n }\n\n\n\n\n /**\n * Fetch and render recommendations automatically using /aip/context endpoint\n *\n * IMPORTANT: Both session_id and messageId MUST be provided by the platform.\n * The SDK NEVER generates these automatically. Use AdMeshSDK.createSession()\n * and AdMeshSDK.createMessageId() on your platform, or generate your own IDs.\n */\n async showRecommendations(options: ShowRecommendationsOptions): Promise<void> {\n try {\n // CRITICAL: session_id MUST be provided by platform - SDK never generates it\n if (!options.session_id || options.session_id.trim() === '') {\n throw new Error('session_id is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n }\n \n // CRITICAL: messageId MUST be provided by the platform - SDK never generates it\n if (!options.messageId || options.messageId.trim() === '') {\n throw new Error('messageId is required and must be provided by the platform. The SDK never generates messageId automatically.');\n }\n \n // Fetch recommendation from /aip/context endpoint\n // platformId is extracted from API key by the backend\n const aipResponse = await this.fetchRecommendationFromAIPContext({\n query: options.query,\n sessionId: options.session_id,\n messageId: options.messageId, // Pass messageId from platform\n platformSurface: options.platformSurface,\n model: options.model,\n messages: options.messages,\n language: options.locale, // locale maps to language\n geo_country: options.geo, // geo maps to geo_country\n userId: options.userId,\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config automatically\n });\n\n // Convert AIP response to AgentRecommendationResponse format for rendering\n const recommendation = this.convertAIPResponseToRecommendation(aipResponse);\n const response: AgentRecommendationResponse = {\n session_id: aipResponse.session_id,\n message_id: `msg_${aipResponse.recommendation_id}`,\n recommendations: [recommendation]\n };\n\n // Standard rendering\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n followups_container_id: options.followups_container_id,\n response,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: options.session_id,\n onPasteToInput: options.onPasteToInput,\n onExecuteQuery: options.onExecuteQuery\n });\n\n // NOTE: Exposure pixels are now fired by AdMeshViewabilityTracker component\n // when ads meet MRC viewability standards (50% visible for 1 second).\n // This ensures MRC-compliant exposure tracking and proper CPX billing.\n } catch (error) {\n logger.error('[AdMeshSDK] Error showing recommendations');\n throw error;\n }\n }\n\n /**\n * Fetch recommendation from the /aip/context endpoint (new auction-based endpoint)\n * \n * Public method for fetching recommendation data without rendering.\n * Useful for format detection and custom rendering logic.\n * \n * IMPORTANT: Both sessionId and messageId MUST be provided by the platform.\n * The SDK NEVER generates these automatically.\n */\n async fetchRecommendationFromAIPContext(params: {\n query: string;\n sessionId: string;\n messageId?: string;\n // platformId is now extracted from API key by the backend\n platformSurface?: string;\n model?: string;\n messages?: Array<{ role: string; content: string; id?: string }>;\n language?: string;\n geo_country?: string;\n userId?: string;\n // latency_budget_ms removed - now fetched from agent profile by backend\n // allowed_formats removed - backend fetches from platform config (configured during onboarding)\n }): Promise<AIPContextResponse> {\n const url = `${this.apiBaseUrl}/aip/context`;\n\n logger.log('[AdMeshSDK] 📥 fetchRecommendationFromAIPContext called');\n\n // CRITICAL: sessionId MUST be provided by platform - SDK never generates it\n if (!params.sessionId || params.sessionId.trim() === '') {\n const error = new Error('sessionId is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n logger.error('[AdMeshSDK] ❌ sessionId not provided by platform - cannot process request');\n throw error;\n }\n\n // CRITICAL: messageId MUST be provided by the platform - SDK never generates it\n // NEVER derive from messages array - platform must pass it explicitly\n const messageId = params.messageId;\n if (!messageId || messageId.trim() === '') {\n const error = new Error('messageId is required and must be provided by the platform. The SDK never generates messageId automatically.');\n logger.error('[AdMeshSDK] ❌ messageId not provided by platform - cannot process request');\n throw error;\n }\n \n // Calculate turn_index from messages length\n const turnIndex = params.messages ? params.messages.length : 0;\n \n // Detect device platform (web by default, could be enhanced with user agent detection)\n const devicePlatform = typeof window !== 'undefined' && window.navigator ? 'web' : 'web';\n \n // Detect form factor (could be enhanced with screen size detection)\n const formFactor = typeof window !== 'undefined' && window.innerWidth \n ? (window.innerWidth < 768 ? 'mobile' : window.innerWidth < 1024 ? 'tablet' : 'desktop')\n : 'desktop';\n\n // Build PlatformRequest payload in UCP structure\n // Note: producer.agent_id will be extracted from API key by the backend\n // \n // The operator receives this PlatformRequest and converts it to ContextRequest:\n // - PlatformRequest uses extensions.aip (with query_text, messages) - this is correct\n // - ContextRequest is flat (no extensions, intent.summary replaces query_text)\n // - Brand agents receive decision context only (no raw queries, no identity fields)\n const payload: PlatformRequest = {\n spec_version: '1.0.0',\n message_id: messageId,\n timestamp: new Date().toISOString(),\n producer: {\n agent_id: 'placeholder', // Will be overridden by backend from API key\n agent_role: 'publisher',\n software: 'admesh_ui_sdk',\n software_version: params.model || '1.0.0'\n },\n context: {\n // context_id removed - operator will set it as message_id when creating ContextRequest for brand agents\n language: params.language || 'en-US',\n publisher: 'placeholder', // Will be overridden by backend from API key\n placement: {\n ad_unit: params.platformSurface || 'web'\n },\n device: {\n platform: devicePlatform,\n form_factor: formFactor\n },\n geography: {\n country: params.geo_country || 'US'\n }\n },\n identity: {\n namespace: 'platform_user',\n value_hash: params.userId || '',\n confidence: 1.0\n },\n extensions: {\n aip: {\n session_id: params.sessionId,\n turn_index: turnIndex,\n query_text: params.query,\n messages: params.messages || [],\n // latency_budget_ms removed - now fetched from agent profile by backend\n cpx_floor: 0.0\n }\n }\n };\n\n // Validate query before sending\n if (!payload.extensions.aip.query_text || !payload.extensions.aip.query_text.trim()) {\n logger.warn('[AdMeshSDK] ⚠️ Warning: Sending request with empty query_text');\n }\n\n const jsonBody = JSON.stringify(payload);\n logger.log('[AdMeshSDK] 📤 Sending request to /aip/context');\n\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.config.apiKey}`\n },\n body: jsonBody\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n const errorMessage = errorData.detail || `HTTP ${response.status}`;\n throw new Error(`Failed to fetch recommendation from /aip/context: ${errorMessage}`);\n }\n\n const data: any = await response.json();\n \n // Log the raw response structure for debugging\n logger.log('[AdMeshSDK] 📥 Raw response from /aip/context:', {\n hasCreative: !!data.creative,\n creativeFormat: data.creative?.format,\n creativeBridgeContent: data.creative?.bridge_content ? data.creative.bridge_content.substring(0, 50) + '...' : undefined,\n hasWinningBid: !!data.winning_bid,\n winningBidPreferredFormat: data.winning_bid?.preferred_format,\n topLevelKeys: Object.keys(data)\n });\n \n \n return data as AIPContextResponse;\n }\n\n\n /**\n * Convert AIP context response to AdMeshRecommendation format for compatibility\n */\n private convertAIPResponseToRecommendation(aipResponse: AIPContextResponse): AdMeshRecommendation {\n const responseAny = aipResponse as any;\n let creativeInput = aipResponse.creative_input || {};\n \n // Extract format and bridge prompt/content from creative object if present\n // The response may have: creative: { format: 'bridge', bridge_prompt: '...', bridge_content: '...' }\n // Check multiple locations for the creative object\n const creative = responseAny.creative || {};\n const format = creative.format || \n responseAny.format || \n responseAny.winning_bid?.preferred_format;\n \n // Extract headline from creative if present (for tail and product_card formats)\n const headlineFromCreative = creative.headline;\n \n // Extract bridge format fields from creative if present\n const bridgeHeadlineFromCreative = creative.bridge_headline;\n const bridgeDescriptionFromCreative = creative.bridge_description;\n const bridgePromptFromCreative =\n creative.bridge_prompt || creative.bridge_content;\n const bridgePrompt =\n bridgePromptFromCreative ||\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content;\n \n // Extract cta_label from creative if present (for bridge format)\n const ctaLabelFromCreative = creative.cta_label;\n const ctaLabel =\n ctaLabelFromCreative ||\n (creativeInput as any).cta_label;\n \n // Extract bridge_headline and bridge_description\n const bridgeHeadline =\n bridgeHeadlineFromCreative ||\n (creativeInput as any).bridge_headline;\n const bridgeDescription =\n bridgeDescriptionFromCreative ||\n (creativeInput as any).bridge_description;\n \n logger.log('[AdMeshSDK] 🔍 Extracting from response:', {\n creativeObject: creative,\n creativeFormat: creative.format,\n creativeBridgeHeadline: bridgeHeadlineFromCreative,\n creativeBridgeDescription: bridgeDescriptionFromCreative\n ? String(bridgeDescriptionFromCreative).substring(0, 50) + '...'\n : undefined,\n creativeBridgePrompt: bridgePromptFromCreative\n ? String(bridgePromptFromCreative).substring(0, 50) + '...'\n : undefined,\n creativeCtaLabel: ctaLabelFromCreative,\n extractedFormat: format,\n extractedBridgeHeadline: bridgeHeadline,\n extractedBridgeDescription: bridgeDescription\n ? String(bridgeDescription).substring(0, 50) + '...'\n : undefined,\n extractedBridgePrompt: bridgePrompt\n ? String(bridgePrompt).substring(0, 50) + '...'\n : undefined,\n extractedCtaLabel: ctaLabel,\n creativeInputHasBridgeHeadline: !!(creativeInput as any).bridge_headline,\n creativeInputHasBridgeDescription: !!(creativeInput as any).bridge_description,\n creativeInputHasBridgePrompt: !!(creativeInput as any).bridge_prompt,\n creativeInputHasBridgeContent: !!(creativeInput as any).bridge_content,\n creativeInputHasCtaLabel: !!(creativeInput as any).cta_label\n });\n \n // ALWAYS merge creative fields into creative_input for format detection\n // This ensures headline, bridge_headline, bridge_description, bridge_prompt and cta_label are available\n // Explicitly preserve assets to ensure logo_url is not lost during merge\n const preservedAssets = creativeInput.assets || {};\n creativeInput = {\n ...creativeInput,\n // Explicitly preserve assets object to ensure logo_url is maintained\n assets: preservedAssets,\n // Prioritize creative object fields over existing creative_input\n ...(headlineFromCreative && { headline: headlineFromCreative }), // For tail and product_card formats\n ...(bridgeHeadline && { bridge_headline: bridgeHeadline }),\n ...(bridgeDescription && { bridge_description: bridgeDescription }),\n ...(bridgePrompt && { bridge_prompt: bridgePrompt }),\n // Keep bridge_content for backward compatibility\n ...(bridgePrompt && { bridge_content: bridgePrompt }),\n ...(ctaLabel && { cta_label: ctaLabel }),\n ...(format && { format: format })\n };\n \n // Ensure recommendation_id is set (use from response or generate fallback)\n const recommendationId = aipResponse.recommendation_id || \n (responseAny as any).recommendation_id ||\n (responseAny as any).bid_id || // Fallback for backward compatibility\n '';\n \n // Ensure admesh_link is set (use click_url if not provided)\n const admeshLink = aipResponse.click_url || \n (aipResponse as any).admesh_link || \n '';\n \n // Build legacy fields from creative_input for backward compatibility\n const recommendation: any = {\n ...aipResponse,\n // Ensure recommendation_id is present\n recommendation_id: recommendationId,\n // Ensure admesh_link is present (use click_url as fallback)\n admesh_link: admeshLink || aipResponse.click_url || '',\n // Remove any ad_id or bid_id fields\n ad_id: undefined,\n bid_id: undefined,\n // Update creative_input with merged fields (CRITICAL: this must include bridge_content)\n creative_input: creativeInput,\n // Legacy field mappings\n product_title: aipResponse.title,\n tail_summary: creativeInput.long_description || '',\n product_summary: creativeInput.short_description || '',\n weave_summary: creativeInput.context_snippet || '',\n product_logo: creativeInput.assets?.logo_url ? {\n url: creativeInput.assets.logo_url\n } : undefined,\n categories: creativeInput.categories || []\n };\n \n // Remove ad_id and bid_id if they exist\n delete recommendation.ad_id;\n delete recommendation.bid_id;\n \n // Preserve format field for bridge format detection\n if (format) {\n recommendation.format = format;\n }\n \n logger.log('[AdMeshSDK] 🔄 Converted recommendation:', {\n hasFormat: !!format,\n format: format,\n hasBridgeHeadline: !!bridgeHeadline,\n bridgeHeadline: bridgeHeadline,\n hasBridgeDescription: !!bridgeDescription,\n bridgeDescriptionPreview: bridgeDescription\n ? String(bridgeDescription).substring(0, 50) + '...'\n : undefined,\n hasBridgePrompt: !!bridgePrompt,\n hasCtaLabel: !!ctaLabel,\n ctaLabel: ctaLabel,\n bridgeHeadlineInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_headline,\n bridgeDescriptionInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_description,\n bridgePromptInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_prompt,\n bridgeContentInCreativeInput: !!(recommendation.creative_input as any)\n ?.bridge_content,\n ctaLabelInCreativeInput: !!(recommendation.creative_input as any)\n ?.cta_label,\n bridgePromptPreview: bridgePrompt\n ? String(bridgePrompt).substring(0, 50) + '...'\n : undefined,\n creativeInputKeys: Object.keys(recommendation.creative_input || {}),\n recommendationFormat: recommendation.format\n });\n \n return recommendation as AdMeshRecommendation;\n }\n\n /**\n * Fire exposure for sponsored followup\n * \n * @param exposureUrl - The exposure URL to fire (can use regular exposure_url)\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID\n */\n fireFollowupExposure(exposureUrl: string, recommendationId: string, sessionId: string): void {\n const tracker = this.getTracker();\n tracker.fireFollowupExposure(exposureUrl, recommendationId, sessionId);\n }\n\n /**\n * Fire engagement for sponsored followup\n * \n * @param engagementUrl - The engagement URL to fire\n * @param recommendationId - The recommendation ID\n * @param sessionId - The session ID\n * @returns Promise that resolves when engagement is fired\n */\n async fireFollowupEngagement(engagementUrl: string, recommendationId: string, sessionId: string): Promise<void> {\n const tracker = this.getTracker();\n return tracker.fireFollowupEngagement(engagementUrl, recommendationId, sessionId);\n }\n}\n\nexport default AdMeshSDK;\n","/**\n * WeaveResponseProcessor\n * \n * Automatically detects and enhances AdMesh recommendation links in organic LLM responses.\n * Handles:\n * - Automatic link detection (AdMesh tracking URLs)\n * - Label enhancement (<sub>[Ad]</sub> subscript labels)\n * - Exposure pixel triggering\n * - Streaming response support (MutationObserver)\n */\n\nimport { logger } from '../utils/logger';\n\nexport interface DetectedLink {\n element: HTMLAnchorElement;\n href: string;\n text: string;\n hasAdLabel: boolean;\n matchedRecommendation?: {\n recommendation_id: string;\n click_url: string;\n exposure_url?: string;\n };\n}\n\nexport interface ProcessorConfig {\n autoAddLabels?: boolean;\n fireExposurePixels?: boolean;\n labelStyle?: {\n fontSize?: string;\n fontWeight?: string;\n color?: string;\n marginLeft?: string;\n };\n}\n\nexport interface ExposurePixelTarget {\n exposureUrl?: string;\n recommendationId?: string;\n linkElement: HTMLAnchorElement;\n}\n\nexport class WeaveResponseProcessor {\n private autoAddLabels: boolean;\n private fireExposurePixels: boolean;\n private labelStyle: Record<string, string>;\n private processedLinks: Set<string> = new Set();\n private mutationObserver: MutationObserver | null = null;\n\n constructor(config: ProcessorConfig = {}) {\n this.autoAddLabels = config.autoAddLabels !== false; // Default: true\n this.fireExposurePixels = config.fireExposurePixels !== false; // Default: true\n this.labelStyle = {\n fontSize: config.labelStyle?.fontSize || '0.75em',\n fontWeight: config.labelStyle?.fontWeight || 'bold',\n color: config.labelStyle?.color || '#666',\n marginLeft: config.labelStyle?.marginLeft || '2px'\n };\n }\n\n /**\n * Get links to process with CSS selector optimization\n *\n * Optimized approach:\n * 1. Use CSS selector to find AdMesh links (97% faster)\n * - Matches: api.useadmesh.com/click/*, *.useadmesh.com/click/*\n * 2. Fallback to scanning all links if selector finds nothing\n * 3. Recommendation validation ensures accuracy\n */\n private getLinksToProcess(container: HTMLElement): HTMLAnchorElement[] {\n // Try optimized selector first for AdMesh links\n // Matches both api.useadmesh.com and *.useadmesh.com domains\n const optimizedSelector = 'a[href*=\"/click/\"][href*=\"admesh.com\"], a[href*=\"/click/\"][href*=\"useadmesh.com\"]';\n const optimizedLinks = container.querySelectorAll(optimizedSelector);\n\n if (optimizedLinks.length > 0) {\n return Array.from(optimizedLinks) as HTMLAnchorElement[];\n }\n\n // Fallback: scan all links\n return Array.from(container.querySelectorAll('a')) as HTMLAnchorElement[];\n }\n\n /**\n * Scan container for ALL links and process only AdMesh recommendation links\n *\n * This method:\n * 1. Scans container for all <a> tags (or uses optimized selector)\n * 2. If recommendations provided: Filters to only process links matching recommendation click_url values\n * 3. If NO recommendations: Detects links by URL pattern (api.useadmesh.com/click/*)\n * 4. Ignores non-AdMesh links (external links, documentation, etc.)\n * 5. Adds [Ad] labels to matching links\n * 6. Fires exposure pixels for each match\n *\n * NOTE: When recommendations array is empty, this method detects AdMesh links by URL pattern.\n * This is important for Weave Ad Format where the backend embeds links in the LLM response\n * and the frontend hook doesn't have access to the recommendations data.\n */\n scanAndProcessLinks(\n container: HTMLElement,\n recommendations: any[],\n onExposurePixel?: (target: ExposurePixelTarget) => void\n ): DetectedLink[] {\n if (!container) {\n return [];\n }\n\n const detectedLinks: DetectedLink[] = [];\n\n // If recommendations provided, use them for matching\n if (recommendations.length > 0) {\n // Get links to process (with optional optimization)\n const links = this.getLinksToProcess(container);\n\n // Build map of AdMesh click URLs for fast lookup\n const clickUrlMap = new Map(\n recommendations\n .filter(r => r.click_url)\n .map(r => [r.click_url, r])\n );\n\n // Build map of brand URLs (redirect_url, url) for matching direct brand links\n const brandUrlMap = new Map<string, any>();\n recommendations.forEach((r: any) => {\n // Match by redirect_url or url (original brand URLs)\n const redirectUrl = r.redirect_url || r.url || (r.creative_input as any)?.cta_url;\n if (redirectUrl && typeof redirectUrl === 'string') {\n // Normalize URL (remove trailing slashes, query params for matching)\n const normalizedUrl = redirectUrl.trim().replace(/\\/$/, '');\n brandUrlMap.set(normalizedUrl, r);\n // Also match with trailing slash\n brandUrlMap.set(`${normalizedUrl}/`, r);\n }\n });\n\n links.forEach((link: HTMLAnchorElement) => {\n const href = link.getAttribute('href') || '';\n const linkKey = `${href}`;\n\n // Skip if already processed\n if (this.processedLinks.has(linkKey)) {\n return;\n }\n\n // First, check if link matches AdMesh click URL\n let recommendation = clickUrlMap.get(href);\n let actualHref = href;\n \n // If not found, check if link matches brand URL (redirect_url or url)\n if (!recommendation) {\n const normalizedHref = href.trim().replace(/\\/$/, '');\n recommendation = brandUrlMap.get(normalizedHref) || brandUrlMap.get(`${normalizedHref}/`);\n \n // If brand URL matched, replace it with click_url\n if (recommendation && recommendation.click_url) {\n logger.log('[WeaveResponseProcessor] 🔄 Found brand URL match, replacing with click_url:', href);\n link.setAttribute('href', recommendation.click_url);\n actualHref = recommendation.click_url;\n }\n }\n \n // Process link if it matches a recommendation (either by click_url or brand URL)\n if (recommendation) {\n this.processedLinks.add(linkKey);\n\n // ALWAYS set target=\"_blank\" and rel=\"noopener noreferrer\" for AdMesh tracking links\n // This ensures all click_url links open in a new tab\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n\n const detectedLink: DetectedLink = {\n element: link,\n href: actualHref, // Use actual href (may be updated click_url)\n text: link.textContent || '',\n hasAdLabel: this.hasAdLabel(link),\n matchedRecommendation: {\n recommendation_id: recommendation.recommendation_id || '',\n click_url: recommendation.click_url,\n exposure_url: recommendation.exposure_url\n }\n };\n\n // Add label if not present\n if (this.autoAddLabels && !detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] 🏷️ Adding [Ad] label to matched recommendation link:', actualHref);\n this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n } else if (!this.autoAddLabels) {\n logger.log('[WeaveResponseProcessor] ℹ️ autoAddLabels is disabled, skipping label');\n } else if (detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has label, skipping');\n }\n\n // Fire exposure pixel\n if (this.fireExposurePixels && recommendation.exposure_url && onExposurePixel && detectedLink.matchedRecommendation) {\n onExposurePixel({\n exposureUrl: recommendation.exposure_url,\n recommendationId: detectedLink.matchedRecommendation.recommendation_id,\n linkElement: link\n });\n }\n\n detectedLinks.push(detectedLink);\n }\n });\n } else {\n // No recommendations provided - detect AdMesh links by URL pattern\n // This is used for Weave Ad Format where backend embeds links in LLM response\n // Links can be in formats like:\n // - https://api.useadmesh.com/click/...\n // - https://api.admesh.com/click/...\n // - https://useadmesh.com/click/...\n // - https://admesh.com/click/...\n // Get ALL links in container and filter programmatically\n const allLinks = Array.from(container.querySelectorAll('a')) as HTMLAnchorElement[];\n\n \n\n allLinks.forEach((link: HTMLAnchorElement) => {\n const href = link.getAttribute('href') || '';\n const linkKey = `${href}`;\n\n // Skip if already processed\n if (this.processedLinks.has(linkKey)) {\n return;\n }\n\n // Check if this is an AdMesh link by examining the URL\n const isAdMeshLink = this.isAdMeshLink(href);\n\n if (isAdMeshLink) {\n\n this.processedLinks.add(linkKey);\n\n const detectedLink: DetectedLink = {\n element: link,\n href,\n text: link.textContent || '',\n hasAdLabel: this.hasAdLabel(link),\n matchedRecommendation: undefined\n };\n\n // Set target=\"_blank\" and rel=\"noopener noreferrer\" for security\n link.setAttribute('target', '_blank');\n link.setAttribute('rel', 'noopener noreferrer');\n\n // Add label if not present\n if (this.autoAddLabels && !detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] 🏷️ Adding [Ad] label to pattern-matched AdMesh link:', href);\n this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n } else if (!this.autoAddLabels) {\n logger.log('[WeaveResponseProcessor] ℹ️ autoAddLabels is disabled, skipping label');\n } else if (detectedLink.hasAdLabel) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has label, skipping');\n }\n\n // Fire exposure tracking with converted exposure URL\n // For Weave Ad Format: LLM embeds click URLs in response text, so we convert to exposure URL\n if (this.fireExposurePixels && onExposurePixel) {\n const exposureUrl = this.convertClickUrlToExposureUrl(href);\n onExposurePixel({\n exposureUrl,\n recommendationId: this.extractRecommendationIdFromUrl(href),\n linkElement: link\n });\n }\n\n detectedLinks.push(detectedLink);\n }\n });\n\n \n }\n\n return detectedLinks;\n }\n\n /**\n * Check if link already has [Ad] label\n *\n * This method checks for [Ad] labels on BOTH sides of the link:\n * - Previous sibling (left side): [Ad] link text\n * - Next sibling (right side): link text [Ad]\n * - Inside the link itself (as a child element)\n *\n * This prevents duplicate label rendering when the LLM response\n * already contains [Ad] labels in any position.\n */\n private hasAdLabel(link: HTMLAnchorElement): boolean {\n // First, check if link itself contains [Ad] in its text content or as a child\n const linkText = link.textContent || '';\n if (linkText.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link text contains [Ad] label');\n return true;\n }\n\n // Check for [Ad] label as a direct child element (e.g., <a>text<sub>[Ad]</sub></a>)\n const childElements = link.querySelectorAll('sub, span');\n for (const child of Array.from(childElements)) {\n if (child.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link has [Ad] label as child element');\n return true;\n }\n }\n\n // Check PREVIOUS sibling (left side) for [Ad] label\n let prevNode = link.previousSibling;\n\n // Skip text nodes that are just whitespace\n while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {\n const text = prevNode.textContent || '';\n if (text.trim() === '') {\n prevNode = prevNode.previousSibling;\n continue;\n }\n // If we find non-whitespace text, check if it contains [Ad]\n if (text.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in previous text sibling');\n return true;\n }\n break;\n }\n\n // Check if previous element node is a <sub> or <span> with [Ad] label\n if (prevNode && prevNode.nodeType === Node.ELEMENT_NODE) {\n const element = prevNode as HTMLElement;\n const tagName = element.tagName.toUpperCase();\n if ((tagName === 'SUB' || tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in previous element sibling:', tagName);\n return true;\n }\n }\n\n // Check NEXT sibling (right side) for [Ad] label\n let nextNode = link.nextSibling;\n\n // Skip text nodes that are just whitespace\n while (nextNode && nextNode.nodeType === Node.TEXT_NODE) {\n const text = nextNode.textContent || '';\n if (text.trim() === '') {\n nextNode = nextNode.nextSibling;\n continue;\n }\n // If we find non-whitespace text, check if it contains [Ad]\n if (text.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in next text sibling');\n return true;\n }\n break;\n }\n\n // Check if next element node is a <sub> or <span> with [Ad] label\n if (nextNode && nextNode.nodeType === Node.ELEMENT_NODE) {\n const element = nextNode as HTMLElement;\n const tagName = element.tagName.toUpperCase();\n if ((tagName === 'SUB' || tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n logger.log('[WeaveResponseProcessor] ℹ️ Found [Ad] label in next element sibling:', tagName);\n return true;\n }\n }\n\n logger.log('[WeaveResponseProcessor] ℹ️ No existing [Ad] label found');\n return false;\n }\n\n /**\n * Add [Ad] label as subscript after link with \"Why this ad?\" tooltip\n *\n * This method:\n * 1. Removes any [Ad] label on the LEFT side (previous sibling)\n * 2. Creates a <sub> element with [Ad] text\n * 3. Positions it immediately after the link (to the RIGHT)\n * 4. Adds tooltip styling with default cursor (not help cursor)\n * 5. Adds click handler for tooltip interaction\n * 6. Ensures only ONE label per link, always on the RIGHT side\n */\n private addAdLabel(link: HTMLAnchorElement): void {\n logger.log('[WeaveResponseProcessor] 🏷️ Attempting to add [Ad] label to link:', link.href);\n \n // Verify link is still in the DOM\n if (!link.isConnected) {\n logger.warn('[WeaveResponseProcessor] ⚠️ Link is not in DOM, cannot add label');\n return;\n }\n\n // Double-check that [Ad] label doesn't already exist to prevent duplicates\n if (this.hasAdLabel(link)) {\n logger.log('[WeaveResponseProcessor] ℹ️ Link already has [Ad] label, skipping');\n return;\n }\n\n // Verify parent node exists before proceeding\n const parentNode = link.parentNode;\n if (!parentNode) {\n logger.error('[WeaveResponseProcessor] ❌ Link has no parent node, cannot add label');\n return;\n }\n\n // Remove any [Ad] label on the LEFT side (previous sibling)\n this.removeLeftAdLabel(link);\n\n // Create the [Ad] label as a <sub> element\n const subLabel = document.createElement('sub');\n subLabel.textContent = '[Ad]';\n subLabel.style.fontSize = this.labelStyle.fontSize;\n subLabel.style.fontWeight = this.labelStyle.fontWeight;\n subLabel.style.color = this.labelStyle.color;\n subLabel.style.marginLeft = this.labelStyle.marginLeft;\n\n // Add tooltip styling with default cursor (not help cursor)\n subLabel.style.cursor = 'pointer';\n subLabel.style.borderBottom = `1px dotted ${this.labelStyle.color}`;\n subLabel.style.whiteSpace = 'nowrap';\n subLabel.title = 'Why this ad? This is a sponsored recommendation based on your search query.';\n\n // Track tooltip state for click interactions\n let isTooltipVisible = false;\n\n // Add hover effect for visual feedback\n subLabel.addEventListener('mouseenter', () => {\n subLabel.style.opacity = '0.7';\n });\n\n subLabel.addEventListener('mouseleave', () => {\n subLabel.style.opacity = '1';\n // Hide tooltip on mouse leave if it was shown by click\n if (isTooltipVisible) {\n isTooltipVisible = false;\n }\n });\n\n // Add click handler to toggle tooltip visibility\n subLabel.addEventListener('click', (event: Event) => {\n event.stopPropagation();\n isTooltipVisible = !isTooltipVisible;\n\n if (isTooltipVisible) {\n // Show tooltip by adding visual indicator\n subLabel.style.textDecoration = 'underline';\n subLabel.style.opacity = '0.7';\n } else {\n // Hide tooltip visual indicator\n subLabel.style.textDecoration = 'none';\n subLabel.style.opacity = '1';\n }\n });\n\n // Close tooltip when clicking elsewhere on the page\n const closeTooltipOnClickOutside = (event: Event) => {\n if (isTooltipVisible && event.target !== subLabel) {\n isTooltipVisible = false;\n subLabel.style.textDecoration = 'none';\n subLabel.style.opacity = '1';\n }\n };\n\n document.addEventListener('click', closeTooltipOnClickOutside);\n\n // Insert the label immediately after the link (to the right)\n // Handle edge cases: if nextSibling is null, appendChild will insert at the end\n try {\n const nextSibling = link.nextSibling;\n if (nextSibling) {\n parentNode.insertBefore(subLabel, nextSibling);\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label inserted before next sibling');\n } else {\n // If no next sibling, append to parent (inserts after link)\n parentNode.appendChild(subLabel);\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label appended to parent (no next sibling)');\n }\n \n // Verify label was successfully inserted\n if (subLabel.isConnected && subLabel.parentNode === parentNode) {\n logger.log('[WeaveResponseProcessor] ✅ [Ad] label successfully added to link:', link.href);\n } else {\n logger.error('[WeaveResponseProcessor] ❌ [Ad] label insertion failed - label not in DOM');\n }\n } catch (error) {\n logger.error('[WeaveResponseProcessor] ❌ Error inserting [Ad] label:', error);\n }\n }\n\n /**\n * Remove [Ad] label from the LEFT side (previous sibling) of a link\n *\n * This ensures that only ONE [Ad] label appears on the RIGHT side,\n * removing any duplicate labels that might be on the left.\n */\n private removeLeftAdLabel(link: HTMLAnchorElement): void {\n let prevNode = link.previousSibling;\n\n // Skip text nodes that are just whitespace\n while (prevNode && prevNode.nodeType === Node.TEXT_NODE) {\n const text = prevNode.textContent || '';\n if (text.trim() === '') {\n prevNode = prevNode.previousSibling;\n continue;\n }\n // If we find non-whitespace text containing [Ad], remove it\n if (text.includes('[Ad]')) {\n prevNode.parentNode?.removeChild(prevNode);\n return;\n }\n break;\n }\n\n // Check if previous element node is a <sub> or <span> with [Ad] label\n if (prevNode && prevNode.nodeType === Node.ELEMENT_NODE) {\n const element = prevNode as HTMLElement;\n if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n element.parentNode?.removeChild(element);\n }\n }\n }\n\n /**\n * Check if a URL is an AdMesh link\n *\n * Detects AdMesh links by checking if the URL contains /click/ and matches AdMesh patterns:\n * - https://api.useadmesh.com/click/...\n * - https://api.admesh.com/click/...\n * - http://localhost:8000/click/... (local development)\n * - Any URL with /click/ that looks like an AdMesh tracking URL\n */\n private isAdMeshLink(href: string): boolean {\n if (!href) {\n return false;\n }\n\n // Check if URL contains /click/ (AdMesh tracking pattern)\n if (!href.includes('/click/')) {\n return false;\n }\n\n // Check for known AdMesh domains\n const admeshDomains = [\n 'useadmesh.com',\n 'admesh.com',\n 'api.useadmesh.com',\n 'api.admesh.com',\n 'localhost:8000', // Local development\n 'localhost:3000', // Local development (if proxied)\n ];\n\n const isKnownDomain = admeshDomains.some(domain => href.includes(domain));\n if (isKnownDomain) {\n return true;\n }\n\n // Fallback: if URL has /click/ and looks like a tracking URL, treat it as AdMesh\n // This handles cases where the domain might be different but the pattern matches\n try {\n const url = new URL(href);\n const pathname = url.pathname;\n\n // Check if pathname starts with /click/ (AdMesh pattern)\n if (pathname.startsWith('/click/')) {\n return true;\n }\n } catch {\n // If URL parsing fails, check string pattern\n if (href.match(/\\/click\\/[a-zA-Z0-9\\-_]+/)) {\n return true;\n }\n }\n\n return false;\n }\n\n /**\n * Extract recommendation ID from AdMesh click URL\n *\n * URL format: https://api.useadmesh.com/click/{recommendation_id}?...\n * Returns the recommendation_id portion or empty string if extraction fails\n */\n private extractRecommendationIdFromUrl(url: string): string {\n try {\n // Try to extract from /click/{id} pattern\n const match = url.match(/\\/click\\/([^/?]+)/);\n if (match && match[1]) {\n return match[1];\n }\n // Fallback to empty string (recommendation_id should be in URL)\n return '';\n } catch {\n return url;\n }\n }\n\n /**\n * Convert AdMesh click URL to exposure URL (Weave Ad Format only)\n *\n * This is ONLY used for Weave Ad Format where the LLM embeds click URLs in the response text\n * and we need to derive the exposure URL for tracking.\n *\n * Click URL format: https://api.useadmesh.com/click/r/{aid}?aid={aid}&rid={rid}&nonce={nonce}&exp={exp}&sig={sig}\n * Exposure URL format: https://api.useadmesh.com/exposure?aid={aid}&rid={rid}&nonce={nonce}&exp={exp}&sig={sig}&cpx={cpx}\n *\n * This method:\n * 1. Parses the click URL to extract the base domain and query parameters\n * 2. Replaces the /click/r/{aid} path with /exposure\n * 3. Preserves all tracking parameters (aid, rid, nonce, exp, sig)\n * 4. Adds cpx=0 as default (actual CPX value is set server-side)\n *\n * @param clickUrl - The AdMesh click tracking URL\n * @returns The corresponding exposure tracking URL, or the original URL if conversion fails\n */\n private convertClickUrlToExposureUrl(clickUrl: string): string {\n try {\n const url = new URL(clickUrl);\n\n // Extract the base URL (protocol + host)\n const baseUrl = `${url.protocol}//${url.host}`;\n\n // Build exposure endpoint URL\n const exposureUrl = `${baseUrl}/exposure`;\n\n // Preserve all existing query parameters from click URL\n // These include: aid, rid, nonce, exp, sig\n const params = new URLSearchParams(url.search);\n\n // Add cpx parameter if not present (default to 0, actual value is set server-side)\n if (!params.has('cpx')) {\n params.set('cpx', '0');\n }\n\n // Construct final exposure URL with all parameters\n return `${exposureUrl}?${params.toString()}`;\n } catch (error) {\n // If URL parsing fails, log warning and return original URL\n logger.warn('[WeaveResponseProcessor] Failed to convert click URL to exposure URL');\n return clickUrl;\n }\n }\n\n /**\n * Watch container for new links (streaming support)\n */\n watchForNewLinks(\n container: HTMLElement,\n recommendations: any[],\n onExposurePixel?: (target: ExposurePixelTarget) => void\n ): void {\n if (!container) {\n return;\n }\n\n // Stop existing observer\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n\n // Create observer for new nodes\n this.mutationObserver = new MutationObserver(() => {\n this.scanAndProcessLinks(container, recommendations, onExposurePixel);\n });\n\n this.mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n }\n\n /**\n * Stop watching for new links\n */\n stopWatching(): void {\n if (this.mutationObserver) {\n this.mutationObserver.disconnect();\n this.mutationObserver = null;\n }\n }\n\n /**\n * Clear processed links cache\n */\n clearCache(): void {\n this.processedLinks.clear();\n }\n}\n\nexport default WeaveResponseProcessor;\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { logger } from '../utils/logger';\nimport { AdMeshSDK } from '../sdk/AdMeshSDK';\nimport { AdMeshContext, type AdMeshContextValue } from './AdMeshContext';\nimport type { AdMeshTheme } from '../types/index';\n\nexport interface AdMeshProviderProps {\n /** AdMesh API key (required) */\n apiKey: string;\n\n /** Session ID (required) */\n sessionId: string;\n\n /** Optional theme configuration */\n theme?: AdMeshTheme;\n\n /** Optional API base URL (defaults to production) */\n apiBaseUrl?: string;\n\n /** Optional user language in BCP 47 format (e.g., \"en-US\") */\n language?: string;\n\n /** Optional user country code in ISO 3166-1 alpha-2 format (e.g., \"US\") */\n geo_country?: string;\n\n /** Optional anonymous hashed user ID */\n userId?: string;\n\n /** Optional AI model identifier (e.g., \"gpt-4o\") - used for producer.software_version in UCP PlatformRequest */\n model?: string;\n\n /** Optional conversation history - used for extensions.aip.messages in UCP PlatformRequest */\n messages?: Array<{ role: string; content: string; id?: string }>;\n\n /** Child components */\n children: React.ReactNode;\n}\n\n/**\n * AdMeshProvider - Simplified SDK integration for React applications\n *\n * Handles:\n * - SDK initialization and lifecycle management\n * - Message deduplication tracking\n * - Session and message ID management\n * - Error handling and logging\n *\n * @example\n * ```tsx\n * <AdMeshProvider\n * apiKey={process.env.NEXT_PUBLIC_ADMESH_API_KEY}\n * sessionId={sessionId}\n * >\n * <Chat messages={messages} />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshProvider: React.FC<AdMeshProviderProps> = ({\n apiKey,\n sessionId,\n theme,\n apiBaseUrl,\n language,\n geo_country,\n userId,\n model,\n messages,\n children,\n}) => {\n const sdkRef = useRef<AdMeshSDK | null>(null);\n const [processedMessageIds, setProcessedMessageIds] = useState<Set<string>>(\n new Set()\n );\n\n // CRITICAL: Validate that sessionId is provided by platform\n // The SDK/provider NEVER generates sessionId automatically\n useEffect(() => {\n if (!sessionId || sessionId.trim() === '') {\n logger.error('[AdMeshProvider] ❌ sessionId is required and must be provided by the platform. The SDK never generates sessionId automatically.');\n return;\n }\n }, [sessionId]);\n\n // Initialize SDK once on mount\n useEffect(() => {\n if (!apiKey) {\n logger.warn('[AdMeshProvider] ⚠️ AdMesh API key not configured');\n return;\n }\n\n if (!sessionId || sessionId.trim() === '') {\n logger.error('[AdMeshProvider] ❌ Cannot initialize SDK: sessionId is required and must be provided by the platform');\n return;\n }\n\n try {\n sdkRef.current = new AdMeshSDK({\n apiKey,\n theme,\n apiBaseUrl,\n });\n logger.log('[AdMeshProvider] ✅ AdMesh SDK initialized');\n if (apiBaseUrl) {\n logger.log('[AdMeshProvider] 📍 Using custom API base URL');\n }\n } catch (error) {\n logger.error('[AdMeshProvider] ❌ Failed to initialize AdMesh SDK');\n }\n\n // Cleanup on unmount\n return () => {\n logger.log('[AdMeshProvider] 🧹 Provider unmounted');\n };\n }, [apiKey, theme, apiBaseUrl]);\n\n // Create context value\n const contextValue: AdMeshContextValue = {\n sdk: sdkRef.current,\n apiKey,\n sessionId,\n theme,\n language,\n geo_country,\n userId,\n model,\n messages,\n processedMessageIds,\n \n markMessageAsProcessed: (messageId: string) => {\n setProcessedMessageIds((prev) => {\n const updated = new Set(prev);\n updated.add(messageId);\n return updated;\n });\n },\n \n isMessageProcessed: (messageId: string) => {\n return processedMessageIds.has(messageId);\n },\n };\n\n return (\n <AdMeshContext.Provider value={contextValue}>\n {children}\n </AdMeshContext.Provider>\n );\n};\n\nexport default AdMeshProvider;\n","'use client';\n\nimport { useEffect, useState, useRef } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { logger } from '../utils/logger';\nimport { AdMeshTailAd } from './AdMeshTailAd';\nimport { AdMeshFollowup } from './AdMeshFollowup';\nimport { AdMeshProductCard } from './AdMeshProductCard';\nimport { AdMeshBridgeFormat } from './AdMeshBridgeFormat';\nimport type { AdMeshRecommendation } from '../types/index';\n\nexport interface AdMeshRecommendationsProps {\n /** Optional callback when recommendations are shown */\n onRecommendationsShown?: (messageId: string) => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /**\n * Message ID for per-message recommendations.\n *\n * This is the primary identifier for fetching recommendations for a specific message.\n */\n messageId?: string;\n\n /**\n * User query text for generating recommendations.\n *\n * This is the query that will be used to fetch recommendations from the backend.\n * Typically the user's question or search query.\n *\n * IMPORTANT: This should be a non-empty string. If not provided or empty,\n * the component will skip rendering to prevent 400 Bad Request errors.\n * The backend requires a valid query parameter.\n */\n query?: string;\n\n /**\n * Callback to paste content to input field (for bridge format CTA button).\n * When provided, the bridge format will show a CTA button that pastes\n * the bridge_content into the input field when clicked.\n */\n onPasteToInput?: (content: string) => void;\n\n /**\n * Optional container ID for follow-up suggestions.\n * When provided and the recommendation includes a followup_query,\n * the SDK will automatically render the follow-up in this container.\n */\n followups_container_id?: string;\n\n /**\n * Callback to execute query when follow-up is selected (required for follow-up functionality).\n * When a user clicks on a follow-up suggestion, this callback is invoked with the followup_query.\n * This allows the platform to continue the conversation with the sponsored follow-up query.\n */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n\n /**\n * Callback when a sponsored followup is detected.\n * This allows third-party applications to integrate the sponsored followup query into their own\n * followup suggestions UI (e.g., adding to a suggestions list, related questions section, etc.).\n * \n * When a user clicks the followup suggestion, the application should:\n * 1. Fire engagement tracking by calling the followupEngagementUrl\n * 2. Execute the followupQuery (e.g., submit it as a new user query)\n * \n * @param followupQuery - The sponsored followup query text to display to the user\n * @param followupEngagementUrl - The engagement tracking URL to call when user clicks the followup\n * @param recommendationId - The recommendation ID for tracking and correlation\n * \n * @example\n * ```tsx\n * <AdMeshRecommendations\n * onFollowupDetected={(query, engagementUrl, recId) => {\n * // Add to your suggestions list\n * setSuggestions(prev => [...prev, {\n * text: query,\n * sponsored: true,\n * engagementUrl,\n * recommendationId: recId\n * }]);\n * }}\n * />\n * ```\n */\n\n onFollowupDetected?: (followupQuery: string, followupEngagementUrl: string, recommendationId: string) => void;\n\n /**\n * Signal indicating if the followup container is ready in the DOM.\n * \n * Useful for scenarios where the container is rendered conditionally or after a delay (e.g. streaming).\n * If provided, the component will wait until this is true before attempting to attach the portal.\n */\n isContainerReady?: boolean;\n}\n\n/**\n * AdMeshRecommendations - Citation/Product Format Recommendation Display\n *\n * Displays recommendations as a separate UI component. Handles all the complexity:\n * - Uses provided messageId directly\n * - Generates container IDs for recommendations\n * - Calls SDK's showRecommendations()\n *\n * For Weave Ad Format (where AdMesh links are embedded in LLM response),\n * use WeaveAdFormatContainer component instead.\n *\n * @example\n * ```tsx\n * // Per-message recommendations with messageId\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * {messages.map((msg) => (\n * <div key={msg.messageId}>\n * {msg.content}\n * {msg.role === 'assistant' && (\n * <AdMeshRecommendations\n * messageId={msg.messageId}\n * query={msg.userQuery}\n * />\n * )}\n * </div>\n * ))}\n * </AdMeshProvider>\n * ```\n *\n * @example\n * ```tsx\n * // Format is auto-detected from brand agent's preferred_format\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <Chat messages={messages} />\n * <AdMeshRecommendations\n * messageId={lastMessageId}\n * query=\"best CRM for small business\"\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshRecommendations = ({\n onRecommendationsShown,\n onError,\n messageId,\n query,\n onPasteToInput,\n followups_container_id: _followups_container_id,\n onExecuteQuery: _onExecuteQuery,\n\n onFollowupDetected,\n isContainerReady,\n}: AdMeshRecommendationsProps) => {\n const { sdk, sessionId, language, geo_country, userId, model, messages, theme } = useAdMesh();\n \n const [recommendation, setRecommendation] = useState<AdMeshRecommendation | null>(null);\n const [detectedFormat, setDetectedFormat] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n \n // Track fetched messageId to prevent duplicate fetches\n const fetchedMessageIdRef = useRef<string | null>(null);\n const isFetchingRef = useRef<boolean>(false);\n \n // Use refs for callbacks to avoid dependency issues\n const onRecommendationsShownRef = useRef(onRecommendationsShown);\n const onErrorRef = useRef(onError);\n const onFollowupDetectedRef = useRef(onFollowupDetected);\n \n // Update refs when callbacks change\n useEffect(() => {\n onRecommendationsShownRef.current = onRecommendationsShown;\n onErrorRef.current = onError;\n onFollowupDetectedRef.current = onFollowupDetected;\n }, [onRecommendationsShown, onError, onFollowupDetected]);\n\n // Helper function to convert AIPContextResponse to AdMeshRecommendation format\n const convertAIPResponseToRecommendation = (aipResponse: any): AdMeshRecommendation => {\n const responseAny = aipResponse as any;\n let creativeInput = aipResponse.creative_input || {};\n \n // Extract format and bridge prompt/content from creative object if present\n const creative = responseAny.creative || {};\n const formatFromResponse = creative.format || \n responseAny.format || \n responseAny.winning_bid?.preferred_format ||\n (aipResponse.creative_input as any)?.preferred_format;\n \n // Extract headline from creative if present (for tail and product_card formats)\n const headlineFromCreative = creative.headline;\n \n // Extract bridge format fields from creative if present\n const bridgeHeadlineFromCreative = creative.bridge_headline;\n const bridgeDescriptionFromCreative = creative.bridge_description;\n const bridgePromptFromCreative = creative.bridge_prompt || creative.bridge_content;\n const bridgePrompt = bridgePromptFromCreative ||\n (creativeInput as any).bridge_prompt ||\n (creativeInput as any).bridge_content;\n \n // Extract cta_label from creative if present (for bridge format)\n const ctaLabelFromCreative = creative.cta_label;\n const ctaLabel = ctaLabelFromCreative || (creativeInput as any).cta_label;\n \n // Extract bridge_headline and bridge_description\n const bridgeHeadline = bridgeHeadlineFromCreative || (creativeInput as any).bridge_headline;\n const bridgeDescription = bridgeDescriptionFromCreative || (creativeInput as any).bridge_description;\n \n // Merge creative fields into creative_input\n // Explicitly preserve assets to ensure logo_url is not lost\n const preservedAssets = creativeInput.assets || {};\n creativeInput = {\n ...creativeInput,\n // Explicitly preserve assets object to ensure logo_url is maintained\n assets: preservedAssets,\n ...(headlineFromCreative && { headline: headlineFromCreative }),\n ...(bridgeHeadline && { bridge_headline: bridgeHeadline }),\n ...(bridgeDescription && { bridge_description: bridgeDescription }),\n ...(bridgePrompt && { bridge_prompt: bridgePrompt }),\n ...(bridgePrompt && { bridge_content: bridgePrompt }), // Backward compatibility\n ...(ctaLabel && { cta_label: ctaLabel }),\n ...(formatFromResponse && { format: formatFromResponse })\n };\n \n // Ensure recommendation_id is set\n const recommendationId = aipResponse.recommendation_id || \n (aipResponse as any).bid_id || // Fallback for backward compatibility\n '';\n \n // Ensure admesh_link is set (use click_url if not provided)\n const admeshLink = aipResponse.click_url || \n (aipResponse as any).admesh_link || \n '';\n \n // Build recommendation object\n const recommendation: any = {\n ...aipResponse,\n // Ensure recommendation_id is present\n recommendation_id: recommendationId,\n // Ensure admesh_link is present (use click_url as fallback)\n admesh_link: admeshLink || aipResponse.click_url || '',\n creative_input: creativeInput,\n // Remove any ad_id or bid_id fields\n ad_id: undefined,\n bid_id: undefined,\n ...(formatFromResponse && { format: formatFromResponse })\n };\n \n // Remove ad_id and bid_id if they exist\n delete recommendation.ad_id;\n delete recommendation.bid_id;\n \n // Explicitly preserve followup fields if present in aipResponse (check both top-level and creative_input)\n // Followup fields can be at top-level (from operator response) or in creative_input (from Firestore)\n const followupQueryTopLevel = (aipResponse as any).followup_query;\n const followupQueryInCreative = (aipResponse.creative_input as any)?.followup_query || (creativeInput as any)?.followup_query;\n const followupQuery = followupQueryTopLevel || followupQueryInCreative;\n \n const followupEngagementUrlTopLevel = (aipResponse as any).followup_engagement_url;\n const followupEngagementUrlInCreative = (aipResponse.creative_input as any)?.followup_engagement_url || (creativeInput as any)?.followup_engagement_url;\n const followupEngagementUrl = followupEngagementUrlTopLevel || followupEngagementUrlInCreative;\n \n const followupExposureUrlTopLevel = (aipResponse as any).followup_exposure_url;\n const followupExposureUrlInCreative = (aipResponse.creative_input as any)?.followup_exposure_url || (creativeInput as any)?.followup_exposure_url;\n const followupExposureUrl = followupExposureUrlTopLevel || followupExposureUrlInCreative;\n \n if (followupQuery) {\n recommendation.followup_query = followupQuery;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_query:', followupQuery.substring(0, 50) + '...');\n }\n if (followupEngagementUrl) {\n recommendation.followup_engagement_url = followupEngagementUrl;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_engagement_url');\n }\n if (followupExposureUrl) {\n recommendation.followup_exposure_url = followupExposureUrl;\n logger.debug('[AdMeshRecommendations] ✅ Extracted followup_exposure_url');\n }\n \n return recommendation as AdMeshRecommendation;\n };\n\n // Reset fetched ref when messageId changes\n useEffect(() => {\n if (fetchedMessageIdRef.current !== messageId) {\n fetchedMessageIdRef.current = null;\n setRecommendation(null);\n setDetectedFormat(null);\n }\n }, [messageId]);\n\n // Find container for followups when recommendation is available\n const [followupContainer, setFollowupContainer] = useState<Element | null>(null);\n\n useEffect(() => {\n // If we don't have a query or container ID, we can't do anything\n if (!recommendation?.followup_query || !_followups_container_id) {\n setFollowupContainer(null);\n return;\n }\n\n // If isContainerReady is explicitly provided and false, wait\n if (isContainerReady === false) {\n logger.debug(`[AdMeshRecommendations] ⏳ Waiting for container signal...`);\n return;\n }\n\n // Try to find the container\n let attempts = 0;\n const maxAttempts = 5; // Reduced from 30 since we now have a signal - just need to handle React render lag\n\n const checkForContainer = () => {\n const container = document.getElementById(_followups_container_id);\n if (container) {\n logger.debug(`[AdMeshRecommendations] ✅ Found followup container: ${_followups_container_id} `);\n setFollowupContainer(container);\n return true;\n }\n return false;\n };\n\n // Check immediately\n if (checkForContainer()) return;\n\n // Short poll to account for React rendering\n const interval = setInterval(() => {\n attempts++;\n if (checkForContainer() || attempts >= maxAttempts) {\n clearInterval(interval);\n if (attempts >= maxAttempts) {\n logger.warn(`[AdMeshRecommendations] ⚠️ Followup container not found after signal: ${_followups_container_id} `);\n }\n }\n }, 100);\n\n return () => clearInterval(interval);\n }, [recommendation, _followups_container_id, isContainerReady]);\n\n // Single fetch effect - fetch recommendation once and detect format\n useEffect(() => {\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n logger.log('[AdMeshRecommendations] ❌ Validation failed - missing required parameters');\n setIsLoading(false);\n return;\n }\n\n if (!sdk?.fetchRecommendationFromAIPContext) {\n logger.log('[AdMeshRecommendations] SDK fetchRecommendationFromAIPContext not available');\n setIsLoading(false);\n return;\n }\n\n // Prevent duplicate fetches for the same messageId\n if (fetchedMessageIdRef.current === messageId) {\n logger.log('[AdMeshRecommendations] ⏭️ Already fetched for this messageId, skipping duplicate fetch');\n return;\n }\n\n // Prevent concurrent fetches\n if (isFetchingRef.current) {\n logger.log('[AdMeshRecommendations] ⏭️ Fetch already in progress, skipping duplicate fetch');\n return;\n }\n\n logger.log('[AdMeshRecommendations] 📤 Fetching recommendation from /aip/context (single fetch)');\n\n const fetchRecommendations = async () => {\n try {\n isFetchingRef.current = true;\n setIsLoading(true);\n setError(null);\n\n // Single fetch call\n // Note: messages is optional - only pass if available\n const aipResponse = await sdk.fetchRecommendationFromAIPContext({\n query: query.trim(),\n sessionId: sessionId,\n messageId: messageId, // REQUIRED: messageId is mandatory\n language: language,\n geo_country: geo_country,\n userId: userId,\n model: model,\n ...(messages && messages.length > 0 && { messages }), // Optional: only pass if messages exist\n });\n\n // Convert response to AdMeshRecommendation format\n const convertedRecommendation = convertAIPResponseToRecommendation(aipResponse);\n \n // Log followup fields for debugging and call onFollowupDetected if present\n const followupQuery = (aipResponse as any).followup_query || (convertedRecommendation as any).followup_query;\n const followupEngagementUrl = (aipResponse as any).followup_engagement_url || (convertedRecommendation as any).followup_engagement_url;\n const recommendationId = convertedRecommendation.recommendation_id || '';\n \n // Only require followupQuery to trigger the callback - other fields are optional\n if (followupQuery) {\n logger.debug('[AdMeshRecommendations] ✅ Followup query detected:', followupQuery);\n logger.debug('[AdMeshRecommendations] ✅ Followup engagement URL:', followupEngagementUrl ? 'present' : 'missing');\n logger.debug('[AdMeshRecommendations] ✅ Recommendation ID:', recommendationId ? recommendationId : 'missing');\n\n // Log when optional fields are missing\n if (!followupEngagementUrl) {\n logger.debug('[AdMeshRecommendations] ⚠️ Followup engagement URL is missing (optional)');\n }\n if (!recommendationId) {\n logger.debug('[AdMeshRecommendations] ⚠️ Recommendation ID is missing (optional)');\n }\n\n // SDK-managed rendering: If followups_container_id is provided, use portal rendering\n // Only use onFollowupDetected callback if container ID is NOT provided (legacy/fallback mode)\n if (_followups_container_id) {\n logger.debug('[AdMeshRecommendations] ✅ Using SDK-managed portal rendering (followups_container_id provided)');\n } else if (onFollowupDetectedRef.current) {\n // Legacy/fallback: Notify third-party application via callback\n logger.debug('[AdMeshRecommendations] 🔔 Using legacy callback mode (followups_container_id not provided)');\n onFollowupDetectedRef.current(followupQuery, followupEngagementUrl || '', recommendationId || '');\n } else {\n logger.debug('[AdMeshRecommendations] ⚠️ Followup detected but no rendering method provided (neither followups_container_id nor onFollowupDetected callback)');\n }\n }\n \n setRecommendation(convertedRecommendation);\n\n // Extract format information for detection\n const responseAny = aipResponse as any;\n const preferredFormat = responseAny.creative?.preferred_format || \n responseAny.winning_bid?.preferred_format ||\n responseAny.preferred_format ||\n (aipResponse.creative_input as any)?.preferred_format;\n\n // Format selection logic - auto-detect from brand agent's preferred_format\n let selectedFormat: string;\n if (preferredFormat) {\n // Brand agent's preferred format (already validated by backend)\n // If weave format is returned, fall back to tail (weave should use WeaveAdFormatContainer)\n selectedFormat = preferredFormat === 'weave' ? 'tail' : preferredFormat;\n } else {\n // Fallback to default\n selectedFormat = 'tail';\n }\n\n setDetectedFormat(selectedFormat);\n fetchedMessageIdRef.current = messageId; // Mark as fetched\n logger.log('[AdMeshRecommendations] 📊 Format detected:', selectedFormat);\n \n setIsLoading(false);\n isFetchingRef.current = false;\n onRecommendationsShownRef.current?.(messageId);\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n // Log error but don't break the UI - network errors are expected in some scenarios\n if (error.message.includes('Failed to fetch') || error.message.includes('NetworkError')) {\n logger.warn(`[AdMeshRecommendations] ⚠️ Network error fetching recommendations(non - critical): ${error.message} `);\n } else {\n logger.error(`[AdMeshRecommendations] ❌ Error fetching recommendations: ${error.message} `);\n }\n setError(error);\n setIsLoading(false);\n isFetchingRef.current = false;\n onErrorRef.current?.(error);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, messageId, query, language, geo_country, userId, model]); // Removed messages, onRecommendationsShown, onError from dependencies\n\n // Helper to render followup portal\n const renderFollowupPortal = () => {\n if (followupContainer && recommendation?.followup_query && sdk) {\n logger.debug(`[AdMeshRecommendations] 🌀 Rendering followup portal into: ${_followups_container_id} `);\n return createPortal(\n <AdMeshFollowup\n recommendation={recommendation}\n theme={theme}\n sdk={sdk}\n sessionId={sessionId}\n onExecuteQuery={_onExecuteQuery}\n />,\n followupContainer\n );\n } else {\n logger.debug(`[AdMeshRecommendations] ❌ Skipping followup portal.Container: ${!!followupContainer}, Query: ${!!recommendation?.followup_query}, SDK: ${!!sdk} `);\n }\n return null;\n };\n\n // Don't render anything if validation fails\n if (!messageId || !query || query.trim() === '') {\n return null;\n }\n\n // Show loading state (optional - can be removed if not needed)\n if (isLoading) {\n return null; // Or return a loading spinner if desired\n }\n\n // Show error state (optional - can be removed if not needed)\n if (error) {\n return null; // Error is already handled by onError callback\n }\n\n // Don't render if no recommendation\n if (!recommendation || !detectedFormat) {\n return null;\n }\n\n // Render appropriate component based on detected format\n const creativeInput = recommendation.creative_input || {};\n const hasBridgePrompt = !!(creativeInput as any).bridge_prompt || !!creativeInput.bridge_content;\n const formatFromRec = (recommendation as any)?.format || (creativeInput as any)?.format;\n const preferredFormatFromRec = (recommendation as any)?.preferred_format;\n const hasBridgeFormat = formatFromRec === 'bridge' || preferredFormatFromRec === 'bridge' || detectedFormat === 'bridge';\n\n // Bridge format detection and rendering\n if (hasBridgePrompt || hasBridgeFormat) {\n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshBridgeFormat\n recommendation={recommendation}\n theme={theme}\n sessionId={sessionId}\n onPasteToInput={onPasteToInput}\n />\n {renderFollowupPortal()}\n </div>\n );\n }\n\n // Product card format\n if (detectedFormat === 'product' || detectedFormat === 'product_card') {\n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshProductCard\n recommendation={recommendation}\n theme={theme}\n sessionId={sessionId}\n />\n {renderFollowupPortal()}\n </div>\n );\n }\n\n // Tail format (default)\n const summaryText = creativeInput.long_description || \n creativeInput.context_snippet || \n creativeInput.short_description || \n '';\n \n return (\n <div className=\"admesh-recommendations-container\" style={{ marginTop: '1rem' }}>\n <AdMeshTailAd\n summaryText={summaryText}\n recommendations={[recommendation]}\n theme={theme}\n sessionId={sessionId}\n />\n {renderFollowupPortal()}\n </div>\n );\n};\n\nexport default AdMeshRecommendations;\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { logger } from '../utils/logger';\n\nexport interface WeaveFallbackRecommendationsProps {\n /** Recommendation format for fallback display\n *\n * - 'product': Display as product cards\n * - 'tail': Display as tail format (default)\n */\n format?: 'product' | 'tail';\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /**\n * Message ID for per-message recommendations.\n *\n * This is the primary identifier for fetching recommendations for a specific message.\n */\n messageId: string;\n\n /**\n * User query text for generating recommendations.\n *\n * This is the query that will be used to fetch recommendations from the backend.\n * Typically the user's question or search query.\n *\n * IMPORTANT: This should be a non-empty string. If not provided or empty,\n * the component will skip rendering to prevent 400 Bad Request errors.\n * The backend requires a valid query parameter.\n */\n query?: string;\n\n /**\n * Fallback state - controls whether to show recommendations\n * When true, recommendations will be fetched and displayed\n * When false or undefined, component will not render\n */\n fallback?: boolean;\n\n /**\n * Previously fetched recommendations from WeaveAdFormatContainer.\n * If provided and not empty, these will be rendered directly without making a new API call.\n * Only makes a new API call if this is empty/null/undefined.\n */\n previousRecommendations?: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any\n}\n\n/**\n * WeaveFallbackRecommendations - Weave Format Fallback Component\n *\n * Displays recommendations as a fallback UI when no AdMesh links are detected\n * in the LLM response. Works independently without requiring WeaveAdFormatContext.\n *\n * This component will:\n * - Accept messageId, query, and fallback state as props\n * - Only render when fallback prop is true\n * - If previousRecommendations are provided and not empty: renders them directly (no API call)\n * - If previousRecommendations are empty/null: calls SDK's showRecommendations() to fetch recommendations\n * - Display recommendations in the specified format (tail or product)\n *\n * IMPORTANT: Pass previousRecommendations from WeaveAdFormatContainer to avoid duplicate API calls.\n *\n * @example\n * ```tsx\n * const [fallback, setFallback] = useState(false);\n *\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <WeaveAdFormatContainer\n * messageId={message.id}\n * query={userQuery}\n * onFallbackChange={setFallback}\n * >\n * {llmResponseContent}\n * </WeaveAdFormatContainer>\n * <WeaveFallbackRecommendations\n * messageId={message.id}\n * query={userQuery}\n * format=\"tail\"\n * fallback={fallback}\n * previousRecommendations={recommendations} // Pass from WeaveAdFormatContainer\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const WeaveFallbackRecommendations: React.FC<WeaveFallbackRecommendationsProps> = ({\n format = 'tail',\n onError,\n messageId,\n query,\n fallback,\n previousRecommendations,\n}) => {\n const { sdk, sessionId, theme } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const [containerId, setContainerId] = useState<string>('');\n\n // Generate container ID based on message ID\n useEffect(() => {\n if (messageId) {\n setContainerId(`admesh-weave-fallback-${messageId}`);\n }\n }, [messageId]);\n\n // Log component render\n logger.log('[WeaveFallbackRecommendations] 🎨 Component render');\n\n useEffect(() => {\n // Log what we're receiving\n logger.log('[WeaveFallbackRecommendations] 🔄 useEffect triggered', {\n fallback,\n hasPreviousRecommendations: previousRecommendations && previousRecommendations.length > 0,\n previousRecommendationsCount: previousRecommendations?.length || 0\n });\n\n // Skip if fallback is not true\n if (!fallback) {\n logger.log('[WeaveFallbackRecommendations] ⏭️ Skipping - fallback is FALSE, not rendering recommendations');\n return;\n }\n\n logger.log('[WeaveFallbackRecommendations] ✅ fallback is TRUE, proceeding with recommendations');\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n logger.log('[WeaveFallbackRecommendations] ❌ Validation failed - returning early');\n return;\n }\n\n logger.log('[WeaveFallbackRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n logger.log('[WeaveFallbackRecommendations] SDK or containerId not ready');\n return;\n }\n\n // Check if we have previous recommendations to reuse\n const hasPreviousRecs = previousRecommendations && previousRecommendations.length > 0;\n \n logger.log('[WeaveFallbackRecommendations] 📊 Checking previous recommendations:', {\n hasPreviousRecs,\n count: previousRecommendations?.length || 0,\n fallback,\n messageId\n });\n \n if (hasPreviousRecs) {\n // Use previous recommendations - render directly without API call\n logger.log(`[WeaveFallbackRecommendations] ♻️ Reusing ${previousRecommendations.length} previous recommendation(s), skipping API call`);\n \n const renderRecommendations = async () => {\n try {\n const aipResponse = previousRecommendations[0];\n \n logger.log('[WeaveFallbackRecommendations] 📦 Processing AIP response:', {\n hasAipResponse: !!aipResponse,\n recommendationId: aipResponse?.recommendation_id,\n hasCreativeInput: !!aipResponse?.creative_input\n });\n \n // Development logging: Log full recommendation details\n if (aipResponse && process.env.NODE_ENV === 'development') {\n logger.log('[WeaveFallbackRecommendations] 📋 Recommendation received (DEV):', {\n recommendation_id: aipResponse.recommendation_id,\n product_id: aipResponse.product_id,\n brand_id: aipResponse.brand_id,\n title: aipResponse.title,\n click_url: aipResponse.click_url,\n contextual_relevance_score: aipResponse.contextual_relevance_score,\n preferred_format: aipResponse.creative_input?.preferred_format,\n resolved_format: aipResponse.format_resolution?.resolved_format,\n creative_input: {\n product_name: aipResponse.creative_input?.product_name,\n brand_name: aipResponse.creative_input?.brand_name,\n cta_url: aipResponse.creative_input?.cta_url,\n short_description: aipResponse.creative_input?.short_description,\n allowed_formats: aipResponse.creative_input?.allowed_formats,\n fallback_formats: aipResponse.creative_input?.fallback_formats,\n },\n format_resolution: aipResponse.format_resolution,\n full_response: aipResponse\n });\n }\n \n if (!aipResponse) {\n logger.warn('[WeaveFallbackRecommendations] ⚠️ Previous recommendation is empty, falling back to API call');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n return;\n }\n\n // Try to access SDK's private methods via type casting\n // These are used internally by showRecommendations\n const sdkAny = sdk as any;\n const convertMethod = sdkAny.convertAIPResponseToRecommendation;\n const getRendererMethod = sdkAny.getRenderer;\n const getTrackerMethod = sdkAny.getTracker;\n const sdkTheme = sdkAny.config?.theme;\n\n if (convertMethod && getRendererMethod && getTrackerMethod) {\n // Convert AIP response to recommendation format (same as showRecommendations does)\n const recommendation = convertMethod.call(sdk, aipResponse);\n \n if (!recommendation) {\n logger.warn('[WeaveFallbackRecommendations] ⚠️ Conversion returned empty, falling back to API call');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n return;\n }\n\n const response = {\n session_id: aipResponse.session_id || sessionId,\n message_id: `msg_${aipResponse.recommendation_id || messageId}`,\n recommendations: [recommendation]\n };\n\n // Get renderer and tracker\n const renderer = getRendererMethod.call(sdk);\n const tracker = getTrackerMethod.call(sdk);\n \n // Render directly\n await renderer.render({\n containerId,\n response,\n theme: theme || sdkTheme,\n tracker: tracker,\n sessionId: sessionId,\n });\n \n logger.log('[WeaveFallbackRecommendations] ✅ Recommendations rendered from previous response (no API call)');\n } else {\n // SDK methods not accessible - fall back to showRecommendations\n // This will use the cached result from auction coordination, so it's still fast\n logger.warn('[WeaveFallbackRecommendations] ⚠️ SDK internal methods not accessible, using showRecommendations (will use cached result)');\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.error(`[WeaveFallbackRecommendations] ❌ Error rendering previous recommendations: ${err.message}`);\n // Fallback to API call on error (will use cached result)\n try {\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId,\n });\n } catch (fallbackError) {\n onError?.(err);\n }\n }\n };\n\n renderRecommendations();\n } else {\n // No previous recommendations - make new API call\n logger.log('[WeaveFallbackRecommendations] 📤 No previous recommendations, calling sdk.showRecommendations');\n \n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n logger.log('[WeaveFallbackRecommendations] SDK showRecommendations not available');\n return;\n }\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n session_id: sessionId,\n messageId: messageId, // REQUIRED: messageId must be provided by platform\n });\n\n logger.log('[WeaveFallbackRecommendations] ✅ Recommendations displayed successfully');\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n logger.log(`[WeaveFallbackRecommendations] ❌ Error: ${err.message}`);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }\n }, [sdk, sessionId, containerId, format, messageId, query, fallback, onError, previousRecommendations, theme]);\n\n // Don't render anything if fallback is not active or validation fails\n if (!fallback || !messageId || !query || query.trim() === '') {\n return null;\n }\n\n // Render the container where fallback recommendations will be displayed\n // Use display: 'none' initially to prevent taking up space until recommendations are loaded\n // The SDK will set display: 'block' when it renders content\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-weave-fallback-recommendations-container\"\n style={{\n marginTop: '1rem',\n display: 'none', // Hidden by default, SDK will show when recommendations are rendered\n }}\n />\n );\n};\n\nexport default WeaveFallbackRecommendations;\n","'use client';\n\nimport React, { createContext, useContext } from 'react';\n\nexport interface WeaveAdFormatContextType {\n /** Whether the fallback UI should be rendered (no AdMesh links detected) */\n shouldRenderFallback: boolean;\n /** The message ID for this Weave Ad Format container (used for deduplication) */\n messageId: string;\n /** The session ID for this Weave Ad Format container (used for tracking) */\n sessionId?: string;\n /** The query for this message (used for fallback API calls) */\n query?: string;\n}\n\nconst WeaveAdFormatContext = createContext<WeaveAdFormatContextType | undefined>(undefined);\n\nexport const WeaveAdFormatProvider: React.FC<{\n children: React.ReactNode;\n shouldRenderFallback: boolean;\n messageId: string;\n sessionId?: string;\n query?: string;\n}> = ({ children, shouldRenderFallback, messageId, sessionId, query }) => {\n return (\n <WeaveAdFormatContext.Provider value={{ shouldRenderFallback, messageId, sessionId, query }}>\n {children}\n </WeaveAdFormatContext.Provider>\n );\n};\n\n/**\n * Hook to access WeaveAdFormatContext\n * \n * Returns the context value if inside a WeaveAdFormatContainer,\n * or undefined if not inside one.\n * \n * @example\n * ```tsx\n * const weaveContext = useWeaveAdFormatContext();\n * if (weaveContext) {\n * // Inside a WeaveAdFormatContainer\n * const { shouldRenderFallback, messageId } = weaveContext;\n * }\n * ```\n */\nexport const useWeaveAdFormatContext = (): WeaveAdFormatContextType | undefined => {\n return useContext(WeaveAdFormatContext);\n};\n\nexport default WeaveAdFormatContext;\n\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshRecommendation, EcommerceProduct } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshEcommerceCardsProps {\n recommendations: AdMeshRecommendation[]; // Required: unified schema recommendations\n title?: string;\n showTitle?: boolean;\n className?: string;\n cardClassName?: string;\n onProductClick?: (product: AdMeshRecommendation) => void;\n showPricing?: boolean;\n showRatings?: boolean;\n showBrand?: boolean;\n showSource?: boolean;\n showShipping?: boolean;\n maxCards?: number;\n cardWidth?: 'sm' | 'md' | 'lg';\n theme?: 'light' | 'dark' | 'auto';\n borderRadius?: 'none' | 'sm' | 'md' | 'lg';\n shadow?: 'none' | 'sm' | 'md' | 'lg';\n}\n\nexport const AdMeshEcommerceCards: React.FC<AdMeshEcommerceCardsProps> = ({\n recommendations,\n title = \"Product Recommendations\",\n showTitle = true,\n className = \"\",\n cardClassName = \"\",\n onProductClick,\n maxCards = 10,\n cardWidth = 'md',\n theme = 'auto',\n borderRadius = 'md',\n shadow = 'sm'\n}) => {\n // Validate recommendations - silently return null if empty\n if (!recommendations || recommendations.length === 0) {\n logger.log('[AdMesh Ecommerce Cards] Empty recommendations - not rendering anything');\n return null;\n }\n\n const displayItems: AdMeshRecommendation[] = recommendations.slice(0, maxCards);\n\n\n\n const getCardWidthClass = () => {\n switch (cardWidth) {\n case 'sm': return 'w-48 min-w-48';\n case 'md': return 'w-64 min-w-64';\n case 'lg': return 'w-80 min-w-80';\n default: return 'w-64 min-w-64';\n }\n };\n\n const getBorderRadiusClass = () => {\n switch (borderRadius) {\n case 'none': return 'rounded-none';\n case 'sm': return 'rounded-sm';\n case 'md': return 'rounded-lg';\n case 'lg': return 'rounded-xl';\n default: return 'rounded-lg';\n }\n };\n\n const getShadowClass = () => {\n switch (shadow) {\n case 'none': return '';\n case 'sm': return 'shadow-sm hover:shadow-md';\n case 'md': return 'shadow-md hover:shadow-lg';\n case 'lg': return 'shadow-lg hover:shadow-xl';\n default: return 'shadow-sm hover:shadow-md';\n }\n };\n\n const getThemeClasses = () => {\n if (theme === 'dark') {\n return 'bg-gray-900 text-white';\n } else if (theme === 'light') {\n return 'bg-white text-gray-900';\n }\n return 'bg-white dark:bg-gray-900 text-gray-900 dark:text-white';\n };\n\n\n\n const handleProductClick = (item: EcommerceProduct | AdMeshRecommendation) => {\n if (onProductClick) {\n // Cast to AdMeshRecommendation for callback\n const rec = item as AdMeshRecommendation;\n onProductClick(rec);\n } else {\n // Default behavior: open product link - prioritize click_url, then admesh_link, then url\n const link = (item as any).click_url || \n (item as any).admesh_link || \n (item as any).url ||\n (item as any).creative_input?.cta_url;\n if (link) {\n window.open(link, '_blank', 'noopener,noreferrer');\n }\n }\n };\n\n // Check if we have any data to display\n if (!recommendations || recommendations.length === 0) {\n return null;\n }\n\n return (\n <div className={classNames('w-full', className)}>\n {showTitle && (\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-semibold text-gray-900 dark:text-white\">\n {title}\n </h3>\n <div className=\"mt-1 h-0.5 w-12 bg-blue-500\"></div>\n </div>\n )}\n \n <div className=\"relative\">\n {displayItems.length === 0 ? (\n <div className=\"text-center py-8 text-gray-500\">\n No products to display\n </div>\n ) : (\n <div className=\"flex gap-4 overflow-x-auto pb-4 scrollbar-hide\">\n {displayItems.map((item) => {\n // Get the appropriate ID for the key and tracking\n const itemId = (item as any).recommendation_id || (item as any).product_id || (item as any).id || '';\n const recommendationId = (item as any).recommendation_id || (item as any).product_id || '';\n const productId = (item as any).product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n key={itemId}\n recommendationId={recommendationId}\n productId={productId}\n className={classNames(\n getCardWidthClass(),\n getBorderRadiusClass(),\n getShadowClass(),\n getThemeClasses(),\n 'flex-shrink-0 border border-gray-200 dark:border-gray-700 transition-all duration-200 cursor-pointer hover:scale-[1.02]',\n cardClassName\n )}\n onClick={() => handleProductClick(item)}\n >\n <div>\n {/* Product Image */}\n <div className=\"relative aspect-square w-full overflow-hidden\">\n {(item.creative_input?.assets?.logo_url || \n item.creative_input?.assets?.image_urls?.[0] || \n item.product_logo?.url) ? (\n <img\n src={item.creative_input?.assets?.image_urls?.[0] || \n item.creative_input?.assets?.logo_url || \n item.product_logo?.url}\n alt={item.creative_input?.product_name || item.title || item.product_title || 'Product'}\n className=\"h-full w-full object-cover transition-transform duration-200 hover:scale-105\"\n loading=\"lazy\"\n onError={(e) => {\n // Hide image if it fails to load\n (e.target as HTMLImageElement).style.display = 'none';\n }}\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gray-100 dark:bg-gray-800\">\n <svg className=\"h-12 w-12 text-gray-400\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z\" />\n </svg>\n </div>\n )}\n </div>\n\n {/* Product Info */}\n <div className=\"p-3\">\n {/* Title */}\n <h4 className=\"text-sm font-medium text-gray-900 dark:text-white line-clamp-2 mb-2 leading-tight\">\n {item.creative_input?.product_name || item.title || item.product_title || ''}\n </h4>\n\n {/* Summary - prioritize creative_input fields */}\n {(item.creative_input?.short_description || \n item.creative_input?.context_snippet || \n item.weave_summary) && (\n <p className=\"text-xs text-gray-600 dark:text-gray-400 line-clamp-2 mb-2\">\n {item.creative_input?.short_description || \n item.creative_input?.context_snippet || \n item.weave_summary}\n </p>\n )}\n\n {/* Offer Summary: Display below short_description */}\n {item.creative_input?.offer_summary && (\n <p className=\"text-xs text-gray-500 dark:text-gray-500 line-clamp-2 mb-2 italic\">\n {item.creative_input.offer_summary}\n </p>\n )}\n\n {/* Value Props from creative_input */}\n {item.creative_input?.value_props && item.creative_input.value_props.length > 0 && (\n <div className=\"mb-2\">\n <ul className=\"space-y-1\">\n {item.creative_input.value_props.slice(0, 2).map((prop, idx) => (\n <li key={idx} className=\"flex items-start gap-1.5 text-xs text-gray-600 dark:text-gray-400\">\n <svg className=\"w-3 h-3 mt-0.5 flex-shrink-0 text-green-500\" fill=\"currentColor\" viewBox=\"0 0 20 20\">\n <path fillRule=\"evenodd\" d=\"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z\" clipRule=\"evenodd\" />\n </svg>\n <span className=\"line-clamp-1\">{prop}</span>\n </li>\n ))}\n </ul>\n </div>\n )}\n\n {/* Categories - fallback if no value props */}\n {(!item.creative_input?.value_props || item.creative_input.value_props.length === 0) && \n item.categories && item.categories.length > 0 && (\n <div className=\"flex flex-wrap gap-1 mb-2\">\n {item.categories.slice(0, 2).map((cat, idx) => (\n <span key={idx} className=\"text-xs bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 px-2 py-1 rounded\">\n {cat}\n </span>\n ))}\n </div>\n )}\n\n {/* Trust Score */}\n {item.trust_score !== undefined && (\n <div className=\"text-xs text-gray-600 dark:text-gray-400\">\n Trust Score: {(item.trust_score * 100).toFixed(0)}%\n </div>\n )}\n </div>\n </div>\n </AdMeshViewabilityTracker>\n );\n })}\n </div>\n )}\n\n {/* Scroll Indicators - only show when there are products */}\n {displayItems.length > 0 && (\n <>\n <div className=\"absolute top-1/2 -left-2 transform -translate-y-1/2 bg-white dark:bg-gray-800 rounded-full shadow-lg p-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n <svg className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M15 19l-7-7 7-7\" />\n </svg>\n </div>\n <div className=\"absolute top-1/2 -right-2 transform -translate-y-1/2 bg-white dark:bg-gray-800 rounded-full shadow-lg p-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n <svg className=\"w-4 h-4 text-gray-600 dark:text-gray-300\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n </div>\n </>\n )}\n </div>\n\n {/* Disclosure */}\n <div className=\"flex justify-between items-center mt-3 pt-2 border-t border-gray-200 dark:border-gray-700\">\n <span className=\"text-xs text-gray-500 dark:text-gray-400\">\n Sponsored\n </span>\n <span className=\"text-xs text-gray-400 dark:text-gray-500\">\n \n </span>\n </div>\n\n {/* Custom scrollbar styles */}\n <style dangerouslySetInnerHTML={{\n __html: `\n .scrollbar-hide {\n -ms-overflow-style: none;\n scrollbar-width: none;\n }\n .scrollbar-hide::-webkit-scrollbar {\n display: none;\n }\n .line-clamp-2 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n `\n }} />\n </div>\n );\n};\n\nexport default AdMeshEcommerceCards;\n","import React from 'react';\nimport { logger } from '../utils/logger';\nimport classNames from 'classnames';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshLinkTracker } from './AdMeshLinkTracker';\n\nexport interface AdMeshInlineCardProps {\n recommendation: AdMeshRecommendation;\n theme?: AdMeshTheme;\n variation?: 'default' | 'statement' | 'question';\n expandable?: boolean; // For question and statement variations, default: false\n className?: string;\n style?: React.CSSProperties;\n}\n\n/**\n * AdMeshInlineCard\n * Compact inline card that uses the same card style as AdMeshProductCard,\n * but with a smaller content footprint suitable for inline placements.\n */\nexport const AdMeshInlineCard: React.FC<AdMeshInlineCardProps> = ({\n recommendation,\n theme,\n variation = 'default',\n expandable = false,\n className,\n style,\n}) => {\n // Validate recommendation - return null if empty\n if (!recommendation) {\n logger.log('[AdMesh Inline Card] No recommendation provided - not rendering');\n return null;\n }\n\n // Get content based on variation\n const getVariationContent = () => {\n // Note: content_variations is not in the finalized minimal schema\n // Using product_title and weave_summary instead\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.weave_summary || recommendation.reason || '',\n ctaText: recommendation.product_title || recommendation.title\n };\n };\n\n const content = getVariationContent();\n\n const cardClasses = classNames(\n 'rounded-xl shadow-sm border border-gray-200 dark:border-gray-800 p-4 bg-white dark:bg-slate-900',\n 'hover:shadow-md transition-shadow duration-200',\n {\n 'cursor-pointer': expandable && (variation === 'question' || variation === 'statement')\n },\n className\n );\n\n return (\n <div\n className={cardClasses}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n // Ensure consistent width: 100% for inline cards\n width: theme?.components?.inlineRecommendation?.width || '100%',\n ...theme?.components?.productCard,\n ...style,\n }}\n data-admesh-theme={theme?.mode}\n >\n <div className=\"h-full flex flex-col\">\n {/* Header with title and CTA button in same row */}\n <div className=\"flex justify-between items-center gap-3 mb-2\">\n <div className=\"flex items-center gap-2 flex-1 min-w-0\">\n {recommendation.product_logo?.url && (\n <img\n src={recommendation.product_logo.url}\n alt={`${recommendation.product_title} logo`}\n className=\"w-5 h-5 rounded flex-shrink-0\"\n onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }}\n />\n )}\n <h4 className=\"font-semibold text-gray-800 dark:text-gray-200 text-sm truncate\">\n {content.title}\n </h4>\n </div>\n\n <div className=\"flex-shrink-0\">\n <AdMeshLinkTracker\n recommendationId={recommendation.recommendation_id || ''}\n admeshLink={recommendation.click_url || \n recommendation.admesh_link || \n recommendation.creative_input?.cta_url ||\n recommendation.url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_title,\n matchScore: recommendation.contextual_relevance_score,\n component: 'inline_card_cta',\n }}\n >\n <button className=\"text-xs px-2 py-1 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 flex items-center transition-all duration-200 transform hover:scale-105 shadow-sm whitespace-nowrap\">\n {variation === 'question' ? 'Try' : 'Visit'}\n <svg className=\"ml-1 h-3 w-3\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14\" />\n </svg>\n </button>\n </AdMeshLinkTracker>\n </div>\n </div>\n\n {/* Brief description (single line clamp) */}\n {content.description && (\n <p className=\"text-xs text-gray-600 dark:text-gray-300 mb-2 line-clamp-2\">\n {content.description}\n </p>\n )}\n\n {/* Offer Summary: Display below description */}\n {recommendation.creative_input?.offer_summary && (\n <p className=\"text-xs text-gray-500 dark:text-gray-400 mb-2 line-clamp-2 italic\">\n {recommendation.creative_input.offer_summary}\n </p>\n )}\n\n {/* Footer with disclosures */}\n <div className=\"mt-auto pt-2 border-t border-gray-100 dark:border-slate-700\">\n <div className=\"flex items-center justify-between text-[11px] text-gray-500 dark:text-gray-400\">\n <span>Sponsored</span>\n <span className=\"text-gray-400 dark:text-gray-500\"></span>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nAdMeshInlineCard.displayName = 'AdMeshInlineCard';\n\n","import React from 'react';\nimport classNames from 'classnames';\nimport type { AdMeshBadgeProps, BadgeType } from '../types/index';\n\n// Badge type to variant mapping\nconst badgeTypeVariants: Record<BadgeType, string> = {\n 'Top Match': 'primary',\n 'Free Tier': 'success',\n 'AI Powered': 'secondary',\n 'Popular': 'warning',\n 'New': 'primary',\n 'Trial Available': 'success'\n};\n\n// Badge type to icon mapping (using clean Unicode symbols)\nconst badgeTypeIcons: Partial<Record<BadgeType, string>> = {\n 'Top Match': '★',\n 'Free Tier': '◆',\n 'AI Powered': '◉',\n 'Popular': '▲',\n 'New': '●',\n 'Trial Available': '◈'\n};\n\nexport const AdMeshBadge: React.FC<AdMeshBadgeProps> = ({\n type,\n variant,\n size = 'md',\n className,\n style\n}) => {\n const effectiveVariant = variant || badgeTypeVariants[type] || 'secondary';\n const icon = badgeTypeIcons[type];\n\n const badgeClasses = classNames(\n 'admesh-component',\n 'admesh-badge',\n `admesh-badge--${effectiveVariant}`,\n `admesh-badge--${size}`,\n className\n );\n\n return (\n <span\n className={badgeClasses}\n style={style}\n >\n {icon && <span className=\"admesh-badge__icon\">{icon}</span>}\n <span className=\"admesh-badge__text\">{type}</span>\n </span>\n );\n};\n\nAdMeshBadge.displayName = 'AdMeshBadge';\n","/**\n * Streaming Events Utilities\n * \n * Custom event system for communicating LLM streaming lifecycle events\n * between the chat frontend and AdMesh WeaveAdFormatContainer.\n * \n * This allows WeaveAdFormatContainer to trigger final link detection\n * when streaming completes, rather than using arbitrary timeouts.\n */\n\nimport { logger } from './logger';\n\nexport const STREAMING_START_EVENT = 'admesh:streamingStart';\nexport const STREAMING_COMPLETE_EVENT = 'admesh:streamingComplete';\n\nexport interface StreamingStartEventDetail {\n messageId: string;\n sessionId: string;\n timestamp: number;\n}\n\nexport interface StreamingCompleteEventDetail {\n messageId: string;\n sessionId: string;\n timestamp: number;\n metadata?: {\n hasRecommendations?: boolean;\n recommendationCount?: number;\n };\n}\n\n/**\n * Dispatch a streaming start event\n * \n * Call this when the backend starts streaming the LLM response.\n * This signals to WeaveAdFormatContainer that content is being received.\n * \n * @param messageId - Unique identifier for the message\n * @param sessionId - Session/chat identifier\n */\nexport function dispatchStreamingStartEvent(\n messageId: string,\n sessionId: string\n): void {\n const detail: StreamingStartEventDetail = {\n messageId,\n sessionId,\n timestamp: Date.now(),\n };\n\n const event = new CustomEvent(STREAMING_START_EVENT, { detail });\n window.dispatchEvent(event);\n\n logger.log('[StreamingEvents] 📢 Dispatched streamingStart event', {\n messageId,\n sessionId\n });\n}\n\n/**\n * Dispatch a streaming complete event\n * \n * Call this when the backend finishes streaming the LLM response.\n * This triggers WeaveAdFormatContainer to perform final link detection.\n * \n * @param messageId - Unique identifier for the message\n * @param sessionId - Session/chat identifier\n * @param metadata - Optional metadata about recommendations\n */\nexport function dispatchStreamingCompleteEvent(\n messageId: string,\n sessionId: string,\n metadata?: {\n hasRecommendations?: boolean;\n recommendationCount?: number;\n }\n): void {\n const detail: StreamingCompleteEventDetail = {\n messageId,\n sessionId,\n timestamp: Date.now(),\n metadata,\n };\n\n const event = new CustomEvent(STREAMING_COMPLETE_EVENT, { detail });\n window.dispatchEvent(event);\n\n logger.log('[StreamingEvents] 📢 Dispatched streamingComplete event', {\n messageId,\n sessionId,\n metadata\n });\n}\n\n/**\n * Listen for streaming start events\n * \n * @param messageId - Filter events by this messageId\n * @param sessionId - Filter events by this sessionId\n * @param callback - Called when matching event is received\n * @returns Cleanup function to remove the event listener\n */\nexport function onStreamingStart(\n messageId: string,\n sessionId: string,\n callback: (detail: StreamingStartEventDetail) => void\n): () => void {\n const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingStartEventDetail>;\n \n // Only trigger callback if this is the message we're waiting for\n if (\n customEvent.detail.messageId === messageId &&\n customEvent.detail.sessionId === sessionId\n ) {\n logger.log('[StreamingEvents] 📨 Received streamingStart event');\n callback(customEvent.detail);\n }\n };\n\n window.addEventListener(STREAMING_START_EVENT, handler);\n\n // Return cleanup function\n return () => {\n window.removeEventListener(STREAMING_START_EVENT, handler);\n };\n}\n\n/**\n * Listen for streaming complete events\n * \n * @param messageId - Filter events by this messageId\n * @param sessionId - Filter events by this sessionId\n * @param callback - Called when matching event is received\n * @returns Cleanup function to remove the event listener\n */\nexport function onStreamingComplete(\n messageId: string,\n sessionId: string,\n callback: (detail: StreamingCompleteEventDetail) => void\n): () => void {\n logger.log('[StreamingEvents] 👂 Setting up streamingComplete listener', {\n expectedMessageId: messageId,\n expectedSessionId: sessionId\n });\n\n const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingCompleteEventDetail>;\n \n logger.log('[StreamingEvents] 📨 Received streamingComplete event (checking match)', {\n receivedMessageId: customEvent.detail.messageId,\n receivedSessionId: customEvent.detail.sessionId,\n expectedMessageId: messageId,\n expectedSessionId: sessionId,\n messageIdMatch: customEvent.detail.messageId === messageId,\n sessionIdMatch: customEvent.detail.sessionId === sessionId\n });\n \n // Only trigger callback if this is the message we're waiting for\n if (\n customEvent.detail.messageId === messageId &&\n customEvent.detail.sessionId === sessionId\n ) {\n logger.log('[StreamingEvents] ✅ Event matched! Calling callback');\n callback(customEvent.detail);\n } else {\n logger.log('[StreamingEvents] ⚠️ Event did not match - ignoring');\n }\n };\n\n window.addEventListener(STREAMING_COMPLETE_EVENT, handler);\n\n // Return cleanup function\n return () => {\n logger.log('[StreamingEvents] 🧹 Removing streamingComplete listener');\n window.removeEventListener(STREAMING_COMPLETE_EVENT, handler);\n };\n}\n","import { logger } from './logger';\n\ninterface InlineExposureTrackerState {\n observer: IntersectionObserver;\n timeoutId: ReturnType<typeof setTimeout> | null;\n viewableStart: number | null;\n}\n\nexport interface InlineExposureTrackingParams {\n exposureUrl?: string;\n recommendationId?: string;\n linkElement?: HTMLElement | null;\n sessionId?: string;\n logPrefix?: string;\n}\n\nexport interface InlineExposureTracker {\n startTracking: (params: InlineExposureTrackingParams) => void;\n cleanup: () => void;\n}\n\n/**\n * Creates an inline exposure tracker that fires AdMesh exposure pixels when\n * detected links meet the MRC viewability threshold (50% visible for 1 second).\n */\nexport const createInlineExposureTracker = (): InlineExposureTracker => {\n const firedKeys = new Set<string>();\n const activeTrackers = new Map<string, InlineExposureTrackerState>();\n\n const cleanupTracker = (key: string) => {\n const tracker = activeTrackers.get(key);\n if (!tracker) {\n return;\n }\n tracker.observer.disconnect();\n if (tracker.timeoutId) {\n clearTimeout(tracker.timeoutId);\n }\n activeTrackers.delete(key);\n };\n\n const startTracking = ({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix = '[AdMesh Exposure]'\n }: InlineExposureTrackingParams) => {\n if (typeof window === 'undefined') {\n return;\n }\n\n if (!exposureUrl || !linkElement) {\n if (logPrefix) {\n logger.warn(`${logPrefix} ⚠️ Missing exposure tracking data`);\n }\n return;\n }\n\n const dedupeKey = `${sessionId || 'anonymous'}::${recommendationId || exposureUrl}`;\n\n if (firedKeys.has(dedupeKey) || activeTrackers.has(dedupeKey)) {\n return;\n }\n\n const trackerState: InlineExposureTrackerState = {\n observer: undefined as unknown as IntersectionObserver,\n timeoutId: null,\n viewableStart: null\n };\n\n const fireExposurePixel = () => {\n cleanupTracker(dedupeKey);\n firedKeys.add(dedupeKey);\n\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n if (logPrefix) {\n logger.log(`${logPrefix} ✅ Exposure pixel fired`);\n }\n })\n .catch(() => {\n if (logPrefix) {\n logger.warn(`${logPrefix} ⚠️ Failed to fire exposure pixel`);\n }\n firedKeys.delete(dedupeKey);\n });\n };\n\n const observer = new IntersectionObserver(\n (entries) => {\n entries.forEach((entry) => {\n const visibility = entry.intersectionRatio;\n\n if (visibility >= 0.5) {\n if (trackerState.viewableStart === null) {\n trackerState.viewableStart = performance.now();\n trackerState.timeoutId = setTimeout(fireExposurePixel, 1000);\n }\n } else {\n trackerState.viewableStart = null;\n if (trackerState.timeoutId) {\n clearTimeout(trackerState.timeoutId);\n trackerState.timeoutId = null;\n }\n }\n });\n },\n {\n threshold: [0, 0.25, 0.5, 0.75, 1],\n rootMargin: '0px'\n }\n );\n\n trackerState.observer = observer;\n observer.observe(linkElement as Element);\n activeTrackers.set(dedupeKey, trackerState);\n };\n\n const cleanup = () => {\n Array.from(activeTrackers.keys()).forEach(cleanupTracker);\n };\n\n return {\n startTracking,\n cleanup\n };\n};\n","'use client';\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react';\nimport { createPortal } from 'react-dom';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { WeaveFallbackRecommendations } from './WeaveFallbackRecommendations';\nimport { AdMeshFollowup } from './AdMeshFollowup';\nimport { onStreamingComplete } from '../utils/streamingEvents';\nimport {\n createInlineExposureTracker,\n type InlineExposureTrackingParams\n} from '../utils/inlineExposureTracker';\nimport { WeaveResponseProcessor } from '../sdk/WeaveResponseProcessor';\nimport { logger } from '../utils/logger';\nimport type { AdMeshRecommendation } from '../types/index';\n\n/**\n * FinalLinkDetectionCheck - Event-driven link detection component\n *\n * This component waits for the 'admesh:streamingComplete' event from ChatWindow\n * before performing final link detection. This eliminates race conditions where\n * timeout-based detection would trigger before streaming completes.\n */\ninterface FinalLinkDetectionCheckProps {\n containerId: string;\n messageId: string;\n sessionId: string;\n sdk: any; // eslint-disable-line @typescript-eslint/no-explicit-any\n query?: string;\n recommendations?: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any\n onLinksFound: (count: number) => void;\n onNoLinksFound: () => void;\n children: React.ReactNode;\n}\n\nconst FinalLinkDetectionCheck: React.FC<FinalLinkDetectionCheckProps> = ({\n containerId,\n messageId,\n sessionId,\n sdk,\n query,\n recommendations = [],\n onLinksFound,\n onNoLinksFound,\n children\n}) => {\n const [checkComplete, setCheckComplete] = useState(false);\n const [waitingForStreamEnd, setWaitingForStreamEnd] = useState(true);\n const [linksFound, setLinksFound] = useState(false);\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n\n // Use refs to avoid recreating the callback on every render\n const onLinksFoundRef = useRef(onLinksFound);\n const onNoLinksFoundRef = useRef(onNoLinksFound);\n const containerIdRef = useRef(containerId);\n const sdkRef = useRef(sdk);\n const sessionIdRef = useRef(sessionId);\n const queryRef = useRef(query);\n\n // Update refs when values change\n useEffect(() => {\n onLinksFoundRef.current = onLinksFound;\n onNoLinksFoundRef.current = onNoLinksFound;\n containerIdRef.current = containerId;\n sdkRef.current = sdk;\n sessionIdRef.current = sessionId;\n queryRef.current = query;\n }, [onLinksFound, onNoLinksFound, containerId, sdk, sessionId, query]);\n\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n const trackExposurePixel = useCallback(\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId: sessionIdRef.current,\n logPrefix: '[FinalLinkDetectionCheck]'\n });\n },\n []\n );\n\n const performFinalCheck = useCallback(() => {\n logger.log('[FinalLinkDetectionCheck] 🔍 Performing final link detection...');\n\n try {\n const container = document.getElementById(containerIdRef.current);\n if (!container) {\n logger.warn('[FinalLinkDetectionCheck] ❌ Container not found');\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Create WeaveResponseProcessor instance\n const weaveProcessor = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n\n // Use fetched recommendations if available, otherwise fall back to pattern-based detection\n const recsToUse = recommendations.length > 0 ? recommendations : [];\n logger.log(`[FinalLinkDetectionCheck] 📋 Using ${recsToUse.length} recommendations for link detection`);\n \n // Development logging: Log recommendation details used for link detection\n if (recsToUse.length > 0 && process.env.NODE_ENV === 'development') {\n recsToUse.forEach((rec, index) => {\n logger.log(`[FinalLinkDetectionCheck] 📋 Recommendation ${index + 1} (DEV):`, {\n recommendation_id: rec.recommendation_id,\n product_id: rec.product_id,\n title: rec.title,\n click_url: rec.click_url,\n preferred_format: rec.creative_input?.preferred_format,\n resolved_format: rec.format_resolution?.resolved_format,\n creative_input: {\n product_name: rec.creative_input?.product_name,\n brand_name: rec.creative_input?.brand_name,\n cta_url: rec.creative_input?.cta_url,\n }\n });\n });\n }\n\n // Scan for AdMesh links one final time\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recsToUse, // Use fetched recommendations for brand URL matching\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) =>\n trackExposurePixel({ exposureUrl, recommendationId, linkElement })\n );\n\n logger.log(`[FinalLinkDetectionCheck] 📊 Final check result: ${detectedLinks.length} links`);\n\n if (detectedLinks.length > 0) {\n // Links found! Cancel fallback\n logger.log('[FinalLinkDetectionCheck] ✅ Links detected - canceling fallback (no API call)');\n setLinksFound(true);\n onLinksFoundRef.current(detectedLinks.length);\n } else {\n // No links found, proceed with fallback\n logger.log(`[FinalLinkDetectionCheck] ⚠️ No links found - rendering fallback (will use ${recsToUse.length} previous recommendation(s) if available)`);\n setLinksFound(false);\n onNoLinksFoundRef.current();\n }\n\n setCheckComplete(true);\n } catch (error) {\n logger.error('[FinalLinkDetectionCheck] ❌ Error during final check');\n setCheckComplete(true);\n onNoLinksFoundRef.current();\n }\n }, [trackExposurePixel, recommendations]);\n\n useEffect(() => {\n logger.log('[FinalLinkDetectionCheck] ⏳ Setting up listener', {\n messageId,\n sessionId,\n recommendationsCount: recommendations.length\n });\n\n let timeoutId: NodeJS.Timeout | null = null;\n let eventReceived = false;\n\n // Listen for the streamingComplete event from ChatWindow\n const cleanup = onStreamingComplete(messageId, sessionId, (detail) => {\n logger.log('[FinalLinkDetectionCheck] 🎯 Received streamingComplete event', {\n messageId: detail.messageId,\n sessionId: detail.sessionId,\n timestamp: detail.timestamp\n });\n eventReceived = true;\n \n // Clear timeout since event was received\n if (timeoutId) {\n clearTimeout(timeoutId);\n timeoutId = null;\n }\n \n setWaitingForStreamEnd(false);\n\n // Small delay to ensure DOM is fully updated\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n });\n\n // Fallback timeout: If streamingComplete doesn't fire within 5 seconds, trigger check anyway\n // This ensures fallback recommendations display even if event system fails\n timeoutId = setTimeout(() => {\n if (!eventReceived) {\n logger.warn('[FinalLinkDetectionCheck] ⏱️ Timeout: streamingComplete event not received, triggering fallback check anyway');\n setWaitingForStreamEnd(false);\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n }\n }, 5000); // 5 second timeout\n\n return () => {\n logger.log('[FinalLinkDetectionCheck] 🧹 Cleaning up listener');\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n cleanup();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messageId, sessionId]); // performFinalCheck depends on recommendations prop, but recommendations should be stable for a given messageId\n\n // Show loading state while waiting for stream to complete\n if (waitingForStreamEnd) {\n return <></>;\n }\n\n // Show checking state while performing final detection\n if (!checkComplete) {\n return <></>;\n }\n\n // Only render children (fallback) if NO links were found\n if (linksFound) {\n logger.log('[FinalLinkDetectionCheck] 🚫 Links found - NOT rendering fallback');\n return <></>;\n }\n\n logger.log('[FinalLinkDetectionCheck] ✅ No links found - rendering fallback');\n return <>{children}</>;\n};\n\nexport interface WeaveAdFormatContainerProps {\n /** Unique ID for this message container */\n messageId: string;\n\n /** The LLM response content (may contain AdMesh links) */\n children: React.ReactNode;\n\n /** Fallback format if no links detected (default: 'tail') */\n fallbackFormat?: 'product' | 'tail';\n\n /** Optional callback when AdMesh links are detected */\n onLinksDetected?: (count: number) => void;\n\n /** Optional callback when no links are detected (fallback should be rendered) */\n onNoLinksDetected?: () => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /** Optional CSS class for the container */\n className?: string;\n\n /** Optional query text for fallback API calls (used when no links are detected) */\n query?: string;\n\n /** Optional callback to notify parent when fallback state changes */\n onFallbackChange?: (shouldFallback: boolean) => void;\n\n /** Optional callback when weave injection is attempted */\n onWeaveAttempt?: (messageId: string) => void;\n\n /** Optional callback when weave injection outcome is determined */\n onWeaveOutcome?: (messageId: string, success: boolean, reason?: string) => void;\n\n /** Optional container ID for follow-up suggestions */\n followups_container_id?: string;\n\n /** Callback to execute query when follow-up is selected (required for follow-up functionality) */\n onExecuteQuery?: (query: string) => void | Promise<void>;\n\n /** Optional callback when a sponsored followup is detected */\n onFollowupDetected?: (followupQuery: string, followupEngagementUrl: string, recommendationId: string) => void;\n\n /** Signal indicating if the followup container is ready in the DOM */\n isContainerReady?: boolean;\n}\n\n/**\n * WeaveAdFormatContainer - Automatic Weave Ad Format handling component\n *\n * Wraps LLM response content and automatically:\n * - Detects AdMesh links in the content\n * - Fires exposure tracking for detected links\n * - Adds [Ad] labels to links\n * - Shows \"Why this ad?\" tooltip on hover\n * - Provides context for WeaveFallbackRecommendations component\n *\n * @example\n * ```tsx\n * <WeaveAdFormatContainer\n * messageId={message.id}\n * query={userQuery}\n * onLinksDetected={(count) => console.log(`Found ${count} ads`)}\n * >\n * {llmResponseContent}\n * </WeaveAdFormatContainer>\n * ```\n */\nexport const WeaveAdFormatContainer: React.FC<WeaveAdFormatContainerProps> = ({\n messageId,\n children,\n fallbackFormat = 'tail',\n onLinksDetected,\n onNoLinksDetected,\n onError,\n className,\n query,\n onFallbackChange,\n onWeaveAttempt,\n onWeaveOutcome,\n followups_container_id: _followups_container_id,\n onExecuteQuery: _onExecuteQuery,\n onFollowupDetected,\n isContainerReady\n}) => {\n const { sessionId, sdk, language, geo_country, userId, model, messages, theme } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const containerId = `weave-ad-container-${messageId}`;\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n const scannedRef = useRef(false);\n const weaveProcessorRef = useRef<WeaveResponseProcessor | null>(null);\n const [recommendations, setRecommendations] = useState<any[]>([]);\n const recommendationsRef = useRef<any[]>([]);\n const [recommendationWithFollowup, setRecommendationWithFollowup] = useState<AdMeshRecommendation | null>(null);\n const [followupContainer, setFollowupContainer] = useState<Element | null>(null);\n\n // Use refs for callbacks to avoid dependency issues\n const onFollowupDetectedRef = useRef(onFollowupDetected);\n \n // Update refs when callbacks change\n useEffect(() => {\n onFollowupDetectedRef.current = onFollowupDetected;\n }, [onFollowupDetected]);\n\n // Initialize WeaveResponseProcessor once\n useEffect(() => {\n if (!weaveProcessorRef.current) {\n weaveProcessorRef.current = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n }\n return () => {\n if (weaveProcessorRef.current) {\n weaveProcessorRef.current.stopWatching();\n weaveProcessorRef.current.clearCache();\n }\n };\n }, []);\n\n // Cleanup exposure tracker on unmount\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n // Fetch recommendations from SDK cache if available\n useEffect(() => {\n const fetchRecommendations = async () => {\n if (!sdk || !query?.trim() || !messageId) {\n logger.log('[WeaveAdFormatContainer] ⚠️ Missing SDK, query, or messageId - using pattern-based detection');\n return;\n }\n\n try {\n logger.log('[WeaveAdFormatContainer] 📤 Fetching recommendations from SDK for brand URL matching');\n // Use context values (from AdMeshProvider) for UCP PlatformRequest fields\n const aipResponse = await sdk.fetchRecommendationFromAIPContext({\n query: query.trim(),\n sessionId: sessionId,\n messageId: messageId,\n language: language, // From context (AdMeshProvider) - maps to context.language in UCP\n geo_country: geo_country, // From context (AdMeshProvider) - maps to context.geography.country in UCP\n userId: userId, // From context (AdMeshProvider)\n model: model, // From context (AdMeshProvider)\n messages: messages, // From context (AdMeshProvider)\n });\n\n // Wrap single recommendation in array for WeaveResponseProcessor\n const recs = aipResponse ? [aipResponse] : [];\n setRecommendations(recs);\n recommendationsRef.current = recs; // Update ref to avoid dependency issues\n logger.log('[WeaveAdFormatContainer] ✅ Fetched recommendations:', recs.length);\n \n // Extract follow-up data from response (similar to AdMeshRecommendations)\n if (aipResponse) {\n const followupQueryTopLevel = (aipResponse as any).followup_query;\n const followupQueryInCreative = (aipResponse.creative_input as any)?.followup_query;\n const followupQuery = followupQueryTopLevel || followupQueryInCreative;\n \n const followupEngagementUrlTopLevel = (aipResponse as any).followup_engagement_url;\n const followupEngagementUrlInCreative = (aipResponse.creative_input as any)?.followup_engagement_url;\n const followupEngagementUrl = followupEngagementUrlTopLevel || followupEngagementUrlInCreative;\n \n const followupExposureUrlTopLevel = (aipResponse as any).followup_exposure_url;\n const followupExposureUrlInCreative = (aipResponse.creative_input as any)?.followup_exposure_url;\n const followupExposureUrl = followupExposureUrlTopLevel || followupExposureUrlInCreative;\n \n const recommendationId = (aipResponse as any).recommendation_id || '';\n \n // If followup query exists, create recommendation object with follow-up data\n if (followupQuery) {\n logger.debug('[WeaveAdFormatContainer] ✅ Followup query detected:', followupQuery);\n logger.debug('[WeaveAdFormatContainer] ✅ Followup engagement URL:', followupEngagementUrl ? 'present' : 'missing');\n logger.debug('[WeaveAdFormatContainer] ✅ Recommendation ID:', recommendationId ? recommendationId : 'missing');\n \n // SDK-managed rendering: If followups_container_id is provided, use portal rendering\n // Only use onFollowupDetected callback if container ID is NOT provided (legacy/fallback mode)\n if (_followups_container_id) {\n logger.debug('[WeaveAdFormatContainer] ✅ Using SDK-managed portal rendering (followups_container_id provided)');\n // Store recommendation with follow-up data for portal rendering\n const recommendationWithFollowupData: any = {\n ...aipResponse,\n followup_query: followupQuery,\n followup_engagement_url: followupEngagementUrl,\n followup_exposure_url: followupExposureUrl,\n recommendation_id: recommendationId || (aipResponse as any).recommendation_id\n };\n setRecommendationWithFollowup(recommendationWithFollowupData as AdMeshRecommendation);\n } else if (onFollowupDetectedRef.current) {\n // Legacy/fallback: Notify third-party application via callback\n logger.debug('[WeaveAdFormatContainer] 🔔 Using legacy callback mode (followups_container_id not provided)');\n onFollowupDetectedRef.current(followupQuery, followupEngagementUrl || '', recommendationId || '');\n } else {\n logger.debug('[WeaveAdFormatContainer] ⚠️ Followup detected but no rendering method provided (neither followups_container_id nor onFollowupDetected callback)');\n }\n } else {\n setRecommendationWithFollowup(null);\n }\n }\n \n // Development logging: Log full recommendation details\n if (aipResponse && process.env.NODE_ENV === 'development') {\n logger.log('[WeaveAdFormatContainer] 📋 Recommendation received (DEV):', {\n recommendation_id: aipResponse.recommendation_id,\n product_id: aipResponse.product_id,\n brand_id: aipResponse.brand_id,\n title: aipResponse.title,\n click_url: aipResponse.click_url,\n contextual_relevance_score: aipResponse.contextual_relevance_score,\n preferred_format: aipResponse.creative_input?.preferred_format,\n resolved_format: aipResponse.format_resolution?.resolved_format,\n creative_input: {\n product_name: aipResponse.creative_input?.product_name,\n brand_name: aipResponse.creative_input?.brand_name,\n cta_url: aipResponse.creative_input?.cta_url,\n short_description: aipResponse.creative_input?.short_description,\n allowed_formats: aipResponse.creative_input?.allowed_formats,\n fallback_formats: aipResponse.creative_input?.fallback_formats,\n },\n format_resolution: aipResponse.format_resolution,\n full_response: aipResponse\n });\n }\n } catch (error) {\n logger.warn('[WeaveAdFormatContainer] ⚠️ Failed to fetch recommendations, falling back to pattern-based detection:', error);\n setRecommendations([]);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, messageId, query, _followups_container_id]);\n\n // Find container for followups when recommendation with follow-up is available\n useEffect(() => {\n // If we don't have a query or container ID, we can't do anything\n if (!recommendationWithFollowup?.followup_query || !_followups_container_id) {\n setFollowupContainer(null);\n return;\n }\n\n // If isContainerReady is explicitly provided and false, wait\n if (isContainerReady === false) {\n logger.debug(`[WeaveAdFormatContainer] ⏳ Waiting for container signal...`);\n return;\n }\n\n // Try to find the container\n let attempts = 0;\n const maxAttempts = 5; // Reduced from 30 since we now have a signal - just need to handle React render lag\n\n const checkForContainer = () => {\n const container = document.getElementById(_followups_container_id);\n if (container) {\n logger.debug(`[WeaveAdFormatContainer] ✅ Found followup container: ${_followups_container_id} `);\n setFollowupContainer(container);\n return true;\n }\n return false;\n };\n\n // Check immediately\n if (checkForContainer()) return;\n\n // Short poll to account for React rendering\n const interval = setInterval(() => {\n attempts++;\n if (checkForContainer() || attempts >= maxAttempts) {\n clearInterval(interval);\n if (attempts >= maxAttempts) {\n logger.warn(`[WeaveAdFormatContainer] ⚠️ Followup container not found after signal: ${_followups_container_id} `);\n }\n }\n }, 100);\n\n return () => clearInterval(interval);\n }, [recommendationWithFollowup, _followups_container_id, isContainerReady]);\n\n // Scan for links immediately when container is ready and on children changes\n useEffect(() => {\n const container = containerRef.current;\n if (!container || !weaveProcessorRef.current) {\n return;\n }\n\n const weaveProcessor = weaveProcessorRef.current;\n\n const trackExposurePixel = ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix: '[WeaveAdFormatContainer]'\n });\n };\n\n // Scan immediately\n const scanLinks = () => {\n // Emit weave attempt signal\n logger.log('[WeaveAdFormatContainer] 🔍 Weave injection attempt started');\n onWeaveAttempt?.(messageId);\n\n // Use fetched recommendations if available, otherwise fall back to pattern-based detection\n // Use ref to avoid triggering re-runs when recommendations change\n const recsToUse = recommendationsRef.current.length > 0 ? recommendationsRef.current : [];\n logger.log(`[WeaveAdFormatContainer] 📋 Using ${recsToUse.length} recommendations for link detection`);\n\n try {\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recsToUse, // Use fetched recommendations for brand URL matching\n trackExposurePixel\n );\n\n if (detectedLinks.length > 0 && !scannedRef.current) {\n scannedRef.current = true;\n logger.log(`[WeaveAdFormatContainer] ✅ Weave injection succeeded: ${detectedLinks.length} links detected`);\n onWeaveOutcome?.(messageId, true, `Found ${detectedLinks.length} links`);\n onLinksDetected?.(detectedLinks.length);\n onFallbackChange?.(false);\n } else if (!scannedRef.current) {\n // No links found on first scan - will be handled by FinalLinkDetectionCheck\n // But we emit the outcome signal here for immediate feedback\n logger.log('[WeaveAdFormatContainer] ⚠️ Weave injection: no links found on initial scan');\n // Don't emit outcome yet - wait for FinalLinkDetectionCheck to confirm\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.error(`[WeaveAdFormatContainer] ❌ Weave injection failed: ${errorMessage}`);\n onWeaveOutcome?.(messageId, false, errorMessage);\n // Immediately trigger fallback on error\n onNoLinksDetected?.();\n onFallbackChange?.(true);\n }\n };\n\n // Initial scan after a small delay to ensure DOM is ready\n const timeoutId = setTimeout(scanLinks, 100);\n\n // Watch for new links (streaming support)\n // Use ref to avoid triggering re-runs when recommendations change\n weaveProcessor.watchForNewLinks(container, recommendationsRef.current, trackExposurePixel);\n\n return () => {\n clearTimeout(timeoutId);\n weaveProcessor.stopWatching();\n };\n }, [children, sessionId, messageId, onLinksDetected, onFallbackChange, onWeaveAttempt, onWeaveOutcome]); // Removed recommendations from deps to prevent infinite loop\n\n // Check if format is \"weave\" - if so, don't show fallback recommendations\n const resolvedFormat = recommendations[0]?.format_resolution?.resolved_format;\n const preferredFormat = recommendations[0]?.creative_input?.preferred_format;\n const isWeaveFormat = resolvedFormat === 'weave' || preferredFormat === 'weave';\n\n // Log when format is weave (for debugging)\n useEffect(() => {\n if (query && isWeaveFormat) {\n logger.log(`[WeaveAdFormatContainer] 🎯 Format is \"weave\" (resolved: ${resolvedFormat}, preferred: ${preferredFormat}) - skipping fallback recommendations`);\n }\n }, [query, isWeaveFormat, resolvedFormat, preferredFormat]);\n\n // Helper to render followup portal\n const renderFollowupPortal = () => {\n if (followupContainer && recommendationWithFollowup?.followup_query && sdk) {\n logger.debug(`[WeaveAdFormatContainer] 🌀 Rendering followup portal into: ${_followups_container_id} `);\n return createPortal(\n <AdMeshFollowup\n recommendation={recommendationWithFollowup}\n theme={theme}\n sdk={sdk}\n sessionId={sessionId}\n onExecuteQuery={_onExecuteQuery}\n />,\n followupContainer\n );\n } else {\n logger.debug(`[WeaveAdFormatContainer] ❌ Skipping followup portal. Container: ${!!followupContainer}, Query: ${!!recommendationWithFollowup?.followup_query}, SDK: ${!!sdk} `);\n }\n return null;\n };\n\n return (\n <>\n <div\n ref={containerRef}\n id={containerId}\n className={className}\n data-weave-ad-format=\"true\"\n data-message-id={messageId}\n >\n {children}\n </div>\n\n {/* Event-driven link detection and fallback rendering - only show if format is NOT weave */}\n {query && !isWeaveFormat && (\n <FinalLinkDetectionCheck\n containerId={containerId}\n messageId={messageId}\n sessionId={sessionId}\n sdk={sdk}\n query={query}\n recommendations={recommendations}\n onLinksFound={(count) => {\n logger.log(`[WeaveAdFormatContainer] ✅ Links found (${count}) - no fallback needed`);\n // Emit weave outcome success if not already emitted\n onWeaveOutcome?.(messageId, true, `Found ${count} links in final check`);\n onLinksDetected?.(count);\n onFallbackChange?.(false);\n }}\n onNoLinksFound={() => {\n logger.log(`[WeaveAdFormatContainer] ⚠️ No links found - rendering fallback`);\n logger.log(`[WeaveAdFormatContainer] 📋 Passing ${recommendations.length} recommendation(s) to fallback component`);\n // Emit weave outcome failure\n onWeaveOutcome?.(messageId, false, 'No links detected in final check');\n onNoLinksDetected?.();\n onFallbackChange?.(true);\n }}\n >\n <WeaveFallbackRecommendations\n format={fallbackFormat}\n messageId={messageId}\n query={query}\n fallback={true}\n onError={onError}\n previousRecommendations={recommendations.length > 0 ? recommendations : undefined}\n />\n </FinalLinkDetectionCheck>\n )}\n\n {/* Render follow-up portal if follow-up exists */}\n {renderFollowupPortal()}\n </>\n );\n};\n\nexport default WeaveAdFormatContainer;\n","'use client';\n\nimport { useEffect, useRef, useCallback } from 'react';\nimport { useAdMesh } from './useAdMesh';\nimport {\n createInlineExposureTracker,\n type InlineExposureTrackingParams\n} from '../utils/inlineExposureTracker';\n\nexport interface UseWeaveAdFormatOptions {\n /** Container ID for the LLM output (where AdMesh links will be detected) */\n llmOutputContainerId: string;\n\n /** Timeout for link detection (default: 900ms) */\n timeoutMs?: number;\n\n /** Fallback format if no links detected (default: 'tail') */\n fallbackFormat?: 'product' | 'tail';\n\n /** Optional callback when AdMesh links are detected */\n onLinksDetected?: (count: number) => void;\n\n /** Optional callback when no links are detected (fallback should be rendered) */\n onNoLinksDetected?: () => void;\n\n /** Optional callback on error */\n onError?: (error: Error) => void;\n\n /** Optional query for fallback API calls (used when no links are detected) */\n query?: string;\n\n /** Optional message ID for fallback API calls (used when no links are detected) */\n messageId?: string;\n}\n\n/**\n * useWeaveAdFormat - Hook for automatic Weave Ad Format handling\n * \n * Automatically:\n * - Detects AdMesh links in LLM output\n * - Fires exposure tracking for detected links\n * - Adds [Ad] labels to links\n * - Shows \"Why this ad?\" tooltip on hover\n * - Renders fallback UI if no links detected\n * \n * @example\n * ```tsx\n * const { isProcessing, detectedLinksCount } = useWeaveAdFormat({\n * llmOutputContainerId: 'llm-output-123',\n * timeoutMs: 1500,\n * fallbackFormat: 'tail'\n * });\n * ```\n */\nexport const useWeaveAdFormat = (options: UseWeaveAdFormatOptions) => {\n const { sdk, sessionId } = useAdMesh();\n const processingRef = useRef(false);\n const detectedLinksRef = useRef(0);\n const linksFoundRef = useRef(false);\n const callbackFiredRef = useRef(false);\n const debounceTimerRef = useRef<NodeJS.Timeout | null>(null);\n const exposureTrackerRef = useRef(createInlineExposureTracker());\n\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n const trackInlineExposure = useCallback(\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n recommendationId,\n linkElement,\n sessionId,\n logPrefix: '[useWeaveAdFormat]'\n });\n },\n [sessionId]\n );\n\n // Phase 1: Enhanced Mutation Silence Detection\n // Track when last mutation occurred for silence detection\n const lastMutationTimeRef = useRef<number>(Date.now());\n // Count consecutive silence checks (requires 2 for confirmation)\n const mutationSilenceCountRef = useRef<number>(0);\n // Store interval timer for cleanup\n const silenceCheckIntervalRef = useRef<NodeJS.Timeout | null>(null);\n\n // Phase 2: Adaptive Debounce Detection\n // Track timestamps of recent mutations to calculate adaptive timeout\n const mutationTimestampsRef = useRef<number[]>([]);\n // Store the calculated adaptive timeout value\n const adaptiveTimeoutRef = useRef<number>(300); // Default to 300ms\n\n const processWeaveFormat = useCallback(async () => {\n if (!sdk || !sessionId || processingRef.current) {\n return;\n }\n\n processingRef.current = true;\n detectedLinksRef.current = 0;\n\n try {\n const container = document.getElementById(options.llmOutputContainerId);\n if (!container) {\n \n return;\n }\n\n // Get the WeaveResponseProcessor from SDK\n const weaveProcessor = (sdk as any).getWeaveProcessor?.(); // eslint-disable-line @typescript-eslint/no-explicit-any\n if (!weaveProcessor) {\n return;\n }\n\n // Scan and process AdMesh links in the container\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n [], // Recommendations will be fetched from SDK cache\n ({ exposureUrl, recommendationId, linkElement }: InlineExposureTrackingParams) =>\n trackInlineExposure({ exposureUrl, recommendationId, linkElement })\n );\n\n detectedLinksRef.current = detectedLinks.length;\n const hasLinks = detectedLinks.length > 0;\n\n // Update the flag indicating whether links were found\n if (hasLinks && !linksFoundRef.current) {\n // Links detected (first time or after streaming)\n linksFoundRef.current = true;\n options.onLinksDetected?.(detectedLinks.length);\n callbackFiredRef.current = true;\n } else if (!hasLinks && linksFoundRef.current) {\n // Links were found before but now they're gone (shouldn't happen in normal flow)\n linksFoundRef.current = false;\n callbackFiredRef.current = false;\n } else if (!hasLinks && !callbackFiredRef.current) {\n // No links found and callback hasn't been fired yet\n // Use enhanced mutation silence detection to wait for streaming to complete\n // This prevents firing the callback too early during streaming\n\n // Clear existing debounce timer\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n\n // Clear existing silence check interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n }\n\n // Reset silence counter when starting new detection\n mutationSilenceCountRef.current = 0;\n lastMutationTimeRef.current = Date.now();\n\n // Phase 1 + Phase 2: Enhanced Mutation Silence Detection with Adaptive Timeout\n // Check for mutation silence at adaptive intervals\n // Require 2 consecutive silence checks before declaring streaming complete\n const silenceCheckInterval = Math.max(100, adaptiveTimeoutRef.current / 3); // Check 3x per adaptive timeout\n\n silenceCheckIntervalRef.current = setInterval(() => {\n const timeSinceLastMutation = Date.now() - lastMutationTimeRef.current;\n const SILENCE_THRESHOLD = adaptiveTimeoutRef.current; // Use adaptive timeout as silence threshold\n\n if (timeSinceLastMutation >= SILENCE_THRESHOLD) {\n // Mutation silence detected\n mutationSilenceCountRef.current++;\n\n // Require 2 consecutive silence checks for confirmation\n if (mutationSilenceCountRef.current >= 2) {\n // Clear the interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n silenceCheckIntervalRef.current = null;\n }\n\n // Fire callback if no links were found\n if (!linksFoundRef.current && !callbackFiredRef.current) {\n options.onNoLinksDetected?.();\n callbackFiredRef.current = true;\n }\n }\n } else {\n // Mutations still happening, reset counter\n mutationSilenceCountRef.current = 0;\n }\n }, silenceCheckInterval);\n\n // Phase 2: Fallback timeout using adaptive timeout\n // Set a maximum timeout to ensure we eventually fire the callback\n // This handles edge cases where mutations might be very infrequent\n const maxFallbackTimeout = Math.max(1500, adaptiveTimeoutRef.current * 5); // At least 1500ms, or 5x adaptive timeout\n\n debounceTimerRef.current = setTimeout(() => {\n // Clear the silence check interval\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n silenceCheckIntervalRef.current = null;\n }\n\n // Fire callback if no links were found\n if (!linksFoundRef.current && !callbackFiredRef.current) {\n options.onNoLinksDetected?.();\n callbackFiredRef.current = true;\n }\n }, maxFallbackTimeout);\n } else if (hasLinks && callbackFiredRef.current && !linksFoundRef.current) {\n // Links detected after we initially said \"no links found\"\n // This happens when links arrive during streaming\n\n\n // Clear debounce timer since we found links\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = null;\n }\n\n linksFoundRef.current = true;\n options.onLinksDetected?.(detectedLinks.length);\n callbackFiredRef.current = true;\n } else {\n \n }\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n options.onError?.(err);\n } finally {\n processingRef.current = false;\n }\n }, [sdk, sessionId, options, trackInlineExposure]);\n\n // Phase 2: Calculate adaptive debounce timeout based on mutation frequency\n const calculateAdaptiveTimeout = useCallback(() => {\n const timestamps = mutationTimestampsRef.current;\n\n // Need at least 2 timestamps to calculate interval\n if (timestamps.length < 2) {\n adaptiveTimeoutRef.current = 300; // Default to 300ms\n return;\n }\n\n // Calculate intervals between consecutive mutations\n const intervals: number[] = [];\n for (let i = 1; i < timestamps.length; i++) {\n intervals.push(timestamps[i] - timestamps[i - 1]);\n }\n\n // Calculate average interval\n const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length;\n\n // Adaptive timeout: 3x the average interval, min 100ms, max 1000ms\n const adaptiveTimeout = Math.max(100, Math.min(avgInterval * 3, 1000));\n\n adaptiveTimeoutRef.current = adaptiveTimeout;\n }, []);\n\n // Watch for changes in the LLM output container\n useEffect(() => {\n const container = document.getElementById(options.llmOutputContainerId);\n if (!container) {\n return;\n }\n\n // Initial scan\n processWeaveFormat();\n\n // Set up MutationObserver for streaming responses\n const observer = new MutationObserver(() => {\n const now = Date.now();\n\n // Phase 1: Update last mutation time for silence detection\n lastMutationTimeRef.current = now;\n // Reset silence counter since we detected a mutation\n mutationSilenceCountRef.current = 0;\n\n // Phase 2: Track mutation timestamp for adaptive debounce calculation\n mutationTimestampsRef.current.push(now);\n // Keep only last 10 timestamps to avoid memory bloat\n if (mutationTimestampsRef.current.length > 10) {\n mutationTimestampsRef.current.shift();\n }\n // Recalculate adaptive timeout based on recent mutation pattern\n calculateAdaptiveTimeout();\n\n processWeaveFormat();\n });\n\n observer.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n\n return () => {\n observer.disconnect();\n // Clean up debounce timer on unmount\n if (debounceTimerRef.current) {\n clearTimeout(debounceTimerRef.current);\n }\n // Phase 1: Clean up silence check interval on unmount\n if (silenceCheckIntervalRef.current) {\n clearInterval(silenceCheckIntervalRef.current);\n }\n };\n }, [options.llmOutputContainerId, processWeaveFormat, calculateAdaptiveTimeout]);\n\n return {\n isProcessing: processingRef.current,\n detectedLinksCount: detectedLinksRef.current,\n linksFound: linksFoundRef.current,\n shouldRenderFallback: !linksFoundRef.current && callbackFiredRef.current,\n processWeaveFormat\n };\n};\n\nexport default useWeaveAdFormat;\n","/**\n * AdMesh UI SDK - Main Entry Point\n * \n * Zero-code integration for displaying AdMesh recommendations\n */\n\n// Core SDK\nexport { AdMeshSDK } from './sdk/AdMeshSDK';\nexport type { AdMeshSDKConfig, ShowRecommendationsOptions } from './sdk/AdMeshSDK';\n\n// Tracking\nexport { AdMeshTracker } from './sdk/AdMeshTracker';\nexport type { TrackerConfig } from './sdk/AdMeshTracker';\n\n// Rendering\nexport { AdMeshRenderer } from './sdk/AdMeshRenderer';\nexport type { RenderOptions } from './sdk/AdMeshRenderer';\n\n// Weave Response Processing\nexport { WeaveResponseProcessor } from './sdk/WeaveResponseProcessor';\nexport type { DetectedLink, ProcessorConfig } from './sdk/WeaveResponseProcessor';\n\n// Weave format is now integrated into AdMeshSDK\n// Use format: 'weave' option in showRecommendations() method\n\n// Provider Pattern (Simplified Integration)\nexport { AdMeshProvider } from './context/AdMeshProvider';\nexport type { AdMeshProviderProps } from './context/AdMeshProvider';\n\n// Components\nexport { AdMeshRecommendations } from './components/AdMeshRecommendations';\nexport type { AdMeshRecommendationsProps } from './components/AdMeshRecommendations';\nexport { WeaveFallbackRecommendations } from './components/WeaveFallbackRecommendations';\nexport type { WeaveFallbackRecommendationsProps } from './components/WeaveFallbackRecommendations';\n\n// Context\nexport { AdMeshContext, useAdMeshContext } from './context/AdMeshContext';\nexport type { AdMeshContextValue } from './context/AdMeshContext';\nexport { WeaveAdFormatProvider, useWeaveAdFormatContext } from './context/WeaveAdFormatContext';\nexport type { WeaveAdFormatContextType } from './context/WeaveAdFormatContext';\n\n// Components\nexport { AdMeshEcommerceCards } from './components/AdMeshEcommerceCards';\nexport { AdMeshProductCard } from './components/AdMeshProductCard';\nexport { AdMeshInlineCard } from './components/AdMeshInlineCard';\nexport { AdMeshLayout } from './components/AdMeshLayout';\nexport { AdMeshSummaryLayout } from './components/AdMeshSummaryLayout';\nexport { AdMeshTailAd } from './components/AdMeshTailAd';\nexport type { AdMeshTailAdProps } from './components/AdMeshTailAd';\nexport { AdMeshBridgeFormat } from './components/AdMeshBridgeFormat';\nexport type { AdMeshBridgeFormatProps } from './components/AdMeshBridgeFormat';\nexport { AdMeshFollowup } from './components/AdMeshFollowup';\nexport type { AdMeshFollowupProps } from './components/AdMeshFollowup';\n// Backward compatibility export\nexport { AdMeshTailAd as AdMeshSummaryUnit } from './components/AdMeshTailAd';\nexport type { AdMeshTailAdProps as AdMeshSummaryUnitProps } from './components/AdMeshTailAd';\nexport { AdMeshViewabilityTracker } from './components/AdMeshViewabilityTracker';\nexport { AdMeshLinkTracker } from './components/AdMeshLinkTracker';\nexport { AdMeshBadge } from './components/AdMeshBadge';\nexport { WeaveAdFormatContainer } from './components/WeaveAdFormatContainer';\nexport type { WeaveAdFormatContainerProps } from './components/WeaveAdFormatContainer';\n\n// Hooks\nexport { useAdMesh } from './hooks/useAdMesh';\nexport { useAdMeshStyles } from './hooks/useAdMeshStyles';\nexport { useAdMeshTracker } from './hooks/useAdMeshTracker';\nexport { useViewabilityTracker } from './hooks/useViewabilityTracker';\nexport { useWeaveAdFormat } from './hooks/useWeaveAdFormat';\nexport type { UseWeaveAdFormatOptions } from './hooks/useWeaveAdFormat';\n\n// Types\nexport type { AdMeshTheme } from './types/index';\n\n// Streaming Events (for event-driven link detection)\nexport {\n dispatchStreamingStartEvent,\n dispatchStreamingCompleteEvent,\n onStreamingStart,\n onStreamingComplete,\n STREAMING_START_EVENT,\n STREAMING_COMPLETE_EVENT\n} from './utils/streamingEvents';\nexport type {\n StreamingStartEventDetail,\n StreamingCompleteEventDetail\n} from './utils/streamingEvents';\n\n// Inline exposure tracking helper\nexport {\n createInlineExposureTracker\n} from './utils/inlineExposureTracker';\nexport type {\n InlineExposureTracker,\n InlineExposureTrackingParams\n} from './utils/inlineExposureTracker';\n\n// Version\nexport const VERSION = '1.0.10';\n"],"names":["isProduction","_a","logger","args","calculateMRCStandards","adWidth","adHeight","customStandards","isLargeAd","detectDeviceType","viewportWidth","calculateVisibilityPercentage","element","rect","viewportHeight","elementHeight","elementWidth","visibleTop","visibleBottom","visibleLeft","visibleRight","visibleHeight","visibleWidth","visibleArea","totalArea","calculateScrollDepth","windowHeight","documentHeight","scrollTop","scrollableHeight","getElementPosition","scrollLeft","collectContextMetrics","position","isDarkMode","generateSessionId","meetsViewabilityThreshold","visibilityPercentage","visibleDuration","standards","formatTimestamp","date","calculateAverage","numbers","acc","num","throttle","func","limit","inThrottle","DEFAULT_CONFIG","globalConfig","useViewabilityTracker","productId","offerId","agentId","recommendationId","elementRef","customConfig","config","sessionId","useRef","state","setState","useState","mrcStandards","visibilityStartTime","viewableStartTime","hoverStartTime","focusStartTime","visibilityPercentages","eventBatch","batchTimeout","log","useCallback","message","sendEvent","eventType","additionalData","contextMetrics","event","flushBatch","updateVisibility","now","loadTime","prev","newState","wasVisible","isNowVisible","wasViewable","isNowViewable","viewableDuration","useEffect","observer","entries","handleScroll","handleMouseEnter","handleMouseLeave","hoverDuration","handleFocus","handleBlur","focusDuration","handleClick","sessionDuration","AdMeshViewabilityTracker","exposureUrl","children","className","style","onViewabilityChange","onVisible","onViewable","onClick","exposureFired","viewabilityState","previousViewable","error","previousVisible","jsx","isValidUrl","url","getCTALabel","ctaLabel","AdMeshTailAd","recommendations","theme","firstRecommendation","creativeInput","shortDescription","offerSummary","brandName","productName","logoUrl","clickUrl","headlineText","headlineSuffix","handleContainerClick","source","e","handleBrandNameClick","handleCTAClick","handleLogoClick","logoError","setLogoError","React","brandInitial","jsxs","Fragment","hasOwn","classNames","classes","i","arg","appendClass","parseValue","key","value","newClass","module","DEFAULT_TRACKING_URL","useAdMeshTracker","isTracking","setIsTracking","setError","mergedConfig","useMemo","sendTrackingEvent","data","payload","lastError","attempt","response","err","resolve","errorMsg","trackClick","trackView","trackConversion","AdMeshLinkTracker","admeshLink","trackingData","hasTrackedView","link","entry","ADMESH_STYLE_ID","ADMESH_RESET_ID","ADMESH_CSS_RESET","ADMESH_CORE_STYLES","injectAdMeshStyles","resetStyle","coreStyle","ADMESH_STYLES","stylesInjected","useAdMeshStyles","styleElement","getRecommendationLabel","recommendation","matchScore","customLabels","getLabelTooltip","_label","AdMeshProductCard","variation","content","title","description","ctaText","cardClasses","cardStyle","_b","_c","_d","_e","_f","_g","_h","_i","_j","_l","_k","_m","_n","_o","_p","_r","_q","_s","_u","_t","_v","_w","_x","_y","_z","prop","index","AdMeshContext","useAdMeshContext","context","useAdMesh","extractCTAText","bridgePrompt","extractedProduct","productPatterns","pattern","match","product","setupMatch","AdMeshBridgeFormat","onLinkClick","onPasteToInput","bridgeHeadline","bridgeDescription","sdk","contextSessionId","effectiveSessionId","apiBaseUrl","shouldShowCTA","AdMeshSummaryLayout","summaryText","validRecs","rec","summary","renderContent","firstRec","hasBridgePrompt","format","preferredFormat","hasBridgeFormat","AdMeshLayout","recs","PlusIcon","size","AdMeshFollowup","onExecuteQuery","followupQuery","followupEngagementUrl","followupExposureUrl","handleFollowupClick","AdMeshTracker","__publicField","timeoutId","cleanupTimeout","threshold","engagementUrl","AdMeshRenderer","options","container","existingRoot","root","ReactDOM","tailSummary","containerId","AdMeshSDK","timestamp","random","aipResponse","renderer","tracker","params","messageId","turnIndex","devicePlatform","formFactor","jsonBody","errorMessage","responseAny","creative","headlineFromCreative","bridgeHeadlineFromCreative","bridgeDescriptionFromCreative","bridgePromptFromCreative","ctaLabelFromCreative","preservedAssets","WeaveResponseProcessor","optimizedLinks","onExposurePixel","detectedLinks","links","clickUrlMap","r","brandUrlMap","redirectUrl","normalizedUrl","href","linkKey","actualHref","normalizedHref","detectedLink","childElements","child","prevNode","text","tagName","nextNode","parentNode","subLabel","isTooltipVisible","closeTooltipOnClickOutside","nextSibling","domain","AdMeshProvider","apiKey","language","geo_country","userId","model","messages","sdkRef","processedMessageIds","setProcessedMessageIds","contextValue","updated","AdMeshRecommendations","onRecommendationsShown","onError","query","_followups_container_id","_onExecuteQuery","onFollowupDetected","isContainerReady","setRecommendation","detectedFormat","setDetectedFormat","isLoading","setIsLoading","fetchedMessageIdRef","isFetchingRef","onRecommendationsShownRef","onErrorRef","onFollowupDetectedRef","convertAIPResponseToRecommendation","formatFromResponse","followupQueryTopLevel","followupQueryInCreative","followupEngagementUrlTopLevel","followupEngagementUrlInCreative","followupExposureUrlTopLevel","followupExposureUrlInCreative","followupContainer","setFollowupContainer","attempts","maxAttempts","checkForContainer","interval","convertedRecommendation","selectedFormat","renderFollowupPortal","createPortal","formatFromRec","preferredFormatFromRec","WeaveFallbackRecommendations","fallback","previousRecommendations","containerRef","setContainerId","hasPreviousRecs","sdkAny","convertMethod","getRendererMethod","getTrackerMethod","sdkTheme","WeaveAdFormatContext","createContext","WeaveAdFormatProvider","shouldRenderFallback","useWeaveAdFormatContext","useContext","AdMeshEcommerceCards","showTitle","cardClassName","onProductClick","maxCards","cardWidth","borderRadius","shadow","displayItems","getCardWidthClass","getBorderRadiusClass","getShadowClass","getThemeClasses","handleProductClick","item","itemId","idx","cat","AdMeshInlineCard","expandable","badgeTypeVariants","badgeTypeIcons","AdMeshBadge","type","variant","effectiveVariant","icon","badgeClasses","STREAMING_START_EVENT","STREAMING_COMPLETE_EVENT","dispatchStreamingStartEvent","detail","dispatchStreamingCompleteEvent","metadata","onStreamingStart","callback","handler","customEvent","onStreamingComplete","createInlineExposureTracker","firedKeys","activeTrackers","cleanupTracker","linkElement","logPrefix","dedupeKey","trackerState","fireExposurePixel","FinalLinkDetectionCheck","onLinksFound","onNoLinksFound","checkComplete","setCheckComplete","waitingForStreamEnd","setWaitingForStreamEnd","linksFound","setLinksFound","exposureTrackerRef","onLinksFoundRef","onNoLinksFoundRef","containerIdRef","sessionIdRef","queryRef","trackExposurePixel","performFinalCheck","weaveProcessor","recsToUse","eventReceived","cleanup","WeaveAdFormatContainer","fallbackFormat","onLinksDetected","onNoLinksDetected","onFallbackChange","onWeaveAttempt","onWeaveOutcome","scannedRef","weaveProcessorRef","setRecommendations","recommendationsRef","recommendationWithFollowup","setRecommendationWithFollowup","recommendationWithFollowupData","resolvedFormat","isWeaveFormat","count","useWeaveAdFormat","processingRef","detectedLinksRef","linksFoundRef","callbackFiredRef","debounceTimerRef","trackInlineExposure","lastMutationTimeRef","mutationSilenceCountRef","silenceCheckIntervalRef","mutationTimestampsRef","adaptiveTimeoutRef","processWeaveFormat","hasLinks","silenceCheckInterval","timeSinceLastMutation","SILENCE_THRESHOLD","maxFallbackTimeout","calculateAdaptiveTimeout","timestamps","intervals","avgInterval","a","b","adaptiveTimeout","VERSION"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","x_google_ignoreList":[5]}