Add: Search bar

This commit is contained in:
hyzen
2026-07-19 21:04:53 +05:30
parent 2467f76797
commit cdd5a7abe1
85 changed files with 940 additions and 73 deletions

View File

@@ -1538,3 +1538,178 @@ th {
border: 1px solid var(--foreground-color);
color: var(--foreground-color);
}
/* ── Navbar search ─────────────────────────────────────────────────────── */
.menu.language .menu__list {
display: flow-root;
margin-bottom: 0.2rem;
}
.menu.language .menu__list > li {
list-style: none;
}
.menu.language .menu__list > li:not(.menu__item--search) {
float: left;
height: 1.8rem;
margin: 0 0.5rem 0.5rem 0;
}
.menu.language .menu__list > li.menu__item--uninotes {
display: none;
}
.hyzen-user .menu.language .menu__list > li.menu__item--uninotes {
display: flex;
}
.menu__item--search {
float: right;
height: 1.8rem;
margin: 0 0 0.5rem 0.5rem;
overflow: hidden;
}
.site-search {
display: flex;
align-items: stretch;
height: 100%;
}
.site-search__input {
width: 9.3rem;
max-width: 32vw;
height: 100%;
box-sizing: border-box;
padding: 0 0.55rem;
border: 1px solid var(--background-color1);
border-right: none;
border-radius: 4px 0 0 4px;
background-color: var(--background-color);
color: var(--foreground-color);
font-size: 0.8rem;
font-family: inherit;
outline: none;
-webkit-appearance: none;
appearance: none;
}
.site-search__input:focus {
border-color: var(--accent-color);
}
.site-search__input::placeholder {
color: var(--foreground-color3, #888);
}
.site-search__input::-webkit-search-cancel-button {
-webkit-appearance: none;
}
.site-search__btn {
display: inline-flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
height: 100%;
padding: 0 0.5rem;
border-radius: 0 4px 4px 0;
border: 1px solid #f26522;
background-color: #f26522;
color: #fff;
cursor: pointer;
line-height: 1;
transition:
background-color 0.2s ease,
border-color 0.2s ease,
color 0.2s ease;
}
.site-search__btn:hover,
.site-search__btn:focus {
background-color: transparent;
border-color: #f26522;
color: #f26522;
}
@media (prefers-color-scheme: dark) {
.site-search__btn {
background-color: transparent;
border-color: currentColor;
color: var(--foreground-color);
}
.site-search__btn:hover,
.site-search__btn:focus {
background-color: var(--foreground-color) !important;
border-color: var(--foreground-color) !important;
color: var(--background-color) !important;
}
}
@media (max-width: 600px) {
.site-search__input {
width: 6.15rem;
max-width: 40vw;
}
}
/* ── Search results page ──────────────────────────────────────────────── */
.search-results__query {
color: var(--foreground-color3, #888);
font-weight: normal;
}
.search-results__status {
margin: 1rem 0;
color: var(--foreground-color3, #888);
}
.search-results__list {
list-style: none;
padding: 0;
margin: 1.5rem 0 0 0;
}
.search-results__item {
margin-bottom: 1.4rem;
border-left: 3px solid var(--accent-color);
padding-left: 0.9rem;
}
.search-results__item-title {
margin: 0 0 0.3rem 0;
font-size: 1.05rem;
}
.search-results__item-title a {
color: var(--foreground-color);
text-decoration: none;
}
.search-results__item-title a:hover {
color: var(--accent-color);
text-decoration: underline;
}
.search-results__item-meta {
display: inline-block;
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.03em;
color: var(--accent-color);
margin-bottom: 0.3rem;
}
.search-results__item-snippet {
margin: 0;
font-size: 0.92rem;
color: var(--foreground-color);
}
.search-results__item-snippet mark {
background-color: var(--accent-color);
color: var(--background-color);
padding: 0 0.1rem;
border-radius: 2px;
}

129
static/js/search.js Normal file
View File

@@ -0,0 +1,129 @@
(function () {
var listEl = document.querySelector('[data-search-list]');
var statusEl = document.querySelector('[data-search-status]');
var queryEl = document.querySelector('[data-search-query]');
if (!listEl || !statusEl) return;
function getQueryParam(name) {
var params = new URLSearchParams(window.location.search);
return params.get(name) || '';
}
function escapeHtml(str) {
return str.replace(/[&<>"']/g, function (c) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c];
});
}
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function buildSnippet(content, term) {
var radius = 90;
var start = 0;
var end = Math.min(content.length, radius * 2);
if (term) {
var idx = content.toLowerCase().indexOf(term.toLowerCase());
if (idx !== -1) {
start = Math.max(0, idx - radius);
end = Math.min(content.length, idx + term.length + radius);
}
}
var snippet = content.slice(start, end);
if (start > 0) snippet = '\u2026' + snippet;
if (end < content.length) snippet = snippet + '\u2026';
var escaped = escapeHtml(snippet);
if (term) {
var re = new RegExp('(' + escapeRegExp(term) + ')', 'ig');
escaped = escaped.replace(re, '<mark>$1</mark>');
}
return escaped;
}
function sectionLabel(section) {
if (section === 'blog') return 'Blog';
if (section === 'services') return 'Service';
return section;
}
var query = getQueryParam('q').trim();
if (queryEl) queryEl.textContent = query ? 'for "' + query + '"' : '';
if (!query) {
statusEl.textContent = 'Enter a search term above.';
return;
}
statusEl.textContent = 'Searching\u2026';
fetch('/index.json')
.then(function (res) {
if (!res.ok) throw new Error('Failed to load search index');
return res.json();
})
.then(function (items) {
var terms = query.toLowerCase().split(/\s+/).filter(Boolean);
var matches = items
.map(function (item) {
var titleLower = (item.title || '').toLowerCase();
var contentLower = (item.content || '').toLowerCase();
var score = 0;
terms.forEach(function (term) {
if (titleLower.indexOf(term) !== -1) score += 10;
if (contentLower.indexOf(term) !== -1) score += 1;
});
return { item: item, score: score };
})
.filter(function (m) {
return m.score > 0;
})
.sort(function (a, b) {
return b.score - a.score;
});
listEl.innerHTML = '';
if (matches.length === 0) {
statusEl.textContent = 'No results found for "' + query + '".';
return;
}
statusEl.textContent =
matches.length + ' result' + (matches.length === 1 ? '' : 's') + ' found.';
matches.forEach(function (m) {
var item = m.item;
var li = document.createElement('li');
li.className = 'search-results__item';
var meta = document.createElement('span');
meta.className = 'search-results__item-meta';
meta.textContent = sectionLabel(item.section);
var h2 = document.createElement('h2');
h2.className = 'search-results__item-title';
var a = document.createElement('a');
a.href = item.url;
a.textContent = item.title;
h2.appendChild(a);
var p = document.createElement('p');
p.className = 'search-results__item-snippet';
p.innerHTML = buildSnippet(item.content || '', terms[0] || query);
li.appendChild(meta);
li.appendChild(h2);
li.appendChild(p);
listEl.appendChild(li);
});
})
.catch(function () {
statusEl.textContent = 'Something went wrong loading search results.';
});
})();