var THEME_KEY = "vyla-theme"; var ICONS = { system: '', light: '', dark: '', search: '', menu: '', doc: '', hash: '', copy: '', check: '', chevron: '', chevronLeft: '', chevronRight: '' }; var SITE_TREE = [ { id: "vyla", title: "Vyla", path: "/", groups: [ { title: "Welcome", items: [ { title: "Who Are We", file: "/start-here/who-are-we.md", path: "/start-here/who-are-we" } ] }, { title: "Start here", items: [ { title: "Introduction", file: "/start-here/introduction.md", path: "/start-here/introduction" }, { title: "Architecture", file: "/start-here/architecture.md", path: "/start-here/architecture" } ] }, { title: "Products", items: [ { title: "Home", file: "/products/home.md", path: "/products/home" }, { title: "Go", file: "/products/go.md", path: "/products/go" }, { title: "Tv", file: "/products/tv.md", path: "/products/tv" }, { title: "Player", file: "/products/player.md", path: "/products/player" } ] }, { title: "Concepts", items: [ { title: "Providers", file: "/concepts/providers.md", path: "/concepts/providers" } ] } ] }, { id: "sdk", title: "SDK", path: "/sdk", groups: [ { title: "Vyla SDK", items: [ { title: "Sdk", file: "/sdk/vyla-sdk/index.md", path: "/sdk" }, { title: "Sdk Quickstart", file: "/sdk/vyla-sdk/quickstart.md", path: "/sdk/vyla-sdk/quickstart" } ] } ] }, { id: "stream-api", title: "Stream API", path: "/stream-api/overview", groups: [ { title: "Overview", items: [ { title: "Overview", file: "/stream-api/overview/index.md", path: "/stream-api/overview" } ] }, { title: "Reference", items: [ { title: "Paths", file: "/stream-api/reference/paths.md", path: "/stream-api/reference/paths" }, { title: "Media Data", file: "/stream-api/reference/media-data.md", path: "/stream-api/reference/media-data" }, { title: "Operations", file: "/stream-api/reference/operations.md", path: "/stream-api/reference/operations" } ] }, { title: "Deployment", items: [ { title: "Self Hosting", file: "/stream-api/deployment/self-hosting.md", path: "/stream-api/deployment/self-hosting" } ] } ] }, { id: "misc", title: "Misc", path: "/misc/project/credit", groups: [ { title: "Project", items: [ { title: "Credit", file: "/misc/project/credit.md", path: "/misc/project/credit" }, { title: "DMCA", file: "/misc/project/DMCA.md", path: "/misc/project/DMCA" } ] } ] } ]; var searchData = null; var searchResults = []; var searchActive = 0; function icon(name) { return '' + ICONS[name] + ''; } function esc(s) { return String(s).replace(/[&<>"]/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """ }[c]; }); } function getThemeMode() { var v = localStorage.getItem(THEME_KEY); return v === "light" || v === "dark" ? v : "system"; } function applyTheme() { var mode = getThemeMode(); var dark = mode === "dark" || (mode === "system" && window.matchMedia("(prefers-color-scheme: dark)").matches); document.documentElement.classList.toggle("dark", dark); var logo = document.getElementById("brand-logo"); if (logo) logo.src = dark ? "/assets/images/logo.png" : "/assets/images/dark_logo.png"; } function setTheme(mode) { if (mode === "system") localStorage.removeItem(THEME_KEY); else localStorage.setItem(THEME_KEY, mode); applyTheme(); refreshThemeUI(); var menu = document.getElementById("theme-menu"); if (menu) menu.classList.remove("open"); } function toggleThemeMenu(e) { e.stopPropagation(); var menu = document.getElementById("theme-menu"); if (menu) menu.classList.toggle("open"); } function refreshThemeUI() { var mode = getThemeMode(); var btn = document.getElementById("theme-toggle"); if (btn) btn.innerHTML = icon(mode); document.querySelectorAll("#theme-menu button, #theme-seg button").forEach(function (b) { b.classList.toggle("on", b.getAttribute("data-mode") === mode); }); } function buildThemeControls() { var btn = document.getElementById("theme-toggle"); var links = document.querySelector("#navbar .links"); if (!btn || !links) return; var wrap = document.createElement("div"); wrap.className = "theme-wrap"; btn.parentNode.insertBefore(wrap, btn); wrap.appendChild(btn); var modes = ["system", "light", "dark"]; var menu = document.createElement("div"); menu.id = "theme-menu"; menu.innerHTML = modes.map(function (m) { return ''; }).join(""); wrap.appendChild(menu); menu.addEventListener("click", function (e) { var b = e.target.closest("button"); if (b) setTheme(b.getAttribute("data-mode")); }); refreshThemeUI(); } function dismissBanner() { var banner = document.getElementById("banner"); if (banner) banner.setAttribute("data-dismissed", "true"); document.body.classList.add("banner-dismissed"); localStorage.setItem("vyla-banner-dismissed", "true"); var header = document.getElementById("header-container"); if (header) document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px"); } function toggleSidebar() { var sidebar = document.getElementById("sidebar"); var nav = document.getElementById("navbar"); var top = Math.max(0, Math.round(nav.getBoundingClientRect().bottom)); sidebar.style.setProperty("--sidebar-top", top + "px"); var open = sidebar.classList.toggle("open"); document.body.classList.toggle("nav-open", open); } function cgSwitch(btn) { var group = btn.closest(".code-group"); var index = btn.getAttribute("data-tab-index"); group.querySelectorAll(".code-group-tab").forEach(function (t) { t.classList.toggle("active", t === btn); }); group.querySelectorAll(".code-group-panel").forEach(function (p, i) { p.classList.toggle("active", String(i) === index); }); window.dispatchEvent(new Event("resize")); } function parseMarkdown(md) { var lines = md.replace(/\r\n/g, "\n").split("\n"); var out = []; var inPre = false; var preLang = ""; var preCode = []; var inTable = false; var tableRows = []; var inList = false; var listType = "ul"; var inCodeGroup = false; var codeGroupTabs = []; var codeGroupPanels = []; function flushList() { if (inList) { out.push(""); inList = false; } } function flushTable() { if (inTable) { var html = '
'; if (tableRows.length > 0) { html += ""; var ths = tableRows[0]; for (var i = 0; i < ths.length; i++) html += ""; html += ""; } if (tableRows.length > 1) { html += ""; for (var r = 1; r < tableRows.length; r++) { html += ""; var tds = tableRows[r]; for (var c = 0; c < tds.length; c++) html += ""; html += ""; } html += ""; } html += "
" + parseInline(ths[i]) + "
" + parseInline(tds[c]) + "
"; out.push(html); inTable = false; tableRows = []; } } function slugify(text) { return text.toLowerCase().replace(/<[^>]+>/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); } function parseInline(text) { return text .replace(/!\[([^\]]*)\]\(([^)]+)\)/g, function (_, alt, src) { var cls = src.indexOf("devices.png") !== -1 || src.indexOf("site_preview.png") !== -1 || src.indexOf("site-preview.png") !== -1 ? ' class="vyla-banner"' : ""; return '' + esc(alt) + '"; }) .replace(/\[([^\]]+)\]\(([^)]+)\)/g, function (_, label, href) { return '' + label + ""; }) .replace(/`([^`]+)`/g, function (_, code) { return "" + esc(code) + ""; }) .replace(/\*\*([^*]+)\*\*/g, "$1") .replace(/\*([^*]+)\*/g, "$1"); } for (var i = 0; i < lines.length; i++) { var line = lines[i]; if (line.startsWith(":::code-group")) { flushList(); flushTable(); inCodeGroup = true; codeGroupTabs = []; codeGroupPanels = []; continue; } if (inCodeGroup && line === ":::") { flushList(); flushTable(); var cgHtml = '
'; codeGroupTabs.forEach(function (tab, idx) { var activeClass = idx === 0 ? " active" : ""; cgHtml += ''; }); cgHtml += '
'; codeGroupPanels.forEach(function (panel, idx) { var activeClass = idx === 0 ? " active" : ""; var langClass = panel.lang ? ' class="language-' + panel.lang + '"' : ' class="language-none"'; cgHtml += '
' + esc(panel.code) + '
'; }); cgHtml += '
'; out.push(cgHtml); inCodeGroup = false; codeGroupTabs = []; codeGroupPanels = []; continue; } if (line.startsWith("```")) { flushList(); flushTable(); if (!inPre) { inPre = true; var info = line.slice(3).trim(); var tabMatch = info.match(/\[([^\]]+)\]/); var tabTitle = tabMatch ? tabMatch[1] : ""; preLang = info.replace(/\[([^\]]+)\]/, "").trim(); if (inCodeGroup && tabTitle) { codeGroupTabs.push(tabTitle); } preCode = []; } else { inPre = false; var codeContent = preCode.join("\n"); var resolvedLang = preLang; if (!resolvedLang) { if (codeContent.trim().startsWith("{") || codeContent.trim().startsWith("[")) resolvedLang = "json"; else if (codeContent.indexOf("=") !== -1 && !codeContent.includes("function") && !codeContent.includes(";")) resolvedLang = "ini"; else if (codeContent.startsWith("http://") || codeContent.startsWith("https://")) resolvedLang = "http"; else resolvedLang = "bash"; } if (inCodeGroup) { codeGroupPanels.push({ lang: resolvedLang, code: codeContent }); } else { var langClass = resolvedLang ? ' class="language-' + resolvedLang + '"' : ' class="language-none"'; out.push('
" + esc(codeContent) + "
"); } } continue; } if (inPre) { preCode.push(line); continue; } if (line.startsWith(":::callout-")) { flushList(); flushTable(); var type = line.slice(11).trim(); out.push('
'); continue; } if (line === ":::") { flushList(); flushTable(); out.push("
"); continue; } if (line.startsWith(" 0 && cells[0].match(/^:?-+:?$/)) { continue; } inTable = true; tableRows.push(cells); continue; } else { flushTable(); } if (line.match(/^---+$/)) { flushList(); out.push("
"); continue; } var hMatch = line.match(/^(#{1,6})\s+(.*)$/); if (hMatch) { flushList(); var level = hMatch[1].length; var titleText = parseInline(hMatch[2]); var rawTitle = hMatch[2]; var id = slugify(rawTitle); out.push("' + titleText + ""); continue; } var olMatch = line.match(/^\d+\.\s+(.*)$/); if (olMatch) { if (!inList || listType !== "ol") { flushList(); inList = true; listType = "ol"; out.push("
    "); } out.push("
  1. " + parseInline(olMatch[1]) + "
  2. "); continue; } var ulMatch = line.match(/^[-*]\s+(.*)$/); if (ulMatch) { if (!inList || listType !== "ul") { flushList(); inList = true; listType = "ul"; out.push("
      "); } out.push("
    • " + parseInline(ulMatch[1]) + "
    • "); continue; } flushList(); if (line.trim().length === 0) { continue; } if (line.startsWith("> ")) { out.push("

      " + parseInline(line.slice(2)) + "

      "); continue; } out.push("

      " + parseInline(line) + "

      "); } flushList(); flushTable(); return out.join("\n"); } function resolveRoute(pathname) { var path = pathname || window.location.pathname; if (!path.startsWith("/")) path = "/" + path; path = path.replace(/\/+$/, "") || "/"; for (var t = 0; t < SITE_TREE.length; t++) { var tab = SITE_TREE[t]; for (var g = 0; g < tab.groups.length; g++) { var grp = tab.groups[g]; for (var i = 0; i < grp.items.length; i++) { var item = grp.items[i]; var itemPath = item.path.replace(/\/+$/, "") || "/"; if (itemPath === path) { return { tab: tab, group: grp, item: item }; } } } } return { tab: SITE_TREE[0], group: SITE_TREE[0].groups[0], item: SITE_TREE[0].groups[0].items[0] }; } function renderNavTabs(currentTab) { var nav = document.getElementById("nav-tabs"); if (!nav) return; nav.innerHTML = SITE_TREE.map(function (tab) { var activeClass = tab.id === currentTab.id ? " active" : ""; return '' + esc(tab.title) + ""; }).join(""); } function renderSidebar(currentTab, currentItem) { var sidebar = document.getElementById("sidebar"); if (!sidebar) return; var html = ""; currentTab.groups.forEach(function (group) { html += '
      '; html += '

      ' + esc(group.title) + "

      "; html += "
        "; group.items.forEach(function (item) { var activeClass = item.path === currentItem.path ? ' class="active"' : ""; html += '
      • ' + esc(item.title) + "
      • "; }); html += "
      "; }); sidebar.innerHTML = html; buildMobileBar(currentTab, currentItem); } function buildMobileBar(currentTab, currentItem) { var oldBar = document.getElementById("mobile-bar"); if (oldBar) oldBar.remove(); var headerContainer = document.getElementById("header-container") || document.getElementById("navbar"); if (!headerContainer) return; var route = resolveRoute(window.location.pathname); var bar = document.createElement("div"); bar.id = "mobile-bar"; bar.innerHTML = '' + '
      ' + '' + esc(route.group.title) + '' + '›' + '' + esc(currentItem.title) + '' + '' + '
      '; headerContainer.appendChild(bar); var pageBtn = bar.querySelector("#mobile-crumb-page"); if (pageBtn) { pageBtn.addEventListener("click", function () { window.scrollTo({ top: 0, behavior: "smooth" }); }); } var subBtn = bar.querySelector("#mobile-crumb-sub"); if (subBtn) { subBtn.addEventListener("click", function () { var targetId = subBtn.getAttribute("data-target-id"); var targetEl = targetId ? document.getElementById(targetId) : null; if (targetEl) { var header = document.getElementById("header-container") || document.getElementById("navbar"); var offset = header ? header.offsetHeight + 16 : 80; var topPos = targetEl.getBoundingClientRect().top + window.pageYOffset - offset; window.scrollTo({ top: Math.max(0, topPos), behavior: "smooth" }); } }); } var sidebar = document.getElementById("sidebar"); if (sidebar) { var existingTabs = sidebar.querySelector(".mobile-tabs"); if (existingTabs) existingTabs.remove(); var g = document.createElement("div"); g.className = "mobile-tabs"; g.innerHTML = '" + '
      ' + SITE_TREE.map(function (t) { var onClass = t.id === currentTab.id ? " on" : ""; return '"; }).join("") + "
      "; sidebar.insertBefore(g, sidebar.firstChild); g.querySelector(".mt-btn").addEventListener("click", function () { g.classList.toggle("open"); }); g.querySelectorAll(".mt-item").forEach(function (btn) { btn.addEventListener("click", function (e) { e.stopPropagation(); var targetId = btn.getAttribute("data-tab-id"); var targetTab = SITE_TREE.find(function (t) { return t.id === targetId; }); if (targetTab) { g.querySelectorAll(".mt-item").forEach(function (el) { el.classList.remove("on"); }); btn.classList.add("on"); g.classList.remove("open"); renderSidebar(targetTab, currentItem); sidebar.scrollTop = 0; } }); }); document.addEventListener("click", function (e) { if (!g.contains(e.target)) g.classList.remove("open"); }); } var existingHead = sidebar.querySelector(".side-head"); if (existingHead) existingHead.remove(); var head = document.createElement("div"); head.className = "side-head"; var brand = document.querySelector("#navbar .brand"); if (brand && sidebar) { var b = brand.cloneNode(true); b.querySelector("img").removeAttribute("id"); var sideLinks = document.createElement("div"); sideLinks.className = "side-links"; var modes = ["system", "light", "dark"]; var seg = document.createElement("div"); seg.id = "theme-seg"; seg.innerHTML = modes.map(function (m) { return ''; }).join(""); seg.addEventListener("click", function (e) { var btn = e.target.closest("button"); if (btn) setTheme(btn.getAttribute("data-mode")); }); sideLinks.appendChild(seg); head.appendChild(b); head.appendChild(sideLinks); sidebar.insertBefore(head, sidebar.firstChild); refreshThemeUI(); } } function updateFooterNav(currentItem) { var allItems = []; SITE_TREE.forEach(function (tab) { tab.groups.forEach(function (grp) { grp.items.forEach(function (itm) { allItems.push(itm); }); }); }); var idx = allItems.findIndex(function (it) { return it.path.replace(/\/+$/, "") === currentItem.path.replace(/\/+$/, ""); }); var prev = idx > 0 ? allItems[idx - 1] : null; var next = idx >= 0 && idx < allItems.length - 1 ? allItems[idx + 1] : null; var prevBtn = document.getElementById("footer-prev"); var nextBtn = document.getElementById("footer-next"); if (prevBtn) { if (prev) { prevBtn.href = prev.path; prevBtn.innerHTML = icon("chevronLeft") + "" + esc(prev.title) + ""; prevBtn.classList.remove("hidden"); } else { prevBtn.classList.add("hidden"); } } if (nextBtn) { if (next) { nextBtn.href = next.path; nextBtn.innerHTML = "" + esc(next.title) + "" + icon("chevronRight"); nextBtn.classList.remove("hidden"); } else { nextBtn.classList.add("hidden"); } } } function updateToc() { var main = document.getElementById("main-content"); var tocList = document.getElementById("toc-list"); if (!main || !tocList) return; var headings = main.querySelectorAll("h2"); var html = ""; headings.forEach(function (h) { var id = h.id || h.textContent.toLowerCase().replace(/[^a-z0-9]+/g, "-"); h.id = id; html += '
    • ' + esc(h.textContent) + "
    • "; }); tocList.innerHTML = html; initTocSpy(); } function initTocSpy() { var main = document.getElementById("main-content"); var links = Array.prototype.slice.call(document.querySelectorAll("aside#toc a")); var headings = main ? Array.prototype.slice.call(main.querySelectorAll("h2, h3")) : []; function update() { var activeHeading = null; headings.forEach(function (h) { if (h.getBoundingClientRect().top <= 160) { activeHeading = h; } }); if (links.length) { var activeId = activeHeading ? activeHeading.id : ""; links.forEach(function (a) { var href = a.getAttribute("href").replace(/^#/, ""); a.classList.toggle("active", href === activeId); }); } var crumbPage = document.getElementById("mobile-crumb-page"); var crumbSub = document.getElementById("mobile-crumb-sub"); var crumbTarget = document.getElementById("mobile-crumb-target"); if (crumbPage && crumbSub && crumbTarget) { if (activeHeading && window.scrollY > 80) { crumbPage.classList.add("muted"); crumbSub.style.display = "inline-flex"; crumbSub.setAttribute("data-target-id", activeHeading.id); crumbTarget.textContent = activeHeading.textContent; } else { crumbPage.classList.remove("muted"); crumbSub.style.display = "none"; crumbSub.removeAttribute("data-target-id"); crumbTarget.textContent = ""; } } } window.addEventListener("scroll", update, { passive: true }); update(); } function addCopyButtons() { document.querySelectorAll(".code-group").forEach(function (cg) { if (cg.querySelector(".cg-copy-btn")) return; var b = document.createElement("button"); b.type = "button"; b.className = "cg-copy-btn"; b.setAttribute("aria-label", "Copy"); b.innerHTML = icon("copy"); b.onclick = function () { var activePre = cg.querySelector(".code-group-panel.active pre"); if (!activePre) return; navigator.clipboard.writeText(activePre.innerText).then(function () { b.innerHTML = icon("check"); setTimeout(function () { b.innerHTML = icon("copy"); }, 1500); }); }; cg.appendChild(b); }); document.querySelectorAll("main.prose pre").forEach(function (pre) { if (pre.closest(".code-group")) return; if (pre.parentNode.classList.contains("code-wrap") && pre.parentNode.querySelector(".copy-btn")) return; var wrap = pre.parentNode.classList.contains("code-wrap") ? pre.parentNode : document.createElement("div"); if (!pre.parentNode.classList.contains("code-wrap")) { wrap.className = "code-wrap"; pre.parentNode.insertBefore(wrap, pre); wrap.appendChild(pre); } var b = document.createElement("button"); b.type = "button"; b.className = "copy-btn"; b.setAttribute("aria-label", "Copy"); b.innerHTML = icon("copy"); b.onclick = function () { navigator.clipboard.writeText(pre.innerText).then(function () { b.innerHTML = icon("check"); setTimeout(function () { b.innerHTML = icon("copy"); }, 1500); }); }; wrap.appendChild(b); }); } function bindScrollFade(box, getScroller) { function update() { var el = getScroller(); var max = el.scrollWidth - el.clientWidth; var left = 0; var right = 0; if (max > 1) { left = Math.min(el.scrollLeft, 56); right = Math.min(Math.max(Math.round(max - el.scrollLeft), 0), 56); } box.style.setProperty("--fade-l", left + "px"); box.style.setProperty("--fade-r", right + "px"); } box.addEventListener("scroll", update, true); window.addEventListener("resize", update); update(); } function initCodeFades() { document.querySelectorAll("main.prose pre").forEach(function (pre) { var code = pre.querySelector("code"); bindScrollFade(pre, function () { return code && code.scrollWidth > code.clientWidth + 1 ? code : pre; }); }); } function wrapTables() { document.querySelectorAll("main.prose table").forEach(function (table) { if (table.parentNode.classList.contains("table-wrap")) { bindScrollFade(table.parentNode, function () { return table.parentNode; }); } }); } function navigateTo(path) { var cleanPath = path.startsWith("/") ? path : "/" + path; if (window.location.pathname !== cleanPath) { window.history.pushState(null, "", cleanPath); } loadPage(); } function loadPage() { var route = resolveRoute(window.location.pathname); renderNavTabs(route.tab); renderSidebar(route.tab, route.item); document.title = route.item.title + " — Vyla"; var contentEl = document.getElementById("main-content"); if (contentEl) { contentEl.classList.remove("content-animate"); contentEl.innerHTML = ''; } fetch(route.item.file) .then(function (res) { if (!res.ok) throw new Error("Not found"); return res.text(); }) .then(function (md) { if (contentEl) { contentEl.innerHTML = parseMarkdown(md); if (window.Prism) { window.Prism.highlightAllUnder(contentEl); } var eyebrow = document.createElement("div"); eyebrow.className = "eyebrow"; eyebrow.textContent = route.group.title; var h1 = contentEl.querySelector("h1"); if (h1) h1.parentNode.insertBefore(eyebrow, h1); contentEl.classList.remove("caught-up"); contentEl.classList.add("content-animate"); var catchUpOnScroll = function () { if (contentEl.classList.contains("content-animate")) { contentEl.classList.add("caught-up"); } window.removeEventListener("scroll", catchUpOnScroll, { passive: true }); window.removeEventListener("wheel", catchUpOnScroll, { passive: true }); window.removeEventListener("touchmove", catchUpOnScroll, { passive: true }); }; window.addEventListener("scroll", catchUpOnScroll, { passive: true }); window.addEventListener("wheel", catchUpOnScroll, { passive: true }); window.addEventListener("touchmove", catchUpOnScroll, { passive: true }); setTimeout(function () { contentEl.classList.add("caught-up"); window.removeEventListener("scroll", catchUpOnScroll); window.removeEventListener("wheel", catchUpOnScroll); window.removeEventListener("touchmove", catchUpOnScroll); }, 700); updateFooterNav(route.item); updateToc(); addCopyButtons(); initCodeFades(); wrapTables(); window.scrollTo(0, 0); } }) .catch(function () { if (contentEl) { contentEl.innerHTML = "

      Page not found

      The requested page could not be located.

      "; } }); var sidebar = document.getElementById("sidebar"); if (sidebar && sidebar.classList.contains("open")) { sidebar.classList.remove("open"); document.body.classList.remove("nav-open"); } } function getRecent() { try { return JSON.parse(localStorage.getItem("vyla-recent-v2")) || []; } catch (e) { return []; } } function saveRecent(item) { if (!item) return; var slim = { title: item.title, crumbs: item.crumbs, href: item.href, type: item.type }; var list = getRecent().filter(function (r) { return r.href !== slim.href; }); list.unshift(slim); localStorage.setItem("vyla-recent-v2", JSON.stringify(list.slice(0, 5))); } function loadSearchData() { if (searchData) return Promise.resolve(searchData); var promises = []; SITE_TREE.forEach(function (tab) { tab.groups.forEach(function (group) { group.items.forEach(function (item) { promises.push( fetch(item.file) .then(function (r) { return r.text(); }) .then(function (text) { return { title: item.title, crumbs: [tab.title, group.title, item.title], href: item.path, type: "page", text: text.replace(/[#*`[\]()]/g, " ").slice(0, 3000) }; }) .catch(function () { return null; }) ); }); }); }); return Promise.all(promises).then(function (results) { searchData = results.filter(Boolean); return searchData; }); } function renderSearch(keepActive) { var q = document.getElementById("search-input").value.trim().toLowerCase(); var tokens = q.split(/\s+/).filter(Boolean); var box = document.getElementById("search-results"); if (!keepActive) searchActive = 0; if (tokens.length) { if (!searchData) { searchResults = []; box.innerHTML = '
      Indexing pages...
      '; return; } searchResults = searchData .map(function (it) { var score = 0; var t = it.title.toLowerCase(); var txt = it.text.toLowerCase(); tokens.forEach(function (tok) { if (t.indexOf(tok) !== -1) score += 20; if (txt.indexOf(tok) !== -1) score += 2; }); return { it: it, s: score }; }) .filter(function (r) { return r.s > 0; }) .sort(function (a, b) { return b.s - a.s; }) .slice(0, 10) .map(function (r) { return r.it; }); } else { searchResults = getRecent(); } if (!searchResults.length) { box.innerHTML = '
      ' + (tokens.length ? "No results found" : "No recent searches") + "
      "; return; } box.innerHTML = (tokens.length ? "" : '
      Recent searches
      ') + searchResults.map(function (it, i) { var crumbs = it.crumbs.join('›'); return '' + icon("doc") + '
      ' + esc(it.title) + "
      " + '
      ' + crumbs + "
      "; }).join(""); } function openSearch() { var input = document.getElementById("search-input"); document.getElementById("search-modal").classList.add("open"); document.body.style.overflow = "hidden"; input.value = ""; renderSearch(false); loadSearchData().then(function () { renderSearch(false); }); input.focus(); } function closeSearch() { document.getElementById("search-modal").classList.remove("open"); document.body.style.overflow = ""; } function onSearchKey(e) { if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") { e.preventDefault(); openSearch(); return; } var modal = document.getElementById("search-modal"); if (!modal || !modal.classList.contains("open")) return; if (e.key === "Escape") { closeSearch(); } else if (e.key === "ArrowDown" || e.key === "ArrowUp") { e.preventDefault(); var n = searchResults.length || 1; searchActive = (searchActive + (e.key === "ArrowDown" ? 1 : -1) + n) % n; renderSearch(true); } else if (e.key === "Enter" && searchResults[searchActive]) { var item = searchResults[searchActive]; saveRecent(item); closeSearch(); navigateTo(item.href); } } function buildSearch() { var trigger = document.getElementById("search-trigger"); if (trigger) trigger.innerHTML = icon("search") + "Search...Ctrl K"; var modal = document.createElement("div"); modal.id = "search-modal"; modal.innerHTML = '
      ' + icon("search") + '
      ' + '
      '; document.body.appendChild(modal); modal.querySelector(".search-backdrop").addEventListener("click", closeSearch); document.getElementById("search-input").addEventListener("input", function () { renderSearch(false); }); document.getElementById("search-results").addEventListener("click", function (e) { var a = e.target.closest(".sr"); if (a) { e.preventDefault(); saveRecent(searchResults[Number(a.getAttribute("data-i"))]); closeSearch(); navigateTo(a.getAttribute("href")); } }); document.addEventListener("keydown", onSearchKey); } document.addEventListener("DOMContentLoaded", function () { if (localStorage.getItem("vyla-banner-dismissed") === "true") { var banner = document.getElementById("banner"); if (banner) banner.setAttribute("data-dismissed", "true"); document.body.classList.add("banner-dismissed"); } var header = document.getElementById("header-container"); if (header) { document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px"); window.addEventListener("resize", function () { document.documentElement.style.setProperty("--header-height", header.offsetHeight + "px"); }); } applyTheme(); buildThemeControls(); buildSearch(); loadPage(); window.addEventListener("popstate", loadPage); document.addEventListener("click", function (e) { var a = e.target.closest("a"); if (a) { var href = a.getAttribute("href"); if (href && href.startsWith("#")) { return; } if (href && href.startsWith("/") && !href.startsWith("//") && a.target !== "_blank") { e.preventDefault(); navigateTo(href); return; } } var sidebar = document.getElementById("sidebar"); var menu = document.getElementById("theme-menu"); if (menu && !e.target.closest(".theme-wrap")) menu.classList.remove("open"); if ( sidebar && sidebar.classList.contains("open") && !sidebar.contains(e.target) && !e.target.closest("#mobile-toggle") && !e.target.closest(".theme-wrap") && !e.target.closest("#theme-seg") ) { sidebar.classList.remove("open"); document.body.classList.remove("nav-open"); } }); });