feat(site): add global search modal, search trigger styling, and remove emoji from docs

- Add search modal with full keyboard navigation (Ctrl+K, arrows, Enter, Esc) to all pages
- Search opens in-page on every page with static docs index; results navigate to docs#section
- Search trigger in desktop nav styled as bordered pill chip with hover states
- Add Search Docs link in mobile hamburger menus
- Fix nav-links vertical alignment with align-items: center
- Remove all colored emoji from docs.html (checkmarks, crosses, music note)
This commit is contained in:
zarzet committed 2026-02-14 00:54:46 +07:00
1 parent c70ba5962e
commit 9b89625660
4 files changed
+6344 -55

No files matched your search

+5533
View File
File diff suppressed because it is too large. Load diff
+271 -18
View File
@@ -32,7 +32,11 @@
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
html { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: #333 transparent; }
html::-webkit-scrollbar { width: 8px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
html::-webkit-scrollbar-thumb:hover { background: #555; }
body {
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
@@ -51,13 +55,13 @@
-webkit-backdrop-filter: blur(20px);
}
.nav-inner {
max-width: var(--max-w); margin: auto;
max-width: 1100px; margin: auto;
display: flex; align-items: center; justify-content: space-between;
padding: 0 24px; height: 64px;
}
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
.nav-links { display: flex; gap: 24px; list-style: none; }
.nav-links { display: flex; align-items: center; gap: 24px; list-style: none; }
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
.nav-links a:hover { color: var(--text); text-decoration: none; }
.nav-links a.active { color: var(--text); font-weight: 600; }
@@ -65,6 +69,21 @@
.nav-links .nav-icon:hover { opacity: 1; }
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
.search-trigger {
display: flex; align-items: center; gap: 6px;
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
border-radius: 8px; padding: 6px 12px;
color: var(--text-dim); font-size: .85rem; cursor: pointer;
font-family: inherit; transition: color .2s, border-color .2s, background .2s;
white-space: nowrap; text-decoration: none;
}
.search-trigger:hover { color: var(--text); border-color: rgba(255,255,255,.25); background: rgba(255,255,255,.1); text-decoration: none; }
.search-trigger svg { width: 14px; height: 14px; fill: currentColor; flex-shrink: 0; }
.search-trigger kbd {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 0px 4px; font-size: .65rem;
font-family: inherit; color: #555; line-height: 1.4; margin-left: 2px;
}
/* ── PAGE HEADER ── */
.page-header {
@@ -179,7 +198,7 @@
background: var(--surface);
padding: 40px 24px; text-align: center;
}
.footer-inner { max-width: var(--max-w); margin: auto; }
.footer-inner { max-width: 1100px; margin: auto; }
.footer-links { display: flex; gap: 24px; justify-content: center; flex-wrap: wrap; margin-bottom: 16px; }
.footer-links a { color: var(--text-dim); font-size: .9rem; }
.footer-links a:hover { color: var(--text); }
@@ -282,23 +301,98 @@
}
.icon-svg { width: 20px; height: 20px; fill: currentColor; }
/* ── SEARCH MODAL ── */
.search-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.6);
z-index: 300; opacity: 0; pointer-events: none;
transition: opacity .2s cubic-bezier(.4,0,.2,1);
display: flex; align-items: flex-start; justify-content: center;
padding-top: min(20vh, 140px);
}
.search-overlay.open { opacity: 1; pointer-events: auto; }
.search-modal {
background: var(--surface); border: 1px solid rgba(255,255,255,.1);
border-radius: 16px; width: 580px; max-width: calc(100vw - 32px);
max-height: min(70vh, 520px); display: flex; flex-direction: column;
box-shadow: 0 16px 70px rgba(0,0,0,.6);
transform: translateY(-12px) scale(.97); opacity: 0;
transition: transform .25s cubic-bezier(.4,0,.2,1), opacity .2s;
}
.search-overlay.open .search-modal { transform: translateY(0) scale(1); opacity: 1; }
.search-header {
display: flex; align-items: center; gap: 10px;
padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.08);
}
.search-header svg { width: 18px; height: 18px; fill: var(--text-dim); flex-shrink: 0; }
.search-input {
flex: 1; background: none; border: none; outline: none;
color: var(--text); font-size: .95rem; font-family: inherit;
}
.search-input::placeholder { color: var(--text-dim); }
.search-esc {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 2px 7px; font-size: .72rem;
color: var(--text-dim); font-family: inherit; cursor: pointer;
}
.search-esc:hover { background: rgba(255,255,255,.14); }
.search-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: #333 transparent; }
.search-body::-webkit-scrollbar { width: 6px; }
.search-body::-webkit-scrollbar-track { background: transparent; }
.search-body::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
.search-group-label {
padding: 8px 10px 4px; font-size: .72rem; font-weight: 600;
color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
}
.search-item {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-radius: 10px; cursor: pointer;
color: var(--text); font-size: .88rem; transition: background .15s;
}
.search-item:hover, .search-item.active { background: rgba(255,255,255,.07); }
.search-item svg { width: 16px; height: 16px; fill: var(--text-dim); flex-shrink: 0; }
.search-item-text { flex: 1; min-width: 0; }
.search-item-title { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search-item-section { font-size: .78rem; color: var(--text-dim); margin-top: 1px; }
.search-item mark { background: rgba(29,185,84,.25); color: var(--text); border-radius: 2px; padding: 0 1px; }
.search-item .search-enter { color: var(--text-dim); font-size: .72rem; opacity: 0; transition: opacity .15s; }
.search-item.active .search-enter { opacity: 1; }
.search-empty { padding: 32px 16px; text-align: center; color: var(--text-dim); font-size: .9rem; }
.search-footer {
padding: 10px 16px; border-top: 1px solid rgba(255,255,255,.06);
display: flex; align-items: center; gap: 16px; font-size: .72rem; color: #555;
}
.search-footer kbd {
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.08);
border-radius: 3px; padding: 1px 4px; font-family: inherit; font-size: .68rem;
}
@media (max-width: 640px) {
.search-trigger kbd { display: none; }
.search-overlay { padding-top: 16px; }
.search-modal { max-height: 80vh; border-radius: 14px; }
}
</style>
</head>
<body>
<nav>
<div class="nav-inner">
<a class="nav-brand" href="index.html">
<a class="nav-brand" href="index">
<img src="icon.png" alt="SpotiFLAC">
SpotiFLAC
SpotiFLAC Mobile
</a>
<ul class="nav-links">
<li><a href="index.html#features">Features</a></li>
<li><a href="downloads.html" class="active">Downloads</a></li>
<li><a href="index.html#faq">FAQ</a></li>
<li><a href="partners.html">Partners</a></li>
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
<li><a href="index#features">Features</a></li>
<li><a href="downloads" class="active">Downloads</a></li>
<li><a href="index#faq">FAQ</a></li>
<li><a href="partners">Partners</a></li>
<li><a href="docs">Docs</a></li>
<li class="nav-divider"></li>
<li><button class="search-trigger" onclick="openSearch()" aria-label="Search documentation">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
Search
<kbd id="searchShortcutHint">Ctrl K</kbd>
</button></li>
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
</ul>
@@ -311,11 +405,12 @@
<!-- MOBILE MENU -->
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
<div class="mobile-menu" id="mobileMenu">
<a href="index.html#features">Features</a>
<a href="downloads.html" class="active">Downloads</a>
<a href="index.html#faq">FAQ</a>
<a href="partners.html">Partners</a>
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
<a href="index#features">Features</a>
<a href="downloads" class="active">Downloads</a>
<a href="index#faq">FAQ</a>
<a href="partners">Partners</a>
<a href="docs">Docs</a>
<a href="javascript:void(0)" onclick="toggleMenu();openSearch()" style="display:flex;align-items:center;gap:6px;color:var(--text-dim)"><svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:currentColor"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>Search Docs</a>
<div class="mobile-divider"></div>
<div class="mobile-icons">
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
@@ -347,11 +442,30 @@
</a>
</div>
<!-- SEARCH MODAL -->
<div class="search-overlay" id="searchOverlay" onclick="if(event.target===this)closeSearch()">
<div class="search-modal">
<div class="search-header">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input class="search-input" id="searchInput" type="text" placeholder="Search documentation..." autocomplete="off" spellcheck="false">
<button class="search-esc" onclick="closeSearch()">Esc</button>
</div>
<div class="search-body" id="searchBody">
<div class="search-empty">Type to search across all documentation sections</div>
</div>
<div class="search-footer">
<span><kbd>&uarr;</kbd> <kbd>&darr;</kbd> to navigate</span>
<span><kbd>Enter</kbd> to select</span>
<span><kbd>Esc</kbd> to close</span>
</div>
</div>
</div>
<footer>
<div class="footer-inner">
<div class="footer-links">
<a href="index.html">Home</a>
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
<a href="index">Home</a>
<a href="docs">Documentation</a>
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
<a href="https://t.me/spotiflac" target="_blank">Telegram</a>
<a href="https://ko-fi.com/zarzet" target="_blank">Support / Ko-fi</a>
@@ -480,6 +594,145 @@ document.getElementById('mobileMenu').addEventListener('click', function(e) {
if (e.target.closest('a')) toggleMenu();
});
</script>
<script>
/* ── DOCS SEARCH ── */
(function() {
var overlay = document.getElementById('searchOverlay');
var input = document.getElementById('searchInput');
var body = document.getElementById('searchBody');
var hintEl = document.getElementById('searchShortcutHint');
var activeIdx = -1;
var results = [];
if (navigator.platform && navigator.platform.indexOf('Mac') > -1) {
if (hintEl) hintEl.textContent = '\u2318 K';
}
var searchIndex = [{"id":"table-of-contents","title":"Table of Contents","level":2,"section":"Table of Contents"},{"id":"introduction","title":"Introduction","level":2,"section":"Introduction"},{"id":"requirements","title":"Requirements","level":3,"section":"Introduction"},{"id":"extension-structure","title":"Extension Structure","level":2,"section":"Extension Structure"},{"id":"manifest-file","title":"Manifest File","level":2,"section":"Manifest File"},{"id":"complete-manifest-example","title":"Complete Manifest Example","level":3,"section":"Manifest File"},{"id":"manifest-fields","title":"Manifest Fields","level":3,"section":"Manifest File"},{"id":"quality-options","title":"Quality Options","level":3,"section":"Manifest File"},{"id":"quality-specific-settings","title":"Quality-Specific Settings","level":3,"section":"Manifest File"},{"id":"permissions","title":"Permissions","level":3,"section":"Manifest File"},{"id":"extension-types","title":"Extension Types","level":3,"section":"Manifest File"},{"id":"settings","title":"Settings","level":3,"section":"Manifest File"},{"id":"button-setting-type","title":"Button Setting Type","level":3,"section":"Manifest File"},{"id":"custom-search-behavior","title":"Custom Search Behavior","level":3,"section":"Manifest File"},{"id":"thumbnail-ratio-presets","title":"Thumbnail Ratio Presets","level":4,"section":"Manifest File"},{"id":"custom-url-handler","title":"Custom URL Handler","level":3,"section":"Manifest File"},{"id":"album--playlist-functions-v301","title":"Album & Playlist Functions (v3.0.1+)","level":3,"section":"Manifest File"},{"id":"artist-support","title":"Artist Support","level":3,"section":"Manifest File"},{"id":"home-feed-support","title":"Home Feed Support","level":3,"section":"Manifest File"},{"id":"track-enrichment","title":"Track Enrichment","level":3,"section":"Manifest File"},{"id":"custom-track-matching","title":"Custom Track Matching","level":3,"section":"Manifest File"},{"id":"post-processing-hooks","title":"Post-Processing Hooks","level":3,"section":"Manifest File"},{"id":"post-process-api-v2-recommended","title":"Post-Process API v2 (Recommended)","level":4,"section":"Manifest File"},{"id":"main-script","title":"Main Script","level":2,"section":"Main Script"},{"id":"basic-structure","title":"Basic Structure","level":3,"section":"Main Script"},{"id":"important-registerextension","title":"Important: registerExtension()","level":3,"section":"Main Script"},{"id":"api-reference","title":"API Reference","level":2,"section":"API Reference"},{"id":"http-api","title":"HTTP API","level":3,"section":"API Reference"},{"id":"request-headers","title":"Request Headers","level":4,"section":"API Reference"},{"id":"response-object","title":"Response Object","level":4,"section":"API Reference"},{"id":"form-encoded-post-applicationx-www-form-urlencoded","title":"Form-Encoded POST (application/x-www-form-urlencoded)","level":4,"section":"API Reference"},{"id":"cookie-jar","title":"Cookie Jar","level":4,"section":"API Reference"},{"id":"youtube-music--innertube-api-example","title":"YouTube Music / Innertube API Example","level":4,"section":"API Reference"},{"id":"browser-like-polyfills","title":"Browser-like Polyfills","level":3,"section":"API Reference"},{"id":"fetch-api","title":"fetch() API","level":4,"section":"API Reference"},{"id":"atob--btoa","title":"atob() / btoa()","level":4,"section":"API Reference"},{"id":"textencoder--textdecoder","title":"TextEncoder / TextDecoder","level":4,"section":"API Reference"},{"id":"url--urlsearchparams","title":"URL / URLSearchParams","level":4,"section":"API Reference"},{"id":"porting-browser-libraries","title":"Porting Browser Libraries","level":4,"section":"API Reference"},{"id":"storage-api","title":"Storage API","level":3,"section":"API Reference"},{"id":"file-api","title":"File API","level":3,"section":"API Reference"},{"id":"logging-api","title":"Logging API","level":3,"section":"API Reference"},{"id":"utility-api","title":"Utility API","level":3,"section":"API Reference"},{"id":"hmac-sha1-for-totp","title":"HMAC-SHA1 for TOTP","level":4,"section":"API Reference"},{"id":"hmac-sha256-example-api-signing","title":"HMAC-SHA256 Example (API Signing)","level":4,"section":"API Reference"},{"id":"go-backend-api","title":"Go Backend API","level":3,"section":"API Reference"},{"id":"using-getlocaltime-for-time-based-greeting","title":"Using getLocalTime() for Time-Based Greeting","level":4,"section":"API Reference"},{"id":"using-getlocaltime-for-timezone-in-api-calls","title":"Using getLocalTime() for Timezone in API Calls","level":4,"section":"API Reference"},{"id":"credentials-api-encrypted","title":"Credentials API (Encrypted)","level":3,"section":"API Reference"},{"id":"auth-api-oauth-support","title":"Auth API (OAuth Support)","level":3,"section":"API Reference"},{"id":"pkce-oauth-flow-recommended","title":"PKCE OAuth Flow (Recommended)","level":3,"section":"API Reference"},{"id":"quick-start-high-level-api","title":"QuiLine truncated
function escHtml(s) {
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function highlight(text, query) {
if (!query) return escHtml(text);
var escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var re = new RegExp('(' + escaped + ')', 'gi');
return escHtml(text).replace(re, '<mark>$1</mark>');
}
function doSearch(query) {
query = query.trim().toLowerCase();
if (!query) {
body.innerHTML = '<div class="search-empty">Type to search across all documentation sections</div>';
results = []; activeIdx = -1; return;
}
var tokens = query.split(/\s+/).filter(Boolean);
var scored = [];
searchIndex.forEach(function(item) {
var titleLow = item.title.toLowerCase();
var sectionLow = item.section.toLowerCase();
var score = 0;
for (var i = 0; i < tokens.length; i++) {
var t = tokens[i];
if (titleLow.includes(t)) {
score += titleLow === t ? 100 : titleLow.startsWith(t) ? 60 : 30;
} else if (sectionLow.includes(t)) {
score += 10;
}
}
if (score > 0 && item.level === 2) score += 8;
if (score > 0 && item.level === 1) score += 15;
if (score > 0) scored.push({ item: item, score: score });
});
scored.sort(function(a, b) { return b.score - a.score; });
results = scored.slice(0, 20);
activeIdx = results.length > 0 ? 0 : -1;
if (!results.length) {
body.innerHTML = '<div class="search-empty">No results found for "' + escHtml(query) + '"</div>';
return;
}
var hashIcon = '<svg viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>';
var html = '';
results.forEach(function(r, i) {
var cls = i === activeIdx ? ' active' : '';
var sectionHint = r.item.section && r.item.section !== r.item.title ? r.item.section : '';
html += '<div class="search-item' + cls + '" data-idx="' + i + '" data-id="' + r.item.id + '">' +
hashIcon +
'<div class="search-item-text">' +
'<div class="search-item-title">' + highlight(r.item.title, query) + '</div>' +
(sectionHint ? '<div class="search-item-section">' + escHtml(sectionHint) + '</div>' : '') +
'</div>' +
'<span class="search-enter">\u21B5</span>' +
'</div>';
});
body.innerHTML = html;
body.querySelectorAll('.search-item').forEach(function(el) {
el.addEventListener('click', function() { navigateTo(el.dataset.id); });
el.addEventListener('mouseenter', function() { setActive(parseInt(el.dataset.idx)); });
});
}
function setActive(idx) {
if (idx === activeIdx) return;
var items = body.querySelectorAll('.search-item');
if (items[activeIdx]) items[activeIdx].classList.remove('active');
activeIdx = idx;
if (items[activeIdx]) {
items[activeIdx].classList.add('active');
items[activeIdx].scrollIntoView({ block: 'nearest' });
}
}
function navigateTo(id) {
closeSearch();
window.location.href = 'docs#' + id;
}
window.openSearch = function() {
overlay.classList.add('open');
document.body.style.overflow = 'hidden';
input.value = '';
doSearch('');
setTimeout(function() { input.focus(); }, 50);
};
window.closeSearch = function() {
overlay.classList.remove('open');
document.body.style.overflow = '';
activeIdx = -1;
};
input.addEventListener('input', function() { doSearch(input.value); });
input.addEventListener('keydown', function(e) {
if (e.key === 'ArrowDown') {
e.preventDefault();
if (results.length) setActive(Math.min(activeIdx + 1, results.length - 1));
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (results.length) setActive(Math.max(activeIdx - 1, 0));
} else if (e.key === 'Enter') {
e.preventDefault();
if (results[activeIdx]) navigateTo(results[activeIdx].item.id);
} else if (e.key === 'Escape') {
e.preventDefault();
closeSearch();
}
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
if (overlay.classList.contains('open')) { closeSearch(); } else { openSearch(); }
}
if (e.key === '/' && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') {
e.preventDefault();
openSearch();
}
});
})();
</script>
</body>
</html>
+269 -19
View File
@@ -39,7 +39,11 @@
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
html { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: #333 transparent; }
html::-webkit-scrollbar { width: 8px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
html::-webkit-scrollbar-thumb:hover { background: #555; }
body {
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
@@ -64,7 +68,7 @@
}
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
.nav-links { display: flex; gap: 24px; list-style: none; }
.nav-links { display: flex; align-items: center; gap: 24px; list-style: none; }
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
.nav-links a:hover { color: var(--text); text-decoration: none; }
.nav-links a.active { color: var(--text); font-weight: 600; }
@@ -72,6 +76,21 @@
.nav-links .nav-icon:hover { opacity: 1; }
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
.search-trigger {
display: flex; align-items: center; gap: 6px;
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
border-radius: 8px; padding: 6px 12px;
color: var(--text-dim); font-size: .85rem; cursor: pointer;
font-family: inherit; transition: color .2s, border-color .2s, background .2s;
white-space: nowrap; text-decoration: none;
}
.search-trigger:hover { color: var(--text); border-color: rgba(255,255,255,.25); background: rgba(255,255,255,.1); text-decoration: none; }
.search-trigger svg { width: 14px; height: 14px; fill: currentColor; flex-shrink: 0; }
.search-trigger kbd {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 0px 4px; font-size: .65rem;
font-family: inherit; color: #555; line-height: 1.4; margin-left: 2px;
}
/* ── HERO ── */
.hero {
@@ -260,6 +279,76 @@
/* ── SVG ICONS ── */
.icon-svg { width: 20px; height: 20px; fill: currentColor; }
/* ── SEARCH MODAL ── */
.search-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.6);
z-index: 300; opacity: 0; pointer-events: none;
transition: opacity .2s cubic-bezier(.4,0,.2,1);
display: flex; align-items: flex-start; justify-content: center;
padding-top: min(20vh, 140px);
}
.search-overlay.open { opacity: 1; pointer-events: auto; }
.search-modal {
background: var(--surface); border: 1px solid rgba(255,255,255,.1);
border-radius: 16px; width: 580px; max-width: calc(100vw - 32px);
max-height: min(70vh, 520px); display: flex; flex-direction: column;
box-shadow: 0 16px 70px rgba(0,0,0,.6);
transform: translateY(-12px) scale(.97); opacity: 0;
transition: transform .25s cubic-bezier(.4,0,.2,1), opacity .2s;
}
.search-overlay.open .search-modal { transform: translateY(0) scale(1); opacity: 1; }
.search-header {
display: flex; align-items: center; gap: 10px;
padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.08);
}
.search-header svg { width: 18px; height: 18px; fill: var(--text-dim); flex-shrink: 0; }
.search-input {
flex: 1; background: none; border: none; outline: none;
color: var(--text); font-size: .95rem; font-family: inherit;
}
.search-input::placeholder { color: var(--text-dim); }
.search-esc {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 2px 7px; font-size: .72rem;
color: var(--text-dim); font-family: inherit; cursor: pointer;
}
.search-esc:hover { background: rgba(255,255,255,.14); }
.search-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: #333 transparent; }
.search-body::-webkit-scrollbar { width: 6px; }
.search-body::-webkit-scrollbar-track { background: transparent; }
.search-body::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
.search-group-label {
padding: 8px 10px 4px; font-size: .72rem; font-weight: 600;
color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
}
.search-item {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-radius: 10px; cursor: pointer;
color: var(--text); font-size: .88rem; transition: background .15s;
}
.search-item:hover, .search-item.active { background: rgba(255,255,255,.07); }
.search-item svg { width: 16px; height: 16px; fill: var(--text-dim); flex-shrink: 0; }
.search-item-text { flex: 1; min-width: 0; }
.search-item-title { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search-item-section { font-size: .78rem; color: var(--text-dim); margin-top: 1px; }
.search-item mark { background: rgba(29,185,84,.25); color: var(--text); border-radius: 2px; padding: 0 1px; }
.search-item .search-enter { color: var(--text-dim); font-size: .72rem; opacity: 0; transition: opacity .15s; }
.search-item.active .search-enter { opacity: 1; }
.search-empty { padding: 32px 16px; text-align: center; color: var(--text-dim); font-size: .9rem; }
.search-footer {
padding: 10px 16px; border-top: 1px solid rgba(255,255,255,.06);
display: flex; align-items: center; gap: 16px; font-size: .72rem; color: #555;
}
.search-footer kbd {
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.08);
border-radius: 3px; padding: 1px 4px; font-family: inherit; font-size: .68rem;
}
@media (max-width: 640px) {
.search-trigger kbd { display: none; }
.search-overlay { padding-top: 16px; }
.search-modal { max-height: 80vh; border-radius: 14px; }
}
</style>
</head>
<body>
@@ -269,15 +358,20 @@
<div class="nav-inner">
<a class="nav-brand" href="#">
<img src="icon.png" alt="SpotiFLAC">
SpotiFLAC
SpotiFLAC Mobile
</a>
<ul class="nav-links">
<li><a href="#features">Features</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="downloads">Downloads</a></li>
<li><a href="#faq">FAQ</a></li>
<li><a href="partners.html">Partners</a></li>
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
<li><a href="partners">Partners</a></li>
<li><a href="docs">Docs</a></li>
<li class="nav-divider"></li>
<li><button class="search-trigger" onclick="openSearch()" aria-label="Search documentation">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
Search
<kbd id="searchShortcutHint">Ctrl K</kbd>
</button></li>
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
</ul>
@@ -291,10 +385,11 @@
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
<div class="mobile-menu" id="mobileMenu">
<a href="#features">Features</a>
<a href="downloads.html">Downloads</a>
<a href="downloads">Downloads</a>
<a href="#faq">FAQ</a>
<a href="partners.html">Partners</a>
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
<a href="partners">Partners</a>
<a href="docs">Docs</a>
<a href="javascript:void(0)" onclick="toggleMenu();openSearch()" style="display:flex;align-items:center;gap:6px;color:var(--text-dim)"><svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:currentColor"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>Search Docs</a>
<div class="mobile-divider"></div>
<div class="mobile-icons">
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
@@ -319,19 +414,16 @@
<svg class="icon-svg" viewBox="0 0 24 24"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.8-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>
iOS 14.0+
</span>
<span class="badge">
<svg class="icon-svg" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/></svg>
Open Source
</span>
</div>
<div class="hero-actions">
<a class="btn btn-primary" href="downloads.html">
<a class="btn btn-primary" href="downloads">
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="#000" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/></svg>
Download
</a>
<a class="btn btn-secondary" href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M12 .3a12 12 0 00-3.8 23.4c.6.1.8-.3.8-.6v-2c-3.3.7-4-1.6-4-1.6-.5-1.4-1.3-1.8-1.3-1.8-1-.7.1-.7.1-.7 1.2.1 1.8 1.2 1.8 1.2 1 1.8 2.8 1.3 3.5 1 .1-.8.4-1.3.7-1.6-2.7-.3-5.5-1.3-5.5-6 0-1.3.5-2.3 1.2-3.2-.1-.3-.5-1.5.1-3.2 0 0 1-.3 3.4 1.2a11.5 11.5 0 016 0c2.3-1.5 3.3-1.2 3.3-1.2.7 1.7.3 2.9.1 3.2.8.8 1.2 1.9 1.2 3.2 0 4.6-2.8 5.6-5.5 5.9.4.4.8 1.1.8 2.2v3.3c0 .3.2.7.8.6A12 12 0 0012 .3z"/></svg>
GitHub
<a class="btn btn-secondary" href="docs">
<svg class="icon-svg" viewBox="0 0 24 24"><path fill="currentColor" d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm2 16H8v-2h8v2zm0-4H8v-2h8v2zm-3-5V3.5L18.5 9H13z"/></svg>
Docs
</a>
</div>
<div class="hero-mockups">
@@ -426,18 +518,37 @@
</details>
<details class="faq-item">
<summary>How do I create my own extension?</summary>
<div class="faq-answer">Check out the <a href="https://zarz.moe/docs" target="_blank">Extension Development Guide</a> for complete documentation on building custom extensions.</div>
<div class="faq-answer">Check out the <a href="docs">Extension Development Guide</a> for complete documentation on building custom extensions.</div>
</details>
</div>
</div>
</section>
<!-- SEARCH MODAL -->
<div class="search-overlay" id="searchOverlay" onclick="if(event.target===this)closeSearch()">
<div class="search-modal">
<div class="search-header">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input class="search-input" id="searchInput" type="text" placeholder="Search documentation..." autocomplete="off" spellcheck="false">
<button class="search-esc" onclick="closeSearch()">Esc</button>
</div>
<div class="search-body" id="searchBody">
<div class="search-empty">Type to search across all documentation sections</div>
</div>
<div class="search-footer">
<span><kbd>&uarr;</kbd> <kbd>&darr;</kbd> to navigate</span>
<span><kbd>Enter</kbd> to select</span>
<span><kbd>Esc</kbd> to close</span>
</div>
</div>
</div>
<!-- FOOTER -->
<footer>
<div class="footer-inner">
<div class="footer-links">
<a href="https://github.com/zarzet/SpotiFLAC-Mobile/releases" target="_blank">Download</a>
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
<a href="docs">Documentation</a>
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
<a href="https://github.com/afkarxyz/SpotiFLAC" target="_blank">Desktop Version</a>
<a href="https://t.me/spotiflac" target="_blank">Telegram Channel</a>
@@ -461,6 +572,145 @@ document.getElementById('mobileMenu').addEventListener('click', function(e) {
if (e.target.closest('a')) toggleMenu();
});
</script>
<script>
/* ── DOCS SEARCH ── */
(function() {
var overlay = document.getElementById('searchOverlay');
var input = document.getElementById('searchInput');
var body = document.getElementById('searchBody');
var hintEl = document.getElementById('searchShortcutHint');
var activeIdx = -1;
var results = [];
if (navigator.platform && navigator.platform.indexOf('Mac') > -1) {
if (hintEl) hintEl.textContent = '\u2318 K';
}
var searchIndex = [{"id":"table-of-contents","title":"Table of Contents","level":2,"section":"Table of Contents"},{"id":"introduction","title":"Introduction","level":2,"section":"Introduction"},{"id":"requirements","title":"Requirements","level":3,"section":"Introduction"},{"id":"extension-structure","title":"Extension Structure","level":2,"section":"Extension Structure"},{"id":"manifest-file","title":"Manifest File","level":2,"section":"Manifest File"},{"id":"complete-manifest-example","title":"Complete Manifest Example","level":3,"section":"Manifest File"},{"id":"manifest-fields","title":"Manifest Fields","level":3,"section":"Manifest File"},{"id":"quality-options","title":"Quality Options","level":3,"section":"Manifest File"},{"id":"quality-specific-settings","title":"Quality-Specific Settings","level":3,"section":"Manifest File"},{"id":"permissions","title":"Permissions","level":3,"section":"Manifest File"},{"id":"extension-types","title":"Extension Types","level":3,"section":"Manifest File"},{"id":"settings","title":"Settings","level":3,"section":"Manifest File"},{"id":"button-setting-type","title":"Button Setting Type","level":3,"section":"Manifest File"},{"id":"custom-search-behavior","title":"Custom Search Behavior","level":3,"section":"Manifest File"},{"id":"thumbnail-ratio-presets","title":"Thumbnail Ratio Presets","level":4,"section":"Manifest File"},{"id":"custom-url-handler","title":"Custom URL Handler","level":3,"section":"Manifest File"},{"id":"album--playlist-functions-v301","title":"Album & Playlist Functions (v3.0.1+)","level":3,"section":"Manifest File"},{"id":"artist-support","title":"Artist Support","level":3,"section":"Manifest File"},{"id":"home-feed-support","title":"Home Feed Support","level":3,"section":"Manifest File"},{"id":"track-enrichment","title":"Track Enrichment","level":3,"section":"Manifest File"},{"id":"custom-track-matching","title":"Custom Track Matching","level":3,"section":"Manifest File"},{"id":"post-processing-hooks","title":"Post-Processing Hooks","level":3,"section":"Manifest File"},{"id":"post-process-api-v2-recommended","title":"Post-Process API v2 (Recommended)","level":4,"section":"Manifest File"},{"id":"main-script","title":"Main Script","level":2,"section":"Main Script"},{"id":"basic-structure","title":"Basic Structure","level":3,"section":"Main Script"},{"id":"important-registerextension","title":"Important: registerExtension()","level":3,"section":"Main Script"},{"id":"api-reference","title":"API Reference","level":2,"section":"API Reference"},{"id":"http-api","title":"HTTP API","level":3,"section":"API Reference"},{"id":"request-headers","title":"Request Headers","level":4,"section":"API Reference"},{"id":"response-object","title":"Response Object","level":4,"section":"API Reference"},{"id":"form-encoded-post-applicationx-www-form-urlencoded","title":"Form-Encoded POST (application/x-www-form-urlencoded)","level":4,"section":"API Reference"},{"id":"cookie-jar","title":"Cookie Jar","level":4,"section":"API Reference"},{"id":"youtube-music--innertube-api-example","title":"YouTube Music / Innertube API Example","level":4,"section":"API Reference"},{"id":"browser-like-polyfills","title":"Browser-like Polyfills","level":3,"section":"API Reference"},{"id":"fetch-api","title":"fetch() API","level":4,"section":"API Reference"},{"id":"atob--btoa","title":"atob() / btoa()","level":4,"section":"API Reference"},{"id":"textencoder--textdecoder","title":"TextEncoder / TextDecoder","level":4,"section":"API Reference"},{"id":"url--urlsearchparams","title":"URL / URLSearchParams","level":4,"section":"API Reference"},{"id":"porting-browser-libraries","title":"Porting Browser Libraries","level":4,"section":"API Reference"},{"id":"storage-api","title":"Storage API","level":3,"section":"API Reference"},{"id":"file-api","title":"File API","level":3,"section":"API Reference"},{"id":"logging-api","title":"Logging API","level":3,"section":"API Reference"},{"id":"utility-api","title":"Utility API","level":3,"section":"API Reference"},{"id":"hmac-sha1-for-totp","title":"HMAC-SHA1 for TOTP","level":4,"section":"API Reference"},{"id":"hmac-sha256-example-api-signing","title":"HMAC-SHA256 Example (API Signing)","level":4,"section":"API Reference"},{"id":"go-backend-api","title":"Go Backend API","level":3,"section":"API Reference"},{"id":"using-getlocaltime-for-time-based-greeting","title":"Using getLocalTime() for Time-Based Greeting","level":4,"section":"API Reference"},{"id":"using-getlocaltime-for-timezone-in-api-calls","title":"Using getLocalTime() for Timezone in API Calls","level":4,"section":"API Reference"},{"id":"credentials-api-encrypted","title":"Credentials API (Encrypted)","level":3,"section":"API Reference"},{"id":"auth-api-oauth-support","title":"Auth API (OAuth Support)","level":3,"section":"API Reference"},{"id":"pkce-oauth-flow-recommended","title":"PKCE OAuth Flow (Recommended)","level":3,"section":"API Reference"},{"id":"quick-start-high-level-api","title":"QuiLine truncated
function escHtml(s) {
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function highlight(text, query) {
if (!query) return escHtml(text);
var escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var re = new RegExp('(' + escaped + ')', 'gi');
return escHtml(text).replace(re, '<mark>$1</mark>');
}
function doSearch(query) {
query = query.trim().toLowerCase();
if (!query) {
body.innerHTML = '<div class="search-empty">Type to search across all documentation sections</div>';
results = []; activeIdx = -1; return;
}
var tokens = query.split(/\s+/).filter(Boolean);
var scored = [];
searchIndex.forEach(function(item) {
var titleLow = item.title.toLowerCase();
var sectionLow = item.section.toLowerCase();
var score = 0;
for (var i = 0; i < tokens.length; i++) {
var t = tokens[i];
if (titleLow.includes(t)) {
score += titleLow === t ? 100 : titleLow.startsWith(t) ? 60 : 30;
} else if (sectionLow.includes(t)) {
score += 10;
}
}
if (score > 0 && item.level === 2) score += 8;
if (score > 0 && item.level === 1) score += 15;
if (score > 0) scored.push({ item: item, score: score });
});
scored.sort(function(a, b) { return b.score - a.score; });
results = scored.slice(0, 20);
activeIdx = results.length > 0 ? 0 : -1;
if (!results.length) {
body.innerHTML = '<div class="search-empty">No results found for "' + escHtml(query) + '"</div>';
return;
}
var hashIcon = '<svg viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>';
var html = '';
results.forEach(function(r, i) {
var cls = i === activeIdx ? ' active' : '';
var sectionHint = r.item.section && r.item.section !== r.item.title ? r.item.section : '';
html += '<div class="search-item' + cls + '" data-idx="' + i + '" data-id="' + r.item.id + '">' +
hashIcon +
'<div class="search-item-text">' +
'<div class="search-item-title">' + highlight(r.item.title, query) + '</div>' +
(sectionHint ? '<div class="search-item-section">' + escHtml(sectionHint) + '</div>' : '') +
'</div>' +
'<span class="search-enter">\u21B5</span>' +
'</div>';
});
body.innerHTML = html;
body.querySelectorAll('.search-item').forEach(function(el) {
el.addEventListener('click', function() { navigateTo(el.dataset.id); });
el.addEventListener('mouseenter', function() { setActive(parseInt(el.dataset.idx)); });
});
}
function setActive(idx) {
if (idx === activeIdx) return;
var items = body.querySelectorAll('.search-item');
if (items[activeIdx]) items[activeIdx].classList.remove('active');
activeIdx = idx;
if (items[activeIdx]) {
items[activeIdx].classList.add('active');
items[activeIdx].scrollIntoView({ block: 'nearest' });
}
}
function navigateTo(id) {
closeSearch();
window.location.href = 'docs#' + id;
}
window.openSearch = function() {
overlay.classList.add('open');
document.body.style.overflow = 'hidden';
input.value = '';
doSearch('');
setTimeout(function() { input.focus(); }, 50);
};
window.closeSearch = function() {
overlay.classList.remove('open');
document.body.style.overflow = '';
activeIdx = -1;
};
input.addEventListener('input', function() { doSearch(input.value); });
input.addEventListener('keydown', function(e) {
if (e.key === 'ArrowDown') {
e.preventDefault();
if (results.length) setActive(Math.min(activeIdx + 1, results.length - 1));
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (results.length) setActive(Math.max(activeIdx - 1, 0));
} else if (e.key === 'Enter') {
e.preventDefault();
if (results[activeIdx]) navigateTo(results[activeIdx].item.id);
} else if (e.key === 'Escape') {
e.preventDefault();
closeSearch();
}
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
if (overlay.classList.contains('open')) { closeSearch(); } else { openSearch(); }
}
if (e.key === '/' && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') {
e.preventDefault();
openSearch();
}
});
})();
</script>
</body>
</html>
+271 -18
View File
@@ -32,7 +32,11 @@
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html { scroll-behavior: smooth; }
html { scroll-behavior: smooth; scrollbar-width: thin; scrollbar-color: #333 transparent; }
html::-webkit-scrollbar { width: 8px; }
html::-webkit-scrollbar-track { background: transparent; }
html::-webkit-scrollbar-thumb { background: #333; border-radius: 4px; }
html::-webkit-scrollbar-thumb:hover { background: #555; }
body {
font-family: 'Google Sans Flex', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
@@ -57,7 +61,7 @@
}
.nav-brand { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 1.1rem; color: var(--text); }
.nav-brand img { width: 32px; height: 32px; border-radius: 50%; }
.nav-links { display: flex; gap: 24px; list-style: none; }
.nav-links { display: flex; align-items: center; gap: 24px; list-style: none; }
.nav-links a { color: var(--text-dim); font-size: .9rem; transition: color .2s; }
.nav-links a:hover { color: var(--text); text-decoration: none; }
.nav-links a.active { color: var(--text); font-weight: 600; }
@@ -65,6 +69,21 @@
.nav-links .nav-icon:hover { opacity: 1; }
.nav-links .nav-icon svg { width: 24px; height: 24px; fill: currentColor; }
.nav-links .nav-divider { width: 1px; height: 20px; background: rgba(255,255,255,.15); margin-left: -4px; }
.search-trigger {
display: flex; align-items: center; gap: 6px;
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.12);
border-radius: 8px; padding: 6px 12px;
color: var(--text-dim); font-size: .85rem; cursor: pointer;
font-family: inherit; transition: color .2s, border-color .2s, background .2s;
white-space: nowrap; text-decoration: none;
}
.search-trigger:hover { color: var(--text); border-color: rgba(255,255,255,.25); background: rgba(255,255,255,.1); text-decoration: none; }
.search-trigger svg { width: 14px; height: 14px; fill: currentColor; flex-shrink: 0; }
.search-trigger kbd {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 0px 4px; font-size: .65rem;
font-family: inherit; color: #555; line-height: 1.4; margin-left: 2px;
}
/* ── PAGE HEADER ── */
.page-header {
@@ -214,22 +233,97 @@
.disclaimer { padding: 0 16px 48px; }
}
</style>
</head>
<style>
/* ── SEARCH MODAL ── */
.search-overlay {
position: fixed; inset: 0; background: rgba(0,0,0,.6);
z-index: 300; opacity: 0; pointer-events: none;
transition: opacity .2s cubic-bezier(.4,0,.2,1);
display: flex; align-items: flex-start; justify-content: center;
padding-top: min(20vh, 140px);
}
.search-overlay.open { opacity: 1; pointer-events: auto; }
.search-modal {
background: var(--surface); border: 1px solid rgba(255,255,255,.1);
border-radius: 16px; width: 580px; max-width: calc(100vw - 32px);
max-height: min(70vh, 520px); display: flex; flex-direction: column;
box-shadow: 0 16px 70px rgba(0,0,0,.6);
transform: translateY(-12px) scale(.97); opacity: 0;
transition: transform .25s cubic-bezier(.4,0,.2,1), opacity .2s;
}
.search-overlay.open .search-modal { transform: translateY(0) scale(1); opacity: 1; }
.search-header {
display: flex; align-items: center; gap: 10px;
padding: 14px 16px; border-bottom: 1px solid rgba(255,255,255,.08);
}
.search-header svg { width: 18px; height: 18px; fill: var(--text-dim); flex-shrink: 0; }
.search-input {
flex: 1; background: none; border: none; outline: none;
color: var(--text); font-size: .95rem; font-family: inherit;
}
.search-input::placeholder { color: var(--text-dim); }
.search-esc {
background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.1);
border-radius: 4px; padding: 2px 7px; font-size: .72rem;
color: var(--text-dim); font-family: inherit; cursor: pointer;
}
.search-esc:hover { background: rgba(255,255,255,.14); }
.search-body { overflow-y: auto; padding: 8px; scrollbar-width: thin; scrollbar-color: #333 transparent; }
.search-body::-webkit-scrollbar { width: 6px; }
.search-body::-webkit-scrollbar-track { background: transparent; }
.search-body::-webkit-scrollbar-thumb { background: #333; border-radius: 3px; }
.search-group-label {
padding: 8px 10px 4px; font-size: .72rem; font-weight: 600;
color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
}
.search-item {
display: flex; align-items: center; gap: 10px;
padding: 10px 12px; border-radius: 10px; cursor: pointer;
color: var(--text); font-size: .88rem; transition: background .15s;
}
.search-item:hover, .search-item.active { background: rgba(255,255,255,.07); }
.search-item svg { width: 16px; height: 16px; fill: var(--text-dim); flex-shrink: 0; }
.search-item-text { flex: 1; min-width: 0; }
.search-item-title { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.search-item-section { font-size: .78rem; color: var(--text-dim); margin-top: 1px; }
.search-item mark { background: rgba(29,185,84,.25); color: var(--text); border-radius: 2px; padding: 0 1px; }
.search-item .search-enter { color: var(--text-dim); font-size: .72rem; opacity: 0; transition: opacity .15s; }
.search-item.active .search-enter { opacity: 1; }
.search-empty { padding: 32px 16px; text-align: center; color: var(--text-dim); font-size: .9rem; }
.search-footer {
padding: 10px 16px; border-top: 1px solid rgba(255,255,255,.06);
display: flex; align-items: center; gap: 16px; font-size: .72rem; color: #555;
}
.search-footer kbd {
background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.08);
border-radius: 3px; padding: 1px 4px; font-family: inherit; font-size: .68rem;
}
@media (max-width: 640px) {
.search-trigger kbd { display: none; }
.search-overlay { padding-top: 16px; }
.search-modal { max-height: 80vh; border-radius: 14px; }
}
</style>
<body>
<nav>
<div class="nav-inner">
<a class="nav-brand" href="index.html">
<a class="nav-brand" href="index">
<img src="icon.png" alt="SpotiFLAC">
SpotiFLAC
SpotiFLAC Mobile
</a>
<ul class="nav-links">
<li><a href="index.html#features">Features</a></li>
<li><a href="downloads.html">Downloads</a></li>
<li><a href="index.html#faq">FAQ</a></li>
<li><a href="partners.html" class="active">Partners</a></li>
<li><a href="https://zarz.moe/docs" target="_blank">Docs</a></li>
<li><a href="index#features">Features</a></li>
<li><a href="downloads">Downloads</a></li>
<li><a href="index#faq">FAQ</a></li>
<li><a href="partners" class="active">Partners</a></li>
<li><a href="docs">Docs</a></li>
<li class="nav-divider"></li>
<li><button class="search-trigger" onclick="openSearch()" aria-label="Search documentation">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
Search
<kbd id="searchShortcutHint">Ctrl K</kbd>
</button></li>
<li><a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" class="nav-icon" aria-label="GitHub"><svg viewBox="0 0 24 24"><path d="M12 .3a12 12 0 0 0-3.8 23.38c.6.12.82-.26.82-.57L9 20.86c-3.34.72-4.04-1.61-4.04-1.61-.55-1.39-1.34-1.76-1.34-1.76-1.09-.74.08-.73.08-.73 1.2.09 1.84 1.24 1.84 1.24 1.07 1.84 2.81 1.3 3.5 1 .1-.78.42-1.31.76-1.61-2.67-.3-5.47-1.33-5.47-5.93 0-1.31.47-2.38 1.24-3.22-.13-.3-.54-1.52.12-3.18 0 0 1-.32 3.3 1.23a11.5 11.5 0 0 1 6.02 0c2.28-1.55 3.29-1.23 3.29-1.23.66 1.66.25 2.88.12 3.18.77.84 1.24 1.91 1.24 3.22 0 4.61-2.81 5.63-5.48 5.92.43.37.81 1.1.81 2.22l-.01 3.29c0 .31.21.69.82.57A12 12 0 0 0 12 .3"/></svg></a></li>
<li><a href="https://t.me/spotiflac" target="_blank" class="nav-icon" aria-label="Telegram"><svg viewBox="0 0 24 24"><path d="M11.94 24c6.6 0 12-5.4 12-12s-5.4-12-12-12-12 5.4-12 12 5.4 12 12 12zm-3.2-8.69l-.37-3.04 8.52-5.18c.38-.23.73.09.45.35l-6.96 6.4-.29 2.97c-.04.35-.48.43-.64.12l-1.64-3.33-3.6-1.17c-.78-.24-.8-.78-.02-1.14l14.04-5.4c.65-.25 1.25.15 1.04.83l-2.39 11.28c-.18.81-.7 1.01-1.42.63l-3.92-2.89-1.89 1.82c-.21.2-.39.38-.65.38l.28-3.06z"/></svg></a></li>
</ul>
@@ -242,11 +336,12 @@
<!-- MOBILE MENU -->
<div class="mobile-overlay" id="mobileOverlay" onclick="toggleMenu()"></div>
<div class="mobile-menu" id="mobileMenu">
<a href="index.html#features">Features</a>
<a href="downloads.html">Downloads</a>
<a href="index.html#faq">FAQ</a>
<a href="partners.html" class="active">Partners</a>
<a href="https://zarz.moe/docs" target="_blank">Docs</a>
<a href="index#features">Features</a>
<a href="downloads">Downloads</a>
<a href="index#faq">FAQ</a>
<a href="partners" class="active">Partners</a>
<a href="docs">Docs</a>
<a href="javascript:void(0)" onclick="toggleMenu();openSearch()" style="display:flex;align-items:center;gap:6px;color:var(--text-dim)"><svg viewBox="0 0 24 24" style="width:16px;height:16px;fill:currentColor"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>Search Docs</a>
<div class="mobile-divider"></div>
<div class="mobile-icons">
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank" aria-label="GitHub">
@@ -486,13 +581,32 @@
<p>SpotiFLAC Mobile is not affiliated with, endorsed by, or connected to any of the services listed above. All trademarks and logos belong to their respective owners. This page is meant to acknowledge and appreciate the platforms that make this project possible.</p>
</div>
<!-- SEARCH MODAL -->
<div class="search-overlay" id="searchOverlay" onclick="if(event.target===this)closeSearch()">
<div class="search-modal">
<div class="search-header">
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27A6.47 6.47 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input class="search-input" id="searchInput" type="text" placeholder="Search documentation..." autocomplete="off" spellcheck="false">
<button class="search-esc" onclick="closeSearch()">Esc</button>
</div>
<div class="search-body" id="searchBody">
<div class="search-empty">Type to search across all documentation sections</div>
</div>
<div class="search-footer">
<span><kbd>&uarr;</kbd> <kbd>&darr;</kbd> to navigate</span>
<span><kbd>Enter</kbd> to select</span>
<span><kbd>Esc</kbd> to close</span>
</div>
</div>
</div>
<!-- FOOTER -->
<footer>
<div class="footer-inner">
<div class="footer-links">
<a href="index.html">Home</a>
<a href="downloads.html">Downloads</a>
<a href="https://zarz.moe/docs" target="_blank">Documentation</a>
<a href="index">Home</a>
<a href="downloads">Downloads</a>
<a href="docs">Documentation</a>
<a href="https://github.com/zarzet/SpotiFLAC-Mobile" target="_blank">GitHub</a>
<a href="https://t.me/spotiflac" target="_blank">Telegram</a>
<a href="https://ko-fi.com/zarzet" target="_blank">Support / Ko-fi</a>
@@ -512,6 +626,145 @@ document.getElementById('mobileMenu').addEventListener('click', function(e) {
if (e.target.closest('a')) toggleMenu();
});
</script>
<script>
/* ── DOCS SEARCH ── */
(function() {
var overlay = document.getElementById('searchOverlay');
var input = document.getElementById('searchInput');
var body = document.getElementById('searchBody');
var hintEl = document.getElementById('searchShortcutHint');
var activeIdx = -1;
var results = [];
if (navigator.platform && navigator.platform.indexOf('Mac') > -1) {
if (hintEl) hintEl.textContent = '\u2318 K';
}
var searchIndex = [{"id":"table-of-contents","title":"Table of Contents","level":2,"section":"Table of Contents"},{"id":"introduction","title":"Introduction","level":2,"section":"Introduction"},{"id":"requirements","title":"Requirements","level":3,"section":"Introduction"},{"id":"extension-structure","title":"Extension Structure","level":2,"section":"Extension Structure"},{"id":"manifest-file","title":"Manifest File","level":2,"section":"Manifest File"},{"id":"complete-manifest-example","title":"Complete Manifest Example","level":3,"section":"Manifest File"},{"id":"manifest-fields","title":"Manifest Fields","level":3,"section":"Manifest File"},{"id":"quality-options","title":"Quality Options","level":3,"section":"Manifest File"},{"id":"quality-specific-settings","title":"Quality-Specific Settings","level":3,"section":"Manifest File"},{"id":"permissions","title":"Permissions","level":3,"section":"Manifest File"},{"id":"extension-types","title":"Extension Types","level":3,"section":"Manifest File"},{"id":"settings","title":"Settings","level":3,"section":"Manifest File"},{"id":"button-setting-type","title":"Button Setting Type","level":3,"section":"Manifest File"},{"id":"custom-search-behavior","title":"Custom Search Behavior","level":3,"section":"Manifest File"},{"id":"thumbnail-ratio-presets","title":"Thumbnail Ratio Presets","level":4,"section":"Manifest File"},{"id":"custom-url-handler","title":"Custom URL Handler","level":3,"section":"Manifest File"},{"id":"album--playlist-functions-v301","title":"Album & Playlist Functions (v3.0.1+)","level":3,"section":"Manifest File"},{"id":"artist-support","title":"Artist Support","level":3,"section":"Manifest File"},{"id":"home-feed-support","title":"Home Feed Support","level":3,"section":"Manifest File"},{"id":"track-enrichment","title":"Track Enrichment","level":3,"section":"Manifest File"},{"id":"custom-track-matching","title":"Custom Track Matching","level":3,"section":"Manifest File"},{"id":"post-processing-hooks","title":"Post-Processing Hooks","level":3,"section":"Manifest File"},{"id":"post-process-api-v2-recommended","title":"Post-Process API v2 (Recommended)","level":4,"section":"Manifest File"},{"id":"main-script","title":"Main Script","level":2,"section":"Main Script"},{"id":"basic-structure","title":"Basic Structure","level":3,"section":"Main Script"},{"id":"important-registerextension","title":"Important: registerExtension()","level":3,"section":"Main Script"},{"id":"api-reference","title":"API Reference","level":2,"section":"API Reference"},{"id":"http-api","title":"HTTP API","level":3,"section":"API Reference"},{"id":"request-headers","title":"Request Headers","level":4,"section":"API Reference"},{"id":"response-object","title":"Response Object","level":4,"section":"API Reference"},{"id":"form-encoded-post-applicationx-www-form-urlencoded","title":"Form-Encoded POST (application/x-www-form-urlencoded)","level":4,"section":"API Reference"},{"id":"cookie-jar","title":"Cookie Jar","level":4,"section":"API Reference"},{"id":"youtube-music--innertube-api-example","title":"YouTube Music / Innertube API Example","level":4,"section":"API Reference"},{"id":"browser-like-polyfills","title":"Browser-like Polyfills","level":3,"section":"API Reference"},{"id":"fetch-api","title":"fetch() API","level":4,"section":"API Reference"},{"id":"atob--btoa","title":"atob() / btoa()","level":4,"section":"API Reference"},{"id":"textencoder--textdecoder","title":"TextEncoder / TextDecoder","level":4,"section":"API Reference"},{"id":"url--urlsearchparams","title":"URL / URLSearchParams","level":4,"section":"API Reference"},{"id":"porting-browser-libraries","title":"Porting Browser Libraries","level":4,"section":"API Reference"},{"id":"storage-api","title":"Storage API","level":3,"section":"API Reference"},{"id":"file-api","title":"File API","level":3,"section":"API Reference"},{"id":"logging-api","title":"Logging API","level":3,"section":"API Reference"},{"id":"utility-api","title":"Utility API","level":3,"section":"API Reference"},{"id":"hmac-sha1-for-totp","title":"HMAC-SHA1 for TOTP","level":4,"section":"API Reference"},{"id":"hmac-sha256-example-api-signing","title":"HMAC-SHA256 Example (API Signing)","level":4,"section":"API Reference"},{"id":"go-backend-api","title":"Go Backend API","level":3,"section":"API Reference"},{"id":"using-getlocaltime-for-time-based-greeting","title":"Using getLocalTime() for Time-Based Greeting","level":4,"section":"API Reference"},{"id":"using-getlocaltime-for-timezone-in-api-calls","title":"Using getLocalTime() for Timezone in API Calls","level":4,"section":"API Reference"},{"id":"credentials-api-encrypted","title":"Credentials API (Encrypted)","level":3,"section":"API Reference"},{"id":"auth-api-oauth-support","title":"Auth API (OAuth Support)","level":3,"section":"API Reference"},{"id":"pkce-oauth-flow-recommended","title":"PKCE OAuth Flow (Recommended)","level":3,"section":"API Reference"},{"id":"quick-start-high-level-api","title":"QuiLine truncated
function escHtml(s) {
return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}
function highlight(text, query) {
if (!query) return escHtml(text);
var escaped = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
var re = new RegExp('(' + escaped + ')', 'gi');
return escHtml(text).replace(re, '<mark>$1</mark>');
}
function doSearch(query) {
query = query.trim().toLowerCase();
if (!query) {
body.innerHTML = '<div class="search-empty">Type to search across all documentation sections</div>';
results = []; activeIdx = -1; return;
}
var tokens = query.split(/\s+/).filter(Boolean);
var scored = [];
searchIndex.forEach(function(item) {
var titleLow = item.title.toLowerCase();
var sectionLow = item.section.toLowerCase();
var score = 0;
for (var i = 0; i < tokens.length; i++) {
var t = tokens[i];
if (titleLow.includes(t)) {
score += titleLow === t ? 100 : titleLow.startsWith(t) ? 60 : 30;
} else if (sectionLow.includes(t)) {
score += 10;
}
}
if (score > 0 && item.level === 2) score += 8;
if (score > 0 && item.level === 1) score += 15;
if (score > 0) scored.push({ item: item, score: score });
});
scored.sort(function(a, b) { return b.score - a.score; });
results = scored.slice(0, 20);
activeIdx = results.length > 0 ? 0 : -1;
if (!results.length) {
body.innerHTML = '<div class="search-empty">No results found for "' + escHtml(query) + '"</div>';
return;
}
var hashIcon = '<svg viewBox="0 0 24 24"><path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/></svg>';
var html = '';
results.forEach(function(r, i) {
var cls = i === activeIdx ? ' active' : '';
var sectionHint = r.item.section && r.item.section !== r.item.title ? r.item.section : '';
html += '<div class="search-item' + cls + '" data-idx="' + i + '" data-id="' + r.item.id + '">' +
hashIcon +
'<div class="search-item-text">' +
'<div class="search-item-title">' + highlight(r.item.title, query) + '</div>' +
(sectionHint ? '<div class="search-item-section">' + escHtml(sectionHint) + '</div>' : '') +
'</div>' +
'<span class="search-enter">\u21B5</span>' +
'</div>';
});
body.innerHTML = html;
body.querySelectorAll('.search-item').forEach(function(el) {
el.addEventListener('click', function() { navigateTo(el.dataset.id); });
el.addEventListener('mouseenter', function() { setActive(parseInt(el.dataset.idx)); });
});
}
function setActive(idx) {
if (idx === activeIdx) return;
var items = body.querySelectorAll('.search-item');
if (items[activeIdx]) items[activeIdx].classList.remove('active');
activeIdx = idx;
if (items[activeIdx]) {
items[activeIdx].classList.add('active');
items[activeIdx].scrollIntoView({ block: 'nearest' });
}
}
function navigateTo(id) {
closeSearch();
window.location.href = 'docs#' + id;
}
window.openSearch = function() {
overlay.classList.add('open');
document.body.style.overflow = 'hidden';
input.value = '';
doSearch('');
setTimeout(function() { input.focus(); }, 50);
};
window.closeSearch = function() {
overlay.classList.remove('open');
document.body.style.overflow = '';
activeIdx = -1;
};
input.addEventListener('input', function() { doSearch(input.value); });
input.addEventListener('keydown', function(e) {
if (e.key === 'ArrowDown') {
e.preventDefault();
if (results.length) setActive(Math.min(activeIdx + 1, results.length - 1));
} else if (e.key === 'ArrowUp') {
e.preventDefault();
if (results.length) setActive(Math.max(activeIdx - 1, 0));
} else if (e.key === 'Enter') {
e.preventDefault();
if (results[activeIdx]) navigateTo(results[activeIdx].item.id);
} else if (e.key === 'Escape') {
e.preventDefault();
closeSearch();
}
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
e.preventDefault();
if (overlay.classList.contains('open')) { closeSearch(); } else { openSearch(); }
}
if (e.key === '/' && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') {
e.preventDefault();
openSearch();
}
});
})();
</script>
</body>
</html>