diff --git a/js/search.js b/js/search.js index 254348a..c2dfd9b 100644 --- a/js/search.js +++ b/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); } }); diff --git a/services.xml b/services.xml index f25e2c9..7e64b08 100644 --- a/services.xml +++ b/services.xml @@ -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 diff --git a/styles.css b/styles.css index 266ecbc..5ea3fd0 100644 --- a/styles.css +++ b/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}