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 '';
}
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("" + listType + ">");
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 += "| " + parseInline(ths[i]) + " | ";
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 += "| " + parseInline(tds[c]) + " | ";
html += "
";
}
html += "";
}
html += "
";
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 '
";
})
.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 += '
';
});
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('");
}
}
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("- " + parseInline(olMatch[1]) + "
");
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 =
'" +
'";
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");
}
});
});