admesh-ui-sdk 1.0.9 → 1.0.11

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/viewabilityTracker.ts","../src/hooks/useViewabilityTracker.ts","../src/components/AdMeshViewabilityTracker.tsx","../src/components/AdMeshSummaryUnit.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/components/AdMeshSummaryLayout.tsx","../src/components/AdMeshLayout.tsx","../src/sdk/AdMeshTracker.ts","../src/sdk/AdMeshRenderer.tsx","../src/sdk/WeaveResponseProcessor.ts","../src/sdk/AdMeshSDK.ts","../src/context/AdMeshContext.ts","../src/context/AdMeshProvider.tsx","../src/hooks/useAdMesh.ts","../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/components/WeaveAdFormatContainer.tsx","../src/hooks/useWeaveAdFormat.ts","../src/index.ts"],"sourcesContent":["/**\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';\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\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 let lastError: Error | null = null;\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 const errorText = await response.text().catch(() => '');\n lastError = new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);\n } catch (error) {\n lastError = error as Error;\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 console.error('[AdMesh Viewability] Failed to send analytics event:', lastError);\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 let lastError: Error | null = null;\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 const errorText = await response.text().catch(() => '');\n lastError = new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);\n } catch (error) {\n lastError = error as Error;\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 console.error('[AdMesh Viewability] Failed to send analytics batch:', lastError);\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","/**\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 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 sendAnalyticsEvent,\n sendAnalyticsBatch,\n throttle\n} from '../utils/viewabilityTracker';\n\n// Default configuration\nconst DEFAULT_CONFIG: ViewabilityTrackerConfig = {\n enabled: true,\n // NOTE: Viewability tracking endpoint for storing analytics in recommendations collection\n // This endpoint stores MRC-compliant viewability analytics for each recommendation\n // Uses batch endpoint to handle multiple events efficiently\n apiEndpoint: 'https://api.useadmesh.com/api/recommendations/viewability/batch',\n enableBatching: true,\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\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 console.warn('[AdMesh Viewability] Analytics sending is DISABLED - no data will be sent to backend');\n } else {\n console.log('[AdMesh Viewability] Analytics sending is ENABLED');\n }\n};\n\ninterface UseViewabilityTrackerProps {\n /** Ad ID */\n adId: string;\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 adId,\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, data?: unknown) => {\n if (config.debug) {\n console.log(`[AdMesh Viewability] ${message}`, data);\n }\n }, [config.debug]);\n\n // Send event (batched or immediate)\n // NOTE: Only sends critical events (ad_viewable, ad_click) for cost optimization\n // Other events are tracked internally but not sent to backend\n // TEMPORARY: Can be disabled globally via disableViewabilityAnalytics()\n const sendEvent = useCallback(async (eventType: ViewabilityEventType, additionalData?: Record<string, unknown>) => {\n if (!config.enabled || !elementRef.current || !mrcStandards.current) return;\n\n // TEMPORARY: Check if analytics are disabled globally\n if (ANALYTICS_DISABLED) {\n log(`Analytics disabled - skipping event: ${eventType}`);\n return;\n }\n\n // OPTIMIZATION: Only send critical events to reduce storage costs by 85-95%\n const criticalEvents = ['ad_viewable', 'ad_click'];\n if (!criticalEvents.includes(eventType)) {\n log(`Skipping non-critical event: ${eventType} (only ad_viewable and ad_click are sent)`);\n return;\n }\n\n const contextMetrics = collectContextMetrics(elementRef.current);\n\n const event: ViewabilityAnalyticsEvent = {\n eventType,\n timestamp: formatTimestamp(),\n sessionId: sessionId.current,\n adId,\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\n log(`Sending critical event: ${eventType}`, event);\n\n // Call custom callback if provided\n if (config.onEvent) {\n config.onEvent(event);\n }\n\n if (config.enableBatching) {\n // Add to batch\n eventBatch.current.push(event);\n\n // Send batch if size limit reached\n if (eventBatch.current.length >= config.batchSize) {\n await flushBatch();\n } else {\n // Set timeout to send batch\n if (batchTimeout.current) clearTimeout(batchTimeout.current);\n batchTimeout.current = setTimeout(flushBatch, config.batchTimeout);\n }\n } else {\n // Send immediately\n await sendAnalyticsEvent(event, config.apiEndpoint, config.maxRetries, config.retryDelay);\n }\n }, [config, adId, productId, offerId, agentId, elementRef, state, log]);\n\n // Flush event batch\n const flushBatch = useCallback(async () => {\n if (eventBatch.current.length === 0) return;\n\n // TEMPORARY: If analytics are disabled, clear batch without sending\n if (ANALYTICS_DISABLED) {\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 }\n\n const events = [...eventBatch.current];\n eventBatch.current = [];\n\n if (batchTimeout.current) {\n clearTimeout(batchTimeout.current);\n batchTimeout.current = null;\n }\n\n const success = await sendAnalyticsBatch(\n events,\n sessionId.current,\n config.apiEndpoint,\n config.maxRetries,\n config.retryDelay\n );\n\n if (success && config.onBatchSent) {\n config.onBatchSent({\n batchId: `batch_${Date.now()}`,\n sessionId: sessionId.current,\n createdAt: formatTimestamp(),\n events,\n eventCount: events.length\n });\n }\n }, [config, 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', mrcStandards.current);\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","/**\n * AdMesh Viewability Tracker Component\n * Wraps any ad component with MRC viewability tracking\n */\n\nimport React, { useRef, useEffect } from 'react';\nimport { useViewabilityTracker } from '../hooks/useViewabilityTracker';\nimport type { ViewabilityTrackerConfig } from '../types/analytics';\n\nexport interface AdMeshViewabilityTrackerProps {\n /** Ad ID */\n adId: string;\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 * adId=\"ad_123\"\n * productId=\"prod_456\"\n * offerId=\"offer_789\"\n * onViewable={() => console.log('Ad is viewable!')}\n * >\n * <YourAdComponent />\n * </AdMeshViewabilityTracker>\n * ```\n */\nexport const AdMeshViewabilityTracker: React.FC<AdMeshViewabilityTrackerProps> = ({\n adId,\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 adId,\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 && exposureUrl && sessionId) {\n exposureFired.current = true;\n\n // Fire the exposure pixel using fetch with keepalive\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n console.log('[AdMesh] ✅ Exposure pixel fired for:', {\n adId,\n recommendationId,\n sessionId: sessionId.substring(0, 20) + '...'\n });\n })\n .catch((error) => {\n console.warn('[AdMesh] ⚠️ Failed to fire exposure pixel:', error);\n // Reset flag to allow retry\n exposureFired.current = false;\n });\n }\n }\n }, [viewabilityState.isViewable, onViewabilityChange, onViewable, exposureUrl, sessionId, adId, 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-ad-id={adId}\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\n","import React from 'react';\nimport type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshSummaryUnitProps {\n summaryText: string; // The citation_summary from backend response\n recommendations: AdMeshRecommendation[]; // Full recommendation objects\n theme?: AdMeshTheme;\n className?: string;\n style?: React.CSSProperties;\n onLinkClick?: (recommendation: AdMeshRecommendation) => void;\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// Process summary text with markdown links [Product Name](click_url)\nconst processSummaryText = (summaryText: string, recommendations: AdMeshRecommendation[]) => {\n // Create lookup map for recommendations by click_url\n const clickUrlToRecMap = new Map<string, AdMeshRecommendation>();\n\n recommendations.forEach(rec => {\n if (rec.click_url) {\n clickUrlToRecMap.set(rec.click_url, rec);\n }\n });\n\n // Debug: Log all available links in recommendations\n console.log('[AdMesh Summary] Processing recommendations:', {\n totalRecommendations: recommendations.length,\n recommendationFields: recommendations.map(rec => ({\n ad_id: rec.ad_id,\n click_url: rec.click_url,\n product_title: rec.product_title\n }))\n });\n\n if (clickUrlToRecMap.size > 0) {\n console.log('[AdMesh Summary] Available recommendation links:', {\n click_urls: Array.from(clickUrlToRecMap.keys())\n });\n } else {\n console.warn('[AdMesh Summary] No click_url found in recommendations!');\n }\n\n // Find markdown links and replace with JSX elements\n const markdownLinkRegex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const parts: (string | React.ReactElement)[] = [];\n let lastIndex = 0;\n let match;\n let linkCounter = 0;\n\n while ((match = markdownLinkRegex.exec(summaryText)) !== null) {\n const [fullMatch, linkText, url] = match;\n\n console.log('[AdMesh Summary] Processing markdown link:', {\n linkText,\n url,\n urlLength: url.length\n });\n\n // Try to find recommendation by exact URL match (click_url)\n let recommendation = clickUrlToRecMap.get(url);\n\n console.log('[AdMesh Summary] URL match result:', {\n found: !!recommendation,\n matchedBy: recommendation ? 'exact_match' : 'none'\n });\n\n // If exact match not found, try to match by ad_id\n if (!recommendation) {\n recommendation = recommendations.find(rec =>\n (rec.ad_id && url.includes(rec.ad_id))\n );\n if (recommendation) {\n console.log('[AdMesh Summary] Found by ad_id match');\n }\n }\n\n // Add text before the link\n if (match.index > lastIndex) {\n parts.push(summaryText.slice(lastIndex, match.index));\n }\n\n if (recommendation) {\n linkCounter++;\n // Use the URL from markdown (click_url)\n const linkUrl = url || recommendation.click_url;\n\n // Create clickable link element\n parts.push(\n <a\n key={`summary-link-${linkCounter}`}\n href={linkUrl}\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\"\n style={{\n color: '#2563eb', // Force blue color\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={() => {\n // Log comprehensive click data\n console.log('AdMesh summary link clicked:', {\n adId: recommendation.ad_id,\n productId: recommendation.product_id,\n title: recommendation.product_title,\n clickUrl: recommendation.click_url,\n source: 'summary'\n });\n\n // Fire tracking asynchronously WITHOUT blocking navigation\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n adId: recommendation.ad_id,\n productId: recommendation.product_id,\n clickUrl: recommendation.click_url,\n source: 'summary'\n }).catch((error: Error) => {\n console.error('[AdMesh] Failed to track summary link click:', error);\n });\n }\n }}\n >\n {linkText}\n </a>\n );\n } else {\n // Create a regular link even if no recommendation found\n console.warn(`[AdMesh Summary] No recommendation found for link: [${linkText}](${url}), creating regular link`, {\n availableLinks: Array.from(clickUrlToRecMap.keys()),\n totalRecommendations: recommendations.length,\n urlToMatch: url\n });\n\n if (isValidUrl(url)) {\n linkCounter++;\n parts.push(\n <a\n key={`summary-link-${linkCounter}`}\n href={url}\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\"\n style={{\n color: '#2563eb', // Force blue color\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={() => {\n // Log click for unmatched links\n console.log('AdMesh summary unmatched link clicked:', {\n linkText,\n url,\n source: 'summary'\n });\n\n // Fire tracking asynchronously WITHOUT blocking navigation\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n url,\n linkText,\n source: 'summary_unmatched'\n }).catch((error: Error) => {\n console.error('[AdMesh] Failed to track unmatched link click:', error);\n });\n }\n }}\n >\n {linkText}\n </a>\n );\n } else {\n // If URL is invalid, just show the link text without markdown\n console.warn(`[AdMesh Summary] Invalid URL in markdown link: [${linkText}](${url})`);\n parts.push(linkText);\n }\n }\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Add remaining text\n if (lastIndex < summaryText.length) {\n parts.push(summaryText.slice(lastIndex));\n }\n\n return parts;\n};\n\nexport const AdMeshSummaryUnit: React.FC<AdMeshSummaryUnitProps> = ({\n summaryText,\n recommendations,\n theme,\n className = '',\n style = {}\n}) => {\n // Validate inputs\n if (!summaryText || !summaryText.trim()) {\n console.warn('[AdMesh Summary] No summary text provided');\n return null;\n }\n\n if (!recommendations || recommendations.length === 0) {\n console.warn('[AdMesh Summary] No recommendations provided');\n // Still process markdown links even without recommendations\n const processedContent = processSummaryText(summaryText, []);\n return (\n <div className={`admesh-summary-unit ${className}`} style={style}>\n <p className=\"text-gray-700 dark:text-gray-300 leading-relaxed\">\n {processedContent.map((part, index) => (\n <React.Fragment key={index}>{part}</React.Fragment>\n ))}\n </p>\n </div>\n );\n }\n\n // Process the summary text to create clickable links\n const processedContent = processSummaryText(summaryText, recommendations);\n\n // Get the first recommendation's ad ID for viewability tracking\n // (Summary unit contains multiple recommendations, but we track the unit as a whole)\n const adId = recommendations[0]?.ad_id || '';\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\n className={`admesh-summary-unit ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n <div>\n {/* Summary Content */}\n <div className=\"summary-content\">\n <p className=\"text-gray-700 dark:text-gray-300 leading-relaxed text-base\">\n {processedContent.map((part, index) => (\n <React.Fragment key={index}>{part}</React.Fragment>\n ))}\n </p>\n </div>\n\n {/* Disclosure */}\n <div className=\"mt-3 pt-2 border-t border-gray-200 dark:border-gray-700\">\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 AdMeshSummaryUnit;\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 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.adId || !data.admeshLink) {\n const errorMsg = 'Missing required tracking data: adId 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 ad_id: data.adId,\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 adId: string, \n additionalParams?: Record<string, string>\n): string => {\n try {\n const url = new URL(baseLink);\n url.searchParams.set('ad_id', adId);\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 console.warn('[AdMesh] Invalid URL provided to buildAdMeshLink:', baseLink, err);\n return baseLink;\n }\n};\n\n// Helper function to extract tracking data from recommendation\nexport const extractTrackingData = (\n recommendation: { ad_id: string; admesh_link: string; product_id: string },\n additionalData?: Partial<TrackingData>\n): TrackingData => {\n return {\n adId: recommendation.ad_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';\n\nexport const AdMeshLinkTracker: React.FC<AdMeshLinkTrackerProps> = ({\n adId,\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 // 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 adId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(console.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 }, [adId, 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 adId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(error => {\n // Log error but don't block navigation\n console.error('[AdMesh] Failed to track click:', error);\n });\n\n // If the children contain a link, let the browser handle navigation\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 }\n // If there's a link, let the browser handle it naturally\n }, [adId, 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 { 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 console.error('[AdMesh] Failed to inject styles:', error);\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 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 // Inject styles automatically\n useAdMeshStyles();\n\n\n\n\n // Get content based on variation\n const getVariationContent = () => {\n if (variation === 'simple') {\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.product_summary || recommendation.weave_summary || recommendation.reason,\n ctaText: recommendation.product_title || recommendation.title,\n isSimple: true\n };\n\n } else {\n // Default variation\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.product_summary || recommendation.weave_summary || recommendation.reason,\n ctaText: recommendation.product_title || recommendation.title\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 adId = recommendation.ad_id || '';\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_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 adId = recommendation.ad_id || '';\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\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.product_logo?.url && (\n <img\n src={recommendation.product_logo.url}\n alt={`${recommendation.product_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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_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 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\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 type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshSummaryUnit } from './AdMeshSummaryUnit';\nimport { AdMeshProductCard } from './AdMeshProductCard';\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\n // Exposure tracking\n sessionId?: string;\n\n // Legacy props for backward compatibility (deprecated)\n response?: {\n layout_type?: string;\n citation_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 sessionId,\n response // Legacy prop for backward compatibility\n}) => {\n // Support both new and legacy props\n const recs = recommendations || response?.recommendations || [];\n const summary = summaryText || response?.citation_summary;\n\n // If recommendations array is empty (e.g., geo-restricted), silently return null\n if (!recs || recs.length === 0) {\n console.log('[AdMesh Summary Layout] Empty recommendations array - not rendering anything');\n return null;\n }\n\n console.log(`[AdMesh Summary Layout] Rendering with ${recs.length} recommendations`);\n\n // Render based on layout type (default to citation)\n const renderContent = () => {\n // Show summary if available\n if (summary) {\n return (\n <AdMeshSummaryUnit\n summaryText={summary}\n recommendations={recs}\n theme={theme}\n onLinkClick={onLinkClick}\n />\n );\n }\n // Fallback to first recommendation if no summary\n return (\n <div className=\"fallback-citation\">\n <AdMeshProductCard\n recommendation={recs[0]}\n theme={theme}\n sessionId={sessionId}\n />\n </div>\n );\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 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\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?.citation_summary;\n\n // Validate that recommendations are provided\n if (!recs || recs.length === 0) {\n console.log('[AdMeshLayout] Empty recommendations array - not rendering anything');\n return null;\n }\n\n return (\n <AdMeshSummaryLayout\n recommendations={recs}\n summaryText={summary}\n theme={theme}\n className={className}\n style={style}\n onLinkClick={onLinkClick}\n sessionId={sessionId}\n />\n );\n};\n\nexport default AdMeshLayout;\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\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 adId - The ad ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireExposure(exposureUrl: string, adId: string, sessionId: string): void {\n const key = `${sessionId}_${adId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n console.log('[Tracker] Exposure already fired for:', adId);\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(error => {\n if (this.debug) {\n console.warn('[Tracker] Failed to fire exposure:', error);\n }\n });\n\n if (this.debug) {\n console.log('[Tracker] Fired MRC-compliant exposure for:', adId);\n }\n } catch (error) {\n if (this.debug) {\n console.error('[Tracker] Error firing exposure:', error);\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 adId - The ad 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 adId: string,\n sessionId: string,\n element: HTMLElement\n ): Promise<void> {\n const key = `${sessionId}_${adId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n console.log('[Tracker] MRC exposure already fired for:', adId);\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 console.log('[Tracker] Ad reached MRC visibility threshold:', {\n adId,\n visibilityPercentage: visibilityPercentage.toFixed(2)\n });\n }\n\n // Set timeout to fire exposure after minimum duration\n timeoutId = setTimeout(() => {\n // Fire the exposure pixel\n this.fireExposure(exposureUrl, adId, 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 console.log('[Tracker] Ad visibility dropped below MRC threshold:', {\n adId,\n visibilityPercentage: visibilityPercentage.toFixed(2)\n });\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","/**\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 } from '../types/index';\nimport { AdMeshLayout } from '../components/AdMeshLayout';\nimport { AdMeshTracker } from './AdMeshTracker';\n\nexport interface RenderOptions {\n containerId: string;\n response: AgentRecommendationResponse;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\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 console.log(`[AdMeshRenderer] 🎨 Attempting to render into container: ${options.containerId}`);\n\n const container = document.getElementById(options.containerId);\n\n if (!container) {\n console.error(`[AdMeshRenderer] ❌ Container not found: ${options.containerId}`);\n console.log(`[AdMeshRenderer] 📋 Available containers in DOM:`,\n Array.from(document.querySelectorAll('[id*=\"admesh\"]')).map(el => el.id)\n );\n throw new Error(`Container with ID \"${options.containerId}\" not found`);\n }\n\n console.log(`[AdMeshRenderer] ✅ Container found:`, {\n id: options.containerId,\n tagName: container.tagName,\n className: container.className,\n innerHTML: container.innerHTML.substring(0, 100)\n });\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n console.log(`[AdMeshRenderer] 🧹 Cleaning up existing root for: ${options.containerId}`);\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 citation_summary from first recommendation if available\n const citationSummary = options.response.recommendations?.[0]?.citation_summary || '';\n\n console.log(`[AdMeshRenderer] 📊 Rendering recommendations:`, {\n containerId: options.containerId,\n recommendationsCount: options.response.recommendations?.length || 0,\n hasCitationSummary: !!citationSummary\n });\n\n root.render(\n <AdMeshLayout\n recommendations={options.response.recommendations || []}\n summaryText={citationSummary}\n theme={options.theme}\n sessionId={options.sessionId}\n />\n );\n\n console.log(`[AdMeshRenderer] ✅ Recommendations rendered successfully into: ${options.containerId}`);\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n } catch (error) {\n console.error(`[AdMeshRenderer] ❌ Error rendering recommendations:`, error);\n throw error;\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 }\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","/**\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\nexport interface DetectedLink {\n element: HTMLAnchorElement;\n href: string;\n text: string;\n hasAdLabel: boolean;\n matchedRecommendation?: {\n ad_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 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?: (exposureUrl: string, adId: string) => 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 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 // Only process links that match AdMesh recommendation click URLs\n // Ignore all other links (external, documentation, etc.)\n const recommendation = clickUrlMap.get(href);\n if (recommendation) {\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: {\n ad_id: recommendation.ad_id || recommendation.meta?.ad_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 this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n }\n\n // Fire exposure pixel\n if (this.fireExposurePixels && recommendation.exposure_url && onExposurePixel && detectedLink.matchedRecommendation) {\n onExposurePixel(recommendation.exposure_url, detectedLink.matchedRecommendation.ad_id);\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 this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n }\n\n // Fire exposure tracking with URL as identifier\n if (this.fireExposurePixels && onExposurePixel) {\n onExposurePixel(href, this.extractAdIdFromUrl(href));\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 *\n * This prevents duplicate label rendering when the LLM response\n * already contains [Ad] labels in either position.\n */\n private hasAdLabel(link: HTMLAnchorElement): boolean {\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 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 if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\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 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 if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n return true;\n }\n }\n\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 \n // Double-check that [Ad] label doesn't already exist to prevent duplicates\n if (this.hasAdLabel(link)) {\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 link.parentNode?.insertBefore(subLabel, link.nextSibling);\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 ad ID from AdMesh click URL\n *\n * URL format: https://api.useadmesh.com/click/{ad_id}?...\n * Returns the ad_id portion or the full URL if extraction fails\n */\n private extractAdIdFromUrl(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 full URL\n return url;\n } catch {\n return url;\n }\n }\n\n /**\n * Watch container for new links (streaming support)\n */\n watchForNewLinks(\n container: HTMLElement,\n recommendations: any[],\n onExposurePixel?: (exposureUrl: string, adId: string) => 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\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 } from '../types/index';\nimport { AdMeshRenderer } from './AdMeshRenderer';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { WeaveResponseProcessor } from './WeaveResponseProcessor';\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 format?: 'auto' | 'product'| 'citation' | 'ecommerce' | 'weave';\n previousQuery?: string;\n previousSummary?: string;\n isFallbackAllowed?: boolean;\n theme?: AdMeshTheme;\n\n // Session and message tracking (provided by the developer's platform)\n session_id?: string;\n message_id?: string;\n\n // Weave format specific options\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Container ID for LLM response (for Weave format).\n * This option is deprecated. For Weave Ad Format, wrap your LLM response\n * with WeaveAdFormatContainer component instead of using this option.\n *\n * @example\n * ```tsx\n * // ❌ OLD (Deprecated)\n * await sdk.showRecommendations({\n * format: 'weave',\n * llmOutputContainerId: 'llm-output-123'\n * });\n *\n * // ✅ NEW (Recommended)\n * <WeaveAdFormatContainer messageId={msg.id}>\n * {msg.content}\n * </WeaveAdFormatContainer>\n * ```\n */\n llmOutputContainerId?: string;\n\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Timeout for consumption ACK polling (default: 900ms).\n * This option is deprecated. Use WeaveAdFormatContainer component instead.\n */\n timeoutMs?: number;\n\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Fallback format if not consumed (default: 'citation').\n * This option is deprecated. Use WeaveAdFormatContainer component instead.\n */\n fallbackFormat?: 'product' | 'citation';\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 and message_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 IDs on your platform\n * const sessionId = AdMeshSDK.createSession();\n * const messageId = AdMeshSDK.createMessageId(sessionId);\n *\n * await admesh.showRecommendations({\n * query: 'best CRM for small business',\n * containerId: 'admesh-recommendations',\n * session_id: sessionId,\n * message_id: messageId\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 private weaveProcessor: WeaveResponseProcessor | 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 * Generate a unique session ID for tracking recommendations\n * Call this on your platform to create a new session\n *\n * @returns A unique session ID\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 * Generate a unique message ID for a session\n * Call this on your platform to create a message ID for tracking\n *\n * @param sessionId The session ID this message belongs to\n * @returns A unique message ID\n */\n static createMessageId(sessionId: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 15);\n return `msg_${sessionId}_${timestamp}_${random}`;\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 * OPTIMIZATION: Lazy initialize weave processor on first use\n */\n private getWeaveProcessor(): WeaveResponseProcessor {\n if (!this.weaveProcessor) {\n this.weaveProcessor = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n }\n return this.weaveProcessor;\n }\n\n\n\n /**\n * Fetch and render recommendations automatically\n *\n * Note: session_id and message_id should be provided by the developer's platform.\n * Use AdMeshSDK.createSession() and AdMeshSDK.createMessageId() to generate IDs.\n */\n async showRecommendations(options: ShowRecommendationsOptions): Promise<void> {\n try {\n // Fetch recommendations directly from API\n const response = await this.fetchRecommendationsFromAPI({\n query: options.query,\n format: options.format || 'auto',\n previousQuery: options.previousQuery,\n previousSummary: options.previousSummary,\n sessionId: options.session_id,\n messageId: options.message_id,\n isFallbackAllowed: options.isFallbackAllowed !== false\n });\n\n // Handle Weave format with consumption ACK\n if (options.format === 'weave') {\n await this.handleWeaveFormat(options, response);\n } else {\n // Standard rendering for non-Weave formats\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n response,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: options.session_id || ''\n });\n\n // Note: MRC-compliant exposure tracking is now handled by AdMeshViewabilityTracker\n // component wrapping each ad. The tracker.fireExposure() method is called by the\n // viewability tracker when ads meet the MRC threshold (50% visible for 1 second).\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 //\n // Legacy immediate exposure firing has been removed to prevent:\n // - Non-MRC compliant exposures\n // - Duplicate exposure tracking\n // - Incorrect CPX billing\n //\n // All components (AdMeshProductCard, AdMeshSummaryLayout, etc.) now use\n // AdMeshViewabilityTracker which handles exposure pixel firing automatically.\n }\n } catch (error) {\n console.error('[AdMeshSDK] Error showing recommendations:', error);\n throw error;\n }\n }\n\n\n\n /**\n * Handle Weave format: scan LLM output for AdMesh links, then render fallback if needed\n *\n * @deprecated Use WeaveAdFormatContainer component instead.\n * This method is deprecated. For Weave Ad Format, wrap your LLM response with\n * WeaveAdFormatContainer component instead of using format: 'weave' with showRecommendations().\n *\n * Flow:\n * 1. Scan LLM output container for AdMesh links (admesh.link or useadmesh.com URLs)\n * 2. If links found: recommendations were consumed by LLM - no fallback needed\n * 3. If no links found after timeout: render fallback UI with specified format\n *\n * This replaces the Pub/Sub consumption ACK approach with a simpler DB-backed approach\n * where the backend's /agent/recommend endpoint handles caching and generation.\n *\n * CRITICAL: This method MUST return early when links are found to prevent rendering\n * both woven ads and fallback UI simultaneously.\n *\n * @example\n * ```tsx\n * // ❌ OLD (Deprecated)\n * await sdk.showRecommendations({\n * format: 'weave',\n * llmOutputContainerId: 'llm-output-123'\n * });\n *\n * // ✅ NEW (Recommended)\n * <WeaveAdFormatContainer messageId={msg.id}>\n * {msg.content}\n * </WeaveAdFormatContainer>\n * ```\n */\n private async handleWeaveFormat(\n options: ShowRecommendationsOptions,\n response: AgentRecommendationResponse\n ): Promise<void> {\n const sessionId = options.session_id || '';\n const messageId = options.message_id || '';\n const timeoutMs = options.timeoutMs || 900;\n const fallbackFormat = options.fallbackFormat || 'citation';\n\n // Cache recommendations for fallback\n const cacheKey = `${sessionId}:${messageId}`;\n this.weaveCache.set(cacheKey, response.recommendations || []);\n\n // Scan LLM output for AdMesh links to detect if recommendations were consumed\n if (options.llmOutputContainerId) {\n try {\n const linksFound = await this.scanLLMOutputForAdMeshLinks(\n options.llmOutputContainerId,\n response.recommendations || [],\n sessionId,\n timeoutMs\n );\n\n // If AdMesh links were found in LLM output, recommendations were consumed\n // No fallback UI needed - the LLM already integrated the recommendations\n // CRITICAL: Return immediately to prevent rendering fallback UI\n if (linksFound) {\n console.log('[AdMeshSDK] Weave format: AdMesh links detected in LLM output. Skipping fallback UI.');\n return;\n }\n } catch (error) {\n console.error('[AdMeshSDK] Error scanning LLM output for AdMesh links:', error);\n // Continue to fallback rendering on error\n }\n }\n\n // No AdMesh links found in LLM output - render fallback UI\n console.log('[AdMeshSDK] Weave format: No AdMesh links detected. Rendering fallback UI with format:', fallbackFormat);\n\n const fallbackResponse = response;\n\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n response: fallbackResponse,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: sessionId\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 //\n // Legacy immediate exposure firing has been removed to prevent:\n // - Non-MRC compliant exposures\n // - Duplicate exposure tracking\n // - Incorrect CPX billing\n //\n // All components (AdMeshProductCard, AdMeshSummaryLayout, etc.) now use\n // AdMeshViewabilityTracker which handles exposure pixel firing automatically.\n }\n\n /**\n * Scan LLM output container for AdMesh links to detect if recommendations were consumed\n *\n * This method polls the LLM output container for AdMesh links (admesh.link or useadmesh.com URLs)\n * to determine if the LLM integrated the recommendations into its response.\n *\n * Uses a combination of polling and MutationObserver for reliable detection:\n * 1. Sets up MutationObserver to watch for new links in real-time\n * 2. Polls container at regular intervals to catch links that appear\n * 3. Returns immediately when links are found\n * 4. Cleans up observer after timeout or when links found\n *\n * @param llmOutputContainerId - ID of the container with LLM output\n * @param recommendations - Array of recommendations to match against\n * @param sessionId - Session ID for tracking\n * @param timeoutMs - How long to wait for links to appear (default: 900ms)\n * @returns true if AdMesh links were found and labeled, false otherwise\n */\n private async scanLLMOutputForAdMeshLinks(\n llmOutputContainerId: string,\n recommendations: AdMeshRecommendation[],\n sessionId: string,\n timeoutMs: number\n ): Promise<boolean> {\n const container = document.getElementById(llmOutputContainerId);\n if (!container) {\n console.warn(`[AdMeshSDK] LLM output container not found: ${llmOutputContainerId}`);\n return false;\n }\n\n const startTime = Date.now();\n const pollIntervalMs = 100;\n let linksFoundFlag = false;\n let observer: MutationObserver | null = null;\n\n try {\n // Create a promise that resolves when links are found\n const linksFoundPromise = new Promise<boolean>((resolve) => {\n const mutationObserver = new MutationObserver(() => {\n const linksFound = this.scanAndLabelAds(\n llmOutputContainerId,\n recommendations,\n sessionId\n );\n\n if (linksFound && !linksFoundFlag) {\n linksFoundFlag = true;\n console.log('[AdMeshSDK] AdMesh links detected via MutationObserver');\n resolve(true);\n }\n });\n\n mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n\n observer = mutationObserver;\n });\n\n // Polling loop with timeout\n const pollingPromise = (async () => {\n while (Date.now() - startTime < timeoutMs) {\n // Scan for AdMesh links in the LLM output\n const linksFound = this.scanAndLabelAds(\n llmOutputContainerId,\n recommendations,\n sessionId\n );\n\n if (linksFound && !linksFoundFlag) {\n linksFoundFlag = true;\n console.log('[AdMeshSDK] AdMesh links detected via polling');\n return true;\n }\n\n // Wait before next scan\n await this.sleep(pollIntervalMs);\n }\n\n return false;\n })();\n\n // Race between MutationObserver and polling timeout\n const result = await Promise.race([\n linksFoundPromise,\n pollingPromise\n ]);\n\n return result;\n } finally {\n // Clean up MutationObserver\n if (observer) {\n (observer as MutationObserver).disconnect();\n }\n }\n }\n\n /**\n * Scan LLM output container and label AdMesh recommendation links\n * Now uses WeaveResponseProcessor for automatic detection and enhancement\n *\n * IMPORTANT: This method only scans for existing links and does NOT set up\n * the MutationObserver. The observer is managed by scanLLMOutputForAdMeshLinks\n * to avoid creating multiple observers during polling.\n *\n * @returns true if AdMesh links were found and labeled, false otherwise\n */\n private scanAndLabelAds(\n llmOutputContainerId: string,\n recommendations: AdMeshRecommendation[],\n sessionId: string\n ): boolean {\n const container = document.getElementById(llmOutputContainerId);\n if (!container) {\n return false;\n }\n\n const weaveProcessor = this.getWeaveProcessor();\n const tracker = this.getTracker();\n\n // Use WeaveResponseProcessor to detect and enhance links\n // This only scans for existing links, does not set up observer\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recommendations,\n (exposureUrl: string, adId: string) => {\n // Fire exposure pixel for detected link\n tracker.fireExposure(exposureUrl, adId, sessionId);\n }\n );\n\n // Return true if any AdMesh links were found\n return detectedLinks.length > 0;\n }\n\n /**\n * Fetch recommendations directly from the /agent/recommend endpoint\n */\n private async fetchRecommendationsFromAPI(params: {\n query: string;\n format?: string;\n previousQuery?: string;\n previousSummary?: string;\n sessionId?: string;\n messageId?: string;\n isFallbackAllowed?: boolean;\n }): Promise<AgentRecommendationResponse> {\n const url = `${this.apiBaseUrl}/agent/recommend`;\n\n console.log('[AdMeshSDK] 📥 fetchRecommendationsFromAPI called with params:', {\n query: params.query ? `\"${params.query.substring(0, 50)}${params.query.length > 50 ? '...' : ''}\"` : '(EMPTY)',\n queryType: typeof params.query,\n queryLength: params.query?.length,\n format: params.format,\n sessionId: params.sessionId,\n messageId: params.messageId,\n });\n\n // Build payload with direct property assignment\n const payload: Record<string, string | boolean> = {\n query: params.query,\n format: params.format || 'auto',\n source: 'admesh_ui_sdk' // Identify requests from the UI SDK\n };\n\n // Only add optional fields if they are provided\n if (params.previousQuery) {\n payload.previous_query = params.previousQuery;\n }\n if (params.previousSummary) {\n payload.previous_summary = params.previousSummary;\n }\n if (params.sessionId) {\n payload.session_id = params.sessionId;\n }\n if (params.messageId) {\n payload.message_id = params.messageId;\n }\n if (params.isFallbackAllowed !== undefined) {\n payload.is_fallback_allowed = params.isFallbackAllowed;\n }\n\n // Validate query before sending\n const queryStr = typeof payload.query === 'string' ? payload.query : '';\n if (!queryStr || !queryStr.trim()) {\n console.warn('[AdMeshSDK] ⚠️ Warning: Sending request with empty query');\n console.log('[AdMeshSDK] Full payload:', JSON.stringify(payload, null, 2));\n }\n\n const jsonBody = JSON.stringify(payload);\n console.log('[AdMeshSDK] 📤 Sending JSON body:', jsonBody);\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 recommendations: ${errorMessage}`);\n }\n\n const data: AgentRecommendationResponse = await response.json();\n return data;\n }\n\n /**\n * Sleep utility\n */\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n // Cache for Weave recommendations\n private weaveCache: Map<string, AdMeshRecommendation[]> = new Map();\n}\n\nexport default AdMeshSDK;\n\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 // 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","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\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 /** 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 children,\n}) => {\n const sdkRef = useRef<AdMeshSDK | null>(null);\n const [processedMessageIds, setProcessedMessageIds] = useState<Set<string>>(\n new Set()\n );\n\n // Initialize SDK once on mount\n useEffect(() => {\n if (!apiKey) {\n console.warn('[AdMeshProvider] ⚠️ AdMesh API key not configured');\n return;\n }\n\n try {\n sdkRef.current = new AdMeshSDK({\n apiKey,\n theme,\n apiBaseUrl,\n });\n console.log('[AdMeshProvider] ✅ AdMesh SDK initialized');\n if (apiBaseUrl) {\n console.log(`[AdMeshProvider] 📍 Using custom API base URL: ${apiBaseUrl}`);\n }\n } catch (error) {\n console.error('[AdMeshProvider] ❌ Failed to initialize AdMesh SDK:', error);\n }\n\n // Cleanup on unmount\n return () => {\n console.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 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\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 /** 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, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport type { ShowRecommendationsOptions } from '../sdk/AdMeshSDK';\n\nexport interface AdMeshRecommendationsProps {\n /** Recommendation format\n *\n * - 'product': Display as product cards\n * - 'citation': Display as citation format (default)\n *\n * Note: For Weave Ad Format (where AdMesh links are embedded in LLM response),\n * use WeaveAdFormatContainer component instead. This component is for displaying\n * recommendations as a separate UI component.\n */\n format?: 'product' | 'citation';\n\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/**\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 * format=\"citation\"\n * />\n * )}\n * </div>\n * ))}\n * </AdMeshProvider>\n * ```\n *\n * @example\n * ```tsx\n * // Product card format recommendations\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <Chat messages={messages} />\n * <AdMeshRecommendations\n * messageId={lastMessageId}\n * query=\"best CRM for small business\"\n * format=\"product\"\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshRecommendations: React.FC<AdMeshRecommendationsProps> = ({\n format = 'citation',\n onRecommendationsShown,\n onError,\n messageId,\n query,\n}) => {\n const { sdk, sessionId } = 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-recommendations-${messageId}`);\n }\n }, [messageId]);\n\n useEffect(() => {\n // Log what we're receiving\n console.log('[AdMeshRecommendations] 🔄 SDK VERSION CHECK - Props received:', {\n sdkVersion: '1.0.6-no-dedup',\n timestamp: new Date().toISOString(),\n messageId,\n query: query ? `\"${query.substring(0, 50)}${query.length > 50 ? '...' : ''}\"` : '(EMPTY)',\n queryType: typeof query,\n queryLength: query?.length,\n format,\n sessionId\n });\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n console.log('[AdMeshRecommendations] ❌ Validation failed - returning early:', {\n hasMessageId: !!messageId,\n hasQuery: !!query,\n queryTrimmed: query?.trim(),\n queryTrimmedLength: query?.trim().length,\n });\n return;\n }\n\n console.log('[AdMeshRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n console.log('[AdMeshRecommendations] SDK or containerId not ready:', { hasSdk: !!sdk, hasContainerId: !!containerId });\n return;\n }\n\n // Fetch recommendations\n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n console.log('[AdMeshRecommendations] SDK showRecommendations not available');\n return;\n }\n\n console.log('[AdMeshRecommendations] 📤 Calling sdk.showRecommendations with:', {\n query: query.trim().substring(0, 50),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n });\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n } as ShowRecommendationsOptions);\n\n onRecommendationsShown?.(messageId);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n console.log('[AdMeshRecommendations] ❌ Error:', err.message);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, containerId, format, messageId, query, onRecommendationsShown, onError]);\n\n // Render the container where recommendations will be displayed\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-recommendations-container\"\n style={{\n marginTop: '1rem',\n minHeight: '100px',\n display: 'block',\n }}\n />\n );\n};\n\nexport default AdMeshRecommendations;\n\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport type { ShowRecommendationsOptions } from '../sdk/AdMeshSDK';\n\nexport interface WeaveFallbackRecommendationsProps {\n /** Recommendation format for fallback display\n *\n * - 'product': Display as product cards\n * - 'citation': Display as citation format (default)\n */\n format?: 'product' | 'citation';\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/**\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 * - Call SDK's showRecommendations() to fetch fresh recommendations\n * - Display recommendations in the specified format (citation or product)\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=\"citation\"\n * fallback={fallback}\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const WeaveFallbackRecommendations: React.FC<WeaveFallbackRecommendationsProps> = ({\n format = 'citation',\n onError,\n messageId,\n query,\n fallback,\n}) => {\n const { sdk, sessionId } = 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 console.log('[WeaveFallbackRecommendations] 🎨 Component render:', {\n messageId,\n fallback,\n hasQuery: !!query,\n timestamp: new Date().toISOString()\n });\n\n useEffect(() => {\n // Log what we're receiving\n console.log('[WeaveFallbackRecommendations] 🔄 useEffect triggered - Props:', {\n timestamp: new Date().toISOString(),\n messageId,\n query: query ? `\"${query.substring(0, 50)}${query.length > 50 ? '...' : ''}\"` : '(EMPTY)',\n queryType: typeof query,\n queryLength: query?.length,\n format,\n sessionId,\n fallback,\n });\n\n // Skip if fallback is not true\n if (!fallback) {\n console.log('[WeaveFallbackRecommendations] ⏭️ Skipping - fallback is FALSE, not rendering recommendations');\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] ✅ fallback is TRUE, proceeding with recommendations');\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n console.log('[WeaveFallbackRecommendations] ❌ Validation failed - returning early:', {\n hasMessageId: !!messageId,\n hasQuery: !!query,\n queryTrimmed: query?.trim(),\n queryTrimmedLength: query?.trim().length,\n });\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n console.log('[WeaveFallbackRecommendations] SDK or containerId not ready:', { hasSdk: !!sdk, hasContainerId: !!containerId });\n return;\n }\n\n // Fetch recommendations\n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n console.log('[WeaveFallbackRecommendations] SDK showRecommendations not available');\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] 📤 Calling sdk.showRecommendations with:', {\n query: query.trim().substring(0, 50),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n });\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n } as ShowRecommendationsOptions);\n\n console.log('[WeaveFallbackRecommendations] ✅ Recommendations displayed successfully');\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n console.log('[WeaveFallbackRecommendations] ❌ Error:', err.message);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, containerId, format, messageId, query, fallback, onError]);\n\n // Render the container where fallback recommendations will be displayed\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-weave-fallback-recommendations-container\"\n style={{\n marginTop: '1rem',\n minHeight: '100px',\n display: 'block',\n }}\n />\n );\n};\n\nexport default WeaveFallbackRecommendations;\n\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 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 console.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\n const link = (item as any).click_url || (item as any).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).product_id || (item as any).id || (item as any).ad_id;\n const adId = (item as any).ad_id || (item as any).product_id || '';\n const productId = (item as any).product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n key={itemId}\n adId={adId}\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.product_logo?.url ? (\n <img\n src={item.product_logo.url}\n alt={item.product_title}\n className=\"h-full w-full object-cover transition-transform duration-200 hover:scale-105\"\n loading=\"lazy\"\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.product_title}\n </h4>\n\n {/* Summary */}\n {item.weave_summary && (\n <p className=\"text-xs text-gray-600 dark:text-gray-400 line-clamp-2 mb-2\">\n {item.weave_summary}\n </p>\n )}\n\n {/* Categories */}\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 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 // 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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_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 {/* 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\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 console.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 console.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 console.log('[StreamingEvents] 📨 Received streamingStart for:', messageId);\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 const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingCompleteEventDetail>;\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 console.log('[StreamingEvents] 📨 Received streamingComplete for:', messageId);\n callback(customEvent.detail);\n }\n };\n\n window.addEventListener(STREAMING_COMPLETE_EVENT, handler);\n\n // Return cleanup function\n return () => {\n window.removeEventListener(STREAMING_COMPLETE_EVENT, handler);\n };\n}\n\n","'use client';\n\nimport React, { useRef, useState, useEffect, useCallback } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport { WeaveFallbackRecommendations } from './WeaveFallbackRecommendations';\nimport { onStreamingComplete } from '../utils/streamingEvents';\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 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 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\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\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 }, [onLinksFound, onNoLinksFound, containerId, sdk]);\n\n const performFinalCheck = useCallback(() => {\n console.log('[FinalLinkDetectionCheck] 🔍 Performing final link detection...');\n\n try {\n const container = document.getElementById(containerIdRef.current);\n if (!container) {\n console.warn(`[FinalLinkDetectionCheck] ❌ Container not found: ${containerIdRef.current}`);\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Get the WeaveResponseProcessor from SDK\n const weaveProcessor = sdkRef.current?.getWeaveProcessor?.();\n if (!weaveProcessor) {\n console.warn('[FinalLinkDetectionCheck] ❌ WeaveProcessor not available');\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Scan for AdMesh links one final time\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n [], // Recommendations will be fetched from SDK cache\n () => {\n // Exposure tracking is handled by WeaveResponseProcessor\n }\n );\n\n console.log(`[FinalLinkDetectionCheck] 📊 Final check result: ${detectedLinks.length} links`);\n\n if (detectedLinks.length > 0) {\n // Links found! Cancel fallback\n console.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 console.log('[FinalLinkDetectionCheck] ⚠️ No links found - rendering fallback (will make API call)');\n setLinksFound(false);\n onNoLinksFoundRef.current();\n }\n\n setCheckComplete(true);\n } catch (error) {\n console.error('[FinalLinkDetectionCheck] ❌ Error during final check:', error);\n setCheckComplete(true);\n onNoLinksFoundRef.current();\n }\n }, []); // No dependencies - all values accessed via refs\n\n useEffect(() => {\n const listenerSetupTime = Date.now();\n console.log('[FinalLinkDetectionCheck] ⏳ Setting up listener at', listenerSetupTime, ':', {\n messageId,\n sessionId\n });\n\n // Listen for the streamingComplete event from ChatWindow\n const cleanup = onStreamingComplete(messageId, sessionId, (detail) => {\n const eventReceivedTime = Date.now();\n console.log('[FinalLinkDetectionCheck] 🎯 Received streamingComplete event at', eventReceivedTime, '(listener was set up', eventReceivedTime - listenerSetupTime, 'ms ago):', detail);\n setWaitingForStreamEnd(false);\n\n // Small delay to ensure DOM is fully updated\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n });\n\n return () => {\n console.log('[FinalLinkDetectionCheck] 🧹 Cleaning up listener for:', messageId);\n cleanup();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messageId, sessionId]); // performFinalCheck is stable (no dependencies), so we don't include it\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 console.log('[FinalLinkDetectionCheck] 🚫 Links found - NOT rendering fallback');\n return <></>;\n }\n\n console.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: 'citation') */\n fallbackFormat?: 'product' | 'citation';\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\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 = 'citation',\n onLinksDetected,\n onNoLinksDetected,\n onError,\n className,\n query,\n onFallbackChange\n}) => {\n const { sessionId, sdk } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const containerId = `weave-ad-container-${messageId}`;\n\n\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 */}\n {query && (\n <FinalLinkDetectionCheck\n containerId={containerId}\n messageId={messageId}\n sessionId={sessionId}\n sdk={sdk}\n onLinksFound={(count) => {\n console.log(`[WeaveAdFormatContainer] ✅ Links found (${count}) - no fallback needed`);\n onLinksDetected?.(count);\n onFallbackChange?.(false);\n }}\n onNoLinksFound={() => {\n console.log(`[WeaveAdFormatContainer] ⚠️ No links found - rendering fallback`);\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 />\n </FinalLinkDetectionCheck>\n )}\n </>\n );\n};\n\nexport default WeaveAdFormatContainer;\n\n","'use client';\n\nimport { useEffect, useRef, useCallback } from 'react';\nimport { useAdMesh } from './useAdMesh';\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: 'citation') */\n fallbackFormat?: 'product' | 'citation';\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: 'citation'\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\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 () => {\n // Exposure tracking is handled by WeaveResponseProcessor\n }\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]);\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","/**\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';\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 { AdMeshSummaryUnit } from './components/AdMeshSummaryUnit';\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// Version\nexport const VERSION = '1.0.0';\n"],"names":["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","generateBatchId","meetsViewabilityThreshold","visibilityPercentage","visibleDuration","standards","formatTimestamp","date","calculateAverage","numbers","acc","num","throttle","func","limit","inThrottle","args","sendAnalyticsEvent","event","apiEndpoint","retryAttempts","retryDelay","lastError","attempt","response","errorText","error","resolve","sendAnalyticsBatch","events","sessionId","batch","DEFAULT_CONFIG","globalConfig","useViewabilityTracker","adId","productId","offerId","agentId","recommendationId","elementRef","customConfig","config","useRef","state","setState","useState","mrcStandards","visibilityStartTime","viewableStartTime","hoverStartTime","focusStartTime","visibilityPercentages","eventBatch","batchTimeout","log","useCallback","message","data","sendEvent","eventType","additionalData","contextMetrics","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","previousVisible","jsx","isValidUrl","url","processSummaryText","summaryText","recommendations","clickUrlToRecMap","rec","markdownLinkRegex","parts","lastIndex","match","linkCounter","fullMatch","linkText","recommendation","linkUrl","AdMeshSummaryUnit","theme","processedContent","part","index","React","_a","hasOwn","classNames","classes","i","arg","appendClass","parseValue","key","value","newClass","module","DEFAULT_TRACKING_URL","useAdMeshTracker","isTracking","setIsTracking","setError","mergedConfig","useMemo","sendTrackingEvent","payload","err","errorMsg","trackClick","trackView","trackConversion","AdMeshLinkTracker","admeshLink","trackingData","hasTrackedView","entry","ADMESH_STYLE_ID","ADMESH_RESET_ID","ADMESH_CSS_RESET","ADMESH_CORE_STYLES","injectAdMeshStyles","resetStyle","coreStyle","ADMESH_STYLES","stylesInjected","useAdMeshStyles","styleElement","getRecommendationLabel","matchScore","customLabels","getLabelTooltip","_label","AdMeshProductCard","variation","content","cardClasses","cardStyle","_b","_c","_d","_e","_f","_g","_h","_i","_j","_l","_k","_m","jsxs","_n","_o","_p","e","AdMeshSummaryLayout","onLinkClick","recs","summary","renderContent","AdMeshLayout","AdMeshTracker","__publicField","timeoutId","cleanupTimeout","threshold","AdMeshRenderer","options","container","el","existingRoot","root","ReactDOM","citationSummary","containerId","WeaveResponseProcessor","optimizedLinks","onExposurePixel","detectedLinks","links","clickUrlMap","r","link","href","linkKey","detectedLink","prevNode","text","nextNode","subLabel","isTooltipVisible","closeTooltipOnClickOutside","domain","AdMeshSDK","timestamp","random","renderer","tracker","messageId","timeoutMs","fallbackFormat","cacheKey","fallbackResponse","llmOutputContainerId","startTime","pollIntervalMs","linksFoundFlag","linksFoundPromise","mutationObserver","pollingPromise","weaveProcessor","params","queryStr","jsonBody","errorMessage","ms","AdMeshContext","useAdMeshContext","context","AdMeshProvider","apiKey","apiBaseUrl","sdkRef","processedMessageIds","setProcessedMessageIds","contextValue","updated","useAdMesh","AdMeshRecommendations","format","onRecommendationsShown","onError","query","sdk","containerRef","setContainerId","WeaveFallbackRecommendations","fallback","WeaveAdFormatContext","createContext","WeaveAdFormatProvider","shouldRenderFallback","useWeaveAdFormatContext","useContext","AdMeshEcommerceCards","title","showTitle","cardClassName","onProductClick","maxCards","cardWidth","borderRadius","shadow","displayItems","getCardWidthClass","getBorderRadiusClass","getShadowClass","getThemeClasses","handleProductClick","item","itemId","cat","idx","Fragment","AdMeshInlineCard","expandable","badgeTypeVariants","badgeTypeIcons","AdMeshBadge","type","variant","size","effectiveVariant","icon","badgeClasses","STREAMING_START_EVENT","STREAMING_COMPLETE_EVENT","dispatchStreamingStartEvent","detail","dispatchStreamingCompleteEvent","metadata","onStreamingStart","callback","handler","customEvent","onStreamingComplete","FinalLinkDetectionCheck","onLinksFound","onNoLinksFound","checkComplete","setCheckComplete","waitingForStreamEnd","setWaitingForStreamEnd","linksFound","setLinksFound","onLinksFoundRef","onNoLinksFoundRef","containerIdRef","performFinalCheck","listenerSetupTime","cleanup","eventReceivedTime","WeaveAdFormatContainer","onLinksDetected","onNoLinksDetected","onFallbackChange","count","useWeaveAdFormat","processingRef","detectedLinksRef","linksFoundRef","callbackFiredRef","debounceTimerRef","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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FhC,MAAMK,EAAkB,uBAClBC,EAAkB,sBAMlBC,GAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA+FnBC,GAAqB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAyNdC,GAAqB,IAAY,CAC5C,GAAI,SAAO,SAAa,MAGpB,WAAS,eAAeH,CAAe,GAAK,SAAS,eAAeD,CAAe,GAKvF,IAAI,CAAC,SAAS,eAAeC,CAAe,EAAG,CAC7C,MAAMI,EAAa,SAAS,cAAc,OAAO,EACjDA,EAAW,GAAKJ,EAChBI,EAAW,YAAcH,GACzB,SAAS,KAAK,YAAYG,CAAU,CACtC,CAGA,GAAI,CAAC,SAAS,eAAeL,CAAe,EAAG,CAC7C,MAAMM,EAAY,SAAS,cAAc,OAAO,EAChDA,EAAU,GAAKN,EACfM,EAAU,YAAcH,GACxB,SAAS,KAAK,YAAYG,CAAS,CACrC,EACF,ECzVMC,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","x_google_ignoreList":[4]}
1
+ {"version":3,"file":"index.js","sources":["../src/utils/viewabilityTracker.ts","../src/hooks/useViewabilityTracker.ts","../src/components/AdMeshViewabilityTracker.tsx","../src/components/AdMeshSummaryUnit.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/components/AdMeshSummaryLayout.tsx","../src/components/AdMeshLayout.tsx","../src/sdk/AdMeshTracker.ts","../src/sdk/AdMeshRenderer.tsx","../src/sdk/WeaveResponseProcessor.ts","../src/sdk/AdMeshSDK.ts","../src/context/AdMeshContext.ts","../src/context/AdMeshProvider.tsx","../src/hooks/useAdMesh.ts","../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 * 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';\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\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 let lastError: Error | null = null;\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 const errorText = await response.text().catch(() => '');\n lastError = new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);\n } catch (error) {\n lastError = error as Error;\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 console.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 let lastError: Error | null = null;\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 const errorText = await response.text().catch(() => '');\n lastError = new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`);\n } catch (error) {\n lastError = error as Error;\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 console.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 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 sendAnalyticsEvent,\n sendAnalyticsBatch,\n throttle\n} from '../utils/viewabilityTracker';\n\n// Default configuration\nconst DEFAULT_CONFIG: ViewabilityTrackerConfig = {\n enabled: true,\n // NOTE: Viewability tracking endpoint for storing analytics in recommendations collection\n // This endpoint stores MRC-compliant viewability analytics for each recommendation\n // Uses batch endpoint to handle multiple events efficiently\n apiEndpoint: 'https://api.useadmesh.com/api/recommendations/viewability/batch',\n enableBatching: true,\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\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 console.warn('[AdMesh Viewability] Analytics sending is DISABLED - no data will be sent to backend');\n } else {\n console.log('[AdMesh Viewability] Analytics sending is ENABLED');\n }\n};\n\ninterface UseViewabilityTrackerProps {\n /** Ad ID */\n adId: string;\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 adId,\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 console.log(`[AdMesh Viewability] ${message}`);\n }\n }, [config.debug]);\n\n // Send event (batched or immediate)\n // NOTE: Only sends critical events (ad_viewable, ad_click) for cost optimization\n // Other events are tracked internally but not sent to backend\n // TEMPORARY: Can be disabled globally via disableViewabilityAnalytics()\n const sendEvent = useCallback(async (eventType: ViewabilityEventType, additionalData?: Record<string, unknown>) => {\n if (!config.enabled || !elementRef.current || !mrcStandards.current) return;\n\n // TEMPORARY: Check if analytics are disabled globally\n if (ANALYTICS_DISABLED) {\n log(`Analytics disabled - skipping event: ${eventType}`);\n return;\n }\n\n // OPTIMIZATION: Only send critical events to reduce storage costs by 85-95%\n const criticalEvents = ['ad_viewable', 'ad_click'];\n if (!criticalEvents.includes(eventType)) {\n log(`Skipping non-critical event: ${eventType} (only ad_viewable and ad_click are sent)`);\n return;\n }\n\n const contextMetrics = collectContextMetrics(elementRef.current);\n\n const event: ViewabilityAnalyticsEvent = {\n eventType,\n timestamp: formatTimestamp(),\n sessionId: sessionId.current,\n adId,\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\n log(`Sending critical event: ${eventType}`);\n\n // Call custom callback if provided\n if (config.onEvent) {\n config.onEvent(event);\n }\n\n if (config.enableBatching) {\n // Add to batch\n eventBatch.current.push(event);\n\n // Send batch if size limit reached\n if (eventBatch.current.length >= config.batchSize) {\n await flushBatch();\n } else {\n // Set timeout to send batch\n if (batchTimeout.current) clearTimeout(batchTimeout.current);\n batchTimeout.current = setTimeout(flushBatch, config.batchTimeout);\n }\n } else {\n // Send immediately\n await sendAnalyticsEvent(event, config.apiEndpoint, config.maxRetries, config.retryDelay);\n }\n }, [config, adId, productId, offerId, agentId, elementRef, state, log]);\n\n // Flush event batch\n const flushBatch = useCallback(async () => {\n if (eventBatch.current.length === 0) return;\n\n // TEMPORARY: If analytics are disabled, clear batch without sending\n if (ANALYTICS_DISABLED) {\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 }\n\n const events = [...eventBatch.current];\n eventBatch.current = [];\n\n if (batchTimeout.current) {\n clearTimeout(batchTimeout.current);\n batchTimeout.current = null;\n }\n\n const success = await sendAnalyticsBatch(\n events,\n sessionId.current,\n config.apiEndpoint,\n config.maxRetries,\n config.retryDelay\n );\n\n if (success && config.onBatchSent) {\n config.onBatchSent({\n batchId: `batch_${Date.now()}`,\n sessionId: sessionId.current,\n createdAt: formatTimestamp(),\n events,\n eventCount: events.length\n });\n }\n }, [config, 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 { useViewabilityTracker } from '../hooks/useViewabilityTracker';\nimport type { ViewabilityTrackerConfig } from '../types/analytics';\n\nexport interface AdMeshViewabilityTrackerProps {\n /** Ad ID */\n adId: string;\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 * adId=\"ad_123\"\n * productId=\"prod_456\"\n * offerId=\"offer_789\"\n * onViewable={() => console.log('Ad is viewable!')}\n * >\n * <YourAdComponent />\n * </AdMeshViewabilityTracker>\n * ```\n */\nexport const AdMeshViewabilityTracker: React.FC<AdMeshViewabilityTrackerProps> = ({\n adId,\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 adId,\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 && exposureUrl && sessionId) {\n exposureFired.current = true;\n\n // Fire the exposure pixel using fetch with keepalive\n fetch(exposureUrl, { method: 'GET', keepalive: true })\n .then(() => {\n console.log('[AdMesh] ✅ Exposure pixel fired');\n })\n .catch(() => {\n console.warn('[AdMesh] ⚠️ Failed to fire exposure pixel');\n // Reset flag to allow retry\n exposureFired.current = false;\n });\n }\n }\n }, [viewabilityState.isViewable, onViewabilityChange, onViewable, exposureUrl, sessionId, adId, 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-ad-id={adId}\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 type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshViewabilityTracker } from './AdMeshViewabilityTracker';\n\nexport interface AdMeshSummaryUnitProps {\n summaryText: string; // The citation_summary from backend response\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// Process summary text with markdown links [Product Name](click_url)\nconst processSummaryText = (summaryText: string, recommendations: AdMeshRecommendation[]) => {\n // Create lookup map for recommendations by click_url\n const clickUrlToRecMap = new Map<string, AdMeshRecommendation>();\n\n recommendations.forEach(rec => {\n if (rec.click_url) {\n clickUrlToRecMap.set(rec.click_url, rec);\n }\n });\n\n // Debug: Log all available links in recommendations\n console.log('[AdMesh Summary] Processing recommendations');\n\n if (clickUrlToRecMap.size > 0) {\n console.log('[AdMesh Summary] Recommendation links detected');\n } else {\n console.warn('[AdMesh Summary] No click_url found in recommendations!');\n }\n\n // Find markdown links and replace with JSX elements\n const markdownLinkRegex = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n const parts: (string | React.ReactElement)[] = [];\n let lastIndex = 0;\n let match;\n let linkCounter = 0;\n\n while ((match = markdownLinkRegex.exec(summaryText)) !== null) {\n const [fullMatch, linkText, url] = match;\n\n console.log('[AdMesh Summary] Processing markdown link');\n\n // Try to find recommendation by exact URL match (click_url)\n let recommendation = clickUrlToRecMap.get(url);\n\n console.log('[AdMesh Summary] URL match result');\n\n // If exact match not found, try to match by ad_id\n if (!recommendation) {\n recommendation = recommendations.find(rec =>\n (rec.ad_id && url.includes(rec.ad_id))\n );\n if (recommendation) {\n console.log('[AdMesh Summary] Found by ad_id match');\n }\n }\n\n // Add text before the link\n if (match.index > lastIndex) {\n parts.push(summaryText.slice(lastIndex, match.index));\n }\n\n if (recommendation) {\n linkCounter++;\n // Use the URL from markdown (click_url)\n const linkUrl = url || recommendation.click_url;\n\n // Create clickable link element\n parts.push(\n <a\n key={`summary-link-${linkCounter}`}\n href={linkUrl}\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\"\n style={{\n color: '#2563eb', // Force blue color\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={() => {\n // Log comprehensive click data\n console.log('AdMesh summary link clicked');\n\n // Fire tracking asynchronously WITHOUT blocking navigation\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n adId: recommendation.ad_id,\n productId: recommendation.product_id,\n clickUrl: recommendation.click_url,\n source: 'summary'\n }).catch(() => {\n console.error('[AdMesh] Failed to track summary link click');\n });\n }\n }}\n >\n {linkText}\n </a>\n );\n } else {\n // Create a regular link even if no recommendation found\n console.warn('[AdMesh Summary] No recommendation found for link, creating regular link');\n\n if (isValidUrl(url)) {\n linkCounter++;\n parts.push(\n <a\n key={`summary-link-${linkCounter}`}\n href={url}\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\"\n style={{\n color: '#2563eb', // Force blue color\n textDecoration: 'underline',\n textDecorationColor: '#2563eb',\n textUnderlineOffset: '2px'\n }}\n onClick={() => {\n // Log click for unmatched links\n console.log('AdMesh summary unmatched link clicked');\n\n // Fire tracking asynchronously WITHOUT blocking navigation\n if (typeof window !== 'undefined' && (window as any).admeshTracker) {\n (window as any).admeshTracker.trackClick({\n url,\n linkText,\n source: 'summary_unmatched'\n }).catch(() => {\n console.error('[AdMesh] Failed to track unmatched link click');\n });\n }\n }}\n >\n {linkText}\n </a>\n );\n } else {\n // If URL is invalid, just show the link text without markdown\n console.warn('[AdMesh Summary] Invalid URL in markdown link');\n parts.push(linkText);\n }\n }\n\n lastIndex = match.index + fullMatch.length;\n }\n\n // Add remaining text\n if (lastIndex < summaryText.length) {\n parts.push(summaryText.slice(lastIndex));\n }\n\n return parts;\n};\n\nexport const AdMeshSummaryUnit: React.FC<AdMeshSummaryUnitProps> = ({\n summaryText,\n recommendations,\n theme,\n className = '',\n style = {},\n sessionId\n}) => {\n // Validate inputs\n if (!summaryText || !summaryText.trim()) {\n console.warn('[AdMesh Summary] No summary text provided');\n return null;\n }\n\n if (!recommendations || recommendations.length === 0) {\n console.warn('[AdMesh Summary] No recommendations provided');\n // Still process markdown links even without recommendations\n const processedContent = processSummaryText(summaryText, []);\n return (\n <div className={`admesh-summary-unit ${className}`} style={style}>\n <p className=\"text-gray-700 dark:text-gray-300 leading-relaxed\">\n {processedContent.map((part, index) => (\n <React.Fragment key={index}>{part}</React.Fragment>\n ))}\n </p>\n </div>\n );\n }\n\n // Process the summary text to create clickable links\n const processedContent = processSummaryText(summaryText, recommendations);\n\n // Get the first recommendation's ad ID for viewability tracking\n // (Summary unit contains multiple recommendations, but we track the unit as a whole)\n const adId = recommendations[0]?.ad_id || '';\n const productId = recommendations[0]?.product_id;\n const exposureUrl = recommendations[0]?.exposure_url;\n const recommendationId = recommendations[0]?.recommendation_id;\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\n productId={productId}\n recommendationId={recommendationId}\n exposureUrl={exposureUrl}\n sessionId={sessionId}\n className={`admesh-summary-unit ${className}`}\n style={{\n fontFamily: theme?.fontFamily || '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n ...style\n }}\n >\n <div>\n {/* Summary Content */}\n <div className=\"summary-content\">\n <p className=\"text-gray-700 dark:text-gray-300 leading-relaxed text-base\">\n {processedContent.map((part, index) => (\n <React.Fragment key={index}>{part}</React.Fragment>\n ))}\n </p>\n </div>\n\n {/* Disclosure */}\n <div className=\"mt-3 pt-2 border-t border-gray-200 dark:border-gray-700\">\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 AdMeshSummaryUnit;\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 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.adId || !data.admeshLink) {\n const errorMsg = 'Missing required tracking data: adId 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 ad_id: data.adId,\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 adId: string, \n additionalParams?: Record<string, string>\n): string => {\n try {\n const url = new URL(baseLink);\n url.searchParams.set('ad_id', adId);\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 console.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: { ad_id: string; admesh_link: string; product_id: string },\n additionalData?: Partial<TrackingData>\n): TrackingData => {\n return {\n adId: recommendation.ad_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';\n\nexport const AdMeshLinkTracker: React.FC<AdMeshLinkTrackerProps> = ({\n adId,\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 // 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 adId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(console.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 }, [adId, 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 adId,\n admeshLink,\n productId,\n ...trackingData\n }).catch(() => {\n // Log error but don't block navigation\n console.error('[AdMesh] Failed to track click');\n });\n\n // If the children contain a link, let the browser handle navigation\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 }\n // If there's a link, let the browser handle it naturally\n }, [adId, 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 { 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 console.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 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 // Inject styles automatically\n useAdMeshStyles();\n\n\n\n\n // Get content based on variation\n const getVariationContent = () => {\n if (variation === 'simple') {\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.product_summary || recommendation.weave_summary || recommendation.reason,\n ctaText: recommendation.product_title || recommendation.title,\n isSimple: true\n };\n\n } else {\n // Default variation\n return {\n title: recommendation.product_title || recommendation.title,\n description: recommendation.product_summary || recommendation.weave_summary || recommendation.reason,\n ctaText: recommendation.product_title || recommendation.title\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 adId = recommendation.ad_id || '';\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_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 adId = recommendation.ad_id || '';\n const productId = recommendation.product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n adId={adId}\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.product_logo?.url && (\n <img\n src={recommendation.product_logo.url}\n alt={`${recommendation.product_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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_url}\n productId={recommendation.product_id}\n trackingData={{\n title: recommendation.product_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 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\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 type { AdMeshRecommendation, AdMeshTheme } from '../types/index';\nimport { AdMeshSummaryUnit } from './AdMeshSummaryUnit';\nimport { AdMeshProductCard } from './AdMeshProductCard';\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\n // Exposure tracking\n sessionId?: string;\n\n // Legacy props for backward compatibility (deprecated)\n response?: {\n layout_type?: string;\n citation_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 sessionId,\n response // Legacy prop for backward compatibility\n}) => {\n // Support both new and legacy props\n const recs = recommendations || response?.recommendations || [];\n const summary = summaryText || response?.citation_summary;\n\n // If recommendations array is empty (e.g., geo-restricted), silently return null\n if (!recs || recs.length === 0) {\n console.log('[AdMesh Summary Layout] Empty recommendations array - not rendering anything');\n return null;\n }\n\n console.log(`[AdMesh Summary Layout] Rendering with ${recs.length} recommendations`);\n\n // Render based on layout type (default to citation)\n const renderContent = () => {\n // Show summary if available\n if (summary) {\n return (\n <AdMeshSummaryUnit\n summaryText={summary}\n recommendations={recs}\n theme={theme}\n onLinkClick={onLinkClick}\n sessionId={sessionId}\n />\n );\n }\n // Fallback to first recommendation if no summary\n return (\n <div className=\"fallback-citation\">\n <AdMeshProductCard\n recommendation={recs[0]}\n theme={theme}\n sessionId={sessionId}\n />\n </div>\n );\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 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\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?.citation_summary;\n\n // Validate that recommendations are provided\n if (!recs || recs.length === 0) {\n console.log('[AdMeshLayout] Empty recommendations array - not rendering anything');\n return null;\n }\n\n return (\n <AdMeshSummaryLayout\n recommendations={recs}\n summaryText={summary}\n theme={theme}\n className={className}\n style={style}\n onLinkClick={onLinkClick}\n sessionId={sessionId}\n />\n );\n};\n\nexport default AdMeshLayout;\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\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 adId - The ad ID for deduplication\n * @param sessionId - The session ID for deduplication\n */\n fireExposure(exposureUrl: string, adId: string, sessionId: string): void {\n const key = `${sessionId}_${adId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n console.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 console.warn('[Tracker] Failed to fire exposure');\n }\n });\n\n if (this.debug) {\n console.log('[Tracker] Fired MRC-compliant exposure');\n }\n } catch (error) {\n if (this.debug) {\n console.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 adId - The ad 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 adId: string,\n sessionId: string,\n element: HTMLElement\n ): Promise<void> {\n const key = `${sessionId}_${adId}`;\n\n // Prevent duplicate exposures\n if (this.firedExposures.has(key)) {\n if (this.debug) {\n console.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 console.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, adId, 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 console.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 * AdMesh Renderer\n * \n * Handles rendering of recommendations in the specified container\n */\n\nimport ReactDOM from 'react-dom/client';\nimport type { AgentRecommendationResponse, AdMeshTheme } from '../types/index';\nimport { AdMeshLayout } from '../components/AdMeshLayout';\nimport { AdMeshTracker } from './AdMeshTracker';\n\nexport interface RenderOptions {\n containerId: string;\n response: AgentRecommendationResponse;\n theme?: AdMeshTheme;\n tracker: AdMeshTracker;\n sessionId: string;\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 console.log('[AdMeshRenderer] 🎨 Attempting to render recommendations');\n\n const container = document.getElementById(options.containerId);\n\n if (!container) {\n console.error('[AdMeshRenderer] ❌ Container not found');\n throw new Error(`Container with ID \"${options.containerId}\" not found`);\n }\n\n console.log('[AdMeshRenderer] ✅ Container ready');\n\n // Clean up existing root if any\n const existingRoot = this.roots.get(options.containerId);\n if (existingRoot) {\n console.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 citation_summary from first recommendation if available\n const citationSummary = options.response.recommendations?.[0]?.citation_summary || '';\n\n console.log('[AdMeshRenderer] 📊 Rendering recommendations');\n\n root.render(\n <AdMeshLayout\n recommendations={options.response.recommendations || []}\n summaryText={citationSummary}\n theme={options.theme}\n sessionId={options.sessionId}\n />\n );\n\n console.log('[AdMeshRenderer] ✅ Recommendations rendered successfully');\n\n // Store root for cleanup\n this.roots.set(options.containerId, root);\n } catch (error) {\n console.error('[AdMeshRenderer] ❌ Error rendering recommendations');\n throw error;\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 }\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 * 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\nexport interface DetectedLink {\n element: HTMLAnchorElement;\n href: string;\n text: string;\n hasAdLabel: boolean;\n matchedRecommendation?: {\n ad_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 adId?: 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 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 // Only process links that match AdMesh recommendation click URLs\n // Ignore all other links (external, documentation, etc.)\n const recommendation = clickUrlMap.get(href);\n if (recommendation) {\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: {\n ad_id: recommendation.ad_id || recommendation.meta?.ad_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 this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\n }\n\n // Fire exposure pixel\n if (this.fireExposurePixels && recommendation.exposure_url && onExposurePixel && detectedLink.matchedRecommendation) {\n onExposurePixel({\n exposureUrl: recommendation.exposure_url,\n adId: detectedLink.matchedRecommendation.ad_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 this.addAdLabel(link);\n detectedLink.hasAdLabel = true;\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 adId: this.extractAdIdFromUrl(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 *\n * This prevents duplicate label rendering when the LLM response\n * already contains [Ad] labels in either position.\n */\n private hasAdLabel(link: HTMLAnchorElement): boolean {\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 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 if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\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 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 if ((element.tagName === 'SUB' || element.tagName === 'SPAN') &&\n element.textContent?.includes('[Ad]')) {\n return true;\n }\n }\n\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 \n // Double-check that [Ad] label doesn't already exist to prevent duplicates\n if (this.hasAdLabel(link)) {\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 link.parentNode?.insertBefore(subLabel, link.nextSibling);\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 ad ID from AdMesh click URL\n *\n * URL format: https://api.useadmesh.com/click/{ad_id}?...\n * Returns the ad_id portion or the full URL if extraction fails\n */\n private extractAdIdFromUrl(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 full URL\n return url;\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 console.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","/**\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 } from '../types/index';\nimport { AdMeshRenderer } from './AdMeshRenderer';\nimport { AdMeshTracker } from './AdMeshTracker';\nimport { WeaveResponseProcessor } from './WeaveResponseProcessor';\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 format?: 'auto' | 'product'| 'citation' | 'ecommerce' | 'weave';\n previousQuery?: string;\n previousSummary?: string;\n isFallbackAllowed?: boolean;\n theme?: AdMeshTheme;\n\n // Session and message tracking (provided by the developer's platform)\n session_id?: string;\n message_id?: string;\n\n // Weave format specific options\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Container ID for LLM response (for Weave format).\n * This option is deprecated. For Weave Ad Format, wrap your LLM response\n * with WeaveAdFormatContainer component instead of using this option.\n *\n * @example\n * ```tsx\n * // ❌ OLD (Deprecated)\n * await sdk.showRecommendations({\n * format: 'weave',\n * llmOutputContainerId: 'llm-output-123'\n * });\n *\n * // ✅ NEW (Recommended)\n * <WeaveAdFormatContainer messageId={msg.id}>\n * {msg.content}\n * </WeaveAdFormatContainer>\n * ```\n */\n llmOutputContainerId?: string;\n\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Timeout for consumption ACK polling (default: 900ms).\n * This option is deprecated. Use WeaveAdFormatContainer component instead.\n */\n timeoutMs?: number;\n\n /**\n * @deprecated Use WeaveAdFormatContainer component instead.\n * Fallback format if not consumed (default: 'citation').\n * This option is deprecated. Use WeaveAdFormatContainer component instead.\n */\n fallbackFormat?: 'product' | 'citation';\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 and message_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 IDs on your platform\n * const sessionId = AdMeshSDK.createSession();\n * const messageId = AdMeshSDK.createMessageId(sessionId);\n *\n * await admesh.showRecommendations({\n * query: 'best CRM for small business',\n * containerId: 'admesh-recommendations',\n * session_id: sessionId,\n * message_id: messageId\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 private weaveProcessor: WeaveResponseProcessor | 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 * Generate a unique session ID for tracking recommendations\n * Call this on your platform to create a new session\n *\n * @returns A unique session ID\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 * Generate a unique message ID for a session\n * Call this on your platform to create a message ID for tracking\n *\n * @param sessionId The session ID this message belongs to\n * @returns A unique message ID\n */\n static createMessageId(sessionId: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 15);\n return `msg_${sessionId}_${timestamp}_${random}`;\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 * OPTIMIZATION: Lazy initialize weave processor on first use\n */\n private getWeaveProcessor(): WeaveResponseProcessor {\n if (!this.weaveProcessor) {\n this.weaveProcessor = new WeaveResponseProcessor({\n autoAddLabels: true,\n fireExposurePixels: true\n });\n }\n return this.weaveProcessor;\n }\n\n\n\n /**\n * Fetch and render recommendations automatically\n *\n * Note: session_id and message_id should be provided by the developer's platform.\n * Use AdMeshSDK.createSession() and AdMeshSDK.createMessageId() to generate IDs.\n */\n async showRecommendations(options: ShowRecommendationsOptions): Promise<void> {\n try {\n // Fetch recommendations directly from API\n const response = await this.fetchRecommendationsFromAPI({\n query: options.query,\n format: options.format || 'auto',\n previousQuery: options.previousQuery,\n previousSummary: options.previousSummary,\n sessionId: options.session_id,\n messageId: options.message_id,\n isFallbackAllowed: options.isFallbackAllowed !== false\n });\n\n // Handle Weave format with consumption ACK\n if (options.format === 'weave') {\n await this.handleWeaveFormat(options, response);\n } else {\n // Standard rendering for non-Weave formats\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n response,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: options.session_id || ''\n });\n\n // Note: MRC-compliant exposure tracking is now handled by AdMeshViewabilityTracker\n // component wrapping each ad. The tracker.fireExposure() method is called by the\n // viewability tracker when ads meet the MRC threshold (50% visible for 1 second).\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 //\n // Legacy immediate exposure firing has been removed to prevent:\n // - Non-MRC compliant exposures\n // - Duplicate exposure tracking\n // - Incorrect CPX billing\n //\n // All components (AdMeshProductCard, AdMeshSummaryLayout, etc.) now use\n // AdMeshViewabilityTracker which handles exposure pixel firing automatically.\n }\n } catch (error) {\n console.error('[AdMeshSDK] Error showing recommendations');\n throw error;\n }\n }\n\n\n\n /**\n * Handle Weave format: scan LLM output for AdMesh links, then render fallback if needed\n *\n * @deprecated Use WeaveAdFormatContainer component instead.\n * This method is deprecated. For Weave Ad Format, wrap your LLM response with\n * WeaveAdFormatContainer component instead of using format: 'weave' with showRecommendations().\n *\n * Flow:\n * 1. Scan LLM output container for AdMesh links (admesh.link or useadmesh.com URLs)\n * 2. If links found: recommendations were consumed by LLM - no fallback needed\n * 3. If no links found after timeout: render fallback UI with specified format\n *\n * This replaces the Pub/Sub consumption ACK approach with a simpler DB-backed approach\n * where the backend's /agent/recommend endpoint handles caching and generation.\n *\n * CRITICAL: This method MUST return early when links are found to prevent rendering\n * both woven ads and fallback UI simultaneously.\n *\n * @example\n * ```tsx\n * // ❌ OLD (Deprecated)\n * await sdk.showRecommendations({\n * format: 'weave',\n * llmOutputContainerId: 'llm-output-123'\n * });\n *\n * // ✅ NEW (Recommended)\n * <WeaveAdFormatContainer messageId={msg.id}>\n * {msg.content}\n * </WeaveAdFormatContainer>\n * ```\n */\n private async handleWeaveFormat(\n options: ShowRecommendationsOptions,\n response: AgentRecommendationResponse\n ): Promise<void> {\n const sessionId = options.session_id || '';\n const messageId = options.message_id || '';\n const timeoutMs = options.timeoutMs || 900;\n const fallbackFormat = options.fallbackFormat || 'citation';\n\n // Cache recommendations for fallback\n const cacheKey = `${sessionId}:${messageId}`;\n this.weaveCache.set(cacheKey, response.recommendations || []);\n\n // Scan LLM output for AdMesh links to detect if recommendations were consumed\n if (options.llmOutputContainerId) {\n try {\n const linksFound = await this.scanLLMOutputForAdMeshLinks(\n options.llmOutputContainerId,\n response.recommendations || [],\n sessionId,\n timeoutMs\n );\n\n // If AdMesh links were found in LLM output, recommendations were consumed\n // No fallback UI needed - the LLM already integrated the recommendations\n // CRITICAL: Return immediately to prevent rendering fallback UI\n if (linksFound) {\n console.log('[AdMeshSDK] Weave format: AdMesh links detected in LLM output. Skipping fallback UI.');\n return;\n }\n } catch (error) {\n console.error('[AdMeshSDK] Error scanning LLM output for AdMesh links');\n // Continue to fallback rendering on error\n }\n }\n\n // No AdMesh links found in LLM output - render fallback UI\n console.log(`[AdMeshSDK] Weave format: No AdMesh links detected. Rendering fallback UI with format: ${fallbackFormat}`);\n\n const fallbackResponse = response;\n\n const renderer = this.getRenderer();\n const tracker = this.getTracker();\n\n await renderer.render({\n containerId: options.containerId,\n response: fallbackResponse,\n theme: options.theme || this.config.theme,\n tracker: tracker,\n sessionId: sessionId\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 //\n // Legacy immediate exposure firing has been removed to prevent:\n // - Non-MRC compliant exposures\n // - Duplicate exposure tracking\n // - Incorrect CPX billing\n //\n // All components (AdMeshProductCard, AdMeshSummaryLayout, etc.) now use\n // AdMeshViewabilityTracker which handles exposure pixel firing automatically.\n }\n\n /**\n * Scan LLM output container for AdMesh links to detect if recommendations were consumed\n *\n * This method polls the LLM output container for AdMesh links (admesh.link or useadmesh.com URLs)\n * to determine if the LLM integrated the recommendations into its response.\n *\n * Uses a combination of polling and MutationObserver for reliable detection:\n * 1. Sets up MutationObserver to watch for new links in real-time\n * 2. Polls container at regular intervals to catch links that appear\n * 3. Returns immediately when links are found\n * 4. Cleans up observer after timeout or when links found\n *\n * @param llmOutputContainerId - ID of the container with LLM output\n * @param recommendations - Array of recommendations to match against\n * @param sessionId - Session ID for tracking\n * @param timeoutMs - How long to wait for links to appear (default: 900ms)\n * @returns true if AdMesh links were found and labeled, false otherwise\n */\n private async scanLLMOutputForAdMeshLinks(\n llmOutputContainerId: string,\n recommendations: AdMeshRecommendation[],\n sessionId: string,\n timeoutMs: number\n ): Promise<boolean> {\n const container = document.getElementById(llmOutputContainerId);\n if (!container) {\n console.warn('[AdMeshSDK] LLM output container not found');\n return false;\n }\n\n const startTime = Date.now();\n const pollIntervalMs = 100;\n let linksFoundFlag = false;\n let observer: MutationObserver | null = null;\n\n try {\n // Create a promise that resolves when links are found\n const linksFoundPromise = new Promise<boolean>((resolve) => {\n const mutationObserver = new MutationObserver(() => {\n const linksFound = this.scanAndLabelAds(\n llmOutputContainerId,\n recommendations,\n sessionId\n );\n\n if (linksFound && !linksFoundFlag) {\n linksFoundFlag = true;\n console.log('[AdMeshSDK] AdMesh links detected via MutationObserver');\n resolve(true);\n }\n });\n\n mutationObserver.observe(container, {\n childList: true,\n subtree: true,\n characterData: false\n });\n\n observer = mutationObserver;\n });\n\n // Polling loop with timeout\n const pollingPromise = (async () => {\n while (Date.now() - startTime < timeoutMs) {\n // Scan for AdMesh links in the LLM output\n const linksFound = this.scanAndLabelAds(\n llmOutputContainerId,\n recommendations,\n sessionId\n );\n\n if (linksFound && !linksFoundFlag) {\n linksFoundFlag = true;\n console.log('[AdMeshSDK] AdMesh links detected via polling');\n return true;\n }\n\n // Wait before next scan\n await this.sleep(pollIntervalMs);\n }\n\n return false;\n })();\n\n // Race between MutationObserver and polling timeout\n const result = await Promise.race([\n linksFoundPromise,\n pollingPromise\n ]);\n\n return result;\n } finally {\n // Clean up MutationObserver\n if (observer) {\n (observer as MutationObserver).disconnect();\n }\n }\n }\n\n /**\n * Scan LLM output container and label AdMesh recommendation links\n * Now uses WeaveResponseProcessor for automatic detection and enhancement\n *\n * IMPORTANT: This method only scans for existing links and does NOT set up\n * the MutationObserver. The observer is managed by scanLLMOutputForAdMeshLinks\n * to avoid creating multiple observers during polling.\n *\n * @returns true if AdMesh links were found and labeled, false otherwise\n */\n private scanAndLabelAds(\n llmOutputContainerId: string,\n recommendations: AdMeshRecommendation[],\n sessionId: string\n ): boolean {\n const container = document.getElementById(llmOutputContainerId);\n if (!container) {\n return false;\n }\n\n const weaveProcessor = this.getWeaveProcessor();\n const tracker = this.getTracker();\n\n // Use WeaveResponseProcessor to detect and enhance links\n // This only scans for existing links, does not set up observer\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n recommendations,\n ({ exposureUrl, adId, linkElement }) => {\n if (!exposureUrl || !adId || !linkElement || !sessionId) {\n return;\n }\n\n tracker.fireExposureWithMRCCompliance(\n exposureUrl,\n adId,\n sessionId,\n linkElement\n );\n }\n );\n\n // Return true if any AdMesh links were found\n return detectedLinks.length > 0;\n }\n\n /**\n * Fetch recommendations directly from the /agent/recommend endpoint\n */\n private async fetchRecommendationsFromAPI(params: {\n query: string;\n format?: string;\n previousQuery?: string;\n previousSummary?: string;\n sessionId?: string;\n messageId?: string;\n isFallbackAllowed?: boolean;\n }): Promise<AgentRecommendationResponse> {\n const url = `${this.apiBaseUrl}/agent/recommend`;\n\n console.log('[AdMeshSDK] 📥 fetchRecommendationsFromAPI called');\n\n // Build payload with direct property assignment\n const payload: Record<string, string | boolean> = {\n query: params.query,\n format: params.format || 'auto',\n source: 'admesh_ui_sdk' // Identify requests from the UI SDK\n };\n\n // Only add optional fields if they are provided\n if (params.previousQuery) {\n payload.previous_query = params.previousQuery;\n }\n if (params.previousSummary) {\n payload.previous_summary = params.previousSummary;\n }\n if (params.sessionId) {\n payload.session_id = params.sessionId;\n }\n if (params.messageId) {\n payload.message_id = params.messageId;\n }\n if (params.isFallbackAllowed !== undefined) {\n payload.is_fallback_allowed = params.isFallbackAllowed;\n }\n\n // Validate query before sending\n const queryStr = typeof payload.query === 'string' ? payload.query : '';\n if (!queryStr || !queryStr.trim()) {\n console.warn('[AdMeshSDK] ⚠️ Warning: Sending request with empty query');\n console.log('[AdMeshSDK] Full payload generated');\n }\n\n const jsonBody = JSON.stringify(payload);\n console.log('[AdMeshSDK] 📤 Sending JSON body');\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 recommendations: ${errorMessage}`);\n }\n\n const data: AgentRecommendationResponse = await response.json();\n return data;\n }\n\n /**\n * Sleep utility\n */\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n // Cache for Weave recommendations\n private weaveCache: Map<string, AdMeshRecommendation[]> = new Map();\n}\n\nexport default AdMeshSDK;\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 // 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","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\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 /** 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 children,\n}) => {\n const sdkRef = useRef<AdMeshSDK | null>(null);\n const [processedMessageIds, setProcessedMessageIds] = useState<Set<string>>(\n new Set()\n );\n\n // Initialize SDK once on mount\n useEffect(() => {\n if (!apiKey) {\n console.warn('[AdMeshProvider] ⚠️ AdMesh API key not configured');\n return;\n }\n\n try {\n sdkRef.current = new AdMeshSDK({\n apiKey,\n theme,\n apiBaseUrl,\n });\n console.log('[AdMeshProvider] ✅ AdMesh SDK initialized');\n if (apiBaseUrl) {\n console.log('[AdMeshProvider] 📍 Using custom API base URL');\n }\n } catch (error) {\n console.error('[AdMeshProvider] ❌ Failed to initialize AdMesh SDK');\n }\n\n // Cleanup on unmount\n return () => {\n console.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 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","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 /** 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, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport type { ShowRecommendationsOptions } from '../sdk/AdMeshSDK';\n\nexport interface AdMeshRecommendationsProps {\n /** Recommendation format\n *\n * - 'product': Display as product cards\n * - 'citation': Display as citation format (default)\n *\n * Note: For Weave Ad Format (where AdMesh links are embedded in LLM response),\n * use WeaveAdFormatContainer component instead. This component is for displaying\n * recommendations as a separate UI component.\n */\n format?: 'product' | 'citation';\n\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/**\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 * format=\"citation\"\n * />\n * )}\n * </div>\n * ))}\n * </AdMeshProvider>\n * ```\n *\n * @example\n * ```tsx\n * // Product card format recommendations\n * <AdMeshProvider apiKey={apiKey} sessionId={sessionId}>\n * <Chat messages={messages} />\n * <AdMeshRecommendations\n * messageId={lastMessageId}\n * query=\"best CRM for small business\"\n * format=\"product\"\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const AdMeshRecommendations: React.FC<AdMeshRecommendationsProps> = ({\n format = 'citation',\n onRecommendationsShown,\n onError,\n messageId,\n query,\n}) => {\n const { sdk, sessionId } = 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-recommendations-${messageId}`);\n }\n }, [messageId]);\n\n useEffect(() => {\n // Log what we're receiving\n console.log('[AdMeshRecommendations] 🔄 Checking SDK readiness');\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n console.log('[AdMeshRecommendations] ❌ Validation failed - missing required parameters');\n return;\n }\n\n console.log('[AdMeshRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n console.log('[AdMeshRecommendations] SDK or containerId not ready');\n return;\n }\n\n // Fetch recommendations\n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n console.log('[AdMeshRecommendations] SDK showRecommendations not available');\n return;\n }\n\n console.log('[AdMeshRecommendations] 📤 Requesting recommendations from SDK');\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n } as ShowRecommendationsOptions);\n\n onRecommendationsShown?.(messageId);\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n console.log(`[AdMeshRecommendations] ❌ Error: ${err.message}`);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, containerId, format, messageId, query, onRecommendationsShown, onError]);\n\n // Render the container where recommendations will be displayed\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-recommendations-container\"\n style={{\n marginTop: '1rem',\n minHeight: '100px',\n display: 'block',\n }}\n />\n );\n};\n\nexport default AdMeshRecommendations;\n","'use client';\n\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useAdMesh } from '../hooks/useAdMesh';\nimport type { ShowRecommendationsOptions } from '../sdk/AdMeshSDK';\n\nexport interface WeaveFallbackRecommendationsProps {\n /** Recommendation format for fallback display\n *\n * - 'product': Display as product cards\n * - 'citation': Display as citation format (default)\n */\n format?: 'product' | 'citation';\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/**\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 * - Call SDK's showRecommendations() to fetch fresh recommendations\n * - Display recommendations in the specified format (citation or product)\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=\"citation\"\n * fallback={fallback}\n * />\n * </AdMeshProvider>\n * ```\n */\nexport const WeaveFallbackRecommendations: React.FC<WeaveFallbackRecommendationsProps> = ({\n format = 'citation',\n onError,\n messageId,\n query,\n fallback,\n}) => {\n const { sdk, sessionId } = 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 console.log('[WeaveFallbackRecommendations] 🎨 Component render');\n\n useEffect(() => {\n // Log what we're receiving\n console.log('[WeaveFallbackRecommendations] 🔄 useEffect triggered');\n\n // Skip if fallback is not true\n if (!fallback) {\n console.log('[WeaveFallbackRecommendations] ⏭️ Skipping - fallback is FALSE, not rendering recommendations');\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] ✅ fallback is TRUE, proceeding with recommendations');\n\n // Validate required parameters\n if (!messageId || !query || query.trim() === '') {\n console.log('[WeaveFallbackRecommendations] ❌ Validation failed - returning early');\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] ✅ Validation passed');\n\n if (!sdk || !containerId) {\n console.log('[WeaveFallbackRecommendations] SDK or containerId not ready');\n return;\n }\n\n // Fetch recommendations\n const fetchRecommendations = async () => {\n try {\n if (!sdk?.showRecommendations) {\n console.log('[WeaveFallbackRecommendations] SDK showRecommendations not available');\n return;\n }\n\n console.log('[WeaveFallbackRecommendations] 📤 Calling sdk.showRecommendations');\n\n await sdk.showRecommendations({\n query: query.trim(),\n containerId,\n format,\n session_id: sessionId,\n message_id: messageId,\n } as ShowRecommendationsOptions);\n\n console.log('[WeaveFallbackRecommendations] ✅ Recommendations displayed successfully');\n } catch (error) {\n const err = error instanceof Error ? error : new Error(String(error));\n console.log(`[WeaveFallbackRecommendations] ❌ Error: ${err.message}`);\n onError?.(err);\n }\n };\n\n fetchRecommendations();\n }, [sdk, sessionId, containerId, format, messageId, query, fallback, onError]);\n\n // Render the container where fallback recommendations will be displayed\n return (\n <div\n ref={containerRef}\n id={containerId}\n className=\"admesh-weave-fallback-recommendations-container\"\n style={{\n marginTop: '1rem',\n minHeight: '100px',\n display: 'block',\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 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 console.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\n const link = (item as any).click_url || (item as any).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).product_id || (item as any).id || (item as any).ad_id;\n const adId = (item as any).ad_id || (item as any).product_id || '';\n const productId = (item as any).product_id || '';\n\n return (\n <AdMeshViewabilityTracker\n key={itemId}\n adId={adId}\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.product_logo?.url ? (\n <img\n src={item.product_logo.url}\n alt={item.product_title}\n className=\"h-full w-full object-cover transition-transform duration-200 hover:scale-105\"\n loading=\"lazy\"\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.product_title}\n </h4>\n\n {/* Summary */}\n {item.weave_summary && (\n <p className=\"text-xs text-gray-600 dark:text-gray-400 line-clamp-2 mb-2\">\n {item.weave_summary}\n </p>\n )}\n\n {/* Categories */}\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 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 // 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 adId={recommendation.ad_id || ''}\n admeshLink={recommendation.click_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 {/* 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\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 console.log('[StreamingEvents] 📢 Dispatched streamingStart event');\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 console.log('[StreamingEvents] 📢 Dispatched streamingComplete event');\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 console.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 const handler = (event: Event) => {\n const customEvent = event as CustomEvent<StreamingCompleteEventDetail>;\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 console.log('[StreamingEvents] 📨 Received streamingComplete event');\n callback(customEvent.detail);\n }\n };\n\n window.addEventListener(STREAMING_COMPLETE_EVENT, handler);\n\n // Return cleanup function\n return () => {\n window.removeEventListener(STREAMING_COMPLETE_EVENT, handler);\n };\n}\n","interface InlineExposureTrackerState {\n observer: IntersectionObserver;\n timeoutId: ReturnType<typeof setTimeout> | null;\n viewableStart: number | null;\n}\n\nexport interface InlineExposureTrackingParams {\n exposureUrl?: string;\n adId?: 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 adId,\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 console.warn(`${logPrefix} ⚠️ Missing exposure tracking data`);\n }\n return;\n }\n\n const dedupeKey = `${sessionId || 'anonymous'}::${adId || 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 console.log(`${logPrefix} ✅ Exposure pixel fired`);\n }\n })\n .catch(() => {\n if (logPrefix) {\n console.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 { useAdMesh } from '../hooks/useAdMesh';\nimport { WeaveFallbackRecommendations } from './WeaveFallbackRecommendations';\nimport { onStreamingComplete } from '../utils/streamingEvents';\nimport {\n createInlineExposureTracker,\n type InlineExposureTrackingParams\n} from '../utils/inlineExposureTracker';\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 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 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\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 }, [onLinksFound, onNoLinksFound, containerId, sdk, sessionId]);\n\n useEffect(() => {\n return () => {\n exposureTrackerRef.current.cleanup();\n };\n }, []);\n\n const trackExposurePixel = useCallback(\n ({ exposureUrl, adId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n adId,\n linkElement,\n sessionId: sessionIdRef.current,\n logPrefix: '[FinalLinkDetectionCheck]'\n });\n },\n []\n );\n\n const performFinalCheck = useCallback(() => {\n console.log('[FinalLinkDetectionCheck] 🔍 Performing final link detection...');\n\n try {\n const container = document.getElementById(containerIdRef.current);\n if (!container) {\n console.warn('[FinalLinkDetectionCheck] ❌ Container not found');\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Get the WeaveResponseProcessor from SDK\n const weaveProcessor = sdkRef.current?.getWeaveProcessor?.();\n if (!weaveProcessor) {\n console.warn('[FinalLinkDetectionCheck] ❌ WeaveProcessor not available');\n setCheckComplete(true);\n onNoLinksFound();\n return;\n }\n\n // Scan for AdMesh links one final time\n const detectedLinks = weaveProcessor.scanAndProcessLinks(\n container,\n [], // Recommendations will be fetched from SDK cache\n ({ exposureUrl, adId, linkElement }: InlineExposureTrackingParams) =>\n trackExposurePixel({ exposureUrl, adId, linkElement })\n );\n\n console.log(`[FinalLinkDetectionCheck] 📊 Final check result: ${detectedLinks.length} links`);\n\n if (detectedLinks.length > 0) {\n // Links found! Cancel fallback\n console.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 console.log('[FinalLinkDetectionCheck] ⚠️ No links found - rendering fallback (will make API call)');\n setLinksFound(false);\n onNoLinksFoundRef.current();\n }\n\n setCheckComplete(true);\n } catch (error) {\n console.error('[FinalLinkDetectionCheck] ❌ Error during final check');\n setCheckComplete(true);\n onNoLinksFoundRef.current();\n }\n }, [trackExposurePixel]);\n\n useEffect(() => {\n console.log('[FinalLinkDetectionCheck] ⏳ Setting up listener');\n\n // Listen for the streamingComplete event from ChatWindow\n const cleanup = onStreamingComplete(messageId, sessionId, (detail) => {\n console.log('[FinalLinkDetectionCheck] 🎯 Received streamingComplete event');\n setWaitingForStreamEnd(false);\n\n // Small delay to ensure DOM is fully updated\n setTimeout(() => {\n performFinalCheck();\n }, 100);\n });\n\n return () => {\n console.log('[FinalLinkDetectionCheck] 🧹 Cleaning up listener');\n cleanup();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [messageId, sessionId]); // performFinalCheck is stable (no dependencies), so we don't include it\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 console.log('[FinalLinkDetectionCheck] 🚫 Links found - NOT rendering fallback');\n return <></>;\n }\n\n console.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: 'citation') */\n fallbackFormat?: 'product' | 'citation';\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\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 = 'citation',\n onLinksDetected,\n onNoLinksDetected,\n onError,\n className,\n query,\n onFallbackChange\n}) => {\n const { sessionId, sdk } = useAdMesh();\n const containerRef = useRef<HTMLDivElement>(null);\n const containerId = `weave-ad-container-${messageId}`;\n\n\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 */}\n {query && (\n <FinalLinkDetectionCheck\n containerId={containerId}\n messageId={messageId}\n sessionId={sessionId}\n sdk={sdk}\n onLinksFound={(count) => {\n console.log(`[WeaveAdFormatContainer] ✅ Links found (${count}) - no fallback needed`);\n onLinksDetected?.(count);\n onFallbackChange?.(false);\n }}\n onNoLinksFound={() => {\n console.log(`[WeaveAdFormatContainer] ⚠️ No links found - rendering fallback`);\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 />\n </FinalLinkDetectionCheck>\n )}\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: 'citation') */\n fallbackFormat?: 'product' | 'citation';\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: 'citation'\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, adId, linkElement }: InlineExposureTrackingParams) => {\n exposureTrackerRef.current.startTracking({\n exposureUrl,\n adId,\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, adId, linkElement }: InlineExposureTrackingParams) =>\n trackInlineExposure({ exposureUrl, adId, 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';\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 { AdMeshSummaryUnit } from './components/AdMeshSummaryUnit';\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":["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","generateBatchId","meetsViewabilityThreshold","visibilityPercentage","visibleDuration","standards","formatTimestamp","date","calculateAverage","numbers","acc","num","throttle","func","limit","inThrottle","args","sendAnalyticsEvent","event","apiEndpoint","retryAttempts","retryDelay","lastError","attempt","response","errorText","error","resolve","sendAnalyticsBatch","events","sessionId","batch","DEFAULT_CONFIG","globalConfig","useViewabilityTracker","adId","productId","offerId","agentId","recommendationId","elementRef","customConfig","config","useRef","state","setState","useState","mrcStandards","visibilityStartTime","viewableStartTime","hoverStartTime","focusStartTime","visibilityPercentages","eventBatch","batchTimeout","log","useCallback","message","sendEvent","eventType","additionalData","contextMetrics","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","previousVisible","jsx","isValidUrl","url","processSummaryText","summaryText","recommendations","clickUrlToRecMap","rec","markdownLinkRegex","parts","lastIndex","match","linkCounter","fullMatch","linkText","recommendation","linkUrl","AdMeshSummaryUnit","theme","processedContent","part","index","React","_a","_b","_c","_d","hasOwn","classNames","classes","i","arg","appendClass","parseValue","key","value","newClass","module","DEFAULT_TRACKING_URL","useAdMeshTracker","isTracking","setIsTracking","setError","mergedConfig","useMemo","sendTrackingEvent","data","payload","err","errorMsg","trackClick","trackView","trackConversion","AdMeshLinkTracker","admeshLink","trackingData","hasTrackedView","entry","ADMESH_STYLE_ID","ADMESH_RESET_ID","ADMESH_CSS_RESET","ADMESH_CORE_STYLES","injectAdMeshStyles","resetStyle","coreStyle","ADMESH_STYLES","stylesInjected","useAdMeshStyles","styleElement","getRecommendationLabel","matchScore","customLabels","getLabelTooltip","_label","AdMeshProductCard","variation","content","cardClasses","cardStyle","_e","_f","_g","_h","_i","_j","_l","_k","_m","jsxs","_n","_o","_p","e","AdMeshSummaryLayout","onLinkClick","recs","summary","renderContent","AdMeshLayout","AdMeshTracker","__publicField","timeoutId","cleanupTimeout","threshold","AdMeshRenderer","options","container","existingRoot","root","ReactDOM","citationSummary","containerId","WeaveResponseProcessor","optimizedLinks","onExposurePixel","detectedLinks","links","clickUrlMap","r","link","href","linkKey","detectedLink","prevNode","text","nextNode","subLabel","isTooltipVisible","closeTooltipOnClickOutside","domain","clickUrl","params","AdMeshSDK","timestamp","random","renderer","tracker","messageId","timeoutMs","fallbackFormat","cacheKey","fallbackResponse","llmOutputContainerId","startTime","pollIntervalMs","linksFoundFlag","linksFoundPromise","mutationObserver","pollingPromise","weaveProcessor","linkElement","queryStr","jsonBody","errorMessage","ms","AdMeshContext","useAdMeshContext","context","AdMeshProvider","apiKey","apiBaseUrl","sdkRef","processedMessageIds","setProcessedMessageIds","contextValue","updated","useAdMesh","AdMeshRecommendations","format","onRecommendationsShown","onError","query","sdk","containerRef","setContainerId","WeaveFallbackRecommendations","fallback","WeaveAdFormatContext","createContext","WeaveAdFormatProvider","shouldRenderFallback","useWeaveAdFormatContext","useContext","AdMeshEcommerceCards","title","showTitle","cardClassName","onProductClick","maxCards","cardWidth","borderRadius","shadow","displayItems","getCardWidthClass","getBorderRadiusClass","getShadowClass","getThemeClasses","handleProductClick","item","itemId","cat","idx","Fragment","AdMeshInlineCard","expandable","badgeTypeVariants","badgeTypeIcons","AdMeshBadge","type","variant","size","effectiveVariant","icon","badgeClasses","STREAMING_START_EVENT","STREAMING_COMPLETE_EVENT","dispatchStreamingStartEvent","detail","dispatchStreamingCompleteEvent","metadata","onStreamingStart","callback","handler","customEvent","onStreamingComplete","createInlineExposureTracker","firedKeys","activeTrackers","cleanupTracker","logPrefix","dedupeKey","trackerState","fireExposurePixel","FinalLinkDetectionCheck","onLinksFound","onNoLinksFound","checkComplete","setCheckComplete","waitingForStreamEnd","setWaitingForStreamEnd","linksFound","setLinksFound","exposureTrackerRef","onLinksFoundRef","onNoLinksFoundRef","containerIdRef","sessionIdRef","trackExposurePixel","performFinalCheck","cleanup","WeaveAdFormatContainer","onLinksDetected","onNoLinksDetected","onFallbackChange","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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8pB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","x_google_ignoreList":[4]}