UNPKG

admesh-ui-sdk

Version:

Beautiful, modern React components for displaying AI-powered product recommendations with citation-based conversation ads, auto-triggered widgets, floating chat, conversational interfaces, persistent sidebar, and built-in tracking. Includes zero-code SDK

1,182 lines 166 kB
var ot = Object.defineProperty; var nt = (t, e, n) => e in t ? ot(t, e, { enumerable: !0, configurable: !0, writable: !0, value: n }) : t[e] = n; var oe = (t, e, n) => nt(t, typeof e != "symbol" ? e + "" : e, n); import { jsx as h, jsxs as z, Fragment as ye } from "react/jsx-runtime"; import xe, { useRef as P, useState as ne, useCallback as he, useEffect as H, createContext as at, useContext as it } from "react"; import He from "react-dom/client"; import { createPortal as Ye } from "react-dom"; let _e = !1; var Ge; try { typeof globalThis.importMeta < "u" && ((Ge = globalThis.importMeta.env) != null && Ge.PROD) && (_e = !0); } catch { } _e || (_e = typeof process < "u" && process.env.NODE_ENV === "production" || typeof process < "u" && process.env.ADMESH_ENV === "production"); const o = { log: (...t) => { _e || console.log(...t); }, warn: (...t) => { _e || console.warn(...t); }, error: (...t) => { console.error(...t); }, info: (...t) => { _e || console.info(...t); }, debug: (...t) => { _e || console.debug(...t); } }; function st(t, e, n) { const i = t * e > 242500; return { ...{ visibilityThreshold: i ? 0.3 : 0.5, // 30% for large, 50% for standard minimumDuration: 1e3, // 1 second in milliseconds isLargeAd: i }, ...n }; } function dt(t) { return t < 768 ? "mobile" : t < 1024 ? "tablet" : "desktop"; } function ct(t) { const e = t.getBoundingClientRect(), n = window.innerHeight || document.documentElement.clientHeight, r = window.innerWidth || document.documentElement.clientWidth, i = e.height, a = e.width; if (i === 0 || a === 0) return 0; const s = Math.max(0, e.top), c = Math.min(n, e.bottom), m = Math.max(0, e.left), u = Math.min(r, e.right), d = Math.max(0, c - s), l = Math.max(0, u - m), y = d * l, g = i * a; return g > 0 ? y / g : 0; } function Ve() { const t = window.innerHeight, e = document.documentElement.scrollHeight, n = window.pageYOffset || document.documentElement.scrollTop, r = e - t; return r <= 0 ? 100 : Math.min(100, n / r * 100); } function lt(t) { const e = t.getBoundingClientRect(), n = window.pageYOffset || document.documentElement.scrollTop, r = window.pageXOffset || document.documentElement.scrollLeft; return { top: e.top + n, left: e.left + r }; } function mt(t) { const e = t.getBoundingClientRect(), n = lt(t), r = window.innerWidth || document.documentElement.clientWidth, i = window.innerHeight || document.documentElement.clientHeight, a = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches; return { pageUrl: window.location.href, pageTitle: document.title, referrer: document.referrer, deviceType: dt(r), viewportWidth: r, viewportHeight: i, adWidth: e.width, adHeight: e.height, adPositionTop: n.top, adPositionLeft: n.left, isDarkMode: a, language: navigator.language, timezone: Intl.DateTimeFormat().resolvedOptions().timeZone }; } function ut() { return `session_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`; } function pt(t, e, n) { return t >= n.visibilityThreshold && e >= n.minimumDuration; } function je(t = /* @__PURE__ */ new Date()) { return t.toISOString(); } function ht(t) { return t.length === 0 ? 0 : t.reduce((n, r) => n + r, 0) / t.length; } function Ke(t, e) { let n; return function(...i) { n || (t(...i), n = !0, setTimeout(() => n = !1, e)); }; } const gt = { enabled: !0, // Analytics endpoint disabled - no analytics will be sent apiEndpoint: "", // Empty string disables analytics sending enableBatching: !1, // Disabled since no endpoint batchSize: 10, batchTimeout: 5e3, // 5 seconds debug: !1, enableRetry: !1, maxRetries: 3, retryDelay: 1e3 }; let ft = gt; function bt({ productId: t, offerId: e, agentId: n, recommendationId: r, elementRef: i, config: a }) { const s = { ...ft, ...a }, c = P(ut()), [m, u] = ne({ isVisible: !1, isViewable: !1, visibilityPercentage: 0, timeMetrics: { loadedAt: je(), totalVisibleDuration: 0, totalViewableDuration: 0, totalHoverDuration: 0, totalFocusDuration: 0 }, engagementMetrics: { currentScrollDepth: 0, viewportEnterCount: 0, viewportExitCount: 0, hoverCount: 0, wasClicked: !1, maxVisibilityPercentage: 0, averageVisibilityPercentage: 0 }, isTracking: s.enabled }), d = P(null), l = P(null), y = P(null), g = P(null), f = P(null), _ = P([]), w = P([]), p = P(null), E = he((x) => { s.debug && o.log(`[AdMesh Viewability] ${x}`); }, [s.debug]), b = he(async (x, T) => { if (!(!s.enabled || !i.current || !d.current) && (E(`Analytics disabled - skipping event: ${x}`), s.onEvent)) { const v = mt(i.current), I = { eventType: x, timestamp: je(), sessionId: c.current, productId: t, offerId: e, agentId: n, recommendationId: r, timeMetrics: m.timeMetrics, engagementMetrics: m.engagementMetrics, contextMetrics: v, mrcStandards: d.current, isViewable: m.isViewable, metadata: T }; s.onEvent(I); } }, [s, t, e, n, r, i, m, E]), N = he(async () => { w.current.length !== 0 && (E("Analytics disabled - clearing batch without sending"), w.current = [], p.current && (clearTimeout(p.current), p.current = null)); }, [E]), M = he(Ke(() => { if (!i.current) return; const x = ct(i.current), T = Date.now(), v = new Date(m.timeMetrics.loadedAt).getTime(); u((I) => { const A = { ...I }; x > 0 && _.current.push(x); const F = I.isVisible, S = x > 0; if (S && !F) l.current = T, A.engagementMetrics.viewportEnterCount++, A.timeMetrics.timeToFirstVisible || (A.timeMetrics.timeToFirstVisible = T - v, A.engagementMetrics.scrollDepthAtFirstVisible = Ve(), b("ad_visible")); else if (!S && F) { if (l.current) { const B = T - l.current; A.timeMetrics.totalVisibleDuration += B, l.current = null; } A.engagementMetrics.viewportExitCount++, b("ad_hidden"); } else if (S && F && l.current) { const B = T - l.current; A.timeMetrics.totalVisibleDuration += B, l.current = T; } if (A.isVisible = S, A.visibilityPercentage = x, x > A.engagementMetrics.maxVisibilityPercentage && (A.engagementMetrics.maxVisibilityPercentage = x), _.current.length > 0 && (A.engagementMetrics.averageVisibilityPercentage = ht(_.current)), d.current) { const B = I.isViewable, k = pt( x, A.timeMetrics.totalVisibleDuration, d.current ); if (k && !B) A.isViewable = !0, A.timeMetrics.timeToViewable = T - v, y.current = T, b("ad_viewable"); else if (k && B && y.current) { const D = T - y.current; A.timeMetrics.totalViewableDuration += D, y.current = T; } } return A.engagementMetrics.currentScrollDepth = Ve(), A; }); }, 100), [i, m.timeMetrics.loadedAt, b]); return H(() => { if (!i.current) return; const x = i.current.getBoundingClientRect(); d.current = st(x.width, x.height, s.mrcStandards), E("Initialized MRC standards"), b("ad_loaded"); }, [i, s.mrcStandards, E, b]), H(() => { if (!s.enabled || !i.current) return; const x = new IntersectionObserver( (T) => { T.forEach(() => { M(); }); }, { threshold: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1], rootMargin: "0px" } ); return x.observe(i.current), () => { x.disconnect(); }; }, [s.enabled, i, M]), H(() => { if (!s.enabled) return; const x = Ke(() => { M(); }, 100); return window.addEventListener("scroll", x, { passive: !0 }), () => window.removeEventListener("scroll", x); }, [s.enabled, M]), H(() => { if (!s.enabled || !i.current) return; const x = i.current, T = () => { g.current = Date.now(), u((I) => ({ ...I, engagementMetrics: { ...I.engagementMetrics, hoverCount: I.engagementMetrics.hoverCount + 1 } })), b("ad_hover_start"); }, v = () => { if (g.current) { const I = Date.now() - g.current; u((A) => ({ ...A, timeMetrics: { ...A.timeMetrics, totalHoverDuration: A.timeMetrics.totalHoverDuration + I } })), g.current = null, b("ad_hover_end", { hoverDuration: I }); } }; return x.addEventListener("mouseenter", T), x.addEventListener("mouseleave", v), () => { x.removeEventListener("mouseenter", T), x.removeEventListener("mouseleave", v); }; }, [s.enabled, i, b]), H(() => { if (!s.enabled || !i.current) return; const x = i.current, T = () => { f.current = Date.now(), b("ad_focus"); }, v = () => { if (f.current) { const I = Date.now() - f.current; u((A) => ({ ...A, timeMetrics: { ...A.timeMetrics, totalFocusDuration: A.timeMetrics.totalFocusDuration + I } })), f.current = null, b("ad_blur", { focusDuration: I }); } }; return x.addEventListener("focus", T), x.addEventListener("blur", v), () => { x.removeEventListener("focus", T), x.removeEventListener("blur", v); }; }, [s.enabled, i, b]), H(() => { if (!s.enabled || !i.current) return; const x = i.current, T = () => { u((v) => ({ ...v, engagementMetrics: { ...v.engagementMetrics, wasClicked: !0 } })), b("ad_click"); }; return x.addEventListener("click", T), () => { x.removeEventListener("click", T); }; }, [s.enabled, i, b]), H(() => () => { const x = Date.now(), T = new Date(m.timeMetrics.loadedAt).getTime(), v = x - T; u((I) => ({ ...I, timeMetrics: { ...I.timeMetrics, sessionDuration: v } })), b("ad_unloaded", { sessionDuration: v }), N(); }, []), m; } const Ie = ({ productId: t, offerId: e, agentId: n, recommendationId: r, exposureUrl: i, sessionId: a, children: s, config: c, className: m, style: u, onViewabilityChange: d, onVisible: l, onViewable: y, onClick: g }) => { const f = P(null), _ = P(!1), w = bt({ productId: t, offerId: e, agentId: n, recommendationId: r, elementRef: f, config: c }), p = P(w.isViewable); H(() => { w.isViewable !== p.current && (p.current = w.isViewable, d && d(w.isViewable), w.isViewable && y && y(), w.isViewable && !_.current && (o.log("[AdMeshViewabilityTracker] 🎯 Ad is viewable, checking exposure pixel requirements:", { exposureUrl: i ? "present" : "MISSING", sessionId: a ? "present" : "MISSING", recommendationId: r }), i && a ? (_.current = !0, o.log("[AdMeshViewabilityTracker] 🔥 Firing exposure pixel:", i), fetch(i, { method: "GET", keepalive: !0 }).then(() => { o.log("[AdMesh] ✅ Exposure pixel fired successfully"); }).catch((N) => { o.warn("[AdMesh] ⚠️ Failed to fire exposure pixel:", N), _.current = !1; })) : o.warn("[AdMeshViewabilityTracker] ⚠️ Cannot fire exposure pixel - missing required data:", { hasExposureUrl: !!i, hasSessionId: !!a }))); }, [w.isViewable, d, y, i, a, r]); const E = P(w.isVisible); return H(() => { w.isVisible !== E.current && (E.current = w.isVisible, w.isVisible && l && l()); }, [w.isVisible, l]), /* @__PURE__ */ h( "div", { ref: f, className: m, style: u, onClick: () => { g && g(); }, "data-admesh-viewability-tracker": !0, "data-recommendation-id": r, "data-is-viewable": w.isViewable, "data-is-visible": w.isVisible, "data-visibility-percentage": w.visibilityPercentage.toFixed(2), children: s } ); }; Ie.displayName = "AdMeshViewabilityTracker"; const Ne = xe.createContext( void 0 ); function xt() { const t = xe.useContext(Ne); if (!t) throw new Error( "useAdMeshContext must be used within an <AdMeshProvider>. Make sure your component is wrapped with <AdMeshProvider>." ); return t; } const vt = (t) => { try { return new URL(t), !0; } catch { return !1; } }, wt = (t) => t && t.trim() ? t.trim() : "", Ze = ({ recommendations: t, theme: e, className: n = "", style: r = {}, sessionId: i }) => { var Y, le, ce, me, ie, $, be, ke, ue, ge, Ae; const a = xe.useContext(Ne), s = a == null ? void 0 : a.userId, c = a == null ? void 0 : a.model, m = a == null ? void 0 : a.sessionId, u = (a == null ? void 0 : a.sdk) || null, d = i || m, [l, y] = xe.useState(!1), [g, f] = xe.useState(!1), [_, w] = xe.useState(!1); if (!t || t.length === 0) return o.log("[AdMesh Tail Ad] No recommendations provided - not rendering"), null; if (l) return null; const p = t[0], E = p == null ? void 0 : p.product_id, b = p == null ? void 0 : p.exposure_url, N = (p == null ? void 0 : p.recommendation_id) || "", M = (p == null ? void 0 : p.creative_input) || {}, x = M.short_description || "", T = M.offer_summary || "", v = M.brand_name || (p == null ? void 0 : p.title) || "", I = M.product_name || "", F = (M.assets || {}).logo_url || "", S = (p == null ? void 0 : p.click_url) || (p == null ? void 0 : p.admesh_link) || M.cta_url || (p == null ? void 0 : p.url), B = wt(M.cta_label); if (!v) return o.log("[AdMesh Tail Ad] No valid recommendation data provided - not rendering", { reason: "brandName is missing", brandName: v, hasFirstRecommendation: !!p, hasCreativeInput: !!M, creativeInputBrandName: M.brand_name, recommendationTitle: p == null ? void 0 : p.title, recommendationId: N, recommendationKeys: p ? Object.keys(p) : [] }), null; let k = v, D = ""; T ? D = T : I && (D = I), D && (k = `${v} — ${D}`), o.debug("[AdMeshTailAd] 📊 Rendering with tracking data:", { recommendationId: N, productId: E, exposureUrl: b ? "present" : "MISSING", sessionId: i ? "present" : "MISSING", recommendationsCount: t.length, clickUrl: S || "MISSING", clickUrlSource: p != null && p.click_url ? "click_url" : p != null && p.admesh_link ? "admesh_link" : M.cta_url ? "cta_url" : p != null && p.url ? "url" : "none", shortDescription: x ? "present" : "MISSING", offerSummary: T || "MISSING", brandName: v, productName: I, headlineText: k }); const Z = (L, te) => { o.log(`AdMesh tail ad ${L} clicked`), typeof window < "u" && window.admeshTracker && window.admeshTracker.trackClick({ recommendationId: p.recommendation_id, productId: p.product_id, clickUrl: S, source: L }).catch(() => { o.error(`[AdMesh] Failed to track ${L} click`); }); }, X = (L) => { L.stopPropagation(), Z("tail_ad_brand_name"); }, ee = (L) => { L.stopPropagation(), Z("tail_ad_cta"); }, ae = (L) => { L.stopPropagation(), Z("tail_ad_logo"); }, G = () => u && typeof u.getApiBaseUrl == "function" ? u.getApiBaseUrl() : u && u.apiBaseUrl ? u.apiBaseUrl : typeof window < "u" && window.__ADMESH_API_BASE_URL__ ? window.__ADMESH_API_BASE_URL__ : "https://api.useadmesh.com", se = async (L) => { if (!(g || _)) { w(!0); try { const te = G(), ve = (p == null ? void 0 : p.agent_id) || "", we = { message_index: 0, // Default to 0, can be enhanced later if message_index is available feedback: L, session_id: d || null, user_id: s || null, agent_id: ve || null, model_used: c || null, recommendationId: N || null }, Se = `${te}/user/feedback/submit`; o.log(`[AdMesh Tail Ad] Submitting feedback: ${L}`, { endpoint: Se, payload: we }); const re = await fetch(Se, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(we) }); if (o.log(`[AdMesh Tail Ad] Feedback response status: ${re.status} ${re.statusText}`), !re.ok) { let Te = `Feedback submission failed: ${re.status} ${re.statusText}`; try { const fe = await re.json().catch(() => null); fe != null && fe.detail && (Te = `Feedback submission failed: ${fe.detail}`), o.error("[AdMesh Tail Ad] Error response body:", fe); } catch { } throw new Error(Te); } const Me = await re.json(); o.log(`[AdMesh Tail Ad] ✅ Feedback submitted successfully: ${L}`, Me), f(!0), L === "dislike" && y(!0); } catch (te) { const ve = te instanceof Error ? { message: te.message, name: te.name, stack: te.stack } : String(te); o.error(`[AdMesh Tail Ad] ❌ Failed to submit feedback: ${L}`, { error: ve, apiBaseUrl: G(), endpoint: `${G()}/user/feedback/submit` }), w(!1); } } }, C = (L) => { L.stopPropagation(), L.preventDefault(), se("like"); }, U = (L) => { L.stopPropagation(), L.preventDefault(), se("dislike"); }, [de, pe] = xe.useState(!1), W = v ? v.charAt(0).toUpperCase() : "B", j = (e == null ? void 0 : e.mode) === "dark" || typeof window < "u" && window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches, R = (e == null ? void 0 : e.surfaceColor) || ((le = (Y = e == null ? void 0 : e.components) == null ? void 0 : Y.card) == null ? void 0 : le.backgroundColor) || (j ? "#1f2937" : "#ffffff"), V = (e == null ? void 0 : e.borderColor) || ((me = (ce = e == null ? void 0 : e.components) == null ? void 0 : ce.card) == null ? void 0 : me.borderColor) || (j ? "rgba(255, 255, 255, 0.1)" : "rgba(0, 0, 0, 0.05)"), q = (e == null ? void 0 : e.borderRadius) || (($ = (ie = e == null ? void 0 : e.components) == null ? void 0 : ie.card) == null ? void 0 : $.borderRadius) || "8px", J = j ? "0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2)" : "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)", Q = j ? "0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3)" : "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)", K = ((be = e == null ? void 0 : e.shadows) == null ? void 0 : be.medium) || ((ue = (ke = e == null ? void 0 : e.components) == null ? void 0 : ke.card) == null ? void 0 : ue.boxShadow) || J, O = ((ge = e == null ? void 0 : e.shadows) == null ? void 0 : ge.large) || Q; return /* @__PURE__ */ h( Ie, { productId: E, recommendationId: N, exposureUrl: b, sessionId: i, className: `admesh-tail-ad ${n}`, style: { fontFamily: (e == null ? void 0 : e.fontFamily) || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', ...r }, children: /* @__PURE__ */ z( "div", { className: "tail-ad-container flex flex-row gap-3", style: { backgroundColor: R, borderRadius: q, padding: "16px", boxShadow: K, border: `1px solid ${V}`, transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)", transform: "translateY(0)", ...(Ae = e == null ? void 0 : e.components) == null ? void 0 : Ae.card }, onMouseEnter: (L) => { L.currentTarget.style.boxShadow = O, L.currentTarget.style.transform = "translateY(-2px)"; }, onMouseLeave: (L) => { L.currentTarget.style.boxShadow = K, L.currentTarget.style.transform = "translateY(0)"; }, children: [ F && /* @__PURE__ */ h( "div", { className: "flex-shrink-0 flex items-center justify-center pr-2", style: { width: "10%", minWidth: "48px" }, children: !de && vt(F) ? /* @__PURE__ */ h( "a", { href: S || "#", target: S ? "_blank" : void 0, rel: S ? "noopener noreferrer" : void 0, onClick: S ? ae : void 0, className: "block", style: { cursor: S ? "pointer" : "default", display: "flex", alignItems: "center", justifyContent: "center", width: "100%", maxWidth: "64px" }, children: /* @__PURE__ */ h( "img", { src: F, alt: `${v} logo`, className: "object-cover", style: { width: "100%", height: "auto", maxWidth: "64px", maxHeight: "64px", objectFit: "contain", borderRadius: "8px" }, onError: () => { pe(!0), o.debug("[AdMesh Tail Ad] Logo failed to load, showing fallback"); } } ) } ) : /* @__PURE__ */ h( "div", { className: "flex items-center justify-center text-lg font-semibold text-gray-600 dark:text-gray-300 bg-gray-100 dark:bg-gray-800", style: { width: "100%", maxWidth: "64px", aspectRatio: "1", borderRadius: "8px", minHeight: "48px" }, children: W } ) } ), /* @__PURE__ */ z( "div", { className: "flex-1", style: { width: F ? "90%" : "100%", minWidth: 0 // Allow flex item to shrink below content size }, children: [ k && /* @__PURE__ */ h("div", { className: "mb-2", children: /* @__PURE__ */ h("h3", { className: "text-black dark:text-white font-semibold text-base", style: { fontSize: "0.7rem" }, children: S && v ? /* @__PURE__ */ z(ye, { children: [ /* @__PURE__ */ h( "a", { href: S, target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-semibold", style: { color: "#2563eb", textDecoration: "underline", textDecorationColor: "#2563eb", textUnderlineOffset: "2px" }, onClick: X, children: v } ), D && ` — ${D}` ] }) : k }) }), x && /* @__PURE__ */ h("p", { className: "text-gray-700 dark:text-gray-300 text-sm mb-3 leading-relaxed", style: { fontSize: "0.7rem" }, children: x }), /* @__PURE__ */ z("div", { className: "flex items-center justify-between mt-2", children: [ /* @__PURE__ */ h("div", { className: "flex items-center gap-2", children: B && S && /* @__PURE__ */ h( "a", { href: S, target: "_blank", rel: "noopener noreferrer", className: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 underline decoration-blue-600 dark:decoration-blue-400 hover:decoration-blue-800 dark:hover:decoration-blue-300 transition-colors duration-200 font-medium text-sm", style: { color: "#2563eb", textDecoration: "underline", textDecorationColor: "#2563eb", textUnderlineOffset: "2px" }, onClick: ee, children: B } ) }), /* @__PURE__ */ z("div", { className: "flex items-center gap-2", children: [ /* @__PURE__ */ h( "button", { onClick: C, disabled: g || _, "aria-label": "Mark as helpful", className: "flex items-center justify-center px-2 py-1 rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed", style: { color: g && !l ? "#10b981" : j ? "#9ca3af" : "#6b7280", backgroundColor: "transparent", border: "none", cursor: g || _ ? "not-allowed" : "pointer", fontSize: "0.75rem", fontWeight: "500", minHeight: "24px" }, onMouseEnter: (L) => { !g && !_ && (L.currentTarget.style.color = "#10b981", L.currentTarget.style.backgroundColor = "rgba(16, 185, 129, 0.1)"); }, onMouseLeave: (L) => { g || (L.currentTarget.style.color = g ? "#10b981" : j ? "#9ca3af" : "#6b7280", L.currentTarget.style.backgroundColor = "transparent"); }, children: "Helpful" } ), /* @__PURE__ */ h( "button", { onClick: U, disabled: g || _, "aria-label": "Mark as not helpful", className: "flex items-center justify-center px-2 py-1 rounded transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed", style: { color: l ? "#ef4444" : j ? "#9ca3af" : "#6b7280", backgroundColor: "transparent", border: "none", cursor: g || _ ? "not-allowed" : "pointer", fontSize: "0.75rem", fontWeight: "500", minHeight: "24px" }, onMouseEnter: (L) => { !g && !_ && (L.currentTarget.style.color = "#ef4444", L.currentTarget.style.backgroundColor = "rgba(239, 68, 68, 0.1)"); }, onMouseLeave: (L) => { l || (L.currentTarget.style.color = j ? "#9ca3af" : "#6b7280", L.currentTarget.style.backgroundColor = "transparent"); }, children: "Not Helpful" } ), /* @__PURE__ */ h("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Sponsored" }) ] }) ] }) ] } ) ] } ) } ); }; function Ee() { const t = xt(); return { /** AdMesh SDK instance */ sdk: t.sdk, /** API key */ apiKey: t.apiKey, /** Session ID */ sessionId: t.sessionId, /** Theme configuration */ theme: t.theme, /** User language in BCP 47 format (e.g., "en-US") - from AdMeshProvider */ language: t.language, /** User country code in ISO 3166-1 alpha-2 format (e.g., "US") - from AdMeshProvider */ geo_country: t.geo_country, /** Anonymous hashed user ID - from AdMeshProvider */ userId: t.userId, /** AI model identifier (e.g., "gpt-4o") - from AdMeshProvider */ model: t.model, /** Conversation history - from AdMeshProvider */ messages: t.messages, /** Set of processed message IDs (for deduplication) */ processedMessageIds: t.processedMessageIds, /** Mark a message as processed to prevent duplicate recommendations */ markMessageAsProcessed: t.markMessageAsProcessed, /** Check if a message has already been processed */ isMessageProcessed: t.isMessageProcessed }; } const yt = (t, e) => { if (!t) return "Get Started"; let n = ""; const r = [ /(?:set up|setup|integrate|add|install|use)\s+([A-Z][a-zA-Z0-9\s]+?)(?:\.|$|,| by)/i, /(?:by|from)\s+([A-Z][a-zA-Z0-9\s]+?)(?:\.|$|,)/ ]; for (const s of r) { const c = t.match(s); if (c && c[1]) { n = c[1].trim(); break; } } const i = e || n; if (i) return `Integrate ${i.split(/\s+/).slice(0, 2).join(" ")}`; const a = t.match(/to\s+set\s+up\s+([a-zA-Z0-9\s]+?)(?:\s+by|\s*[.,]|$)/i); return a && a[1] ? `Integrate ${a[1].trim().split(/\s+/).slice(0, 2).join(" ")}` : "Integrate"; }, Xe = ({ recommendation: t, theme: e, className: n = "", style: r = {}, sessionId: i, onLinkClick: a, onPasteToInput: s }) => { var M, x, T, v, I, A, F; const c = t.creative_input || {}, m = c.bridge_headline || "", u = c.bridge_description || "", d = c.bridge_prompt || c.bridge_content || "", l = c.product_name || "", y = c.cta_label || ""; if (!u && !d) return null; const g = t.product_id || "", f = t.recommendation_id || "", { sdk: _, sessionId: w } = Ee(), p = i || w, E = async (S) => { if (S.preventDefault(), !!d) { if (f && p) try { const B = (_ == null ? void 0 : _.apiBaseUrl) || typeof window < "u" && window.__ADMESH_API_BASE_URL__ || "https://api.useadmesh.com", k = await fetch(`${B}/click/bridge-engagement`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ recommendation_id: f, session_id: p, agent_id: t.agent_id, user_id: t.user_id || void 0, is_test: !1 // TODO: Get from config if needed }) }); k.ok ? o.log("[AdMesh Bridge] ✅ Engagement tracked successfully") : o.warn("[AdMesh Bridge] ⚠️ Failed to track engagement:", k.statusText); } catch (B) { o.error("[AdMesh Bridge] ❌ Error tracking engagement:", B); } a && a(t), s ? s(d) : typeof window < "u" && window.__admesh_setMessage && window.__admesh_setMessage(d); } }, b = y || yt(d, l), N = !!b; return /* @__PURE__ */ h( Ie, { productId: g, recommendationId: t.recommendation_id || "", exposureUrl: t.exposure_url, sessionId: i, className: `admesh-bridge-format ${n}`, style: { fontFamily: (e == null ? void 0 : e.fontFamily) || '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', padding: "1rem", border: "none", borderRadius: (e == null ? void 0 : e.borderRadius) || "0.5rem", backgroundColor: "transparent", color: (e == null ? void 0 : e.textColor) || "inherit", ...r }, children: /* @__PURE__ */ z("div", { "data-admesh-theme": (e == null ? void 0 : e.mode) || "light", children: [ m && /* @__PURE__ */ h( "div", { className: "admesh-bridge-headline", style: { marginBottom: u ? "0.75rem" : N ? "1rem" : "0", fontSize: ((M = e == null ? void 0 : e.fontSize) == null ? void 0 : M.large) || "1rem", fontWeight: 600, color: (e == null ? void 0 : e.textColor) || "inherit", lineHeight: "1.4" }, children: m } ), u && /* @__PURE__ */ h( "div", { className: "admesh-bridge-description", style: { marginBottom: N || (x = t.creative_input) != null && x.offer_summary ? "1rem" : "0", lineHeight: "1.6", fontSize: ((T = e == null ? void 0 : e.fontSize) == null ? void 0 : T.base) || "0.875rem", color: (e == null ? void 0 : e.textColor) || "inherit" }, children: u } ), ((v = t.creative_input) == null ? void 0 : v.offer_summary) && /* @__PURE__ */ h( "div", { className: "admesh-bridge-offer-summary", style: { marginBottom: N ? "1rem" : "0", lineHeight: "1.5", fontSize: ((I = e == null ? void 0 : e.fontSize) == null ? void 0 : I.small) || "0.75rem", color: e != null && e.textSecondaryColor || (e == null ? void 0 : e.mode) === "dark" ? "#9ca3af" : "#6b7280", fontStyle: "italic" }, children: t.creative_input.offer_summary } ), N && /* @__PURE__ */ h("div", { style: { marginBottom: "0.75rem" }, children: /* @__PURE__ */ h( "button", { onClick: E, className: "admesh-bridge-cta-button", style: { padding: "0.5rem 1rem", backgroundColor: "#000000", color: "#ffffff", fontSize: ((A = e == null ? void 0 : e.fontSize) == null ? void 0 : A.small) || "0.875rem", fontWeight: 500, borderRadius: (e == null ? void 0 : e.borderRadius) || "0.5rem", border: "none", cursor: "pointer", transition: "background-color 0.2s, opacity 0.2s" }, onMouseEnter: (S) => { S.currentTarget.style.backgroundColor = "#1a1a1a", S.currentTarget.style.opacity = "0.9"; }, onMouseLeave: (S) => { S.currentTarget.style.backgroundColor = "#000000", S.currentTarget.style.opacity = "1"; }, children: b } ) }), /* @__PURE__ */ h("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: "0.5rem" }, children: /* @__PURE__ */ h( "div", { className: "admesh-bridge-label", style: { fontSize: ((F = e == null ? void 0 : e.fontSize) == null ? void 0 : F.small) || "0.75rem", color: e != null && e.textSecondaryColor || (e == null ? void 0 : e.mode) === "dark" ? "#9ca3af" : "#6b7280", fontStyle: "italic" }, children: "Sponsored" } ) }) ] }) } ); }; function _t(t) { return t && t.__esModule && Object.prototype.hasOwnProperty.call(t, "default") ? t.default : t; } var Fe = { exports: {} }; /*! Copyright (c) 2018 Jed Watson. Licensed under the MIT License (MIT), see http://jedwatson.github.io/classnames */ var Oe; function kt() { return Oe || (Oe = 1, (function(t) { (function() { var e = {}.hasOwnProperty; function n() { for (var a = "", s = 0; s < arguments.length; s++) { var c = arguments[s]; c && (a = i(a, r(c))); } return a; } function r(a) { if (typeof a == "string" || typeof a == "number") return a; if (typeof a != "object") return ""; if (Array.isArray(a)) return n.apply(null, a); if (a.toString !== Object.prototype.toString && !a.toString.toString().includes("[native code]")) return a.toString(); var s = ""; for (var c in a) e.call(a, c) && a[c] && (s = i(s, c)); return s; } function i(a, s) { return s ? a ? a + " " + s : a + s : a; } t.exports ? (n.default = n, t.exports = n) : window.classNames = n; })(); })(Fe)), Fe.exports; } var At = kt(); const Ce = /* @__PURE__ */ _t(At), Je = ({ recommendationId: t, admeshLink: e, productId: n, children: r, trackingData: i, className: a, style: s }) => { const c = P(null); H(() => { if (!c.current) return; c.current.querySelectorAll("a").forEach((d) => { (!d.hasAttribute("target") || d.getAttribute("target") !== "_blank") && (d.setAttribute("target", "_blank"), d.setAttribute("rel", "noopener noreferrer")); }); }, [r]); const m = he((u) => { const l = u.target.closest("a"); l ? ((!l.href || l.href === "#" || l.href === "") && e && (l.href = e), (!l.hasAttribute("target") || l.getAttribute("target") !== "_blank") && (l.setAttribute("target", "_blank"), l.setAttribute("rel", "noopener noreferrer"))) : e ? window.open(e, "_blank", "noopener,noreferrer") : o.warn("[AdMeshLinkTracker] No admeshLink provided for click tracking"); }, [e]); return /* @__PURE__ */ h( "div", { ref: c, className: a, onClick: m, style: { cursor: "pointer", ...s }, children: r } ); }; Je.displayName = "AdMeshLinkTracker"; const et = ({ brand: t, title: e = "Product Recommendations", showTitle: n = !1, className: r = "", cardClassName: i = "", onProductClick: a, maxCards: s = 10, cardWidth: c = "md", theme: m = "auto", borderRadius: u = "md", shadow: d = "sm", sessionId: l, onFeedback: y }) => { const [g, f] = xe.useState(null); if (!t) return o.log("[AdMesh Ecommerce Cards] Brand object is required but was not provided"), null; if (!t.products || t.products.length === 0) return o.log("[AdMesh Ecommerce Cards] Products array is required but was empty or missing. Both brand info and products are required for ecommerce cards."), null; const _ = t.products.slice(0, s), w = () => { var C; return t.brand_name || ((C = t.creative_input) == null ? void 0 : C.brand_name) || ""; }, p = () => { var C, U; return ((U = (C = t.creative_input) == null ? void 0 : C.assets) == null ? void 0 : U.logo_url) || ""; }, E = () => { var C; return ((C = t.creative_input) == null ? void 0 : C.short_description) || t.description || ""; }, b = (C) => C.product_link || C.product_click_url || "", N = (C) => C.product_cta_label || "Shop", M = (C) => { const U = C.product_price; if (U != null) { if (typeof U == "string") return U; if (typeof U == "number") return `$${U.toFixed(2)}`; } return ""; }, x = (C) => C.product_name || "", T = (C) => { if (C.product_features && C.product_features.length > 0) { let U = 0; const de = C.product_id || ""; for (let W = 0; W < de.length; W++) U = (U << 5) - U + de.charCodeAt(W), U |= 0; const pe = Math.abs(U) % C.product_features.length; return C.product_features[pe]; } return C.product_description || ""; }, v = (C) => C.product_image_url || null, I = (C) => { const U = C.product_discount; if (U != null) { if (typeof U == "string") return U; if (typeof U == "number") return `${U}%`; } return null; }, A = () => { switch (c) { case "sm": return "w-[305px] min-w-[305px] max-w-[305px]"; case "md": return "w-[345px] min-w-[345px] max-w-[345px]"; case "lg": return "w-[385px] min-w-[385px] max-w-[385px]"; default: return "w-[345px] min-w-[345px] max-w-[345px]"; } }, F = () => { switch (u) { case "none": return "rounded-none"; case "sm": return "rounded-sm"; case "md": return "rounded-lg"; case "lg": return "rounded-xl"; default: return "rounded-lg"; } }, S = () => { switch (d) { case "none": return ""; case "sm": return "shadow-sm hover:shadow-md"; case "md": return "shadow-md hover:shadow-lg"; case "lg": return "shadow-lg hover:shadow-xl"; default: return "shadow-sm hover:shadow-md"; } }, B = () => m === "dark" ? "bg-gray-900 text-white" : m === "light" ? "bg-white text-gray-900" : "bg-white dark:bg-gray-900 text-gray-900 dark:text-white", k = (C) => { if (a) a(C); else { const U = C.product_link || C.product_click_url; U && window.open(U, "_blank", "noopener,noreferrer"); } }, D = w(), Z = p(), X = E(), { sessionId: ee, userId: ae } = Ee(), G = l || ee, se = t.exposure_url; return /* @__PURE__ */ z( Ie, { recommendationId: t.recommendation_id, exposureUrl: se, sessionId: G, className: Ce("w-full", r), children: [ /* @__PURE__ */ z("div", { className: Ce( "bg-white dark:bg-gray-900", "border border-gray-200 dark:border-gray-700", "rounded-xl", "shadow-[0_4px_6px_-1px_rgba(0,0,0,0.1),0_2px_4px_-1px_rgba(0,0,0,0.06)]", "dark:shadow-[0_4px_6px_-1px_rgba(0,0,0,0.3),0_2px_4px_-1px_rgba(0,0,0,0.2)]", "p-6", "transition-shadow duration-200", "hover:shadow-[0_10px_15px_-3px_rgba(0,0,0,0.1),0_4px_6px_-2px_rgba(0,0,0,0.05)]", "dark:hover:shadow-[0_10px_15px_-3px_rgba(0,0,0,0.4),0_4px_6px_-2px_rgba(0,0,0,0.3)]" ), children: [ (D || Z || X) && /* @__PURE__ */ h("div", { className: "mb-4", children: /* @__PURE__ */ z("div", { className: "flex items-start gap-3", children: [ Z && /* @__PURE__ */ h("div", { className: "flex-shrink-0", children: /* @__PURE__ */ h( "div", { className: "rounded-full overflow-hidden bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center", style: { aspectRatio: "1", width: "48px", height: "48px" }, children: /* @__PURE__ */ h( "img", { src: Z, alt: `${D} logo`, className: "w-full h-full object-contain", style: { aspectRatio: "1" }, onError: (C) => { C.target.style.display = "none"; } } ) } ) }), /* @__PURE__ */ z("div", { className: "flex-1", children: [ D && /* @__PURE__ */ h("div", { className: "font-bold text-gray-900 dark:text-white text-base mb-1", children: D }), X && /* @__PURE__ */ h("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: X }) ] }) ] }) }), /* @__PURE__ */ z("div", { className: "relative", children: [ _.length === 0 ? /* @__PURE__ */ h("div", { className: "text-center py-8 text-gray-500", children: "No products to display" }) : /* @__PURE__ */ h("div", { className: "flex gap-3 sm:gap-4 overflow-x-auto pb-4 scrollbar-hide", children: _.map((C) => { const U = C.product_id, de = U, pe = b(C), W = N(C), j = M(C), R = x(C), V = T(C), q = I(C), J = v(C), Q = C.product_link || C.product_click_url || "", K = t.recommendation_id; return /* @__PURE__ */ h( Je, { recommendationId: K, admeshLink: Q, productId: U, trackingData: { sessionId: G, userId: ae }, children: /* @__PURE__ */ h( "div", { className: Ce( A(), F(), S(), B(), "flex-shrink-0 border border-gray-200 dark:border-gray-700 transition-all duration-200 cursor-pointer hover:scale-[1.02] overflow-hidden box-border", i ), style: { width: c === "sm" ? "305px" : c === "lg" ? "385px" : "345px", minWidth: c === "sm" ? "305px" : c === "lg" ? "385px" : "345px", maxWidth: c === "sm" ? "305px" : c === "lg" ? "385px" : "345px" }, onClick: () => k(C), children: /* @__PURE__ */ z("div", { className: "p-3 w-full overflow-hidden box-border", children: [ /* @__PURE__ */ z("div", { className: "flex items-start gap-3 mb-3 w-full", children: [ /* @__PURE__ */ h("div", { className: "flex-shrink-0", children: /* @__PURE__ */ h( "div", { className: "rounded-full overflow-hidden bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center", style: { aspectRatio: "1", width: "48px", height: "48px" }, children: J ? /* @__PURE__ */ h( "img", { src: J, alt: R, className: "w-full h-full object-cover", style: { aspectRatio: "1" }, onError: (O) => { O.target.style.display = "none"; const Y = O.target.parentElement; Y && (Y.innerHTML = ` <svg class="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="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" /> </svg> `); } } ) : ( /* Placeholder icon when no product image */ /* @__PURE__ */ h("svg", { className: "w-6 h-6 text-gray-400", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ h("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" }) }) ) } ) }), /* @__PURE__ */ z("div", { className: "flex-1 min-w-0 max-w-full overflow-hidden", children: [ R && /* @__PURE__ */ h("h4", { className: "text-sm font-medium text-gray-900 dark:text-white mb-1 leading-tight break-words overflow-wrap-anywhere", children: R }), V && /* @__PURE__ */ h("p", { className: "text-xs text-gray-600 dark:text-gray-400 break-words overflow-wrap-anywhere line-clamp-2", title: V, children: V }) ] }) ] }), /* @__PURE__ */ z("div", { className: "mt-3 flex items-center justify-between gap-2", children: [ /* @__PURE__ */ z("div", { className: "flex items-center gap-2", children: [ j && /* @__PURE__ */ h("span", { className: "text-sm font-semibold text-gray-900 dark:text-white", children: j }), q && /* @__PURE__ */ z("span", { className: "text-xs text-red-600 dark:text-red-400 font-medium", children: [ q, " OFF" ] }) ] }), pe && /* @__PURE__ */ h( "button", { onClick: (O) => { O.stopPropagation(), k(C); }, className: "px-3 py-1.5 text-xs font-medium text-gray-100 bg-blue-600 hover:bg-blue-700 dark:bg-blue-500 dark:hover:bg-blue-600 rounded-md transition-colors duration-200 whitespace-nowrap", children: W } ) ] }) ] }) } ) }, de ); }) }), _.length > 0 && /* @__PURE__ */ z(ye, { children: [ /* @__PURE__ */ h("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", children: /* @__PURE__ */ h("svg", { className: "w-4 h-4 text-gray-600 dark:text-gray-300", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ h("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }) }), /* @__PURE__ */ h("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 transi