* Complete refactor Signed-off-by: Stephen Simpson <ssimpson89@users.noreply.github.com> * Complete refactor Signed-off-by: Stephen Simpson <ssimpson89@users.noreply.github.com> --------- Signed-off-by: Stephen Simpson <ssimpson89@users.noreply.github.com>
359 lines
9.1 KiB
HTML
359 lines
9.1 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block header_title %}Rocky Linux {{ version }} Man Pages{% endblock %}
|
|
{% block header_subtitle %}Search and browse {{ total_pages }} man pages{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
.search-box {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 0.75rem 1rem;
|
|
font-size: 1rem;
|
|
background-color: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
color: var(--text-primary);
|
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.search-input:focus {
|
|
outline: none;
|
|
border-color: var(--accent-primary);
|
|
box-shadow: 0 0 0 3px rgba(88, 166, 255, 0.2);
|
|
}
|
|
|
|
.search-input:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.search-stats {
|
|
margin-top: 1rem;
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.results-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
}
|
|
|
|
.result-item {
|
|
padding: 0.75rem 0;
|
|
border-bottom: 1px solid var(--border-color);
|
|
}
|
|
|
|
.result-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.result-link {
|
|
font-size: 1.1rem;
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.result-section {
|
|
color: var(--text-secondary);
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.result-package {
|
|
color: var(--text-secondary);
|
|
font-size: 0.85rem;
|
|
margin-left: auto;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 2rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.spinner {
|
|
display: inline-block;
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 3px solid var(--border-color);
|
|
border-top-color: var(--accent-primary);
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
.no-results {
|
|
text-align: center;
|
|
padding: 3rem 1rem;
|
|
color: var(--text-secondary);
|
|
}
|
|
|
|
.quick-links {
|
|
margin-top: 2rem;
|
|
padding-top: 1.5rem;
|
|
border-top: 1px solid var(--border-color);
|
|
}
|
|
|
|
.quick-links h3 {
|
|
margin-bottom: 1rem;
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
.package-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.package-link {
|
|
padding: 0.5rem;
|
|
background-color: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 4px;
|
|
text-align: center;
|
|
transition: background-color 0.2s, border-color 0.2s;
|
|
min-height: 44px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.package-link:hover {
|
|
background-color: var(--bg-primary);
|
|
border-color: var(--accent-primary);
|
|
text-decoration: none;
|
|
}
|
|
|
|
.view-all-container {
|
|
text-align: center;
|
|
margin-top: 1.5rem;
|
|
}
|
|
|
|
.view-all-button {
|
|
display: inline-block;
|
|
padding: 0.75rem 1.5rem;
|
|
background-color: var(--bg-tertiary);
|
|
border: 1px solid var(--border-color);
|
|
border-radius: 6px;
|
|
color: var(--accent-primary);
|
|
text-decoration: none;
|
|
font-weight: 600;
|
|
transition: all 0.2s;
|
|
min-height: 44px;
|
|
}
|
|
|
|
.view-all-button:hover {
|
|
background-color: var(--bg-primary);
|
|
border-color: var(--accent-primary);
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
text-decoration: none;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.search-input {
|
|
font-size: 16px;
|
|
}
|
|
|
|
.package-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
}
|
|
|
|
.result-link {
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
.result-package {
|
|
margin-left: 0;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 480px) {
|
|
.package-grid {
|
|
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
|
}
|
|
|
|
.quick-links h3 {
|
|
font-size: 1.2rem;
|
|
}
|
|
}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="search-box">
|
|
<input type="text" id="searchInput" class="search-input" placeholder="Loading search index..." disabled>
|
|
<div class="search-stats" id="searchStats">
|
|
<div class="loading">
|
|
<span class="spinner"></span>
|
|
<span style="margin-left: 0.5rem;">Loading search index...</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="resultsContainer">
|
|
<ul class="results-list" id="resultsList"></ul>
|
|
</div>
|
|
|
|
<div class="quick-links">
|
|
<h3>Browse by Package</h3>
|
|
<div class="package-grid">
|
|
{% for package in packages[:50] %}
|
|
<a href="#" class="package-link" data-package="{{ package }}">{{ package }}</a>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
<div class="view-all-container">
|
|
<a href="packages.html" class="button view-all-button">
|
|
View All Packages →
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0/dist/fuse.min.js"></script>
|
|
<script>
|
|
let fuse;
|
|
let searchData = [];
|
|
|
|
// Load search index
|
|
fetch('search.json.gz')
|
|
.then(response => response.body.pipeThrough(new DecompressionStream('gzip')))
|
|
.then(stream => new Response(stream))
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
// Flatten data for searching
|
|
searchData = [];
|
|
for (const [pkg, pages] of Object.entries(data)) {
|
|
for (const [key, page] of Object.entries(pages)) {
|
|
searchData.push({
|
|
...page,
|
|
package: pkg
|
|
});
|
|
}
|
|
}
|
|
|
|
// Initialize Fuse.js
|
|
fuse = new Fuse(searchData, {
|
|
keys: [
|
|
{ name: 'name', weight: 2.0 },
|
|
{ name: 'display_name', weight: 1.5 },
|
|
{ name: 'package', weight: 1.0 },
|
|
{ name: 'full_name', weight: 0.8 }
|
|
],
|
|
threshold: 0.25,
|
|
minMatchCharLength: 2,
|
|
ignoreLocation: false,
|
|
location: 0
|
|
});
|
|
|
|
// Enable search
|
|
const searchInput = document.getElementById('searchInput');
|
|
searchInput.disabled = false;
|
|
searchInput.placeholder = 'Search man pages... (e.g., "bash", "printf", "systemd")';
|
|
|
|
document.getElementById('searchStats').innerHTML =
|
|
`<strong>${searchData.length}</strong> man pages available`;
|
|
|
|
// Check for query parameter
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const query = urlParams.get('q');
|
|
|
|
if (query && query.trim()) {
|
|
searchInput.value = query;
|
|
searchInput.focus();
|
|
runSearch(query.trim());
|
|
} else {
|
|
// Show all initially
|
|
displayResults(searchData.slice(0, 50));
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error loading search index:', error);
|
|
document.getElementById('searchStats').innerHTML =
|
|
'<span style="color: var(--warning)">Error loading search index</span>';
|
|
});
|
|
|
|
// Search function
|
|
function runSearch(query) {
|
|
if (!query) {
|
|
displayResults(searchData.slice(0, 50));
|
|
document.getElementById('searchStats').innerHTML =
|
|
`Showing 50 of <strong>${searchData.length}</strong> man pages`;
|
|
return;
|
|
}
|
|
|
|
const results = fuse.search(query, { limit: 100 });
|
|
const items = results.map(r => r.item);
|
|
|
|
displayResults(items);
|
|
|
|
document.getElementById('searchStats').innerHTML =
|
|
`Found <strong>${items.length}</strong> result${items.length !== 1 ? 's' : ''}`;
|
|
}
|
|
|
|
// Search handler
|
|
document.getElementById('searchInput').addEventListener('input', function (e) {
|
|
const query = e.target.value.trim();
|
|
runSearch(query);
|
|
|
|
// Update URL with search query
|
|
const url = new URL(window.location);
|
|
if (query) {
|
|
url.searchParams.set('q', query);
|
|
} else {
|
|
url.searchParams.delete('q');
|
|
}
|
|
window.history.replaceState({}, '', url);
|
|
});
|
|
|
|
// Package link handler
|
|
document.querySelectorAll('.package-link').forEach(link => {
|
|
link.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const pkg = this.dataset.package;
|
|
const pkgResults = searchData.filter(item => item.package === pkg);
|
|
displayResults(pkgResults);
|
|
document.getElementById('searchInput').value = pkg;
|
|
document.getElementById('searchStats').innerHTML =
|
|
`<strong>${pkgResults.length}</strong> man pages in <strong>${pkg}</strong>`;
|
|
|
|
// Update URL with package search
|
|
const url = new URL(window.location);
|
|
url.searchParams.set('q', pkg);
|
|
window.history.replaceState({}, '', url);
|
|
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
});
|
|
});
|
|
|
|
function displayResults(items) {
|
|
const resultsList = document.getElementById('resultsList');
|
|
|
|
if (items.length === 0) {
|
|
resultsList.innerHTML =
|
|
'<div class="no-results">No man pages found. Try a different search term.</div>';
|
|
return;
|
|
}
|
|
|
|
resultsList.innerHTML = items.map(item => `
|
|
<li class="result-item">
|
|
<a href="${item.url}" class="result-link">
|
|
<span>${item.display_name}</span>
|
|
<span class="result-package">${item.package}</span>
|
|
</a>
|
|
</li>
|
|
`).join('');
|
|
}
|
|
</script>
|
|
{% endblock %} |