Implement DuckDuckGo search functionality with button and Enter key support; update styles for search input and button
This commit is contained in:
30
js/search.js
30
js/search.js
@@ -1,6 +1,32 @@
|
||||
// Search functionality
|
||||
(function initSearch(){
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const ddgButton = document.getElementById('ddg-search-btn');
|
||||
|
||||
// DuckDuckGo search function
|
||||
function searchDuckDuckGo() {
|
||||
const query = searchInput.value.trim();
|
||||
if (query) {
|
||||
const ddgUrl = `https://duckduckgo.com/?q=${encodeURIComponent(query)}`;
|
||||
window.open(ddgUrl, '_blank', 'noopener,noreferrer');
|
||||
}
|
||||
}
|
||||
|
||||
// Add click handler for DDG button
|
||||
if (ddgButton) {
|
||||
ddgButton.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
searchDuckDuckGo();
|
||||
});
|
||||
}
|
||||
|
||||
// Add Enter key handler for DDG search
|
||||
searchInput.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
searchDuckDuckGo();
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for services to load
|
||||
const checkServicesLoaded = setInterval(() => {
|
||||
@@ -32,7 +58,7 @@
|
||||
|
||||
// Hide empty groups
|
||||
groupSections.forEach(section => {
|
||||
const visibleCards = section.querySelectorAll('.card:not([style*="display: none"])');
|
||||
const visibleCards = section.querySelectorAll('.service-card:not([style*="display: none"])');
|
||||
section.style.display = visibleCards.length > 0 ? '' : 'none';
|
||||
});
|
||||
|
||||
@@ -42,7 +68,7 @@
|
||||
if(visibleCount === 0 && searchTerm !== ''){
|
||||
const msg = document.createElement('p');
|
||||
msg.className = 'notes no-results';
|
||||
msg.textContent = `No services found matching "${e.target.value}"`;
|
||||
msg.textContent = `No services found matching "${e.target.value}". Press Enter to search on DuckDuckGo.`;
|
||||
container.appendChild(msg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
- logo: filename in /logos/ (optional, default: default.svg)
|
||||
- status: maintenance only (optional) - will override health check
|
||||
- check-health: true/false (optional, default: true) - disable auto health check
|
||||
- health-path: custom health check endpoint (optional, e.g., /api/health, /ping)
|
||||
- local-ip: override IP for health checks (optional, overrides tailscale-ip)
|
||||
|
||||
Status Behavior:
|
||||
- If status="maintenance": Shows orange dot, skips health check
|
||||
|
||||
12
styles.css
12
styles.css
@@ -26,10 +26,14 @@ header,main,footer{position:relative;z-index:10}
|
||||
header{padding:24px 20px;text-align:center}
|
||||
header h1{margin:0;font-size:28px}
|
||||
.subtitle{color:var(--muted);margin-top:6px}
|
||||
.search-container{margin-top:16px;max-width:400px;margin-left:auto;margin-right:auto}
|
||||
#search-input{width:100%;padding:10px 16px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);background:rgba(0,15,30,0.8);color:#e6eef8;font-size:14px;outline:none;transition:all .3s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
|
||||
.search-container{margin-top:16px;max-width:400px;margin-left:auto;margin-right:auto;position:relative}
|
||||
#search-input{width:100%;padding:10px 50px 10px 16px;border-radius:8px;border:1px solid rgba(255,255,255,0.15);background:rgba(0,15,30,0.8);color:#e6eef8;font-size:14px;outline:none;transition:all .3s ease;backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
|
||||
#search-input::placeholder{color:var(--muted)}
|
||||
#search-input:focus{border-color:rgba(79,70,229,0.5);box-shadow:0 0 0 3px rgba(79,70,229,0.2);background:rgba(0,15,30,0.9)}
|
||||
#ddg-search-btn{position:absolute;right:8px;top:50%;transform:translateY(-50%);width:32px;height:32px;border:none;background:rgba(79,70,229,0.3);border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.3s ease;padding:6px;backdrop-filter:blur(5px)}
|
||||
#ddg-search-btn svg{width:20px;height:20px;color:#e6eef8}
|
||||
#ddg-search-btn:hover{background:rgba(79,70,229,0.6);transform:translateY(-50%) scale(1.05)}
|
||||
#ddg-search-btn:active{transform:translateY(-50%) scale(0.95)}
|
||||
main{max-width:1100px;margin:18px auto;padding:12px}
|
||||
.service-group{margin-bottom:32px}
|
||||
.group-header{font-size:18px;font-weight:600;color:#e6eef8;margin:0 0 12px 0;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.1);position:relative}
|
||||
@@ -43,8 +47,8 @@ main{max-width:1100px;margin:18px auto;padding:12px}
|
||||
.card:hover{transform:translateY(-6px);box-shadow:0 10px 40px rgba(79,70,229,0.4),0 0 20px rgba(139,92,246,0.3);border-color:rgba(139,92,246,0.5)}
|
||||
.card:hover::after{opacity:1}
|
||||
.card.selected{transform:translateY(-4px);box-shadow:0 8px 30px rgba(79,70,229,0.5),0 0 15px rgba(139,92,246,0.4);border-color:rgba(139,92,246,0.7);outline:2px solid rgba(79,70,229,0.6);outline-offset:2px}
|
||||
.card .logo{width:36px;height:36px;margin-right:12px;flex:0 0 36px;filter:brightness(0) invert(1)}
|
||||
.card .label{flex:1;text-align:left}
|
||||
.card .logo,.service-card .logo{width:36px;height:36px;margin-right:12px;flex:0 0 36px;filter:brightness(0) invert(1)}
|
||||
.card .label,.service-card .label{flex:1;text-align:left}
|
||||
.status-dot{position:absolute;top:6px;right:6px;width:10px;height:10px;border-radius:50%;border:2px solid rgba(255,255,255,0.3);animation:pulse 2s infinite}
|
||||
.status-dot.status-online{background:#10b981;box-shadow:0 0 8px rgba(16,185,129,0.6)}
|
||||
.status-dot.status-offline{background:#ef4444;box-shadow:0 0 8px rgba(239,68,68,0.6);animation:none}
|
||||
|
||||
Reference in New Issue
Block a user