Show mini preview of Twitch Stream.
// ==UserScript== // @name TiwtchPreview // @namespace Violentmonkey Scripts // @description Show mini preview of Twitch Stream. // @match *://*.twitch.tv/* // @version 0.0.1 // @license MIT // @icon https://external-content.duckduckgo.com/ip3/www.twitch.tv.ico // @author Pipos_ // @require https://cdn.jsdelivr.net/npm/@violentmonkey/dom@2 // @require https://cdn.jsdelivr.net/npm/@violentmonkey/[email protected] // @homepageURL https://github.com/P1P0S/TwitchPreview // @supportURL https://github.com/P1P0S/TwitchPreview/issues // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function () { 'use strict'; const IS_DEV = false; const equalFn = (a, b) => a === b; const $TRACK = Symbol("solid-track"); const signalOptions = { equals: equalFn }; let runEffects = runQueue; const STALE = 1; const PENDING = 2; const UNOWNED = { owned: null, cleanups: null, context: null, owner: null }; var Owner = null; let Transition = null; let ExternalSourceConfig = null; let Listener = null; let Updates = null; let Effects = null; let ExecCount = 0; function createRoot(fn, detachedOwner) { const listener = Listener, owner = Owner, unowned = fn.length === 0, current = detachedOwner === undefined ? owner : detachedOwner, root = unowned ? UNOWNED : { owned: null, cleanups: null, context: current ? current.context : null, owner: current }, updateFn = unowned ? fn : () => fn(() => untrack(() => cleanNode(root))); Owner = root; Listener = null; try { return runUpdates(updateFn, true); } finally { Listener = listener; Owner = owner; } } function createSignal(value, options) { options = options ? Object.assign({}, signalOptions, options) : signalOptions; const s = { value, observers: null, observerSlots: null, comparator: options.equals || undefined }; const setter = value => { if (typeof value === "function") { value = value(s.value); } return writeSignal(s, value); }; return [readSignal.bind(s), setter]; } function createRenderEffect(fn, value, options) { const c = createComputation(fn, value, false, STALE); updateComputation(c); } function createMemo(fn, value, options) { options = options ? Object.assign({}, signalOptions, options) : signalOptions; const c = createComputation(fn, value, true, 0); c.observers = null; c.observerSlots = null; c.comparator = options.equals || undefined; updateComputation(c); return readSignal.bind(c); } function untrack(fn) { if (Listener === null) return fn(); const listener = Listener; Listener = null; try { if (ExternalSourceConfig) ; return fn(); } finally { Listener = listener; } } function onCleanup(fn) { if (Owner === null) ;else if (Owner.cleanups === null) Owner.cleanups = [fn];else Owner.cleanups.push(fn); return fn; } function readSignal() { if (this.sources && (this.state)) { if ((this.state) === STALE) updateComputation(this);else { const updates = Updates; Updates = null; runUpdates(() => lookUpstream(this), false); Updates = updates; } } if (Listener) { const sSlot = this.observers ? this.observers.length : 0; if (!Listener.sources) { Listener.sources = [this]; Listener.sourceSlots = [sSlot]; } else { Listener.sources.push(this); Listener.sourceSlots.push(sSlot); } if (!this.observers) { this.observers = [Listener]; this.observerSlots = [Listener.sources.length - 1]; } else { this.observers.push(Listener); this.observerSlots.push(Listener.sources.length - 1); } } return this.value; } function writeSignal(node, value, isComp) { let current = node.value; if (!node.comparator || !node.comparator(current, value)) { node.value = value; if (node.observers && node.observers.length) { runUpdates(() => { for (let i = 0; i < node.observers.length; i += 1) { const o = node.observers[i]; const TransitionRunning = Transition && Transition.running; if (TransitionRunning && Transition.disposed.has(o)) ; if (TransitionRunning ? !o.tState : !o.state) { if (o.pure) Updates.push(o);else Effects.push(o); if (o.observers) markDownstream(o); } if (!TransitionRunning) o.state = STALE; } if (Updates.length > 10e5) { Updates = []; if (IS_DEV) ; throw new Error(); } }, false); } } return value; } function updateComputation(node) { if (!node.fn) return; cleanNode(node); const time = ExecCount; runComputation(node, node.value, time); } function runComputation(node, value, time) { let nextValue; const owner = Owner, listener = Listener; Listener = Owner = node; try { nextValue = node.fn(value); } catch (err) { if (node.pure) { { node.state = STALE; node.owned && node.owned.forEach(cleanNode); node.owned = null; } } node.updatedAt = time + 1; return handleError(err); } finally { Listener = listener; Owner = owner; } if (!node.updatedAt || node.updatedAt <= time) { if (node.updatedAt != null && "observers" in node) { writeSignal(node, nextValue); } else node.value = nextValue; node.updatedAt = time; } } function createComputation(fn, init, pure, state = STALE, options) { const c = { fn, state: state, updatedAt: null, owned: null, sources: null, sourceSlots: null, cleanups: null, value: init, owner: Owner, context: Owner ? Owner.context : null, pure }; if (Owner === null) ;else if (Owner !== UNOWNED) { { if (!Owner.owned) Owner.owned = [c];else Owner.owned.push(c); } } return c; } function runTop(node) { if ((node.state) === 0) return; if ((node.state) === PENDING) return lookUpstream(node); if (node.suspense && untrack(node.suspense.inFallback)) return node.suspense.effects.push(node); const ancestors = [node]; while ((node = node.owner) && (!node.updatedAt || node.updatedAt < ExecCount)) { if (node.state) ancestors.push(node); } for (let i = ancestors.length - 1; i >= 0; i--) { node = ancestors[i]; if ((node.state) === STALE) { updateComputation(node); } else if ((node.state) === PENDING) { const updates = Updates; Updates = null; runUpdates(() => lookUpstream(node, ancestors[0]), false); Updates = updates; } } } function runUpdates(fn, init) { if (Updates) return fn(); let wait = false; if (!init) Updates = []; if (Effects) wait = true;else Effects = []; ExecCount++; try { const res = fn(); completeUpdates(wait); return res; } catch (err) { if (!wait) Effects = null; Updates = null; handleError(err); } } function completeUpdates(wait) { if (Updates) { runQueue(Updates); Updates = null; } if (wait) return; const e = Effects; Effects = null; if (e.length) runUpdates(() => runEffects(e), false); } function runQueue(queue) { for (let i = 0; i < queue.length; i++) runTop(queue[i]); } function lookUpstream(node, ignore) { node.state = 0; for (let i = 0; i < node.sources.length; i += 1) { const source = node.sources[i]; if (source.sources) { const state = source.state; if (state === STALE) { if (source !== ignore && (!source.updatedAt || source.updatedAt < ExecCount)) runTop(source); } else if (state === PENDING) lookUpstream(source, ignore); } } } function markDownstream(node) { for (let i = 0; i < node.observers.length; i += 1) { const o = node.observers[i]; if (!o.state) { o.state = PENDING; if (o.pure) Updates.push(o);else Effects.push(o); o.observers && markDownstream(o); } } } function cleanNode(node) { let i; if (node.sources) { while (node.sources.length) { const source = node.sources.pop(), index = node.sourceSlots.pop(), obs = source.observers; if (obs && obs.length) { const n = obs.pop(), s = source.observerSlots.pop(); if (index < obs.length) { n.sourceSlots[s] = index; obs[index] = n; source.observerSlots[index] = s; } } } } if (node.tOwned) { for (i = node.tOwned.length - 1; i >= 0; i--) cleanNode(node.tOwned[i]); delete node.tOwned; } if (node.owned) { for (i = node.owned.length - 1; i >= 0; i--) cleanNode(node.owned[i]); node.owned = null; } if (node.cleanups) { for (i = node.cleanups.length - 1; i >= 0; i--) node.cleanups[i](); node.cleanups = null; } node.state = 0; } function castError(err) { if (err instanceof Error) return err; return new Error(typeof err === "string" ? err : "Unknown error", { cause: err }); } function handleError(err, owner = Owner) { const error = castError(err); throw error; } const FALLBACK = Symbol("fallback"); function dispose(d) { for (let i = 0; i < d.length; i++) d[i](); } function mapArray(list, mapFn, options = {}) { let items = [], mapped = [], disposers = [], len = 0, indexes = mapFn.length > 1 ? [] : null; onCleanup(() => dispose(disposers)); return () => { let newItems = list() || [], newLen = newItems.length, i, j; newItems[$TRACK]; return untrack(() => { let newIndices, newIndicesNext, temp, tempdisposers, tempIndexes, start, end, newEnd, item; if (newLen === 0) { if (len !== 0) { dispose(disposers); disposers = []; items = []; mapped = []; len = 0; indexes && (indexes = []); } if (options.fallback) { items = [FALLBACK]; mapped[0] = createRoot(disposer => { disposers[0] = disposer; return options.fallback(); }); len = 1; } } else if (len === 0) { mapped = new Array(newLen); for (j = 0; j < newLen; j++) { items[j] = newItems[j]; mapped[j] = createRoot(mapper); } len = newLen; } else { temp = new Array(newLen); tempdisposers = new Array(newLen); indexes && (tempIndexes = new Array(newLen)); for (start = 0, end = Math.min(len, newLen); start < end && items[start] === newItems[start]; start++); for (end = len - 1, newEnd = newLen - 1; end >= start && newEnd >= start && items[end] === newItems[newEnd]; end--, newEnd--) { temp[newEnd] = mapped[end]; tempdisposers[newEnd] = disposers[end]; indexes && (tempIndexes[newEnd] = indexes[end]); } newIndices = new Map(); newIndicesNext = new Array(newEnd + 1); for (j = newEnd; j >= start; j--) { item = newItems[j]; i = newIndices.get(item); newIndicesNext[j] = i === undefined ? -1 : i; newIndices.set(item, j); } for (i = start; i <= end; i++) { item = items[i]; j = newIndices.get(item); if (j !== undefined && j !== -1) { temp[j] = mapped[i]; tempdisposers[j] = disposers[i]; indexes && (tempIndexes[j] = indexes[i]); j = newIndicesNext[j]; newIndices.set(item, j); } else disposers[i](); } for (j = start; j < newLen; j++) { if (j in temp) { mapped[j] = temp[j]; disposers[j] = tempdisposers[j]; if (indexes) { indexes[j] = tempIndexes[j]; indexes[j](j); } } else mapped[j] = createRoot(mapper); } mapped = mapped.slice(0, len = newLen); items = newItems.slice(0); } return mapped; }); function mapper(disposer) { disposers[j] = disposer; if (indexes) { const [s, set] = createSignal(j); indexes[j] = set; return mapFn(newItems[j], s); } return mapFn(newItems[j]); } }; } function createComponent(Comp, props) { return untrack(() => Comp(props || {})); } const narrowedError = name => `Stale read from <${name}>.`; function For(props) { const fallback = "fallback" in props && { fallback: () => props.fallback }; return createMemo(mapArray(() => props.each, props.children, fallback || undefined)); } function Show(props) { const keyed = props.keyed; const conditionValue = createMemo(() => props.when, undefined, undefined); const condition = keyed ? conditionValue : createMemo(conditionValue, undefined, { equals: (a, b) => !a === !b }); return createMemo(() => { const c = condition(); if (c) { const child = props.children; const fn = typeof child === "function" && child.length > 0; return fn ? untrack(() => child(keyed ? c : () => { if (!untrack(condition)) throw narrowedError("Show"); return conditionValue(); })) : child; } return props.fallback; }, undefined, undefined); } function reconcileArrays(parentNode, a, b) { let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = a[aEnd - 1].nextSibling, map = null; while (aStart < aEnd || bStart < bEnd) { if (a[aStart] === b[bStart]) { aStart++; bStart++; continue; } while (a[aEnd - 1] === b[bEnd - 1]) { aEnd--; bEnd--; } if (aEnd === aStart) { const node = bEnd < bLength ? bStart ? b[bStart - 1].nextSibling : b[bEnd - bStart] : after; while (bStart < bEnd) parentNode.insertBefore(b[bStart++], node); } else if (bEnd === bStart) { while (aStart < aEnd) { if (!map || !map.has(a[aStart])) a[aStart].remove(); aStart++; } } else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) { const node = a[--aEnd].nextSibling; parentNode.insertBefore(b[bStart++], a[aStart++].nextSibling); parentNode.insertBefore(b[--bEnd], node); a[aEnd] = b[bEnd]; } else { if (!map) { map = new Map(); let i = bStart; while (i < bEnd) map.set(b[i], i++); } const index = map.get(a[aStart]); if (index != null) { if (bStart < index && index < bEnd) { let i = aStart, sequence = 1, t; while (++i < aEnd && i < bEnd) { if ((t = map.get(a[i])) == null || t !== index + sequence) break; sequence++; } if (sequence > index - bStart) { const node = a[aStart]; while (bStart < index) parentNode.insertBefore(b[bStart++], node); } else parentNode.replaceChild(b[bStart++], a[aStart++]); } else aStart++; } else a[aStart++].remove(); } } } const $$EVENTS = "_$DX_DELEGATE"; function render(code, element, init, options = {}) { let disposer; createRoot(dispose => { disposer = dispose; element === document ? code() : insert(element, code(), element.firstChild ? null : undefined, init); }, options.owner); return () => { disposer(); element.textContent = ""; }; } function template(html, isImportNode, isSVG, isMathML) { let node; const create = () => { const t = isMathML ? document.createElementNS("http://www.w3.org/1998/Math/MathML", "template") : document.createElement("template"); t.innerHTML = html; return isSVG ? t.content.firstChild.firstChild : isMathML ? t.firstChild : t.content.firstChild; }; const fn = isImportNode ? () => untrack(() => document.importNode(node || (node = create()), true)) : () => (node || (node = create())).cloneNode(true); fn.cloneNode = fn; return fn; } function delegateEvents(eventNames, document = window.document) { const e = document[$$EVENTS] || (document[$$EVENTS] = new Set()); for (let i = 0, l = eventNames.length; i < l; i++) { const name = eventNames[i]; if (!e.has(name)) { e.add(name); document.addEventListener(name, eventHandler); } } } function setAttribute(node, name, value) { if (value == null) node.removeAttribute(name);else node.setAttribute(name, value); } function addEventListener(node, name, handler, delegate) { if (delegate) { if (Array.isArray(handler)) { node[`$$${name}`] = handler[0]; node[`$$${name}Data`] = handler[1]; } else node[`$$${name}`] = handler; } else if (Array.isArray(handler)) { const handlerFn = handler[0]; node.addEventListener(name, handler[0] = e => handlerFn.call(node, handler[1], e)); } else node.addEventListener(name, handler, typeof handler !== "function" && handler); } function style(node, value, prev) { if (!value) return prev ? setAttribute(node, "style") : value; const nodeStyle = node.style; if (typeof value === "string") return nodeStyle.cssText = value; typeof prev === "string" && (nodeStyle.cssText = prev = undefined); prev || (prev = {}); value || (value = {}); let v, s; for (s in prev) { value[s] == null && nodeStyle.removeProperty(s); delete prev[s]; } for (s in value) { v = value[s]; if (v !== prev[s]) { nodeStyle.setProperty(s, v); prev[s] = v; } } return prev; } function use(fn, element, arg) { return untrack(() => fn(element, arg)); } function insert(parent, accessor, marker, initial) { if (marker !== undefined && !initial) initial = []; if (typeof accessor !== "function") return insertExpression(parent, accessor, initial, marker); createRenderEffect(current => insertExpression(parent, accessor(), current, marker), initial); } function eventHandler(e) { let node = e.target; const key = `$$${e.type}`; const oriTarget = e.target; const oriCurrentTarget = e.currentTarget; const retarget = value => Object.defineProperty(e, "target", { configurable: true, value }); const handleNode = () => { const handler = node[key]; if (handler && !node.disabled) { const data = node[`${key}Data`]; data !== undefined ? handler.call(node, data, e) : handler.call(node, e); if (e.cancelBubble) return; } node.host && typeof node.host !== "string" && !node.host._$host && node.contains(e.target) && retarget(node.host); return true; }; const walkUpTree = () => { while (handleNode() && (node = node._$host || node.parentNode || node.host)); }; Object.defineProperty(e, "currentTarget", { configurable: true, get() { return node || document; } }); if (e.composedPath) { const path = e.composedPath(); retarget(path[0]); for (let i = 0; i < path.length - 2; i++) { node = path[i]; if (!handleNode()) break; if (node._$host) { node = node._$host; walkUpTree(); break; } if (node.parentNode === oriCurrentTarget) { break; } } } else walkUpTree(); retarget(oriTarget); } function insertExpression(parent, value, current, marker, unwrapArray) { while (typeof current === "function") current = current(); if (value === current) return current; const t = typeof value, multi = marker !== undefined; parent = multi && current[0] && current[0].parentNode || parent; if (t === "string" || t === "number") { if (t === "number") { value = value.toString(); if (value === current) return current; } if (multi) { let node = current[0]; if (node && node.nodeType === 3) { node.data !== value && (node.data = value); } else node = document.createTextNode(value); current = cleanChildren(parent, current, marker, node); } else { if (current !== "" && typeof current === "string") { current = parent.firstChild.data = value; } else current = parent.textContent = value; } } else if (value == null || t === "boolean") { current = cleanChildren(parent, current, marker); } else if (t === "function") { createRenderEffect(() => { let v = value(); while (typeof v === "function") v = v(); current = insertExpression(parent, v, current, marker); }); return () => current; } else if (Array.isArray(value)) { const array = []; const currentArray = current && Array.isArray(current); if (normalizeIncomingArray(array, value, current, unwrapArray)) { createRenderEffect(() => current = insertExpression(parent, array, current, marker, true)); return () => current; } if (array.length === 0) { current = cleanChildren(parent, current, marker); if (multi) return current; } else if (currentArray) { if (current.length === 0) { appendNodes(parent, array, marker); } else reconcileArrays(parent, current, array); } else { current && cleanChildren(parent); appendNodes(parent, array); } current = array; } else if (value.nodeType) { if (Array.isArray(current)) { if (multi) return current = cleanChildren(parent, current, marker, value); cleanChildren(parent, current, null, value); } else if (current == null || current === "" || !parent.firstChild) { parent.appendChild(value); } else parent.replaceChild(value, parent.firstChild); current = value; } else ; return current; } function normalizeIncomingArray(normalized, array, current, unwrap) { let dynamic = false; for (let i = 0, len = array.length; i < len; i++) { let item = array[i], prev = current && current[normalized.length], t; if (item == null || item === true || item === false) ; else if ((t = typeof item) === "object" && item.nodeType) { normalized.push(item); } else if (Array.isArray(item)) { dynamic = normalizeIncomingArray(normalized, item, prev) || dynamic; } else if (t === "function") { if (unwrap) { while (typeof item === "function") item = item(); dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item], Array.isArray(prev) ? prev : [prev]) || dynamic; } else { normalized.push(item); dynamic = true; } } else { const value = String(item); if (prev && prev.nodeType === 3 && prev.data === value) normalized.push(prev);else normalized.push(document.createTextNode(value)); } } return dynamic; } function appendNodes(parent, array, marker = null) { for (let i = 0, len = array.length; i < len; i++) parent.insertBefore(array[i], marker); } function cleanChildren(parent, current, marker, replacement) { if (marker === undefined) return parent.textContent = ""; const node = replacement || document.createTextNode(""); if (current.length) { let inserted = false; for (let i = current.length - 1; i >= 0; i--) { const el = current[i]; if (node !== el) { const isParent = el.parentNode === parent; if (!inserted && !i) isParent ? parent.replaceChild(node, el) : parent.insertBefore(node, marker);else isParent && el.remove(); } else inserted = true; } } else parent.insertBefore(node, marker); return [node]; } const DEFAULT_PANEL_WIDTH = 460; const DEFAULT_PANEL_HEIGHT = 290; const DEFAULT_HOVER_DELAY = 500; const DEFAULT_HIDE_DELAY = 300; const DEFAULT_BLOCKED_ROUTES = ['directory', 'downloads', 'jobs', 'p', 'search', 'settings', 'subscriptions', 'turbo', 'wallet', 'videos']; function loadSetting(key, fallback) { try { const val = GM_getValue(key, undefined); if (val !== undefined) return val; } catch (_unused) {/* noop */} return fallback; } function saveSetting(key, value) { try { GM_setValue(key, value); } catch (_unused2) {/* noop */} } const [panelWidth, _setPanelWidth] = createSignal(loadSetting('panelWidth', DEFAULT_PANEL_WIDTH)); const [panelHeight, _setPanelHeight] = createSignal(loadSetting('panelHeight', DEFAULT_PANEL_HEIGHT)); const [hoverDelay, _setHoverDelay] = createSignal(loadSetting('hoverDelay', DEFAULT_HOVER_DELAY)); const [hideDelay, _setHideDelay] = createSignal(loadSetting('hideDelay', DEFAULT_HIDE_DELAY)); const [blockedRoutes, _setBlockedRoutes] = createSignal(loadSetting('blockedRoutes', DEFAULT_BLOCKED_ROUTES)); function setPanelWidth(v) { _setPanelWidth(v); saveSetting('panelWidth', v); } function setPanelHeight(v) { _setPanelHeight(v); saveSetting('panelHeight', v); } function setHoverDelay(v) { _setHoverDelay(v); saveSetting('hoverDelay', v); } function setHideDelay(v) { _setHideDelay(v); saveSetting('hideDelay', v); } function setBlockedRoutes(v) { _setBlockedRoutes(v); saveSetting('blockedRoutes', v); } function resetAllSettings() { setPanelWidth(DEFAULT_PANEL_WIDTH); setPanelHeight(DEFAULT_PANEL_HEIGHT); setHoverDelay(DEFAULT_HOVER_DELAY); setHideDelay(DEFAULT_HIDE_DELAY); setBlockedRoutes([...DEFAULT_BLOCKED_ROUTES]); } function getTwitchParent() { return window.location.hostname; } function buildEmbedUrl(channelLogin) { const parent = encodeURIComponent(getTwitchParent()); const ch = encodeURIComponent(channelLogin); return `https://player.twitch.tv/?channel=${ch}&parent=${parent}&muted=true&autoplay=true`; } function extractChannelLoginFromLink(a) { const href = a.getAttribute('href'); if (!href) return null; const clean = href.split('?')[0].split('#')[0]; const parts = clean.split('/').filter(Boolean); if (!parts.length) return null; const login = parts[0]; const blocked = new Set(blockedRoutes()); if (blocked.has(login)) return null; if (!/^[a-z0-9_]{2,25}$/i.test(login)) return null; return login; } function findAnchorFromTarget(target) { if (!(target instanceof Element)) return null; return target.closest('a[href^="/"]'); } function isSidebarChannelLink(a) { const nav = a.closest('nav'); if (!nav) return false; const rect = nav.getBoundingClientRect(); return rect.left < 80 && rect.width < 500; } function useDrag(onDragEndOutside) { const [isDragging, setIsDragging] = createSignal(false); let dragOffsetX = 0; let dragOffsetY = 0; let currentPanelRef = null; const onDragMove = e => { if (!isDragging() || !currentPanelRef) return; e.preventDefault(); let left = e.clientX - dragOffsetX; let top = e.clientY - dragOffsetY; left = Math.max(0, Math.min(window.innerWidth - panelWidth(), left)); top = Math.max(0, Math.min(window.innerHeight - panelHeight(), top)); currentPanelRef.style.left = `${left}px`; currentPanelRef.style.top = `${top}px`; }; const onDragEnd = e => { if (!isDragging()) return; setIsDragging(false); window.removeEventListener('mousemove', onDragMove); window.removeEventListener('mouseup', onDragEnd); if (currentPanelRef) { const rect = currentPanelRef.getBoundingClientRect(); const isOutside = e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom; if (isOutside) { onDragEndOutside(); } } }; const onDragStart = (e, panelRef) => { e.preventDefault(); currentPanelRef = panelRef; setIsDragging(true); dragOffsetX = e.clientX - panelRef.offsetLeft; dragOffsetY = e.clientY - panelRef.offsetTop; window.addEventListener('mousemove', onDragMove); window.addEventListener('mouseup', onDragEnd); }; const cleanupDrag = () => { window.removeEventListener('mousemove', onDragMove); window.removeEventListener('mouseup', onDragEnd); }; return { isDragging, onDragStart, cleanupDrag }; } function useTimers() { let hoverTimer = null; let hideTimer = null; const cancelHide = () => { if (hideTimer) { window.clearTimeout(hideTimer); hideTimer = null; } }; const cancelHover = () => { if (hoverTimer) { window.clearTimeout(hoverTimer); hoverTimer = null; } }; const scheduleHide = callback => { cancelHide(); hideTimer = window.setTimeout(() => { callback(); hideTimer = null; }, hideDelay()); }; const setHoverTimer = (callback, delay) => { cancelHover(); hoverTimer = window.setTimeout(() => { callback(); hoverTimer = null; }, delay); }; const clearAllTimers = () => { cancelHide(); cancelHover(); }; return { scheduleHide, cancelHide, setHoverTimer, cancelHover, clearAllTimers }; } function usePreviewPanel() { const [getChannel, setChannel] = createSignal(null); const [isLoading, setIsLoading] = createSignal(false); const [isVisible, setIsVisible] = createSignal(false); const [isPinned, setIsPinned] = createSignal(false); const [showSettings, setShowSettings] = createSignal(false); let iframeEl = null; let panelRef; const timers = useTimers(); const hidePanel = () => { setIsVisible(false); setIsPinned(false); setTimeout(() => { setChannel(null); if (iframeEl) iframeEl.src = ''; }, 200); }; const requestHide = () => { if (isPinned() || drag.isDragging()) return; timers.scheduleHide(hidePanel); }; const drag = useDrag(() => { if (!isPinned()) { requestHide(); } }); const showPanel = (login, linkRect) => { setChannel(login); setIsLoading(true); setIsVisible(true); if (iframeEl) { iframeEl.src = buildEmbedUrl(login); setTimeout(() => setIsLoading(false), 800); } if (!panelRef) return; let left = linkRect.right + 15; let top = linkRect.top - 20; if (left + panelWidth() > window.innerWidth) { left = linkRect.left - panelWidth() - 15; } if (top + panelHeight() > window.innerHeight) { top = window.innerHeight - panelHeight() - 10; } if (top < 10) top = 10; panelRef.style.left = `${left}px`; panelRef.style.top = `${top}px`; }; const onMouseOver = ev => { const a = findAnchorFromTarget(ev.target); if (!a || !isSidebarChannelLink(a)) return; const login = extractChannelLoginFromLink(a); if (!login) return; timers.cancelHide(); if (getChannel() === login) return; timers.setHoverTimer(() => { const rect = a.getBoundingClientRect(); showPanel(login, rect); }, hoverDelay()); }; const onMouseOut = ev => { const a = findAnchorFromTarget(ev.target); if (!a || !isSidebarChannelLink(a)) return; timers.cancelHover(); requestHide(); }; const openInTwitch = () => { const ch = getChannel(); if (ch) { window.open(`https://www.twitch.tv/${ch}`, '_blank'); } }; const togglePin = () => { setIsPinned(!isPinned()); }; const toggleSettings = () => { setShowSettings(!showSettings()); }; const handlePanelMouseEnter = () => { timers.cancelHide(); }; const handlePanelMouseLeave = () => { requestHide(); }; const handleDragStart = e => { if (!panelRef) return; timers.cancelHide(); drag.onDragStart(e, panelRef); }; window.addEventListener('mouseover', onMouseOver, true); window.addEventListener('mouseout', onMouseOut, true); onCleanup(() => { var _panelRef; window.removeEventListener('mouseover', onMouseOver, true); window.removeEventListener('mouseout', onMouseOut, true); drag.cleanupDrag(); timers.clearAllTimers(); if ((_panelRef = panelRef) != null && _panelRef.parentNode) { panelRef.parentNode.removeChild(panelRef); } }); return { getChannel, isLoading, isVisible, isPinned, isDragging: drag.isDragging, showSettings, setIframeRef: el => { iframeEl = el; }, setPanelRef: el => { panelRef = el; }, hidePanel, openInTwitch, togglePin, toggleSettings, handlePanelMouseEnter, handlePanelMouseLeave, handleDragStart }; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } const FONT_STACK = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'; function panelStyle(isVisible, isDragging, hasChannel) { return { position: 'fixed', 'z-index': 999999, display: hasChannel ? 'block' : 'none', width: `${panelWidth()}px`, height: `${panelHeight()}px`, background: '#18181b', border: '1px solid #323237', 'border-radius': '12px', 'box-shadow': '0 8px 32px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05)', padding: 0, 'pointer-events': 'auto', overflow: 'hidden', 'user-select': 'none', opacity: isVisible ? '1' : '0', transform: isVisible ? 'scale(1)' : 'scale(0.95)', transition: isDragging ? 'none' : 'opacity 0.2s ease, transform 0.2s ease' }; } const headerStyle = { display: 'flex', 'align-items': 'center', 'justify-content': 'space-between', padding: '10px 12px', background: 'linear-gradient(to bottom, #1f1f23, #18181b)', 'border-bottom': '1px solid #323237' }; const headerLeftStyle = { display: 'flex', 'align-items': 'center', gap: '8px', flex: 1, 'min-width': 0 }; const liveDotStyle = { width: '8px', height: '8px', 'border-radius': '50%', background: '#ff4655', 'box-shadow': '0 0 8px rgba(255, 70, 85, 0.6)', animation: 'pulse 2s ease-in-out infinite' }; const channelNameStyle = { color: '#efeff1', 'font-size': '14px', 'font-weight': '600', 'font-family': FONT_STACK, 'white-space': 'nowrap', overflow: 'hidden', 'text-overflow': 'ellipsis' }; const liveBadgeStyle = { color: '#fff', 'font-size': '10px', 'font-weight': '700', 'font-family': FONT_STACK, 'text-transform': 'uppercase', background: '#e91916', padding: '2px 6px', 'border-radius': '4px', 'letter-spacing': '0.5px' }; const buttonGroupStyle = { display: 'flex', gap: '6px' }; const iconButtonStyle = { background: 'transparent', border: 'none', color: '#efeff1', cursor: 'pointer', padding: '4px 8px', 'border-radius': '6px', display: 'flex', 'align-items': 'center', 'justify-content': 'center', transition: 'background 0.2s ease' }; const dragButtonStyle = _extends({}, iconButtonStyle, { cursor: 'grab' }); const loaderOverlayStyle = { position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)', 'z-index': 10, display: 'flex', 'flex-direction': 'column', 'align-items': 'center', gap: '12px' }; const spinnerStyle = { width: '32px', height: '32px', border: '3px solid rgba(145, 71, 255, 0.2)', 'border-top-color': '#9147ff', 'border-radius': '50%', animation: 'spin 0.8s linear infinite' }; const loaderTextStyle = { color: '#efeff1', 'font-size': '12px', 'font-family': FONT_STACK }; const iframeStyle = { width: '100%', height: 'calc(100% - 40px)', border: 0, display: 'block', background: '#0e0e10' }; function applyHover(e) { e.currentTarget.style.background = 'rgba(255, 255, 255, 0.1)'; } function removeHover(e) { e.currentTarget.style.background = 'transparent'; } var _tmpl$$2 = /*#__PURE__*/template(`<svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2><circle cx=12 cy=5 r=1></circle><circle cx=19 cy=5 r=1></circle><circle cx=5 cy=5 r=1></circle><circle cx=12 cy=12 r=1></circle><circle cx=19 cy=12 r=1></circle><circle cx=5 cy=12 r=1></circle><circle cx=12 cy=19 r=1></circle><circle cx=19 cy=19 r=1></circle><circle cx=5 cy=19 r=1>`), _tmpl$2$2 = /*#__PURE__*/template(`<svg width=16 height=16 viewBox="0 0 24 24"stroke=currentColor stroke-width=2><path d="M12 17v5"></path><path d="M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z">`), _tmpl$3 = /*#__PURE__*/template(`<svg xmlns=http://www.w3.org/2000/svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915"></path><circle cx=12 cy=12 r=3>`), _tmpl$4 = /*#__PURE__*/template(`<svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2><path d="M15 3h6v6"></path><path d="M10 14 21 3"></path><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6">`), _tmpl$5 = /*#__PURE__*/template(`<svg width=16 height=16 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2><circle cx=12 cy=12 r=10></circle><path d="m15 9-6 6"></path><path d="m9 9 6 6">`), _tmpl$6 = /*#__PURE__*/template(`<div><div><div></div><span></span><span>LIVE</span></div><button title="Drag to move"></button><div><button></button><button title=Settings></button><button title="Open in new tab"></button><button title=Close>`); function DragIcon() { return _tmpl$$2(); } function PinIcon(props) { return (() => { var _el$2 = _tmpl$2$2(); createRenderEffect(() => setAttribute(_el$2, "fill", props.filled ? 'currentColor' : 'none')); return _el$2; })(); } function SettingsIcon() { return _tmpl$3(); } function ExternalLinkIcon() { return _tmpl$4(); } function CloseIcon() { return _tmpl$5(); } function HeaderBar(props) { return (() => { var _el$6 = _tmpl$6(), _el$7 = _el$6.firstChild, _el$8 = _el$7.firstChild, _el$9 = _el$8.nextSibling, _el$0 = _el$9.nextSibling, _el$1 = _el$7.nextSibling, _el$10 = _el$1.nextSibling, _el$11 = _el$10.firstChild, _el$12 = _el$11.nextSibling, _el$13 = _el$12.nextSibling, _el$14 = _el$13.nextSibling; insert(_el$9, () => props.channel()); addEventListener(_el$1, "mouseleave", removeHover); addEventListener(_el$1, "mouseenter", applyHover); addEventListener(_el$1, "mousedown", props.onDragStart, true); insert(_el$1, createComponent(DragIcon, {})); addEventListener(_el$11, "mouseleave", removeHover); addEventListener(_el$11, "mouseenter", applyHover); addEventListener(_el$11, "click", props.onTogglePin, true); insert(_el$11, createComponent(PinIcon, { get filled() { return props.isPinned(); } })); addEventListener(_el$12, "mouseleave", removeHover); addEventListener(_el$12, "mouseenter", applyHover); addEventListener(_el$12, "click", props.onOpenSettings, true); insert(_el$12, createComponent(SettingsIcon, {})); addEventListener(_el$13, "mouseleave", removeHover); addEventListener(_el$13, "mouseenter", applyHover); addEventListener(_el$13, "click", props.onOpenInTwitch, true); insert(_el$13, createComponent(ExternalLinkIcon, {})); addEventListener(_el$14, "mouseleave", removeHover); addEventListener(_el$14, "mouseenter", applyHover); addEventListener(_el$14, "click", props.onClose, true); insert(_el$14, createComponent(CloseIcon, {})); createRenderEffect(_p$ => { var _v$ = headerStyle, _v$2 = headerLeftStyle, _v$3 = liveDotStyle, _v$4 = channelNameStyle, _v$5 = liveBadgeStyle, _v$6 = dragButtonStyle, _v$7 = buttonGroupStyle, _v$8 = iconButtonStyle, _v$9 = props.isPinned() ? 'Unpin' : 'Pin', _v$0 = iconButtonStyle, _v$1 = iconButtonStyle, _v$10 = iconButtonStyle; _p$.e = style(_el$6, _v$, _p$.e); _p$.t = style(_el$7, _v$2, _p$.t); _p$.a = style(_el$8, _v$3, _p$.a); _p$.o = style(_el$9, _v$4, _p$.o); _p$.i = style(_el$0, _v$5, _p$.i); _p$.n = style(_el$1, _v$6, _p$.n); _p$.s = style(_el$10, _v$7, _p$.s); _p$.h = style(_el$11, _v$8, _p$.h); _v$9 !== _p$.r && setAttribute(_el$11, "title", _p$.r = _v$9); _p$.d = style(_el$12, _v$0, _p$.d); _p$.l = style(_el$13, _v$1, _p$.l); _p$.u = style(_el$14, _v$10, _p$.u); return _p$; }, { e: undefined, t: undefined, a: undefined, o: undefined, i: undefined, n: undefined, s: undefined, h: undefined, r: undefined, d: undefined, l: undefined, u: undefined }); return _el$6; })(); } delegateEvents(["mousedown", "click"]); var _tmpl$$1 = /*#__PURE__*/template(`<div><div><div><span>Settings</span><button><svg width=18 height=18 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg></button></div><div><label>Blocked Routes</label><textarea></textarea><div>Comma-separated list of routes to ignore</div></div><div><button>Reset Defaults</button><div style=flex:1></div><button>Cancel</button><button>Save`), _tmpl$2$1 = /*#__PURE__*/template(`<div><label></label><input><div>`); const FONT = 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'; const overlayStyle = { position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh', background: 'rgba(0, 0, 0, 0.7)', 'z-index': 9999999, display: 'flex', 'align-items': 'center', 'justify-content': 'center', 'backdrop-filter': 'blur(4px)' }; const modalStyle = { background: '#1f1f23', border: '1px solid #323237', 'border-radius': '12px', padding: '24px', width: '380px', 'max-height': '80vh', 'overflow-y': 'auto', color: '#efeff1', 'font-family': FONT, 'box-shadow': '0 16px 48px rgba(0, 0, 0, 0.8)' }; const titleStyle = { 'font-size': '18px', 'font-weight': '700', margin: '0 0 20px 0', display: 'flex', 'align-items': 'center', 'justify-content': 'space-between' }; const fieldStyle = { 'margin-bottom': '16px' }; const labelStyle = { display: 'block', 'font-size': '12px', 'font-weight': '600', color: '#adadb8', 'margin-bottom': '6px', 'text-transform': 'uppercase', 'letter-spacing': '0.5px' }; const inputStyle = { width: '100%', padding: '8px 12px', background: '#0e0e10', border: '1px solid #323237', 'border-radius': '6px', color: '#efeff1', 'font-size': '14px', 'font-family': FONT, outline: 'none', 'box-sizing': 'border-box', transition: 'border-color 0.2s' }; const textareaStyle = _extends({}, inputStyle, { 'min-height': '80px', resize: 'vertical' }); const hintStyle = { 'font-size': '11px', color: '#71717a', 'margin-top': '4px' }; const btnRowStyle = { display: 'flex', gap: '8px', 'margin-top': '20px', 'justify-content': 'flex-end' }; const btnBase = { padding: '8px 16px', 'border-radius': '6px', border: 'none', 'font-size': '13px', 'font-weight': '600', 'font-family': FONT, cursor: 'pointer', transition: 'background 0.2s' }; const btnPrimary = _extends({}, btnBase, { background: '#9147ff', color: '#fff' }); const btnSecondary = _extends({}, btnBase, { background: '#323237', color: '#efeff1' }); const btnDanger = _extends({}, btnBase, { background: 'transparent', color: '#e91916', padding: '8px 12px' }); const closeBtnStyle = { background: 'transparent', border: 'none', color: '#efeff1', cursor: 'pointer', padding: '4px', 'border-radius': '6px', display: 'flex', 'align-items': 'center', 'justify-content': 'center' }; function SettingsPanel(props) { const [width, setWidth] = createSignal(String(panelWidth())); const [height, setHeight] = createSignal(String(panelHeight())); const [hover, setHover] = createSignal(String(hoverDelay())); const [hide, setHide] = createSignal(String(hideDelay())); const [routes, setRoutes] = createSignal(blockedRoutes().join(', ')); const handleSave = () => { const w = parseInt(width(), 10); const h = parseInt(height(), 10); const hov = parseInt(hover(), 10); const hid = parseInt(hide(), 10); if (!isNaN(w) && w >= 200 && w <= 1200) setPanelWidth(w); if (!isNaN(h) && h >= 150 && h <= 800) setPanelHeight(h); if (!isNaN(hov) && hov >= 0 && hov <= 5000) setHoverDelay(hov); if (!isNaN(hid) && hid >= 0 && hid <= 5000) setHideDelay(hid); const parsed = routes().split(',').map(s => s.trim().toLowerCase()).filter(Boolean); setBlockedRoutes(parsed); props.onClose(); }; const handleReset = () => { resetAllSettings(); setWidth(String(DEFAULT_PANEL_WIDTH)); setHeight(String(DEFAULT_PANEL_HEIGHT)); setHover(String(DEFAULT_HOVER_DELAY)); setHide(String(DEFAULT_HIDE_DELAY)); setRoutes(blockedRoutes().join(', ')); }; const handleOverlayClick = e => { if (e.target === e.currentTarget) props.onClose(); }; const fields = [{ label: 'Panel Width (px)', value: width, setter: setWidth, hint: `Min: 200, Max: 1200, Default: ${DEFAULT_PANEL_WIDTH}`, type: 'number' }, { label: 'Panel Height (px)', value: height, setter: setHeight, hint: `Min: 150, Max: 800, Default: ${DEFAULT_PANEL_HEIGHT}`, type: 'number' }, { label: 'Hover Delay (ms)', value: hover, setter: setHover, hint: `Time before preview appears. Default: ${DEFAULT_HOVER_DELAY}`, type: 'number' }, { label: 'Hide Delay (ms)', value: hide, setter: setHide, hint: `Time before preview hides. Default: ${DEFAULT_HIDE_DELAY}`, type: 'number' }]; return (() => { var _el$ = _tmpl$$1(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild, _el$4 = _el$3.firstChild, _el$5 = _el$4.nextSibling, _el$6 = _el$3.nextSibling, _el$7 = _el$6.firstChild, _el$8 = _el$7.nextSibling, _el$9 = _el$8.nextSibling, _el$0 = _el$6.nextSibling, _el$1 = _el$0.firstChild, _el$10 = _el$1.nextSibling, _el$11 = _el$10.nextSibling, _el$12 = _el$11.nextSibling; _el$.$$click = handleOverlayClick; style(_el$, overlayStyle); _el$2.$$click = e => e.stopPropagation(); style(_el$2, modalStyle); style(_el$3, titleStyle); _el$5.addEventListener("mouseleave", e => { e.currentTarget.style.background = 'transparent'; }); _el$5.addEventListener("mouseenter", e => { e.currentTarget.style.background = 'rgba(255,255,255,0.1)'; }); addEventListener(_el$5, "click", props.onClose, true); style(_el$5, closeBtnStyle); insert(_el$2, createComponent(For, { each: fields, children: field => (() => { var _el$13 = _tmpl$2$1(), _el$14 = _el$13.firstChild, _el$15 = _el$14.nextSibling, _el$16 = _el$15.nextSibling; insert(_el$14, () => field.label); _el$15.addEventListener("blur", e => { e.currentTarget.style['border-color'] = '#323237'; }); _el$15.addEventListener("focus", e => { e.currentTarget.style['border-color'] = '#9147ff'; }); _el$15.$$input = e => field.setter(e.currentTarget.value); insert(_el$16, () => field.hint); createRenderEffect(_p$ => { var _v$8 = fieldStyle, _v$9 = labelStyle, _v$0 = field.type, _v$1 = inputStyle, _v$10 = hintStyle; _p$.e = style(_el$13, _v$8, _p$.e); _p$.t = style(_el$14, _v$9, _p$.t); _v$0 !== _p$.a && setAttribute(_el$15, "type", _p$.a = _v$0); _p$.o = style(_el$15, _v$1, _p$.o); _p$.i = style(_el$16, _v$10, _p$.i); return _p$; }, { e: undefined, t: undefined, a: undefined, o: undefined, i: undefined }); createRenderEffect(() => _el$15.value = field.value()); return _el$13; })() }), _el$6); _el$8.addEventListener("blur", e => { e.currentTarget.style['border-color'] = '#323237'; }); _el$8.addEventListener("focus", e => { e.currentTarget.style['border-color'] = '#9147ff'; }); _el$8.$$input = e => setRoutes(e.currentTarget.value); style(_el$0, btnRowStyle); _el$1.addEventListener("mouseleave", e => { e.currentTarget.style.background = 'transparent'; }); _el$1.addEventListener("mouseenter", e => { e.currentTarget.style.background = 'rgba(233, 25, 22, 0.1)'; }); _el$1.$$click = handleReset; _el$11.addEventListener("mouseleave", e => { e.currentTarget.style.background = '#323237'; }); _el$11.addEventListener("mouseenter", e => { e.currentTarget.style.background = '#3f3f46'; }); addEventListener(_el$11, "click", props.onClose, true); _el$12.addEventListener("mouseleave", e => { e.currentTarget.style.background = '#9147ff'; }); _el$12.addEventListener("mouseenter", e => { e.currentTarget.style.background = '#772ce8'; }); _el$12.$$click = handleSave; createRenderEffect(_p$ => { var _v$ = fieldStyle, _v$2 = labelStyle, _v$3 = textareaStyle, _v$4 = hintStyle, _v$5 = btnDanger, _v$6 = btnSecondary, _v$7 = btnPrimary; _p$.e = style(_el$6, _v$, _p$.e); _p$.t = style(_el$7, _v$2, _p$.t); _p$.a = style(_el$8, _v$3, _p$.a); _p$.o = style(_el$9, _v$4, _p$.o); _p$.i = style(_el$1, _v$5, _p$.i); _p$.n = style(_el$11, _v$6, _p$.n); _p$.s = style(_el$12, _v$7, _p$.s); return _p$; }, { e: undefined, t: undefined, a: undefined, o: undefined, i: undefined, n: undefined, s: undefined }); createRenderEffect(() => _el$8.value = routes()); return _el$; })(); } delegateEvents(["click", "input"]); var _tmpl$ = /*#__PURE__*/template(`<div><div></div><span>Loading...`), _tmpl$2 = /*#__PURE__*/template(`<div><iframe allow="autoplay; fullscreen"allowfullscreen loading=eager></iframe><style>\n @keyframes pulse \{\n 0%, 100% \{ opacity: 1; }\n 50% \{ opacity: 0.5; }\n }\n @keyframes spin \{\n to \{ transform: rotate(360deg); }\n }\n `, true, false, false); function App() { const panel = usePreviewPanel(); return [(() => { var _el$ = _tmpl$2(), _el$5 = _el$.firstChild; addEventListener(_el$, "mouseleave", panel.handlePanelMouseLeave); addEventListener(_el$, "mouseenter", panel.handlePanelMouseEnter); var _ref$ = panel.setPanelRef; typeof _ref$ === "function" ? use(_ref$, _el$) : panel.setPanelRef = _el$; insert(_el$, createComponent(HeaderBar, { get channel() { return panel.getChannel; }, get isPinned() { return panel.isPinned; }, get onDragStart() { return panel.handleDragStart; }, get onTogglePin() { return panel.togglePin; }, get onOpenSettings() { return panel.toggleSettings; }, get onOpenInTwitch() { return panel.openInTwitch; }, get onClose() { return panel.hidePanel; } }), _el$5); insert(_el$, createComponent(Show, { get when() { return panel.isLoading(); }, get children() { var _el$2 = _tmpl$(), _el$3 = _el$2.firstChild, _el$4 = _el$3.nextSibling; createRenderEffect(_p$ => { var _v$ = loaderOverlayStyle, _v$2 = spinnerStyle, _v$3 = loaderTextStyle; _p$.e = style(_el$2, _v$, _p$.e); _p$.t = style(_el$3, _v$2, _p$.t); _p$.a = style(_el$4, _v$3, _p$.a); return _p$; }, { e: undefined, t: undefined, a: undefined }); return _el$2; } }), _el$5); var _ref$2 = panel.setIframeRef; typeof _ref$2 === "function" ? use(_ref$2, _el$5) : panel.setIframeRef = _el$5; createRenderEffect(_p$ => { var _v$4 = panelStyle(panel.isVisible(), panel.isDragging(), !!panel.getChannel()), _v$5 = iframeStyle; _p$.e = style(_el$, _v$4, _p$.e); _p$.t = style(_el$5, _v$5, _p$.t); return _p$; }, { e: undefined, t: undefined }); return _el$; })(), createComponent(Show, { get when() { return panel.showSettings(); }, get children() { return createComponent(SettingsPanel, { get onClose() { return panel.toggleSettings; } }); } })]; } const root = document.createElement('div'); document.body.appendChild(root); render(() => createComponent(App, {}), root); })();