stormcloud-video-player 0.7.38 → 0.7.39
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/stormcloud-vp.min.js +1 -1
- package/lib/index.cjs +1 -1
- package/lib/index.cjs.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/ui/OverlayRenderer.cjs +1 -1
- package/lib/ui/OverlayRenderer.cjs.map +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs +1 -1
- package/lib/ui/StormcloudVideoPlayer.cjs.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/ui/OverlayRenderer.cjs","../../src/ui/OverlayRenderer.tsx","../../src/utils/overlays.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","OverlayRenderer_exports","OverlayRenderer","module","exports","import_react","require","OVERLAY_API_BASE","timeStringToSeconds","timeStr","parts","split","length","hours","parseInt","minutes","secStr","dotIdx","indexOf","seconds","substring","msFrag","ms","padEnd","num","parseFloat","isFinite","Math","max","isOverlayActive","overlay","currentTime","visible","startSec","start_time","durationSec","duration","resolveImageUrl","imageUrl","apiBaseUrl","startsWith","url","URL","origin","import_jsx_runtime","computeVideoDimensions","video","nativeWidth","videoWidth","nativeHeight","videoHeight","displayWidth","offsetWidth","displayHeight","offsetHeight","renderWidth","renderHeight","offsetX","offsetY","displayAspect","videoAspect","ImageOverlay","image_url","jsx","src","draggable","width","height","objectFit","display","pointerEvents","userSelect","content","alignItems","justifyContent","fontFamily","fontWeight","textAlign","boxSizing","wordBreak","textShadow","parseRSSXml","xmlText","maxItems","parser","DOMParser","doc","parseFromString","stripped","items","Array","querySelectorAll","map","item","title","querySelector","textContent","replace","trim","description","pubDate","author","category","filter","i","slice","Error","RSS_CACHE_TTL_MS","rssCache","Map","cachedFetchRSSItems","rssUrl","now","Date","cached","expiresAt","promise","fetchRSSItems","catch","err","delete","set","encoded","resp","text","data","encodeURIComponent","window","location","fetch","ok","includes","json","contents","ScrollerOverlay","cfg","scroller_config","uid","useId","useState","rssItems","setRssItems","rssLoading","setRssLoading","rssError","setRssError","rss_url","max_items","autoRefresh","auto_refresh","updateInterval","update_interval","useEffect","use_custom_text","custom_text","cancelled","then","finally","interval","setInterval","clearInterval","sep","separator_char","segments","show_title","push","show_description","show_timestamp","toLocaleDateString","show_author","show_category","join","scrollSpeed","scroll_speed","direction","fontSize","font_size","font_family","font_weight","textColor","text_color","bgColor","background_color","bgOpacity","background_opacity","borderRadius","border_radius","itemSpacing","label","labelLine2","item_spacing","labelColor","label_line2","labelTextColor","label_color","label_text_color","accentColor","accent_color","isHorizontal","isReverse","fullText","animId","id","keyframes","jsxs","Fragment","children","style","flexDirection","overflow","backgroundColor","hexToRgb","color","showAccentLine","background","flexShrink","flex","minHeight","padding","minWidth","letterSpacing","lineHeight","textTransform","whiteSpace","gap","opacity","position","animation","willChange","copy","paddingRight","seg","React","margin","paddingBottom","parseConfig","JSON","parse"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nfunction parseRSSXml(xmlText, maxItems) {\n const stripped = xmlText.replace(/^<\\?xml[^?]*\\?>\\s*/, \"\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(stripped, \"text/xml\");\n const items = Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n if (items.length === 0 && doc.querySelector(\"parsererror\")) {\n throw new Error(\"Invalid RSS XML\");\n }\n return items;\n}\nvar RSS_CACHE_TTL_MS = 6e4;\nvar rssCache = /* @__PURE__ */ new Map();\nfunction cachedFetchRSSItems(rssUrl, maxItems) {\n const now = Date.now();\n const cached = rssCache.get(rssUrl);\n if (cached && cached.expiresAt > now) return cached.promise;\n const promise = fetchRSSItems(rssUrl, maxItems).catch((err) => {\n rssCache.delete(rssUrl);\n throw err;\n });\n rssCache.set(rssUrl, { promise, expiresAt: now + RSS_CACHE_TTL_MS });\n return promise;\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const encoded = encodeURIComponent(rssUrl);\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n throw new Error(\"All RSS proxies failed\");\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(true);\n const [rssError, setRssError] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n cachedFetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) {\n setRssItems(items);\n setRssError(false);\n }\n }).catch(() => {\n if (!cancelled) setRssError(true);\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n rssCache.delete(rssUrl);\n cachedFetchRSSItems(rssUrl, maxItems).then((items) => {\n setRssItems(items);\n setRssError(false);\n }).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(8, Math.min(size.h * 0.13, size.w * 0.048));\n const showSubtitle = size.h >= 60;\n const showThumbnail = false;\n const thumbW = 0;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: {\n width: \"100%\",\n height: \"100%\",\n borderRadius: Math.max(2, size.w * 0.025),\n display: \"flex\",\n background: cfg.backgroundColor,\n color: cfg.textColor,\n fontFamily: \"'Arial', 'Helvetica Neue', Helvetica, sans-serif\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n fontSize: `${f}px`,\n WebkitFontSmoothing: \"antialiased\"\n }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(3, size.w * 0.018), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: {\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n padding: `${f * 0.35}px ${f * 0.75}px`,\n minWidth: 0,\n gap: `${f * 0.08}px`\n }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.7em\",\n fontWeight: 700,\n textTransform: \"uppercase\",\n letterSpacing: \"0.09em\",\n color: cfg.accentColor,\n lineHeight: 1\n }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"1.2em\",\n fontWeight: 700,\n lineHeight: 1.2,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n }, children: cfg.title }),\n showSubtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.82em\",\n opacity: 0.65,\n lineHeight: 1.1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.9em\",\n fontWeight: 700,\n color: cfg.accentColor,\n lineHeight: 1\n }, children: cfg.scheduledTime })\n ] }),\n showThumbnail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: `${thumbW}px`, overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src: cfg.thumbnailUrl,\n alt: \"\",\n style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }\n }\n ) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar SHOWCASE_CYCLE_MS_DEFAULT = 6e4;\nvar SHOWCASE_POP_IN_MS = 420;\nvar SHOWCASE_STEADY_END_MS = 46e3;\nvar SHOWCASE_POP_OUT_MS = 450;\nvar SHOWCASE_POP_OUT_END_MS = SHOWCASE_STEADY_END_MS + SHOWCASE_POP_OUT_MS;\nfunction easeOutCubic(t) {\n const u = 1 - t;\n return 1 - u * u * u;\n}\nfunction easeInCubic(t) {\n return t * t * t;\n}\nfunction easeOutBack(t) {\n const c1 = 1.70158;\n const c3 = c1 + 1;\n return 1 + c3 * (t - 1) ** 3 + c1 * (t - 1) ** 2;\n}\nfunction showcaseOpacity(phase) {\n if (phase < SHOWCASE_POP_IN_MS) return easeOutCubic(phase / SHOWCASE_POP_IN_MS);\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\nfunction showcaseScale(phase) {\n if (phase < SHOWCASE_POP_IN_MS) {\n return 0.78 + 0.22 * easeOutBack(phase / SHOWCASE_POP_IN_MS);\n }\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - 0.14 * easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\nfunction useShowcasePhase(enabled, cycleMs) {\n const [phase, setPhase] = (0, import_react.useState)(0);\n (0, import_react.useEffect)(() => {\n if (!enabled) return;\n setPhase(Date.now() % cycleMs);\n const id = window.setInterval(() => {\n setPhase(Date.now() % cycleMs);\n }, 48);\n return () => clearInterval(id);\n }, [enabled, cycleMs]);\n return phase;\n}\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n showcaseMode = false,\n showcaseCycleMs = SHOWCASE_CYCLE_MS_DEFAULT\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const showcasePhase = useShowcasePhase(showcaseMode, showcaseCycleMs);\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(() => {\n if (showcaseMode) {\n return overlays.filter((o) => {\n if (!o.visible) return false;\n return showcasePhase < SHOWCASE_POP_OUT_END_MS;\n });\n }\n return overlays.filter((o) => isOverlayActive(o, currentTime));\n }, [overlays, currentTime, showcaseMode, showcasePhase]);\n const activeBeatIds = (0, import_react.useMemo)(\n () => new Set(activeOverlays.map((o) => o.id)),\n [activeOverlays]\n );\n (0, import_react.useLayoutEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const inShowcaseBeat = showcaseMode && activeBeatIds.has(overlay.id);\n const useShowcasePop = inShowcaseBeat;\n const opacity = useShowcasePop ? baseOpacity * showcaseOpacity(showcasePhase) : visible ? baseOpacity : 0;\n const popScale = useShowcasePop ? showcaseScale(showcasePhase) : 1;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: useShowcasePop ? \"none\" : `opacity ${FADE_DURATION_MS}ms ease`,\n transform: showcaseMode && useShowcasePop ? `scale(${popScale})` : void 0,\n transformOrigin: showcaseMode && useShowcasePop ? \"center center\" : void 0,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n","import React, {\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n useId,\n} from \"react\";\nimport {\n type SwirlOverlay,\n isOverlayActive,\n resolveImageUrl,\n} from \"../utils/overlays\";\n\ninterface VideoDimensions {\n nativeWidth: number;\n nativeHeight: number;\n displayWidth: number;\n displayHeight: number;\n offsetX: number;\n offsetY: number;\n scaleX: number;\n scaleY: number;\n}\n\ninterface OverlayRendererProps {\n overlays: SwirlOverlay[];\n currentTime: number;\n videoRef: React.RefObject<HTMLVideoElement | null>;\n coordinateSpace?: { width: number; height: number } | null;\n showcaseMode?: boolean;\n showcaseCycleMs?: number;\n}\n\nfunction computeVideoDimensions(\n video: HTMLVideoElement\n): VideoDimensions | null {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n\n let renderWidth: number;\n let renderHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight,\n };\n}\n\nfunction ImageOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return (\n <img\n src={src}\n alt={overlay.name}\n draggable={false}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n />\n );\n}\n\nfunction TextOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const text = overlay.content || \"\";\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3,\n }}\n >\n {text}\n </div>\n );\n}\n\ninterface RSSItem {\n title: string;\n description: string;\n pubDate: string;\n author: string;\n category: string;\n}\n\nfunction parseRSSXml(xmlText: string, maxItems: number): RSSItem[] {\n const stripped = xmlText.replace(/^<\\?xml[^?]*\\?>\\s*/, \"\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(stripped, \"text/xml\");\n const items = Array.from(doc.querySelectorAll(\"item\"))\n .map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\",\n }))\n .filter((i) => i.title)\n .slice(0, maxItems);\n if (items.length === 0 && doc.querySelector(\"parsererror\")) {\n throw new Error(\"Invalid RSS XML\");\n }\n return items;\n}\n\nconst RSS_CACHE_TTL_MS = 60_000;\nconst rssCache = new Map<string, { promise: Promise<RSSItem[]>; expiresAt: number }>();\n\nfunction cachedFetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const now = Date.now();\n const cached = rssCache.get(rssUrl);\n if (cached && cached.expiresAt > now) return cached.promise;\n const promise = fetchRSSItems(rssUrl, maxItems).catch((err) => {\n rssCache.delete(rssUrl);\n throw err;\n });\n rssCache.set(rssUrl, { promise, expiresAt: now + RSS_CACHE_TTL_MS });\n return promise;\n}\n\nasync function fetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const encoded = encodeURIComponent(rssUrl);\n\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n throw new Error(\"All RSS proxies failed\");\n}\n\nfunction ScrollerOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const cfg = overlay.scroller_config;\n const uid = useId().replace(/:/g, \"\");\n\n const [rssItems, setRssItems] = useState<RSSItem[]>([]);\n const [rssLoading, setRssLoading] = useState(true);\n const [rssError, setRssError] = useState(false);\n\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n\n useEffect(() => {\n if (!rssUrl || (cfg?.use_custom_text && cfg?.custom_text)) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n cachedFetchRSSItems(rssUrl, maxItems)\n .then((items) => { if (!cancelled) { setRssItems(items); setRssError(false); } })\n .catch(() => { if (!cancelled) setRssError(true); })\n .finally(() => { if (!cancelled) setRssLoading(false); });\n return () => { cancelled = true; };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n useEffect(() => {\n if (!rssUrl || !autoRefresh || (cfg?.use_custom_text && cfg?.custom_text)) return;\n const interval = setInterval(() => {\n rssCache.delete(rssUrl);\n cachedFetchRSSItems(rssUrl, maxItems)\n .then((items) => { setRssItems(items); setRssError(false); })\n .catch(() => { /* keep showing last good items */ });\n }, updateInterval * 60 * 1000);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n const sep = cfg?.separator_char ?? \"◆\";\n\n let segments: string[];\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts: string[] = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try { parts.push(new Date(item.pubDate).toLocaleDateString()); } catch { /* ignore */ }\n }\n if (cfg?.show_author && item.author) parts.push(`— ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed…\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed…\"] : [\"RSS Ticker\"];\n }\n\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== undefined ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, (fullText.length * 9) / scrollSpeed);\n\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal\n ? `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\"\n : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }`\n : `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\"\n : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n\n return (\n <>\n <style>{keyframes}</style>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : undefined,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n >\n {/* Top accent line */}\n {showAccentLine && (\n <div style={{ height: 3, background: accentColor, flexShrink: 0, width: \"100%\" }} />\n )}\n\n {/* Main row */}\n <div style={{ display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }}>\n {/* Label badge */}\n {label && (\n <div\n style={{\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1,\n }}\n >\n <span\n style={{\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\",\n }}\n >\n {label}\n </span>\n {labelLine2 && (\n <span\n style={{\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\",\n }}\n >\n {labelLine2}\n </span>\n )}\n </div>\n )}\n\n {/* Accent divider */}\n {label && (\n <div style={{ width: 3, background: accentColor, flexShrink: 0 }} />\n )}\n\n {/* Scrolling text */}\n <div\n style={{\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {isHorizontal ? (\n <div\n style={{\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) => (\n <span key={copy} style={{ paddingRight: `${itemSpacing}px` }}>\n {segments.map((seg, i) => (\n <React.Fragment key={i}>\n {i > 0 && (\n <span style={{ opacity: 0.5, margin: \"0 8px\" }}>{sep}</span>\n )}\n <span style={{ textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }}>{seg}</span>\n </React.Fragment>\n ))}\n </span>\n ))}\n </div>\n ) : (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) =>\n segments.map((seg, i) => (\n <div key={`${copy}-${i}`} style={{ paddingBottom: `${itemSpacing / 4}px` }}>\n {seg}\n </div>\n ))\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction parseConfig<T>(content?: string): T | null {\n if (!content) return null;\n try { return JSON.parse(content) as T; } catch { return null; }\n}\n\ninterface OverlaySize { w: number; h: number; }\n\ninterface ScoreBugCfg { homeTeam: string; awayTeam: string; homeScore: number; awayScore: number; period: string; clock: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface LowerThirdCfg { headline: string; subtitle: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; style: string; }\ninterface QrCodeCfg { url: string; ctaText: string; description: string; size: number; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ComingUpNextCfg { title: string; subtitle: string; scheduledTime: string; thumbnailUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ContextualTriggerCfg { triggerType: string; headline: string; message: string; iconType: string; backgroundColor: string; textColor: string; accentColor: string; animationStyle: string; }\ninterface OddsBettingCfg { eventTitle: string; options: Array<{ label: string; odds: string }>; sponsorText: string; backgroundColor: string; textColor: string; accentColor: string; oddsFormat: string; }\ninterface BreakingNewsCfg { headline: string; body: string; urgency: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface CountdownCfg { eventName: string; targetTime: string; message: string; showDays: boolean; showHours: boolean; showMinutes: boolean; showSeconds: boolean; backgroundColor: string; textColor: string; accentColor: string; }\n\nfunction ScoreBugOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ScoreBugCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }}>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.homeTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.homeScore}</div>\n </div>\n <div style={{ fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }}>\n <div>{cfg.period}</div>\n <div>{cfg.clock}</div>\n </div>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.awayTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.awayScore}</div>\n </div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction LowerThirdOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<LowerThirdCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }}>\n <div style={{ fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }}>{cfg.subtitle}</div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction QrCodeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<QrCodeCfg>(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }}>\n <div style={{ flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }}>\n <img src={qrUrl} alt=\"QR Code\" style={{ width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" }} />\n </div>\n <div style={{ fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.ctaText}</div>\n {cfg.description && <div style={{ fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.description}</div>}\n </div>\n );\n}\n\nfunction ComingUpNextOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ComingUpNextCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(8, Math.min(size.h * 0.13, size.w * 0.048));\n const showSubtitle = size.h >= 60;\n const showThumbnail = false;\n const thumbW = 0;\n return (\n <div style={{\n width: \"100%\", height: \"100%\",\n borderRadius: Math.max(2, size.w * 0.025),\n display: \"flex\",\n background: cfg.backgroundColor,\n color: cfg.textColor,\n fontFamily: \"'Arial', 'Helvetica Neue', Helvetica, sans-serif\",\n overflow: \"hidden\",\n pointerEvents: \"none\", userSelect: \"none\",\n fontSize: `${f}px`,\n WebkitFontSmoothing: \"antialiased\",\n } as React.CSSProperties}>\n <div style={{ width: Math.max(3, size.w * 0.018), flexShrink: 0, backgroundColor: cfg.accentColor }} />\n <div style={{\n flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\",\n padding: `${f * 0.35}px ${f * 0.75}px`,\n minWidth: 0, gap: `${f * 0.08}px`,\n }}>\n <div style={{\n fontSize: \"0.7em\", fontWeight: 700,\n textTransform: \"uppercase\", letterSpacing: \"0.09em\",\n color: cfg.accentColor, lineHeight: 1,\n }}>\n Coming Up Next\n </div>\n <div style={{\n fontSize: \"1.2em\", fontWeight: 700, lineHeight: 1.2,\n overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\",\n }}>\n {cfg.title}\n </div>\n {showSubtitle && (\n <div style={{\n fontSize: \"0.82em\", opacity: 0.65, lineHeight: 1.1,\n overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\",\n }}>\n {cfg.subtitle}\n </div>\n )}\n {cfg.scheduledTime && (\n <div style={{\n fontSize: \"0.9em\", fontWeight: 700,\n color: cfg.accentColor, lineHeight: 1,\n }}>\n {cfg.scheduledTime}\n </div>\n )}\n </div>\n {showThumbnail && (\n <div style={{ flexShrink: 0, width: `${thumbW}px`, overflow: \"hidden\" }}>\n <img\n src={cfg.thumbnailUrl}\n alt=\"\"\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContextualTriggerOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ContextualTriggerCfg>(overlay.content);\n if (!cfg) return null;\n const icons: Record<string, string> = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\uD83C\\uDF89\", info: \"\\u2139\\uFE0F\", warning: \"\\uD83D\\uDD14\" };\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <span style={{ fontSize: \"2em\", flexShrink: 0 }}>{icons[cfg.iconType] || \"\\u26A1\"}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.message}</div>\n </div>\n </div>\n );\n}\n\nfunction OddsBettingOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<OddsBettingCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventTitle}</div>\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }}>\n {(cfg.options || []).slice(0, 5).map((opt, i) => (\n <div key={i} style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }}>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }}>{opt.label}</span>\n <span style={{ fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }}>{opt.odds}</span>\n </div>\n ))}\n </div>\n {cfg.sponsorText && <div style={{ fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }}>{cfg.sponsorText}</div>}\n </div>\n );\n}\n\nfunction BreakingNewsOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<BreakingNewsCfg>(overlay.content);\n if (!cfg) return null;\n const urgencyColors: Record<string, string> = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }}>{label}</div>\n <div style={{ flex: 1, padding: `0 ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n {cfg.body && <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.body}</div>}\n </div>\n </div>\n );\n}\n\nfunction calcCountdownRemaining(targetTime: string) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 86400000),\n h: Math.floor((diff % 86400000) / 3600000),\n m: Math.floor((diff % 3600000) / 60000),\n s: Math.floor((diff % 60000) / 1000),\n expired: diff === 0,\n };\n}\n\nfunction CountdownOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<CountdownCfg>(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = useState(() =>\n targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n\n useEffect(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1000);\n return () => clearInterval(id);\n }, [targetTime]);\n\n if (!cfg) return null;\n\n const f = Math.max(6, size.w * 0.055);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const units: Array<{ show: boolean; value: string; label: string }> = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" },\n ];\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventName}</div>\n {remaining.expired ? (\n <div style={{ fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }}>{cfg.message || \"Event ended\"}</div>\n ) : (\n <div style={{ display: \"flex\", gap: f * 0.6, alignItems: \"center\" }}>\n {units.filter(u => u.show).map((u, i, arr) => (\n <React.Fragment key={u.label}>\n <div style={{ textAlign: \"center\" }}>\n <div style={{ fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }}>{u.value}</div>\n <div style={{ fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }}>{u.label}</div>\n </div>\n {i < arr.length - 1 && <div style={{ fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }}>:</div>}\n </React.Fragment>\n ))}\n </div>\n )}\n {!remaining.expired && cfg.message && <div style={{ fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }}>{cfg.message}</div>}\n </div>\n );\n}\n\nfunction ShapeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }}>\n <div style={{ fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }}>{overlay.name}</div>\n </div>\n );\n}\n\nfunction hexToRgb(hex: string): string {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${(num >> 16) & 255},${(num >> 8) & 255},${num & 255}`;\n}\n\ninterface OverlayFadeState {\n overlay: SwirlOverlay;\n visible: boolean;\n}\n\nconst FADE_DURATION_MS = 1000;\n\n/** One full cycle (~60s): pop in → hold → pop out → quiet until next cycle. */\nconst SHOWCASE_CYCLE_MS_DEFAULT = 60_000;\nconst SHOWCASE_POP_IN_MS = 420;\nconst SHOWCASE_STEADY_END_MS = 46_000;\nconst SHOWCASE_POP_OUT_MS = 450;\nconst SHOWCASE_POP_OUT_END_MS = SHOWCASE_STEADY_END_MS + SHOWCASE_POP_OUT_MS;\n\nfunction easeOutCubic(t: number): number {\n const u = 1 - t;\n return 1 - u * u * u;\n}\n\nfunction easeInCubic(t: number): number {\n return t * t * t;\n}\n\nfunction easeOutBack(t: number): number {\n const c1 = 1.70158;\n const c3 = c1 + 1;\n return 1 + c3 * (t - 1) ** 3 + c1 * (t - 1) ** 2;\n}\n\nfunction showcaseOpacity(phase: number): number {\n if (phase < SHOWCASE_POP_IN_MS) return easeOutCubic(phase / SHOWCASE_POP_IN_MS);\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\n\nfunction showcaseScale(phase: number): number {\n if (phase < SHOWCASE_POP_IN_MS) {\n return 0.78 + 0.22 * easeOutBack(phase / SHOWCASE_POP_IN_MS);\n }\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - 0.14 * easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\n\nfunction useShowcasePhase(enabled: boolean, cycleMs: number): number {\n const [phase, setPhase] = useState(0);\n useEffect(() => {\n if (!enabled) return;\n setPhase(Date.now() % cycleMs);\n const id = window.setInterval(() => {\n setPhase(Date.now() % cycleMs);\n }, 48);\n return () => clearInterval(id);\n }, [enabled, cycleMs]);\n return phase;\n}\n\nexport const OverlayRenderer: React.FC<OverlayRendererProps> = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n showcaseMode = false,\n showcaseCycleMs = SHOWCASE_CYCLE_MS_DEFAULT,\n}) => {\n const [dims, setDims] = useState<VideoDimensions | null>(null);\n const rafRef = useRef<number | null>(null);\n const [fadeMap, setFadeMap] = useState<Map<number, OverlayFadeState>>(new Map());\n const removeTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const showcasePhase = useShowcasePhase(showcaseMode, showcaseCycleMs);\n\n const updateDims = useCallback(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (\n !computed ||\n (prev &&\n prev.nativeWidth === computed.nativeWidth &&\n prev.nativeHeight === computed.nativeHeight &&\n prev.displayWidth === computed.displayWidth &&\n prev.displayHeight === computed.displayHeight &&\n prev.offsetX === computed.offsetX &&\n prev.offsetY === computed.offsetY)\n ) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n\n useEffect(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n\n const activeOverlays = useMemo(() => {\n if (showcaseMode) {\n return overlays.filter((o) => {\n if (!o.visible) return false;\n return showcasePhase < SHOWCASE_POP_OUT_END_MS;\n });\n }\n return overlays.filter((o) => isOverlayActive(o, currentTime));\n }, [overlays, currentTime, showcaseMode, showcasePhase]);\n\n const activeBeatIds = useMemo(\n () => new Set(activeOverlays.map((o) => o.id)),\n [activeOverlays]\n );\n\n useLayoutEffect(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n\n setFadeMap((prev) => {\n const next = new Map(prev);\n\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id)!;\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n\n return next;\n });\n }, [activeOverlays]);\n\n useEffect(() => {\n const toFadeIn: number[] = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n\n useEffect(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n\n if (!dims || fadeMap.size === 0) return null;\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8,\n }}\n >\n {[...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX =\n coordinateSpace?.width\n ? dims.displayWidth / coordinateSpace.width\n : dims.scaleX;\n const scaleY =\n coordinateSpace?.height\n ? dims.displayHeight / coordinateSpace.height\n : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const inShowcaseBeat = showcaseMode && activeBeatIds.has(overlay.id);\n const useShowcasePop = inShowcaseBeat;\n const opacity = useShowcasePop\n ? baseOpacity * showcaseOpacity(showcasePhase)\n : visible\n ? baseOpacity\n : 0;\n const popScale = useShowcasePop ? showcaseScale(showcasePhase) : 1;\n const sz: OverlaySize = { w: width, h: height };\n\n return (\n <div\n key={overlay.id}\n style={{\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: useShowcasePop\n ? \"none\"\n : `opacity ${FADE_DURATION_MS}ms ease`,\n transform: showcaseMode && useShowcasePop ? `scale(${popScale})` : undefined,\n transformOrigin: showcaseMode && useShowcasePop ? \"center center\" : undefined,\n zIndex: overlay.z_index,\n overflow: \"hidden\",\n }}\n >\n {overlay.type === \"image\" && <ImageOverlay overlay={overlay} />}\n {overlay.type === \"text\" && <TextOverlay overlay={overlay} />}\n {overlay.type === \"scroller\" && <ScrollerOverlay overlay={overlay} />}\n {overlay.type === \"shape\" && <ShapeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"score_bug\" && <ScoreBugOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"lower_third\" && <LowerThirdOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"qr_code\" && <QrCodeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"coming_up_next\" && <ComingUpNextOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"contextual_trigger\" && <ContextualTriggerOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"odds_betting\" && <OddsBettingOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"breaking_news\" && <BreakingNewsOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"countdown\" && <CountdownOverlay overlay={overlay} size={sz} />}\n </div>\n );\n })}\n </div>\n );\n};\n","export const OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\n\nexport interface OverlayCoordinateSpace {\n width: number;\n height: number;\n}\n\nexport interface SwirlScrollerConfig {\n rss_url?: string;\n update_interval?: number;\n scroll_speed?: number;\n direction?: string;\n font_size?: number;\n font_family?: string;\n font_weight?: string;\n text_color?: string;\n background_color?: string;\n background_opacity?: number;\n border_color?: string;\n border_width?: number;\n border_radius?: number;\n padding?: number;\n margin?: number;\n show_title?: boolean;\n show_description?: boolean;\n show_timestamp?: boolean;\n show_author?: boolean;\n show_category?: boolean;\n max_items?: number;\n item_spacing?: number;\n fade_in_out?: boolean;\n fade_distance?: number;\n auto_refresh?: boolean;\n use_custom_text?: boolean;\n custom_text?: string;\n label?: string;\n label_line2?: string;\n label_color?: string;\n label_text_color?: string;\n accent_color?: string;\n show_accent_line?: boolean;\n separator_char?: string;\n preset?: string;\n}\n\nexport type SwirlOverlayType =\n | \"image\"\n | \"text\"\n | \"scroller\"\n | \"shape\"\n | \"score_bug\"\n | \"lower_third\"\n | \"qr_code\"\n | \"coming_up_next\"\n | \"contextual_trigger\"\n | \"odds_betting\"\n | \"breaking_news\"\n | \"countdown\";\n\nexport interface SwirlOverlay {\n id: number;\n project_id: number;\n name: string;\n type: SwirlOverlayType | string;\n visible: boolean;\n x: number;\n y: number;\n width: number;\n height: number;\n opacity: number;\n start_time: string;\n duration: string;\n content?: string;\n image_url?: string;\n scroller_config?: SwirlScrollerConfig;\n z_index: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport function timeStringToSeconds(timeStr: string): number {\n if (!timeStr) return 0;\n\n const parts = timeStr.split(\":\");\n\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1000;\n }\n\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1000;\n }\n\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\n\nexport function isOverlayActive(\n overlay: SwirlOverlay,\n currentTime: number\n): boolean {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\n\nexport function scrollerLooksLikeMarketsStock(o: SwirlOverlay): boolean {\n if (o.type !== \"scroller\") return false;\n const cfg = o.scroller_config;\n const blob = `${o.name} ${cfg?.label ?? \"\"} ${cfg?.label_line2 ?? \"\"} ${cfg?.custom_text ?? \"\"} ${cfg?.preset ?? \"\"}`;\n return /\\b(MARKETS?|NYSE|NASDAQ|DJIA|\\bS&P\\b|STOCK|AAPL|TSLA|NVDA|EQUITIES)\\b/i.test(blob);\n}\n\nexport type DemoStockTickerOptions = {\n stackAboveNews?: boolean;\n};\n\nexport function createDemoStockTickerOverlay(\n projectId: number,\n opts?: DemoStockTickerOptions\n): SwirlOverlay {\n const stack = opts?.stackAboveNews === true;\n return {\n id: -9001,\n project_id: projectId,\n name: \"Demo stock ticker\",\n type: \"scroller\",\n visible: true,\n x: 144,\n y: stack ? 818 : 950,\n width: 1632,\n height: stack ? 78 : 97,\n opacity: 100,\n start_time: \"00:00:00.000\",\n duration: \"24:00:00.000\",\n z_index: stack ? 125 : 120,\n scroller_config: {\n use_custom_text: true,\n custom_text:\n \"AAPL +1.24% • MSFT +0.42% • GOOGL −0.11% • TSLA +2.31% • NVDA +1.87% • AMZN +0.65% • META +0.38% • DJIA +0.52% • S&P 500 +0.33% • NDAQ +0.41%\",\n direction: \"left\",\n scroll_speed: 42,\n font_size: 15,\n font_weight: \"700\",\n text_color: \"#f8fafc\",\n background_color: \"#0f172a\",\n background_opacity: 92,\n border_radius: 4,\n label: \"MARKETS\",\n label_line2: \"LIVE\",\n label_color: \"#0ea5e9\",\n label_text_color: \"#ffffff\",\n accent_color: \"#38bdf8\",\n show_accent_line: true,\n separator_char: \"•\",\n },\n };\n}\n\nfunction normalizeScrollerConfig(\n raw: SwirlScrollerConfig | Record<string, unknown> | undefined\n): SwirlScrollerConfig | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const r = raw as Record<string, unknown>;\n const merged: SwirlScrollerConfig = { ...(raw as SwirlScrollerConfig) };\n if (merged.use_custom_text === undefined && typeof r.useCustomText === \"boolean\") {\n merged.use_custom_text = r.useCustomText;\n }\n if ((merged.custom_text === undefined || merged.custom_text === \"\") && typeof r.customText === \"string\") {\n merged.custom_text = r.customText;\n }\n if (!merged.rss_url && typeof r.rssUrl === \"string\") {\n merged.rss_url = r.rssUrl;\n }\n return merged;\n}\n\nexport function normalizeSwirlOverlay(raw: SwirlOverlay & Record<string, unknown>): SwirlOverlay {\n const o = { ...raw };\n if (o.type === \"scroller\") {\n const sc = raw.scroller_config ?? (raw as Record<string, unknown>).scrollerConfig;\n const normalized = normalizeScrollerConfig(sc as SwirlScrollerConfig);\n if (normalized) o.scroller_config = normalized;\n }\n return o;\n}\n\nexport async function fetchProjectOverlays(\n projectId: number,\n apiBaseUrl: string = OVERLAY_API_BASE\n): Promise<SwirlOverlay[]> {\n const base = apiBaseUrl.replace(/\\/$/, \"\");\n const response = await fetch(\n `${base}/adstorm/swirl/projects/${projectId}/overlays`\n );\n if (!response.ok) {\n throw new Error(\n `Failed to fetch overlays: ${response.status} ${response.statusText}`\n );\n }\n const data = await response.json();\n if (!Array.isArray(data)) return [];\n return data.map((row: SwirlOverlay & Record<string, unknown>) =>\n normalizeSwirlOverlay(row)\n );\n}\n\nexport function resolveImageUrl(\n imageUrl: string,\n apiBaseUrl: string = OVERLAY_API_BASE\n): string {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/ubuntu24-new/Dev/stormcloud-vp/lib/ui/OverlayRenderer.cjs","../../src/ui/OverlayRenderer.tsx","../../src/utils/overlays.ts"],"names":["__create","Object","create","__defProp","defineProperty","__getOwnPropDesc","getOwnPropertyDescriptor","__getOwnPropNames","getOwnPropertyNames","__getProtoOf","getPrototypeOf","__hasOwnProp","prototype","hasOwnProperty","__export","target","all","name","get","enumerable","__copyProps","to","from","except","desc","key","call","__toESM","mod","isNodeMode","__esModule","value","__toCommonJS","OverlayRenderer_exports","OverlayRenderer","module","exports","import_react","require","OVERLAY_API_BASE","timeStringToSeconds","timeStr","parts","split","length","hours","parseInt","minutes","secStr","dotIdx","indexOf","seconds","substring","msFrag","ms","padEnd","num","parseFloat","isFinite","Math","max","isOverlayActive","overlay","currentTime","visible","startSec","start_time","durationSec","duration","resolveImageUrl","imageUrl","apiBaseUrl","startsWith","url","URL","origin","import_jsx_runtime","computeVideoDimensions","video","nativeWidth","videoWidth","nativeHeight","videoHeight","displayWidth","offsetWidth","displayHeight","offsetHeight","renderWidth","renderHeight","offsetX","offsetY","displayAspect","videoAspect","ImageOverlay","image_url","jsx","src","draggable","width","height","objectFit","display","pointerEvents","userSelect","content","alignItems","justifyContent","fontFamily","fontWeight","textAlign","boxSizing","wordBreak","textShadow","parseRSSXml","xmlText","maxItems","parser","DOMParser","doc","parseFromString","stripped","items","Array","querySelectorAll","map","item","title","querySelector","textContent","replace","trim","description","pubDate","author","category","filter","i","slice","Error","RSS_CACHE_TTL_MS","rssCache","Map","cachedFetchRSSItems","rssUrl","now","Date","cached","expiresAt","promise","fetchRSSItems","catch","err","delete","set","encoded","resp","text","data","encodeURIComponent","window","location","fetch","ok","includes","json","contents","ScrollerOverlay","cfg","scroller_config","uid","useId","useState","rssItems","setRssItems","rssLoading","setRssLoading","rssError","setRssError","rss_url","max_items","autoRefresh","auto_refresh","updateInterval","update_interval","useEffect","use_custom_text","custom_text","cancelled","then","finally","interval","setInterval","clearInterval","sep","separator_char","segments","show_title","push","show_description","show_timestamp","toLocaleDateString","show_author","show_category","join","scrollSpeed","scroll_speed","direction","fontSize","font_size","font_family","font_weight","textColor","text_color","bgColor","background_color","bgOpacity","background_opacity","borderRadius","border_radius","itemSpacing","item_spacing","label","labelLine2","label_line2","labelColor","labelTextColor","accentColor","label_color","showAccentLine","label_text_color","accent_color","isHorizontal","show_accent_line","isReverse","fullText","animId","id","keyframes","jsxs","Fragment","children","style","flexDirection","overflow","backgroundColor","hexToRgb","color","background","flexShrink","flex","minHeight","padding","minWidth","gap","letterSpacing","lineHeight","textTransform","whiteSpace","opacity","position","animation","willChange","copy","paddingRight","seg","React","margin","paddingBottom","parseConfig","JSON","parse","ScoreBugOverlay","size"],"mappings":"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","sourcesContent":["\"use strict\";\nvar __create = Object.create;\nvar __defProp = Object.defineProperty;\nvar __getOwnPropDesc = Object.getOwnPropertyDescriptor;\nvar __getOwnPropNames = Object.getOwnPropertyNames;\nvar __getProtoOf = Object.getPrototypeOf;\nvar __hasOwnProp = Object.prototype.hasOwnProperty;\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, { get: all[name], enumerable: true });\n};\nvar __copyProps = (to, from, except, desc) => {\n if (from && typeof from === \"object\" || typeof from === \"function\") {\n for (let key of __getOwnPropNames(from))\n if (!__hasOwnProp.call(to, key) && key !== except)\n __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });\n }\n return to;\n};\nvar __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(\n // If the importer is in node compatibility mode or this is not an ESM\n // file that has been converted to a CommonJS file using a Babel-\n // compatible transform (i.e. \"__esModule\" has not been set), then set\n // \"default\" to the CommonJS \"module.exports\" for node compatibility.\n isNodeMode || !mod || !mod.__esModule ? __defProp(target, \"default\", { value: mod, enumerable: true }) : target,\n mod\n));\nvar __toCommonJS = (mod) => __copyProps(__defProp({}, \"__esModule\", { value: true }), mod);\n\n// src/ui/OverlayRenderer.tsx\nvar OverlayRenderer_exports = {};\n__export(OverlayRenderer_exports, {\n OverlayRenderer: () => OverlayRenderer\n});\nmodule.exports = __toCommonJS(OverlayRenderer_exports);\nvar import_react = __toESM(require(\"react\"), 1);\n\n// src/utils/overlays.ts\nvar OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\nfunction timeStringToSeconds(timeStr) {\n if (!timeStr) return 0;\n const parts = timeStr.split(\":\");\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1e3;\n }\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds = parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1e3;\n }\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\nfunction isOverlayActive(overlay, currentTime) {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\nfunction resolveImageUrl(imageUrl, apiBaseUrl = OVERLAY_API_BASE) {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n\n// src/ui/OverlayRenderer.tsx\nvar import_jsx_runtime = require(\"react/jsx-runtime\");\nfunction computeVideoDimensions(video) {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n let renderWidth;\n let renderHeight;\n let offsetX;\n let offsetY;\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight\n };\n}\nfunction ImageOverlay({ overlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src,\n alt: overlay.name,\n draggable: false,\n style: {\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\"\n }\n }\n );\n}\nfunction TextOverlay({ overlay }) {\n const text = overlay.content || \"\";\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3\n },\n children: text\n }\n );\n}\nfunction parseRSSXml(xmlText, maxItems) {\n const stripped = xmlText.replace(/^<\\?xml[^?]*\\?>\\s*/, \"\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(stripped, \"text/xml\");\n const items = Array.from(doc.querySelectorAll(\"item\")).map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\"\n })).filter((i) => i.title).slice(0, maxItems);\n if (items.length === 0 && doc.querySelector(\"parsererror\")) {\n throw new Error(\"Invalid RSS XML\");\n }\n return items;\n}\nvar RSS_CACHE_TTL_MS = 6e4;\nvar rssCache = /* @__PURE__ */ new Map();\nfunction cachedFetchRSSItems(rssUrl, maxItems) {\n const now = Date.now();\n const cached = rssCache.get(rssUrl);\n if (cached && cached.expiresAt > now) return cached.promise;\n const promise = fetchRSSItems(rssUrl, maxItems).catch((err) => {\n rssCache.delete(rssUrl);\n throw err;\n });\n rssCache.set(rssUrl, { promise, expiresAt: now + RSS_CACHE_TTL_MS });\n return promise;\n}\nasync function fetchRSSItems(rssUrl, maxItems) {\n const encoded = encodeURIComponent(rssUrl);\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch {\n }\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch {\n }\n throw new Error(\"All RSS proxies failed\");\n}\nfunction ScrollerOverlay({ overlay }) {\n const cfg = overlay.scroller_config;\n const uid = (0, import_react.useId)().replace(/:/g, \"\");\n const [rssItems, setRssItems] = (0, import_react.useState)([]);\n const [rssLoading, setRssLoading] = (0, import_react.useState)(true);\n const [rssError, setRssError] = (0, import_react.useState)(false);\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n (0, import_react.useEffect)(() => {\n if (!rssUrl || cfg?.use_custom_text && cfg?.custom_text) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n cachedFetchRSSItems(rssUrl, maxItems).then((items) => {\n if (!cancelled) {\n setRssItems(items);\n setRssError(false);\n }\n }).catch(() => {\n if (!cancelled) setRssError(true);\n }).finally(() => {\n if (!cancelled) setRssLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n (0, import_react.useEffect)(() => {\n if (!rssUrl || !autoRefresh || cfg?.use_custom_text && cfg?.custom_text) return;\n const interval = setInterval(() => {\n rssCache.delete(rssUrl);\n cachedFetchRSSItems(rssUrl, maxItems).then((items) => {\n setRssItems(items);\n setRssError(false);\n }).catch(() => {\n });\n }, updateInterval * 60 * 1e3);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n const sep = cfg?.separator_char ?? \"\\u25C6\";\n let segments;\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try {\n parts.push(new Date(item.pubDate).toLocaleDateString());\n } catch {\n }\n }\n if (cfg?.show_author && item.author) parts.push(`\\u2014 ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed\\u2026\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed\\u2026\"] : [\"RSS Ticker\"];\n }\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== void 0 ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, fullText.length * 9 / scrollSpeed);\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal ? `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\" : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }` : `@keyframes ${animId} {\n ${isReverse ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\" : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"style\", { children: keyframes }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : void 0,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\"\n },\n children: [\n showAccentLine && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { height: 3, background: accentColor, flexShrink: 0, width: \"100%\" } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }, children: [\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1\n },\n children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\"\n },\n children: label\n }\n ),\n labelLine2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"span\",\n {\n style: {\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\"\n },\n children: labelLine2\n }\n )\n ]\n }\n ),\n label && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: 3, background: accentColor, flexShrink: 0 } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\"\n },\n children: isHorizontal ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map((copy) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { paddingRight: `${itemSpacing}px` }, children: segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n i > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { opacity: 0.5, margin: \"0 8px\" }, children: sep }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }, children: seg })\n ] }, i)) }, copy))\n }\n ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n style: {\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\"\n },\n children: [0, 1].map(\n (copy) => segments.map((seg, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { paddingBottom: `${itemSpacing / 4}px` }, children: seg }, `${copy}-${i}`))\n )\n }\n )\n }\n )\n ] })\n ]\n }\n )\n ] });\n}\nfunction parseConfig(content) {\n if (!content) return null;\n try {\n return JSON.parse(content);\n } catch {\n return null;\n }\n}\nfunction ScoreBugOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.homeTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.homeScore })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.period }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { children: cfg.clock })\n ] }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700 }, children: cfg.awayTeam }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }, children: cfg.awayScore })\n ] })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction LowerThirdOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }, children: cfg.subtitle })\n ] }),\n (cfg.sponsorText || cfg.sponsorImageUrl) && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }, children: [\n cfg.sponsorImageUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: cfg.sponsorImageUrl, alt: \"sponsor\", style: { height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 } }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.sponsorText })\n ] })\n ] });\n}\nfunction QrCodeOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"img\", { src: qrUrl, alt: \"QR Code\", style: { width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" } }) }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.ctaText }),\n cfg.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }, children: cfg.description })\n ] });\n}\nfunction ComingUpNextOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(8, Math.min(size.h * 0.13, size.w * 0.048));\n const showSubtitle = size.h >= 60;\n const showThumbnail = false;\n const thumbW = 0;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: {\n width: \"100%\",\n height: \"100%\",\n borderRadius: Math.max(2, size.w * 0.025),\n display: \"flex\",\n background: cfg.backgroundColor,\n color: cfg.textColor,\n fontFamily: \"'Arial', 'Helvetica Neue', Helvetica, sans-serif\",\n overflow: \"hidden\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n fontSize: `${f}px`,\n WebkitFontSmoothing: \"antialiased\"\n }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: Math.max(3, size.w * 0.018), flexShrink: 0, backgroundColor: cfg.accentColor } }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: {\n flex: 1,\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n padding: `${f * 0.35}px ${f * 0.75}px`,\n minWidth: 0,\n gap: `${f * 0.08}px`\n }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.7em\",\n fontWeight: 700,\n textTransform: \"uppercase\",\n letterSpacing: \"0.09em\",\n color: cfg.accentColor,\n lineHeight: 1\n }, children: \"Coming Up Next\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"1.2em\",\n fontWeight: 700,\n lineHeight: 1.2,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n }, children: cfg.title }),\n showSubtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.82em\",\n opacity: 0.65,\n lineHeight: 1.1,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\"\n }, children: cfg.subtitle }),\n cfg.scheduledTime && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: {\n fontSize: \"0.9em\",\n fontWeight: 700,\n color: cfg.accentColor,\n lineHeight: 1\n }, children: cfg.scheduledTime })\n ] }),\n showThumbnail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flexShrink: 0, width: `${thumbW}px`, overflow: \"hidden\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"img\",\n {\n src: cfg.thumbnailUrl,\n alt: \"\",\n style: { width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }\n }\n ) })\n ] });\n}\nfunction ContextualTriggerOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const icons = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\u{1F389}\", info: \"\\u2139\\uFE0F\", warning: \"\\u{1F514}\" };\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontSize: \"2em\", flexShrink: 0 }, children: icons[cfg.iconType] || \"\\u26A1\" }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.message })\n ] })\n ] });\n}\nfunction OddsBettingOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventTitle }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }, children: (cfg.options || []).slice(0, 5).map((opt, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }, children: opt.label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"span\", { style: { fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }, children: opt.odds })\n ] }, i)) }),\n cfg.sponsorText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }, children: cfg.sponsorText })\n ] });\n}\nfunction BreakingNewsOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n if (!cfg) return null;\n const urgencyColors = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }, children: label }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { flex: 1, padding: `0 ${f * 1}px`, minWidth: 0 }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.headline }),\n cfg.body && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }, children: cfg.body })\n ] })\n ] });\n}\nfunction calcCountdownRemaining(targetTime) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 864e5),\n h: Math.floor(diff % 864e5 / 36e5),\n m: Math.floor(diff % 36e5 / 6e4),\n s: Math.floor(diff % 6e4 / 1e3),\n expired: diff === 0\n };\n}\nfunction CountdownOverlay({ overlay, size }) {\n const cfg = parseConfig(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = (0, import_react.useState)(\n () => targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n (0, import_react.useEffect)(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1e3);\n return () => clearInterval(id);\n }, [targetTime]);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n const pad = (n) => String(n).padStart(2, \"0\");\n const units = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" }\n ];\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }, children: cfg.eventName }),\n remaining.expired ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }, children: cfg.message || \"Event ended\" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { display: \"flex\", gap: f * 0.6, alignItems: \"center\" }, children: units.filter((u) => u.show).map((u, i, arr) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.default.Fragment, { children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\"div\", { style: { textAlign: \"center\" }, children: [\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }, children: u.value }),\n /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }, children: u.label })\n ] }),\n i < arr.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }, children: \":\" })\n ] }, u.label)) }),\n !remaining.expired && cfg.message && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }, children: cfg.message })\n ] });\n}\nfunction ShapeOverlay({ overlay, size }) {\n const f = Math.max(6, size.w * 0.05);\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\"div\", { style: { fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }, children: overlay.name }) });\n}\nfunction hexToRgb(hex) {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${num >> 16 & 255},${num >> 8 & 255},${num & 255}`;\n}\nvar FADE_DURATION_MS = 1e3;\nvar SHOWCASE_CYCLE_MS_DEFAULT = 6e4;\nvar SHOWCASE_POP_IN_MS = 420;\nvar SHOWCASE_STEADY_END_MS = 46e3;\nvar SHOWCASE_POP_OUT_MS = 450;\nvar SHOWCASE_POP_OUT_END_MS = SHOWCASE_STEADY_END_MS + SHOWCASE_POP_OUT_MS;\nfunction easeOutCubic(t) {\n const u = 1 - t;\n return 1 - u * u * u;\n}\nfunction easeInCubic(t) {\n return t * t * t;\n}\nfunction easeOutBack(t) {\n const c1 = 1.70158;\n const c3 = c1 + 1;\n return 1 + c3 * (t - 1) ** 3 + c1 * (t - 1) ** 2;\n}\nfunction showcaseOpacity(phase) {\n if (phase < SHOWCASE_POP_IN_MS) return easeOutCubic(phase / SHOWCASE_POP_IN_MS);\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\nfunction showcaseScale(phase) {\n if (phase < SHOWCASE_POP_IN_MS) {\n return Math.min(\n 1,\n 0.78 + 0.22 * easeOutBack(phase / SHOWCASE_POP_IN_MS)\n );\n }\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - 0.14 * easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\nfunction useShowcasePhase(enabled, cycleMs) {\n const [phase, setPhase] = (0, import_react.useState)(0);\n (0, import_react.useEffect)(() => {\n if (!enabled) return;\n setPhase(Date.now() % cycleMs);\n const id = window.setInterval(() => {\n setPhase(Date.now() % cycleMs);\n }, 48);\n return () => clearInterval(id);\n }, [enabled, cycleMs]);\n return phase;\n}\nvar OverlayRenderer = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n showcaseMode = false,\n showcaseCycleMs = SHOWCASE_CYCLE_MS_DEFAULT\n}) => {\n const [dims, setDims] = (0, import_react.useState)(null);\n const rafRef = (0, import_react.useRef)(null);\n const [fadeMap, setFadeMap] = (0, import_react.useState)(/* @__PURE__ */ new Map());\n const removeTimers = (0, import_react.useRef)(/* @__PURE__ */ new Map());\n const showcasePhase = useShowcasePhase(showcaseMode, showcaseCycleMs);\n const updateDims = (0, import_react.useCallback)(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (!computed || prev && prev.nativeWidth === computed.nativeWidth && prev.nativeHeight === computed.nativeHeight && prev.displayWidth === computed.displayWidth && prev.displayHeight === computed.displayHeight && prev.offsetX === computed.offsetX && prev.offsetY === computed.offsetY) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n (0, import_react.useEffect)(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n const activeOverlays = (0, import_react.useMemo)(() => {\n if (showcaseMode) {\n return overlays.filter((o) => {\n if (!o.visible) return false;\n return showcasePhase < SHOWCASE_POP_OUT_END_MS;\n });\n }\n return overlays.filter((o) => isOverlayActive(o, currentTime));\n }, [overlays, currentTime, showcaseMode, showcasePhase]);\n const activeBeatIds = (0, import_react.useMemo)(\n () => new Set(activeOverlays.map((o) => o.id)),\n [activeOverlays]\n );\n (0, import_react.useLayoutEffect)(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id);\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n return next;\n });\n }, [activeOverlays]);\n (0, import_react.useEffect)(() => {\n const toFadeIn = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n (0, import_react.useEffect)(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n if (!dims || fadeMap.size === 0) return null;\n return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(\n \"div\",\n {\n \"aria-hidden\": \"true\",\n style: {\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8\n },\n children: [...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX = coordinateSpace?.width ? dims.displayWidth / coordinateSpace.width : dims.scaleX;\n const scaleY = coordinateSpace?.height ? dims.displayHeight / coordinateSpace.height : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const inShowcaseBeat = showcaseMode && activeBeatIds.has(overlay.id);\n const useShowcasePop = inShowcaseBeat;\n const opacity = useShowcasePop ? baseOpacity * showcaseOpacity(showcasePhase) : visible ? baseOpacity : 0;\n const popScale = useShowcasePop ? showcaseScale(showcasePhase) : 1;\n const sz = { w: width, h: height };\n return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(\n \"div\",\n {\n style: {\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: useShowcasePop ? \"none\" : `opacity ${FADE_DURATION_MS}ms ease`,\n transform: showcaseMode && useShowcasePop ? `scale(${popScale})` : void 0,\n transformOrigin: showcaseMode && useShowcasePop ? \"center center\" : void 0,\n zIndex: overlay.z_index,\n overflow: \"hidden\"\n },\n children: [\n overlay.type === \"image\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ImageOverlay, { overlay }),\n overlay.type === \"text\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextOverlay, { overlay }),\n overlay.type === \"scroller\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScrollerOverlay, { overlay }),\n overlay.type === \"shape\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ShapeOverlay, { overlay, size: sz }),\n overlay.type === \"score_bug\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ScoreBugOverlay, { overlay, size: sz }),\n overlay.type === \"lower_third\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LowerThirdOverlay, { overlay, size: sz }),\n overlay.type === \"qr_code\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QrCodeOverlay, { overlay, size: sz }),\n overlay.type === \"coming_up_next\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ComingUpNextOverlay, { overlay, size: sz }),\n overlay.type === \"contextual_trigger\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContextualTriggerOverlay, { overlay, size: sz }),\n overlay.type === \"odds_betting\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(OddsBettingOverlay, { overlay, size: sz }),\n overlay.type === \"breaking_news\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BreakingNewsOverlay, { overlay, size: sz }),\n overlay.type === \"countdown\" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CountdownOverlay, { overlay, size: sz })\n ]\n },\n overlay.id\n );\n })\n }\n );\n};\n// Annotate the CommonJS export names for ESM import in node:\n0 && (module.exports = {\n OverlayRenderer\n});\n","import React, {\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n useCallback,\n useMemo,\n useId,\n} from \"react\";\nimport {\n type SwirlOverlay,\n isOverlayActive,\n resolveImageUrl,\n} from \"../utils/overlays\";\n\ninterface VideoDimensions {\n nativeWidth: number;\n nativeHeight: number;\n displayWidth: number;\n displayHeight: number;\n offsetX: number;\n offsetY: number;\n scaleX: number;\n scaleY: number;\n}\n\ninterface OverlayRendererProps {\n overlays: SwirlOverlay[];\n currentTime: number;\n videoRef: React.RefObject<HTMLVideoElement | null>;\n coordinateSpace?: { width: number; height: number } | null;\n showcaseMode?: boolean;\n showcaseCycleMs?: number;\n}\n\nfunction computeVideoDimensions(\n video: HTMLVideoElement\n): VideoDimensions | null {\n const nativeWidth = video.videoWidth;\n const nativeHeight = video.videoHeight;\n if (!nativeWidth || !nativeHeight) return null;\n\n const displayWidth = video.offsetWidth;\n const displayHeight = video.offsetHeight;\n if (!displayWidth || !displayHeight) return null;\n\n const videoAspect = nativeWidth / nativeHeight;\n const displayAspect = displayWidth / displayHeight;\n\n let renderWidth: number;\n let renderHeight: number;\n let offsetX: number;\n let offsetY: number;\n\n if (videoAspect > displayAspect) {\n renderWidth = displayWidth;\n renderHeight = displayWidth / videoAspect;\n offsetX = 0;\n offsetY = (displayHeight - renderHeight) / 2;\n } else {\n renderHeight = displayHeight;\n renderWidth = displayHeight * videoAspect;\n offsetX = (displayWidth - renderWidth) / 2;\n offsetY = 0;\n }\n\n return {\n nativeWidth,\n nativeHeight,\n displayWidth: renderWidth,\n displayHeight: renderHeight,\n offsetX,\n offsetY,\n scaleX: renderWidth / nativeWidth,\n scaleY: renderHeight / nativeHeight,\n };\n}\n\nfunction ImageOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const src = resolveImageUrl(overlay.image_url || \"\");\n if (!src) return null;\n return (\n <img\n src={src}\n alt={overlay.name}\n draggable={false}\n style={{\n width: \"100%\",\n height: \"100%\",\n objectFit: \"contain\",\n display: \"block\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n />\n );\n}\n\nfunction TextOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const text = overlay.content || \"\";\n return (\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n color: \"#ffffff\",\n fontSize: \"clamp(10px, 1.4vw, 20px)\",\n fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\",\n fontWeight: 600,\n textAlign: \"center\",\n padding: \"4px 8px\",\n boxSizing: \"border-box\",\n wordBreak: \"break-word\",\n textShadow: \"0 1px 4px rgba(0,0,0,0.7)\",\n pointerEvents: \"none\",\n userSelect: \"none\",\n lineHeight: 1.3,\n }}\n >\n {text}\n </div>\n );\n}\n\ninterface RSSItem {\n title: string;\n description: string;\n pubDate: string;\n author: string;\n category: string;\n}\n\nfunction parseRSSXml(xmlText: string, maxItems: number): RSSItem[] {\n const stripped = xmlText.replace(/^<\\?xml[^?]*\\?>\\s*/, \"\");\n const parser = new DOMParser();\n const doc = parser.parseFromString(stripped, \"text/xml\");\n const items = Array.from(doc.querySelectorAll(\"item\"))\n .map((item) => ({\n title: (item.querySelector(\"title\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n description: (item.querySelector(\"description\")?.textContent || \"\").replace(/<[^>]*>/g, \"\").trim(),\n pubDate: item.querySelector(\"pubDate\")?.textContent || \"\",\n author: item.querySelector(\"author, dc\\\\:creator\")?.textContent || \"\",\n category: item.querySelector(\"category\")?.textContent || \"\",\n }))\n .filter((i) => i.title)\n .slice(0, maxItems);\n if (items.length === 0 && doc.querySelector(\"parsererror\")) {\n throw new Error(\"Invalid RSS XML\");\n }\n return items;\n}\n\nconst RSS_CACHE_TTL_MS = 60_000;\nconst rssCache = new Map<string, { promise: Promise<RSSItem[]>; expiresAt: number }>();\n\nfunction cachedFetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const now = Date.now();\n const cached = rssCache.get(rssUrl);\n if (cached && cached.expiresAt > now) return cached.promise;\n const promise = fetchRSSItems(rssUrl, maxItems).catch((err) => {\n rssCache.delete(rssUrl);\n throw err;\n });\n rssCache.set(rssUrl, { promise, expiresAt: now + RSS_CACHE_TTL_MS });\n return promise;\n}\n\nasync function fetchRSSItems(rssUrl: string, maxItems: number): Promise<RSSItem[]> {\n const encoded = encodeURIComponent(rssUrl);\n\n try {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n const resp = await fetch(`${origin}/api/rss-proxy?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text.includes(\"<item\")) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://api.allorigins.win/get?url=${encoded}`);\n if (resp.ok) {\n const data = await resp.json();\n if (data.contents) return parseRSSXml(data.contents, maxItems);\n }\n } catch { /* fall through */ }\n\n try {\n const resp = await fetch(`https://corsproxy.io/?url=${encoded}`);\n if (resp.ok) {\n const text = await resp.text();\n if (text) return parseRSSXml(text, maxItems);\n }\n } catch { /* fall through */ }\n\n throw new Error(\"All RSS proxies failed\");\n}\n\nfunction ScrollerOverlay({ overlay }: { overlay: SwirlOverlay }) {\n const cfg = overlay.scroller_config;\n const uid = useId().replace(/:/g, \"\");\n\n const [rssItems, setRssItems] = useState<RSSItem[]>([]);\n const [rssLoading, setRssLoading] = useState(true);\n const [rssError, setRssError] = useState(false);\n\n const rssUrl = cfg?.rss_url || \"\";\n const maxItems = cfg?.max_items ?? 10;\n const autoRefresh = cfg?.auto_refresh !== false;\n const updateInterval = cfg?.update_interval ?? 5;\n\n useEffect(() => {\n if (!rssUrl || (cfg?.use_custom_text && cfg?.custom_text)) {\n setRssLoading(false);\n return;\n }\n let cancelled = false;\n setRssLoading(true);\n setRssError(false);\n cachedFetchRSSItems(rssUrl, maxItems)\n .then((items) => { if (!cancelled) { setRssItems(items); setRssError(false); } })\n .catch(() => { if (!cancelled) setRssError(true); })\n .finally(() => { if (!cancelled) setRssLoading(false); });\n return () => { cancelled = true; };\n }, [rssUrl, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n useEffect(() => {\n if (!rssUrl || !autoRefresh || (cfg?.use_custom_text && cfg?.custom_text)) return;\n const interval = setInterval(() => {\n rssCache.delete(rssUrl);\n cachedFetchRSSItems(rssUrl, maxItems)\n .then((items) => { setRssItems(items); setRssError(false); })\n .catch(() => { /* keep showing last good items */ });\n }, updateInterval * 60 * 1000);\n return () => clearInterval(interval);\n }, [rssUrl, autoRefresh, updateInterval, maxItems, cfg?.use_custom_text, cfg?.custom_text]);\n\n const sep = cfg?.separator_char ?? \"◆\";\n\n let segments: string[];\n if (cfg?.use_custom_text && cfg?.custom_text) {\n segments = [cfg.custom_text];\n } else if (rssItems.length > 0) {\n segments = rssItems.map((item) => {\n const parts: string[] = [];\n if (cfg?.show_title !== false && item.title) parts.push(item.title);\n if (cfg?.show_description && item.description) parts.push(item.description);\n if (cfg?.show_timestamp && item.pubDate) {\n try { parts.push(new Date(item.pubDate).toLocaleDateString()); } catch { /* ignore */ }\n }\n if (cfg?.show_author && item.author) parts.push(`— ${item.author}`);\n if (cfg?.show_category && item.category) parts.push(`[${item.category}]`);\n return parts.join(\" \");\n });\n } else if (rssLoading) {\n segments = [\"Loading feed…\"];\n } else if (rssError) {\n segments = overlay.content ? [overlay.content] : [\"RSS feed unavailable\"];\n } else if (overlay.content) {\n segments = [overlay.content];\n } else {\n segments = rssUrl ? [\"Loading feed…\"] : [\"RSS Ticker\"];\n }\n\n const scrollSpeed = cfg?.scroll_speed ?? 40;\n const direction = cfg?.direction ?? \"left\";\n const fontSize = cfg?.font_size ?? 15;\n const fontFamily = cfg?.font_family || \"Roboto, 'Segoe UI', Arial, sans-serif\";\n const fontWeight = cfg?.font_weight || \"700\";\n const textColor = cfg?.text_color || \"#ffffff\";\n const bgColor = cfg?.background_color || \"#0d0d1a\";\n const bgOpacity = cfg?.background_opacity !== undefined ? cfg.background_opacity / 100 : 0.95;\n const borderRadius = cfg?.border_radius ?? 0;\n const itemSpacing = cfg?.item_spacing ?? 60;\n\n const label = cfg?.label ?? \"NEWS\";\n const labelLine2 = cfg?.label_line2 ?? \"\";\n const labelColor = cfg?.label_color ?? \"#f97316\";\n const labelTextColor = cfg?.label_text_color ?? \"#ffffff\";\n const accentColor = cfg?.accent_color ?? labelColor;\n const showAccentLine = cfg?.show_accent_line !== false;\n\n const isHorizontal = direction === \"left\" || direction === \"right\";\n const isReverse = direction === \"right\" || direction === \"down\";\n\n const fullText = segments.join(` ${sep} `);\n const durationSec = Math.max(6, (fullText.length * 9) / scrollSpeed);\n\n const animId = `sc-ticker-${overlay.id}-${uid}`;\n const keyframes = isHorizontal\n ? `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateX(-50%); } 100% { transform: translateX(0%); }\"\n : \"0% { transform: translateX(0); } 100% { transform: translateX(-50%); }\"}\n }`\n : `@keyframes ${animId} {\n ${isReverse\n ? \"0% { transform: translateY(-50%); } 100% { transform: translateY(0%); }\"\n : \"0% { transform: translateY(0); } 100% { transform: translateY(-50%); }\"}\n }`;\n\n return (\n <>\n <style>{keyframes}</style>\n <div\n style={{\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n overflow: \"hidden\",\n borderRadius: borderRadius > 0 ? `${borderRadius}px` : undefined,\n backgroundColor: `rgba(${hexToRgb(bgColor)}, ${bgOpacity})`,\n fontFamily,\n fontSize: `${fontSize}px`,\n fontWeight,\n color: textColor,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n >\n {/* Top accent line */}\n {showAccentLine && (\n <div style={{ height: 3, background: accentColor, flexShrink: 0, width: \"100%\" }} />\n )}\n\n {/* Main row */}\n <div style={{ display: \"flex\", flex: 1, overflow: \"hidden\", minHeight: 0 }}>\n {/* Label badge */}\n {label && (\n <div\n style={{\n background: labelColor,\n color: labelTextColor,\n padding: \"0 14px\",\n display: \"flex\",\n flexDirection: \"column\",\n alignItems: \"center\",\n justifyContent: \"center\",\n flexShrink: 0,\n minWidth: 72,\n textAlign: \"center\",\n gap: 1,\n }}\n >\n <span\n style={{\n fontWeight: 800,\n fontSize: \"0.82em\",\n letterSpacing: \"0.05em\",\n lineHeight: 1.1,\n textTransform: \"uppercase\",\n whiteSpace: \"nowrap\",\n }}\n >\n {label}\n </span>\n {labelLine2 && (\n <span\n style={{\n fontWeight: 500,\n fontSize: \"0.62em\",\n letterSpacing: \"0.03em\",\n lineHeight: 1.1,\n opacity: 0.85,\n whiteSpace: \"nowrap\",\n }}\n >\n {labelLine2}\n </span>\n )}\n </div>\n )}\n\n {/* Accent divider */}\n {label && (\n <div style={{ width: 3, background: accentColor, flexShrink: 0 }} />\n )}\n\n {/* Scrolling text */}\n <div\n style={{\n flex: 1,\n overflow: \"hidden\",\n position: \"relative\",\n display: \"flex\",\n alignItems: \"center\",\n }}\n >\n {isHorizontal ? (\n <div\n style={{\n display: \"inline-flex\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) => (\n <span key={copy} style={{ paddingRight: `${itemSpacing}px` }}>\n {segments.map((seg, i) => (\n <React.Fragment key={i}>\n {i > 0 && (\n <span style={{ opacity: 0.5, margin: \"0 8px\" }}>{sep}</span>\n )}\n <span style={{ textShadow: \"0 1px 3px rgba(0,0,0,0.6)\" }}>{seg}</span>\n </React.Fragment>\n ))}\n </span>\n ))}\n </div>\n ) : (\n <div\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n whiteSpace: \"nowrap\",\n animation: `${animId} ${durationSec}s linear infinite`,\n willChange: \"transform\",\n }}\n >\n {[0, 1].map((copy) =>\n segments.map((seg, i) => (\n <div key={`${copy}-${i}`} style={{ paddingBottom: `${itemSpacing / 4}px` }}>\n {seg}\n </div>\n ))\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n </>\n );\n}\n\nfunction parseConfig<T>(content?: string): T | null {\n if (!content) return null;\n try { return JSON.parse(content) as T; } catch { return null; }\n}\n\ninterface OverlaySize { w: number; h: number; }\n\ninterface ScoreBugCfg { homeTeam: string; awayTeam: string; homeScore: number; awayScore: number; period: string; clock: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface LowerThirdCfg { headline: string; subtitle: string; sponsorText: string; sponsorImageUrl: string; backgroundColor: string; textColor: string; accentColor: string; style: string; }\ninterface QrCodeCfg { url: string; ctaText: string; description: string; size: number; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ComingUpNextCfg { title: string; subtitle: string; scheduledTime: string; thumbnailUrl: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface ContextualTriggerCfg { triggerType: string; headline: string; message: string; iconType: string; backgroundColor: string; textColor: string; accentColor: string; animationStyle: string; }\ninterface OddsBettingCfg { eventTitle: string; options: Array<{ label: string; odds: string }>; sponsorText: string; backgroundColor: string; textColor: string; accentColor: string; oddsFormat: string; }\ninterface BreakingNewsCfg { headline: string; body: string; urgency: string; backgroundColor: string; textColor: string; accentColor: string; }\ninterface CountdownCfg { eventName: string; targetTime: string; message: string; showDays: boolean; showHours: boolean; showMinutes: boolean; showSeconds: boolean; backgroundColor: string; textColor: string; accentColor: string; }\n\nfunction ScoreBugOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ScoreBugCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.058);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ flex: 1, display: \"flex\", alignItems: \"center\", padding: `0 ${f * 0.8}px`, gap: f * 0.4 }}>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.homeTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.homeScore}</div>\n </div>\n <div style={{ fontSize: \"0.8em\", textAlign: \"center\", opacity: 0.7, padding: `0 ${f * 0.4}px` }}>\n <div>{cfg.period}</div>\n <div>{cfg.clock}</div>\n </div>\n <div style={{ flex: 1, textAlign: \"center\" }}>\n <div style={{ fontSize: \"1em\", fontWeight: 700 }}>{cfg.awayTeam}</div>\n <div style={{ fontSize: \"1.8em\", fontWeight: 900, lineHeight: 1 }}>{cfg.awayScore}</div>\n </div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", textAlign: \"center\", opacity: 0.6, padding: `${f * 0.2}px ${f * 0.4}px`, borderTop: `1px solid ${cfg.accentColor}40`, display: \"flex\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction LowerThirdOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<LowerThirdCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.055);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", flexDirection: \"column\", justifyContent: \"flex-end\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ width: \"100%\", height: Math.max(2, size.h * 0.06), backgroundColor: cfg.accentColor }} />\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\", padding: `${f * 0.5}px ${f * 1.2}px` }}>\n <div style={{ fontSize: \"1.4em\", fontWeight: 700, lineHeight: 1.2, textShadow: \"0 1px 4px rgba(0,0,0,0.5)\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"1em\", opacity: 0.7, marginTop: f * 0.2 }}>{cfg.subtitle}</div>\n </div>\n {(cfg.sponsorText || cfg.sponsorImageUrl) && (\n <div style={{ fontSize: \"0.7em\", opacity: 0.5, padding: `0 ${f * 1.2}px ${f * 0.4}px`, display: \"flex\", alignItems: \"center\", gap: f * 0.4, overflow: \"hidden\" }}>\n {cfg.sponsorImageUrl && <img src={cfg.sponsorImageUrl} alt=\"sponsor\" style={{ height: `${f * 1.4}px`, objectFit: \"contain\", flexShrink: 0 }} />}\n {cfg.sponsorText && <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.sponsorText}</span>}\n </div>\n )}\n </div>\n );\n}\n\nfunction QrCodeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<QrCodeCfg>(overlay.content);\n if (!cfg) return null;\n const qrSide = Math.max(32, Math.min(size.w, size.h) * 0.55);\n const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${Math.round(qrSide * 2)}x${Math.round(qrSide * 2)}&data=${encodeURIComponent(cfg.url || \"https://example.com\")}`;\n const f = Math.max(6, size.w * 0.06);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", gap: f * 0.4, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", padding: f * 0.6, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", overflow: \"hidden\" }}>\n <div style={{ flexShrink: 0, background: \"#fff\", borderRadius: Math.max(2, qrSide * 0.06), padding: Math.max(2, qrSide * 0.06), lineHeight: 0 }}>\n <img src={qrUrl} alt=\"QR Code\" style={{ width: `${qrSide}px`, height: `${qrSide}px`, display: \"block\" }} />\n </div>\n <div style={{ fontSize: `${f * 1.1}px`, fontWeight: 700, textAlign: \"center\", color: cfg.accentColor, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.ctaText}</div>\n {cfg.description && <div style={{ fontSize: `${f * 0.75}px`, opacity: 0.6, textAlign: \"center\", overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", width: \"100%\" }}>{cfg.description}</div>}\n </div>\n );\n}\n\nfunction ComingUpNextOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ComingUpNextCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(8, Math.min(size.h * 0.13, size.w * 0.048));\n const showSubtitle = size.h >= 60;\n const showThumbnail = false;\n const thumbW = 0;\n return (\n <div style={{\n width: \"100%\", height: \"100%\",\n borderRadius: Math.max(2, size.w * 0.025),\n display: \"flex\",\n background: cfg.backgroundColor,\n color: cfg.textColor,\n fontFamily: \"'Arial', 'Helvetica Neue', Helvetica, sans-serif\",\n overflow: \"hidden\",\n pointerEvents: \"none\", userSelect: \"none\",\n fontSize: `${f}px`,\n WebkitFontSmoothing: \"antialiased\",\n } as React.CSSProperties}>\n <div style={{ width: Math.max(3, size.w * 0.018), flexShrink: 0, backgroundColor: cfg.accentColor }} />\n <div style={{\n flex: 1, display: \"flex\", flexDirection: \"column\", justifyContent: \"center\",\n padding: `${f * 0.35}px ${f * 0.75}px`,\n minWidth: 0, gap: `${f * 0.08}px`,\n }}>\n <div style={{\n fontSize: \"0.7em\", fontWeight: 700,\n textTransform: \"uppercase\", letterSpacing: \"0.09em\",\n color: cfg.accentColor, lineHeight: 1,\n }}>\n Coming Up Next\n </div>\n <div style={{\n fontSize: \"1.2em\", fontWeight: 700, lineHeight: 1.2,\n overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\",\n }}>\n {cfg.title}\n </div>\n {showSubtitle && (\n <div style={{\n fontSize: \"0.82em\", opacity: 0.65, lineHeight: 1.1,\n overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\",\n }}>\n {cfg.subtitle}\n </div>\n )}\n {cfg.scheduledTime && (\n <div style={{\n fontSize: \"0.9em\", fontWeight: 700,\n color: cfg.accentColor, lineHeight: 1,\n }}>\n {cfg.scheduledTime}\n </div>\n )}\n </div>\n {showThumbnail && (\n <div style={{ flexShrink: 0, width: `${thumbW}px`, overflow: \"hidden\" }}>\n <img\n src={cfg.thumbnailUrl}\n alt=\"\"\n style={{ width: \"100%\", height: \"100%\", objectFit: \"cover\", display: \"block\" }}\n />\n </div>\n )}\n </div>\n );\n}\n\nfunction ContextualTriggerOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<ContextualTriggerCfg>(overlay.content);\n if (!cfg) return null;\n const icons: Record<string, string> = { alert: \"\\u26A0\\uFE0F\", celebration: \"\\uD83C\\uDF89\", info: \"\\u2139\\uFE0F\", warning: \"\\uD83D\\uDD14\" };\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", alignItems: \"center\", gap: f * 0.8, padding: `0 ${f * 1.2}px`, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", borderLeft: `${Math.max(2, size.w * 0.02)}px solid ${cfg.accentColor}`, boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <span style={{ fontSize: \"2em\", flexShrink: 0 }}>{icons[cfg.iconType] || \"\\u26A1\"}</span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.message}</div>\n </div>\n </div>\n );\n}\n\nfunction OddsBettingOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<OddsBettingCfg>(overlay.content);\n if (!cfg) return null;\n const f = Math.max(6, size.w * 0.052);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.9em\", fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventTitle}</div>\n <div style={{ flex: 1, display: \"flex\", flexDirection: \"column\", gap: f * 0.2, justifyContent: \"center\" }}>\n {(cfg.options || []).slice(0, 5).map((opt, i) => (\n <div key={i} style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"center\", padding: `${f * 0.2}px ${f * 0.6}px`, borderRadius: Math.max(2, f * 0.3), background: `${cfg.accentColor}15`, fontSize: \"1em\" }}>\n <span style={{ overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\", flex: 1 }}>{opt.label}</span>\n <span style={{ fontWeight: 700, marginLeft: f * 0.8, flexShrink: 0, color: cfg.accentColor }}>{opt.odds}</span>\n </div>\n ))}\n </div>\n {cfg.sponsorText && <div style={{ fontSize: \"0.7em\", opacity: 0.4, textAlign: \"center\", marginTop: f * 0.4 }}>{cfg.sponsorText}</div>}\n </div>\n );\n}\n\nfunction BreakingNewsOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<BreakingNewsCfg>(overlay.content);\n if (!cfg) return null;\n const urgencyColors: Record<string, string> = { breaking: \"#dc2626\", urgent: \"#ea580c\", normal: \"#2563eb\" };\n const labelBg = urgencyColors[cfg.urgency] || urgencyColors.normal;\n const label = cfg.urgency === \"breaking\" ? \"BREAKING\" : cfg.urgency === \"urgent\" ? \"URGENT\" : \"NEWS\";\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.02), display: \"flex\", alignItems: \"center\", background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", overflow: \"hidden\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ padding: `0 ${f * 0.8}px`, height: \"100%\", display: \"flex\", alignItems: \"center\", background: labelBg, color: \"#fff\", fontSize: \"1em\", fontWeight: 900, textTransform: \"uppercase\", letterSpacing: \"0.05em\", flexShrink: 0 }}>{label}</div>\n <div style={{ flex: 1, padding: `0 ${f * 1.0}px`, minWidth: 0 }}>\n <div style={{ fontSize: \"1.3em\", fontWeight: 700, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.headline}</div>\n {cfg.body && <div style={{ fontSize: \"0.9em\", opacity: 0.7, overflow: \"hidden\", textOverflow: \"ellipsis\", whiteSpace: \"nowrap\" }}>{cfg.body}</div>}\n </div>\n </div>\n );\n}\n\nfunction calcCountdownRemaining(targetTime: string) {\n const diff = Math.max(0, new Date(targetTime).getTime() - Date.now());\n return {\n d: Math.floor(diff / 86400000),\n h: Math.floor((diff % 86400000) / 3600000),\n m: Math.floor((diff % 3600000) / 60000),\n s: Math.floor((diff % 60000) / 1000),\n expired: diff === 0,\n };\n}\n\nfunction CountdownOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const cfg = parseConfig<CountdownCfg>(overlay.content);\n const targetTime = cfg?.targetTime ?? \"\";\n const [remaining, setRemaining] = useState(() =>\n targetTime ? calcCountdownRemaining(targetTime) : { d: 0, h: 0, m: 0, s: 0, expired: false }\n );\n\n useEffect(() => {\n if (!targetTime) return;\n setRemaining(calcCountdownRemaining(targetTime));\n const id = setInterval(() => setRemaining(calcCountdownRemaining(targetTime)), 1000);\n return () => clearInterval(id);\n }, [targetTime]);\n\n if (!cfg) return null;\n\n const f = Math.max(6, size.w * 0.055);\n const pad = (n: number) => String(n).padStart(2, \"0\");\n const units: Array<{ show: boolean; value: string; label: string }> = [\n { show: cfg.showDays, value: pad(remaining.d), label: \"DAYS\" },\n { show: cfg.showHours, value: pad(remaining.h), label: \"HRS\" },\n { show: cfg.showMinutes, value: pad(remaining.m), label: \"MIN\" },\n { show: cfg.showSeconds, value: pad(remaining.s), label: \"SEC\" },\n ];\n\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.035), display: \"flex\", flexDirection: \"column\", alignItems: \"center\", justifyContent: \"center\", padding: f * 0.8, background: cfg.backgroundColor, color: cfg.textColor, fontFamily: \"Roboto, 'Segoe UI', Arial, sans-serif\", boxSizing: \"border-box\", pointerEvents: \"none\", userSelect: \"none\", fontSize: `${f}px` }}>\n <div style={{ fontSize: \"0.8em\", fontWeight: 600, textTransform: \"uppercase\", letterSpacing: \"0.05em\", color: cfg.accentColor, marginBottom: f * 0.4 }}>{cfg.eventName}</div>\n {remaining.expired ? (\n <div style={{ fontSize: \"1em\", fontWeight: 700, opacity: 0.6 }}>{cfg.message || \"Event ended\"}</div>\n ) : (\n <div style={{ display: \"flex\", gap: f * 0.6, alignItems: \"center\" }}>\n {units.filter(u => u.show).map((u, i, arr) => (\n <React.Fragment key={u.label}>\n <div style={{ textAlign: \"center\" }}>\n <div style={{ fontSize: \"2em\", fontWeight: 900, lineHeight: 1, borderRadius: Math.max(2, f * 0.4), padding: `${f * 0.2}px ${f * 0.4}px`, background: `${cfg.accentColor}20` }}>{u.value}</div>\n <div style={{ fontSize: \"0.5em\", opacity: 0.5, marginTop: f * 0.2 }}>{u.label}</div>\n </div>\n {i < arr.length - 1 && <div style={{ fontSize: \"1.8em\", fontWeight: 700, opacity: 0.3 }}>:</div>}\n </React.Fragment>\n ))}\n </div>\n )}\n {!remaining.expired && cfg.message && <div style={{ fontSize: \"0.8em\", opacity: 0.6, marginTop: f * 0.4, textAlign: \"center\" }}>{cfg.message}</div>}\n </div>\n );\n}\n\nfunction ShapeOverlay({ overlay, size }: { overlay: SwirlOverlay; size: OverlaySize }) {\n const f = Math.max(6, size.w * 0.05);\n return (\n <div style={{ width: \"100%\", height: \"100%\", borderRadius: Math.max(2, size.w * 0.03), background: \"rgba(99, 102, 241, 0.2)\", border: \"2px solid rgba(99, 102, 241, 0.4)\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", pointerEvents: \"none\", userSelect: \"none\" }}>\n <div style={{ fontSize: `${f}px`, fontWeight: 500, color: \"rgba(163, 163, 163, 0.8)\", textTransform: \"uppercase\" }}>{overlay.name}</div>\n </div>\n );\n}\n\nfunction hexToRgb(hex: string): string {\n if (!hex || !hex.startsWith(\"#\")) return \"0,0,0\";\n const clean = hex.slice(1);\n const num = parseInt(clean.length === 3 ? clean.replace(/./g, \"$&$&\") : clean, 16);\n return `${(num >> 16) & 255},${(num >> 8) & 255},${num & 255}`;\n}\n\ninterface OverlayFadeState {\n overlay: SwirlOverlay;\n visible: boolean;\n}\n\nconst FADE_DURATION_MS = 1000;\n\n/** One full cycle (~60s): pop in → hold → pop out → quiet until next cycle. */\nconst SHOWCASE_CYCLE_MS_DEFAULT = 60_000;\nconst SHOWCASE_POP_IN_MS = 420;\nconst SHOWCASE_STEADY_END_MS = 46_000;\nconst SHOWCASE_POP_OUT_MS = 450;\nconst SHOWCASE_POP_OUT_END_MS = SHOWCASE_STEADY_END_MS + SHOWCASE_POP_OUT_MS;\n\nfunction easeOutCubic(t: number): number {\n const u = 1 - t;\n return 1 - u * u * u;\n}\n\nfunction easeInCubic(t: number): number {\n return t * t * t;\n}\n\nfunction easeOutBack(t: number): number {\n const c1 = 1.70158;\n const c3 = c1 + 1;\n return 1 + c3 * (t - 1) ** 3 + c1 * (t - 1) ** 2;\n}\n\nfunction showcaseOpacity(phase: number): number {\n if (phase < SHOWCASE_POP_IN_MS) return easeOutCubic(phase / SHOWCASE_POP_IN_MS);\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\n\nfunction showcaseScale(phase: number): number {\n if (phase < SHOWCASE_POP_IN_MS) {\n return Math.min(\n 1,\n 0.78 + 0.22 * easeOutBack(phase / SHOWCASE_POP_IN_MS)\n );\n }\n if (phase >= SHOWCASE_STEADY_END_MS && phase < SHOWCASE_POP_OUT_END_MS) {\n return 1 - 0.14 * easeInCubic((phase - SHOWCASE_STEADY_END_MS) / SHOWCASE_POP_OUT_MS);\n }\n return 1;\n}\n\nfunction useShowcasePhase(enabled: boolean, cycleMs: number): number {\n const [phase, setPhase] = useState(0);\n useEffect(() => {\n if (!enabled) return;\n setPhase(Date.now() % cycleMs);\n const id = window.setInterval(() => {\n setPhase(Date.now() % cycleMs);\n }, 48);\n return () => clearInterval(id);\n }, [enabled, cycleMs]);\n return phase;\n}\n\nexport const OverlayRenderer: React.FC<OverlayRendererProps> = ({\n overlays,\n currentTime,\n videoRef,\n coordinateSpace,\n showcaseMode = false,\n showcaseCycleMs = SHOWCASE_CYCLE_MS_DEFAULT,\n}) => {\n const [dims, setDims] = useState<VideoDimensions | null>(null);\n const rafRef = useRef<number | null>(null);\n const [fadeMap, setFadeMap] = useState<Map<number, OverlayFadeState>>(new Map());\n const removeTimers = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());\n\n const showcasePhase = useShowcasePhase(showcaseMode, showcaseCycleMs);\n\n const updateDims = useCallback(() => {\n const video = videoRef.current;\n if (video) {\n const computed = computeVideoDimensions(video);\n setDims((prev) => {\n if (\n !computed ||\n (prev &&\n prev.nativeWidth === computed.nativeWidth &&\n prev.nativeHeight === computed.nativeHeight &&\n prev.displayWidth === computed.displayWidth &&\n prev.displayHeight === computed.displayHeight &&\n prev.offsetX === computed.offsetX &&\n prev.offsetY === computed.offsetY)\n ) {\n return prev;\n }\n return computed;\n });\n }\n }, [videoRef]);\n\n useEffect(() => {\n updateDims();\n const interval = setInterval(updateDims, 500);\n\n const handleResize = () => {\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(updateDims);\n };\n window.addEventListener(\"resize\", handleResize);\n\n return () => {\n clearInterval(interval);\n window.removeEventListener(\"resize\", handleResize);\n if (rafRef.current) cancelAnimationFrame(rafRef.current);\n };\n }, [updateDims]);\n\n const activeOverlays = useMemo(() => {\n if (showcaseMode) {\n return overlays.filter((o) => {\n if (!o.visible) return false;\n return showcasePhase < SHOWCASE_POP_OUT_END_MS;\n });\n }\n return overlays.filter((o) => isOverlayActive(o, currentTime));\n }, [overlays, currentTime, showcaseMode, showcasePhase]);\n\n const activeBeatIds = useMemo(\n () => new Set(activeOverlays.map((o) => o.id)),\n [activeOverlays]\n );\n\n useLayoutEffect(() => {\n const activeIds = new Set(activeOverlays.map((o) => o.id));\n\n setFadeMap((prev) => {\n const next = new Map(prev);\n\n for (const overlay of activeOverlays) {\n if (!next.has(overlay.id)) {\n next.set(overlay.id, { overlay, visible: false });\n } else {\n const existing = next.get(overlay.id)!;\n next.set(overlay.id, { ...existing, overlay });\n }\n }\n\n for (const [id, state] of next) {\n if (!activeIds.has(id) && state.visible) {\n next.set(id, { ...state, visible: false });\n if (!removeTimers.current.has(id)) {\n const timer = setTimeout(() => {\n setFadeMap((m) => {\n const updated = new Map(m);\n updated.delete(id);\n return updated;\n });\n removeTimers.current.delete(id);\n }, FADE_DURATION_MS);\n removeTimers.current.set(id, timer);\n }\n } else if (!activeIds.has(id) && !state.visible) {\n }\n }\n\n return next;\n });\n }, [activeOverlays]);\n\n useEffect(() => {\n const toFadeIn: number[] = [];\n for (const [id, state] of fadeMap) {\n if (!state.visible) {\n const isActive = activeOverlays.some((o) => o.id === id);\n if (isActive) toFadeIn.push(id);\n }\n }\n if (toFadeIn.length === 0) return;\n\n const raf = requestAnimationFrame(() => {\n setFadeMap((prev) => {\n const next = new Map(prev);\n for (const id of toFadeIn) {\n const state = next.get(id);\n if (state) next.set(id, { ...state, visible: true });\n }\n return next;\n });\n });\n return () => cancelAnimationFrame(raf);\n }, [fadeMap, activeOverlays]);\n\n useEffect(() => {\n return () => {\n for (const timer of removeTimers.current.values()) clearTimeout(timer);\n };\n }, []);\n\n if (!dims || fadeMap.size === 0) return null;\n\n return (\n <div\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n left: `${dims.offsetX}px`,\n top: `${dims.offsetY}px`,\n width: `${dims.displayWidth}px`,\n height: `${dims.displayHeight}px`,\n pointerEvents: \"none\",\n overflow: \"hidden\",\n zIndex: 8,\n }}\n >\n {[...fadeMap.values()].map(({ overlay, visible }) => {\n const scaleX =\n coordinateSpace?.width\n ? dims.displayWidth / coordinateSpace.width\n : dims.scaleX;\n const scaleY =\n coordinateSpace?.height\n ? dims.displayHeight / coordinateSpace.height\n : dims.scaleY;\n const left = overlay.x * scaleX;\n const top = overlay.y * scaleY;\n const width = overlay.width * scaleX;\n const height = overlay.height * scaleY;\n const baseOpacity = Math.max(0, Math.min(100, overlay.opacity)) / 100;\n const inShowcaseBeat = showcaseMode && activeBeatIds.has(overlay.id);\n const useShowcasePop = inShowcaseBeat;\n const opacity = useShowcasePop\n ? baseOpacity * showcaseOpacity(showcasePhase)\n : visible\n ? baseOpacity\n : 0;\n const popScale = useShowcasePop ? showcaseScale(showcasePhase) : 1;\n const sz: OverlaySize = { w: width, h: height };\n\n return (\n <div\n key={overlay.id}\n style={{\n position: \"absolute\",\n left: `${left}px`,\n top: `${top}px`,\n width: `${width}px`,\n height: `${height}px`,\n opacity,\n transition: useShowcasePop\n ? \"none\"\n : `opacity ${FADE_DURATION_MS}ms ease`,\n transform: showcaseMode && useShowcasePop ? `scale(${popScale})` : undefined,\n transformOrigin: showcaseMode && useShowcasePop ? \"center center\" : undefined,\n zIndex: overlay.z_index,\n overflow: \"hidden\",\n }}\n >\n {overlay.type === \"image\" && <ImageOverlay overlay={overlay} />}\n {overlay.type === \"text\" && <TextOverlay overlay={overlay} />}\n {overlay.type === \"scroller\" && <ScrollerOverlay overlay={overlay} />}\n {overlay.type === \"shape\" && <ShapeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"score_bug\" && <ScoreBugOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"lower_third\" && <LowerThirdOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"qr_code\" && <QrCodeOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"coming_up_next\" && <ComingUpNextOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"contextual_trigger\" && <ContextualTriggerOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"odds_betting\" && <OddsBettingOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"breaking_news\" && <BreakingNewsOverlay overlay={overlay} size={sz} />}\n {overlay.type === \"countdown\" && <CountdownOverlay overlay={overlay} size={sz} />}\n </div>\n );\n })}\n </div>\n );\n};\n","export const OVERLAY_API_BASE = \"https://adstorm.co/api-adstorm-dev\";\n\nexport interface OverlayCoordinateSpace {\n width: number;\n height: number;\n}\n\nexport interface SwirlScrollerConfig {\n rss_url?: string;\n update_interval?: number;\n scroll_speed?: number;\n direction?: string;\n font_size?: number;\n font_family?: string;\n font_weight?: string;\n text_color?: string;\n background_color?: string;\n background_opacity?: number;\n border_color?: string;\n border_width?: number;\n border_radius?: number;\n padding?: number;\n margin?: number;\n show_title?: boolean;\n show_description?: boolean;\n show_timestamp?: boolean;\n show_author?: boolean;\n show_category?: boolean;\n max_items?: number;\n item_spacing?: number;\n fade_in_out?: boolean;\n fade_distance?: number;\n auto_refresh?: boolean;\n use_custom_text?: boolean;\n custom_text?: string;\n label?: string;\n label_line2?: string;\n label_color?: string;\n label_text_color?: string;\n accent_color?: string;\n show_accent_line?: boolean;\n separator_char?: string;\n preset?: string;\n}\n\nexport type SwirlOverlayType =\n | \"image\"\n | \"text\"\n | \"scroller\"\n | \"shape\"\n | \"score_bug\"\n | \"lower_third\"\n | \"qr_code\"\n | \"coming_up_next\"\n | \"contextual_trigger\"\n | \"odds_betting\"\n | \"breaking_news\"\n | \"countdown\";\n\nexport interface SwirlOverlay {\n id: number;\n project_id: number;\n name: string;\n type: SwirlOverlayType | string;\n visible: boolean;\n x: number;\n y: number;\n width: number;\n height: number;\n opacity: number;\n start_time: string;\n duration: string;\n content?: string;\n image_url?: string;\n scroller_config?: SwirlScrollerConfig;\n z_index: number;\n created_at?: string;\n updated_at?: string;\n}\n\nexport function timeStringToSeconds(timeStr: string): number {\n if (!timeStr) return 0;\n\n const parts = timeStr.split(\":\");\n\n if (parts.length >= 3) {\n const hours = parseInt(parts[0] ?? \"0\", 10) || 0;\n const minutes = parseInt(parts[1] ?? \"0\", 10) || 0;\n const secStr = parts[2] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return hours * 3600 + minutes * 60 + seconds + ms / 1000;\n }\n\n if (parts.length === 2) {\n const minutes = parseInt(parts[0] ?? \"0\", 10) || 0;\n const secStr = parts[1] ?? \"0\";\n const dotIdx = secStr.indexOf(\".\");\n const seconds =\n parseInt(dotIdx >= 0 ? secStr.substring(0, dotIdx) : secStr, 10) || 0;\n const msFrag = dotIdx >= 0 ? secStr.substring(dotIdx + 1) : \"\";\n const ms = msFrag ? parseInt(msFrag.padEnd(3, \"0\").substring(0, 3), 10) || 0 : 0;\n return minutes * 60 + seconds + ms / 1000;\n }\n\n const num = parseFloat(timeStr);\n return isFinite(num) ? Math.max(0, num) : 0;\n}\n\nexport function isOverlayActive(\n overlay: SwirlOverlay,\n currentTime: number\n): boolean {\n if (!overlay.visible) return false;\n const startSec = timeStringToSeconds(overlay.start_time);\n const durationSec = timeStringToSeconds(overlay.duration);\n if (durationSec <= 0) return false;\n return currentTime >= startSec && currentTime < startSec + durationSec;\n}\n\nexport function scrollerLooksLikeMarketsStock(o: SwirlOverlay): boolean {\n if (o.type !== \"scroller\") return false;\n const cfg = o.scroller_config;\n const blob = `${o.name} ${cfg?.label ?? \"\"} ${cfg?.label_line2 ?? \"\"} ${cfg?.custom_text ?? \"\"} ${cfg?.preset ?? \"\"}`;\n return /\\b(MARKETS?|NYSE|NASDAQ|DJIA|\\bS&P\\b|STOCK|AAPL|TSLA|NVDA|EQUITIES)\\b/i.test(blob);\n}\n\nexport type DemoStockTickerOptions = {\n stackAboveNews?: boolean;\n};\n\nexport function createDemoStockTickerOverlay(\n projectId: number,\n opts?: DemoStockTickerOptions\n): SwirlOverlay {\n const stack = opts?.stackAboveNews === true;\n return {\n id: -9001,\n project_id: projectId,\n name: \"Demo stock ticker\",\n type: \"scroller\",\n visible: true,\n x: 144,\n y: stack ? 818 : 950,\n width: 1632,\n height: stack ? 78 : 97,\n opacity: 100,\n start_time: \"00:00:00.000\",\n duration: \"24:00:00.000\",\n z_index: stack ? 125 : 120,\n scroller_config: {\n use_custom_text: true,\n custom_text:\n \"AAPL +1.24% • MSFT +0.42% • GOOGL −0.11% • TSLA +2.31% • NVDA +1.87% • AMZN +0.65% • META +0.38% • DJIA +0.52% • S&P 500 +0.33% • NDAQ +0.41%\",\n direction: \"left\",\n scroll_speed: 42,\n font_size: 15,\n font_weight: \"700\",\n text_color: \"#f8fafc\",\n background_color: \"#0f172a\",\n background_opacity: 92,\n border_radius: 4,\n label: \"MARKETS\",\n label_line2: \"LIVE\",\n label_color: \"#0ea5e9\",\n label_text_color: \"#ffffff\",\n accent_color: \"#38bdf8\",\n show_accent_line: true,\n separator_char: \"•\",\n },\n };\n}\n\nfunction normalizeScrollerConfig(\n raw: SwirlScrollerConfig | Record<string, unknown> | undefined\n): SwirlScrollerConfig | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const r = raw as Record<string, unknown>;\n const merged: SwirlScrollerConfig = { ...(raw as SwirlScrollerConfig) };\n if (merged.use_custom_text === undefined && typeof r.useCustomText === \"boolean\") {\n merged.use_custom_text = r.useCustomText;\n }\n if ((merged.custom_text === undefined || merged.custom_text === \"\") && typeof r.customText === \"string\") {\n merged.custom_text = r.customText;\n }\n if (!merged.rss_url && typeof r.rssUrl === \"string\") {\n merged.rss_url = r.rssUrl;\n }\n return merged;\n}\n\nexport function normalizeSwirlOverlay(raw: SwirlOverlay & Record<string, unknown>): SwirlOverlay {\n const o = { ...raw };\n if (o.type === \"scroller\") {\n const sc = raw.scroller_config ?? (raw as Record<string, unknown>).scrollerConfig;\n const normalized = normalizeScrollerConfig(sc as SwirlScrollerConfig);\n if (normalized) o.scroller_config = normalized;\n }\n return o;\n}\n\nexport async function fetchProjectOverlays(\n projectId: number,\n apiBaseUrl: string = OVERLAY_API_BASE\n): Promise<SwirlOverlay[]> {\n const base = apiBaseUrl.replace(/\\/$/, \"\");\n const response = await fetch(\n `${base}/adstorm/swirl/projects/${projectId}/overlays`\n );\n if (!response.ok) {\n throw new Error(\n `Failed to fetch overlays: ${response.status} ${response.statusText}`\n );\n }\n const data = await response.json();\n if (!Array.isArray(data)) return [];\n return data.map((row: SwirlOverlay & Record<string, unknown>) =>\n normalizeSwirlOverlay(row)\n );\n}\n\nexport function resolveImageUrl(\n imageUrl: string,\n apiBaseUrl: string = OVERLAY_API_BASE\n): string {\n if (!imageUrl) return \"\";\n if (imageUrl.startsWith(\"http://\") || imageUrl.startsWith(\"https://\")) {\n return imageUrl;\n }\n if (imageUrl.startsWith(\"/\")) {\n try {\n const url = new URL(apiBaseUrl);\n return `${url.origin}${imageUrl}`;\n } catch {\n return imageUrl;\n }\n }\n return `${apiBaseUrl}/${imageUrl}`;\n}\n"]}
|