Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', () => { | |
| // --- DOM --- | |
| const clockEl = document.getElementById('clock'); | |
| const dateEl = document.getElementById('date'); | |
| const greetingEl = document.getElementById('greeting'); | |
| const searchForm = document.getElementById('search-form'); | |
| const searchInput = document.getElementById('search-input'); | |
| const searchEngine = document.getElementById('search-engine'); | |
| const themeToggle = document.getElementById('theme-toggle'); | |
| const notesToggle = document.getElementById('notes-toggle'); // optional in your HTML | |
| const notesPanel = document.getElementById('notes-panel'); // optional | |
| const cardGrid = document.getElementById('card-grid'); | |
| // --- HARD-CODED CATEGORIES/LINKS (edit these arrays to taste) --- | |
| const CATEGORIES = [ | |
| { | |
| id: 'reddit', | |
| name: 'Reddit', | |
| icon: 'fa-brands fa-reddit', | |
| links: [ | |
| { name: 'r/EmulationOnAndroid', url: 'https://www.reddit.com/r/emulationonandroid/', icon: 'fa-solid fa-gamepad' }, | |
| { name: 'r/Singularity', url: 'https://www.reddit.com/r/singularity/', icon: 'fa-solid fa-robot' }, | |
| { name: 'r/OpenAI', url: 'https://www.reddit.com/r/openai/', icon: 'fa-solid fa-brain' }, | |
| { name: 'r/Programming', url: 'https://www.reddit.com/r/programming/', icon: 'fa-solid fa-code' }, | |
| { name: 'r/MLB', url: 'https://www.reddit.com/r/MLB/', icon: 'fa-solid fa-baseball' }, | |
| { name: 'r/DataIsBeautiful', url: 'https://www.reddit.com/r/dataisbeautiful/', icon: 'fa-solid fa-chart-simple' } | |
| ] | |
| }, | |
| { | |
| id: 'news', | |
| name: 'News & Tech', | |
| icon: 'fa-solid fa-newspaper', | |
| links: [ | |
| { name: 'Hacker News', url: 'https://news.ycombinator.com/', icon: 'fa-brands fa-hacker-news' }, | |
| { name: 'TechCrunch', url: 'https://techcrunch.com/', icon: 'fa-solid fa-microchip' }, | |
| { name: 'The Verge', url: 'https://www.theverge.com/', icon: 'fa-solid fa-bolt' }, | |
| { name: 'Ars Technica', url: 'https://arstechnica.com/', icon: 'fa-solid fa-flask' }, | |
| { name: 'Reuters', url: 'https://www.reuters.com/', icon: 'fa-solid fa-globe' }, | |
| { name: 'AP News', url: 'https://apnews.com/', icon: 'fa-solid fa-circle-info' }, | |
| { name: 'BBC', url: 'https://www.bbc.com/news', icon: 'fa-solid fa-earth-europe' } | |
| ] | |
| }, | |
| { | |
| id: 'streaming', | |
| name: 'Streaming', | |
| icon: 'fa-solid fa-clapperboard', | |
| links: [ | |
| { name: 'YouTube', url: 'https://www.youtube.com/', icon: 'fa-brands fa-youtube' }, | |
| { name: 'Netflix', url: 'https://www.netflix.com/', icon: 'fa-brands fa-n' }, // FA6 has brand 'n' via kit/alias; if it doesn’t show, swap to fa-tv | |
| { name: 'Hulu', url: 'https://www.hulu.com/', icon: 'fa-solid fa-film' }, | |
| { name: 'Prime Video', url: 'https://www.primevideo.com/', icon: 'fa-brands fa-amazon' }, | |
| { name: 'Disney+', url: 'https://www.disneyplus.com/', icon: 'fa-solid fa-star' }, | |
| { name: 'Max', url: 'https://www.max.com/', icon: 'fa-solid fa-video' }, | |
| { name: 'Crunchyroll', url: 'https://www.crunchyroll.com/', icon: 'fa-brands fa-critical-role' } // fallback brand icon | |
| ] | |
| } | |
| ]; | |
| // --- SEARCH ENGINES (+ optional HF Space variable DEFAULT_ENGINE) --- | |
| const SEARCH_ENGINES = { | |
| google: 'https://www.google.com/search?q=', | |
| duckduckgo: 'https://duckduckgo.com/?q=', | |
| youtube: 'https://www.youtube.com/results?search_query=', | |
| github: 'https://github.com/search?q=' | |
| }; | |
| if (window.huggingface?.variables?.DEFAULT_ENGINE && | |
| SEARCH_ENGINES[window.huggingface.variables.DEFAULT_ENGINE]) { | |
| if (searchEngine) searchEngine.value = window.huggingface.variables.DEFAULT_ENGINE; | |
| } | |
| // --- RENDER --- | |
| function renderCards() { | |
| if (!cardGrid) return; | |
| cardGrid.innerHTML = ''; | |
| CATEGORIES.forEach(cat => cardGrid.appendChild(createCard(cat))); | |
| } | |
| function createCard(category) { | |
| const card = document.createElement('div'); | |
| card.className = 'card'; | |
| card.dataset.categoryId = category.id; | |
| // Links list | |
| const linksHtml = category.links.map(link => ` | |
| <li> | |
| <a href="${link.url}" target="_blank" rel="noopener noreferrer"> | |
| <i class="${link.icon || 'fa-solid fa-link'}"></i> ${link.name} | |
| </a> | |
| </li> | |
| `).join(''); | |
| card.innerHTML = ` | |
| <div class="card-header"> | |
| <h2><i class="${category.icon || 'fa-solid fa-folder'}"></i> ${category.name}</h2> | |
| </div> | |
| <ul>${linksHtml}</ul> | |
| `; | |
| return card; | |
| } | |
| // --- SEARCH --- | |
| if (searchForm) { | |
| searchForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| const q = (searchInput?.value || '').trim(); | |
| if (!q) return; | |
| const engine = searchEngine?.value || 'google'; | |
| window.open(SEARCH_ENGINES[engine] + encodeURIComponent(q), '_blank', 'noopener'); | |
| if (searchInput) searchInput.value = ''; | |
| }); | |
| } | |
| // --- THEME --- | |
| if (themeToggle) { | |
| themeToggle.addEventListener('click', () => { | |
| document.body.classList.toggle('light-theme'); | |
| const isLight = document.body.classList.contains('light-theme'); | |
| localStorage.setItem('theme', isLight ? 'light' : 'dark'); | |
| themeToggle.innerHTML = isLight ? '<i class="fa-solid fa-sun"></i>' : '<i class="fa-solid fa-moon"></i>'; | |
| }); | |
| const savedTheme = localStorage.getItem('theme'); | |
| if (savedTheme === 'light') { | |
| document.body.classList.add('light-theme'); | |
| themeToggle.innerHTML = '<i class="fa-solid fa-sun"></i>'; | |
| } | |
| } | |
| // --- CLOCK & GREETING --- | |
| function updateClock() { | |
| const now = new Date(); | |
| if (clockEl) { | |
| clockEl.textContent = [ | |
| String(now.getHours()).padStart(2,'0'), | |
| String(now.getMinutes()).padStart(2,'0'), | |
| String(now.getSeconds()).padStart(2,'0') | |
| ].join(':'); | |
| } | |
| if (dateEl) { | |
| dateEl.textContent = now.toLocaleDateString('en-US', { weekday:'long', year:'numeric', month:'long', day:'numeric' }); | |
| } | |
| } | |
| function setGreeting() { | |
| const hour = new Date().getHours(); | |
| const name = localStorage.getItem('userName') || ''; | |
| const g = hour < 12 ? 'Good Morning' : hour < 18 ? 'Good Afternoon' : 'Good Evening'; | |
| if (greetingEl) greetingEl.textContent = g + (name ? `, ${name}` : ''); | |
| } | |
| // --- KEYBOARD SHORTCUTS --- | |
| document.addEventListener('keydown', (e) => { | |
| if (!isInputFocused() && (e.key === '/' || (e.ctrlKey && e.key === 'k'))) { | |
| e.preventDefault(); | |
| searchInput?.focus(); | |
| } | |
| if (e.key === 'n' && !isInputFocused() && notesToggle && notesPanel) { | |
| // Toggle notes panel if you kept it in the HTML | |
| notesPanel.classList.toggle('active'); | |
| } | |
| if (e.key === 'Escape' && notesPanel) { | |
| notesPanel.classList.remove('active'); | |
| searchInput?.blur(); | |
| } | |
| }); | |
| function isInputFocused() { | |
| const a = document.activeElement; | |
| return a && ['INPUT','TEXTAREA','SELECT'].includes(a.tagName); | |
| } | |
| // --- INIT --- | |
| updateClock(); | |
| setGreeting(); | |
| setInterval(updateClock, 1000); | |
| renderCards(); | |
| }); | |