From 18811fb350262be7f4b3a750dab74ec4cf256b43 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 14 Jun 2026 03:36:18 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20Dashboard?= =?UTF-8?q?=20keyboard=20shortcut=20and=20UI=20hints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement 'D' keyboard shortcut to toggle Dashboard panel. - Update #dashboardBtn title with '(D)' hint for discoverability. - Consolidate 'B' and 'T' shortcuts in global keydown listener. - Add e.preventDefault() to global shortcuts to prevent default browser actions. Co-authored-by: ruhdevops <203426218+ruhdevops@users.noreply.github.com> --- index.html | 2 +- js/app.js | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index dda1d88..ea181e4 100644 --- a/index.html +++ b/index.html @@ -92,7 +92,7 @@
Try different keywords or browse by category to find what you're looking for.
-No saved episodes.
')}}function updateStats(){DOM.statTotal&&(DOM.statTotal.textContent=AppState.videos.length),DOM.statSaved&&(DOM.statSaved.textContent=AppState.watchLater.length),DOM.statProgress&&(DOM.statProgress.textContent=Object.keys(AppState.progress).length),DOM.watchLaterCount&&(DOM.watchLaterCount.textContent=AppState.watchLater.length),DOM.watchLaterBadge&&DOM.watchLaterBadge.setAttribute("aria-label",`Open watch later list (${AppState.watchLater.length} episodes)`)}function openVideo(e){var t;DOM.modal&&DOM.player&&(t=getProgress((AppState.current=e).id)?.time||0,DOM.player.src=`https://www.youtube.com/embed/${e.id}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1&start=`+Math.floor(t),DOM.modal.style.display="flex",DOM.modal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.modal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"),(t=document.getElementById("video-title"))&&(t.textContent=e.title),DOM.transcriptPanel&&DOM.transcriptPanel.setAttribute("aria-hidden","true"),DOM.sharePanel)&&DOM.sharePanel.setAttribute("aria-hidden","true")}function closeVideo(){DOM.modal&&DOM.player&&(DOM.player.src="",DOM.modal.style.display="none",DOM.modal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.current=null,clearInterval(AppState.progressTimer),renderContinueWatching(),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function navigateVideo(t){if(AppState.current&&AppState.filtered.length){var a=AppState.filtered.findIndex(e=>e.id===AppState.current.id);if(-1!==a){let e=a+t;(e=e<0?AppState.filtered.length-1:e)>=AppState.filtered.length&&(e=0),openVideo(AppState.filtered[e])}}}function toggleWatchLater(t){var e=AppState.watchLater.findIndex(e=>e.id===t.id);-1===e?(AppState.watchLater.push(t),Utils.showToast("Added to Watch Later")):(AppState.watchLater.splice(e,1),Utils.showToast("Removed from Watch Later")),Utils.saveLS(CONFIG.STORAGE.WATCH_LATER_KEY,AppState.watchLater),updateStats(),AppState.hero&&AppState.hero.id===t.id&&renderHero(AppState.hero),renderGrid(),DOM.watchLaterContainer&&renderWatchLater()}function openWatchLater(){DOM.watchLaterPage&&(renderWatchLater(),DOM.watchLaterPage.style.display="block",DOM.watchLaterPage.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.watchLaterPage),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeWatchLater(){DOM.watchLaterPage&&(DOM.watchLaterPage.style.display="none",DOM.watchLaterPage.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}let initAnalyticsChart=async()=>{var e=document.getElementById("analyticsChart");e&&(window.Chart||await new Promise(e=>{var t=document.createElement("script");t.src="https://cdn.jsdelivr.net/npm/chart.js",t.onload=e,document.head.appendChild(t)}),e=e.getContext("2d"),window.myChart&&window.myChart.destroy(),window.myChart=new Chart(e,{type:"line",data:{labels:["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],datasets:[{label:"Subscriber Growth",data:[12,19,3,5,2,3,9],borderColor:"#e50914",backgroundColor:"rgba(229, 9, 20, 0.1)",fill:!0,tension:.4}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{y:{display:!1},x:{grid:{display:!1},ticks:{color:"#808080",font:{size:10}}}}}}))},initAIAssistant=()=>{var e;if(isFeatureEnabled("AI_ASSISTANT")){let s=document.getElementById("ai-score-title"),a=document.getElementById("ai-generate-hook"),o=document.getElementById("ai-title-input");s&&s.addEventListener("click",()=>{o.value.trim()?(s.disabled=!0,s.innerHTML='',setTimeout(()=>{var e=Math.floor(30*Math.random()+65),t=document.getElementById("ai-score-result"),a=document.getElementById("ai-score-value"),o=document.getElementById("ai-score-bar");t.classList.remove("hidden"),a.textContent=e+"/100",o.style.width=e+"%",s.disabled=!1,s.textContent="Score",Utils.showToast("Title analyzed!","success")},1e3)):Utils.showToast("Please enter a title","warning")}),a&&a.addEventListener("click",()=>{a.disabled=!0,a.innerHTML=' Analyzing...';let t=["What if everything you knew about the fall of Andalusia was wrong?","Behind the silence of history lies a truth far more cinematic than fiction.","The year was 1492. The world was changing. And at the center of it all?","How did one decision change the course of human history forever?"];setTimeout(()=>{var e=document.getElementById("ai-hook-result");e.classList.remove("hidden"),e.textContent=t[Math.floor(Math.random()*t.length)],a.disabled=!1,a.innerHTML=' Generate Viral Hook',Utils.showToast("Hook generated!","success")},1200)}),document.querySelectorAll("#ai-topics span").forEach(t=>{t.addEventListener("click",()=>{var e=t.textContent;o&&(o.value=e,Utils.showToast("Selected: "+e,"info"))}),t.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),t.click())})})}else(e=document.getElementById("ai-assistant-panel"))&&(e.style.display="none")};function openDashboard(){DOM.dashboardModal&&(renderDashboard(),initAnalyticsChart(),initAIAssistant(),DOM.dashboardModal.style.display="block",DOM.dashboardModal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.dashboardModal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeDashboard(){DOM.dashboardModal&&(DOM.dashboardModal.style.display="none",DOM.dashboardModal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function setTheme(t){AppState.theme=t,Utils.saveLS(CONFIG.STORAGE.THEME_KEY,t),DOM.body.classList.remove("light-mode","theme-neon"),"light"===t?DOM.body.classList.add("light-mode"):"neon"===t&&DOM.body.classList.add("theme-neon");var e=DOM.themeToggle?.querySelector("i");e&&(e.className="dark"===t?"fa-regular fa-moon":"neon"===t?"fa-solid fa-bolt":"fa-regular fa-sun"),document.querySelectorAll(".theme-opt").forEach(e=>{e.classList.toggle("active",e.dataset.theme===t)})}function toggleTheme(){setTheme("dark"===AppState.theme?"light":"dark")}function switchMode(e){"creator"===e?(DOM.studioRoot&&(DOM.studioRoot.style.display="block"),DOM.appRoot&&(DOM.appRoot.style.display="none"),DOM.heroSection&&(DOM.heroSection.style.display="none"),DOM.continueBlockSec&&(DOM.continueBlockSec.style.display="none"),updateBreadcrumbs("Studio > Projects")):(DOM.studioRoot&&(DOM.studioRoot.style.display="none"),DOM.appRoot&&(DOM.appRoot.style.display="block"),DOM.heroSection&&(DOM.heroSection.style.display="block"),DOM.continueBlockSec&&AppState.videos.some(e=>getProgress(e.id))&&(DOM.continueBlockSec.style.display="block"))}function updateBreadcrumbs(e){if(DOM.studioBreadcrumbs){let a=e.split(" > ");DOM.studioBreadcrumbs.innerHTML=a.map((e,t)=>t===a.length-1?`${e}`:`${e}`).join(' ')}}function renderProjects(){var e=document.getElementById("studioProjectsList");if(e){let o=Utils.getLS(CONFIG.STORAGE.PROJECTS_KEY,[{id:"p1",title:"The Fall of the Abbasids",status:"Writing",progress:65,date:"2024-05-10"},{id:"p2",title:"Prophecy & Modernity",status:"Researching",progress:30,date:"2024-05-12"},{id:"p3",title:"The Silent Silk Road",status:"Editing",progress:90,date:"2024-05-08"},{id:"p4",title:"The Golden Age",status:"Published",progress:100,date:"2024-05-01"}]);"list"===AppState.currentView?(e.className="studio-projects-grid",e.innerHTML=o.map(e=>` - `).join('') - : 'No saved episodes.
'; - } -} - -function updateStats() { if (DOM.statTotal) DOM.statTotal.textContent = AppState.videos.length; if (DOM.statSaved) DOM.statSaved.textContent = AppState.watchLater.length; if (DOM.statProgress) DOM.statProgress.textContent = Object.keys(AppState.progress).length; @@ -1321,18 +945,19 @@ function bindEvents() { } else if (key === 'k') { e.preventDefault(); navigateVideo(1); + } else if (key === 'b') { + e.preventDefault(); + toggleWatchLater(AppState.current); } } // Theme toggle if (key === 't') { - e.preventDefault(); toggleTheme(); } // Watch Later toggle if (key === 'b') { - e.preventDefault(); if (AppState.current) { toggleWatchLater(AppState.current); } else if (DOM.watchLaterPage) { @@ -1343,16 +968,6 @@ function bindEvents() { } } } - - // Dashboard toggle - if (key === 'd' && DOM.dashboardModal) { - e.preventDefault(); - if (DOM.dashboardModal.style.display === 'block') { - closeDashboard(); - } else { - openDashboard(); - } - } }); // Mouse move effect for cards diff --git a/js/app.js.new b/js/app.js.new new file mode 100644 index 0000000..8ab8d3f --- /dev/null +++ b/js/app.js.new @@ -0,0 +1,500 @@ +import { DeepSearch } from './search.js'; +import { isFeatureEnabled } from './config.js'; +import { initMonitoring } from './monitoring.js'; +import { initScriptStudio } from './script.js'; +import { initIslamic } from './islamic.js'; + +/** + * YT Studio - Main Application + * YouTube API V3 Integration via Cloudflare Worker + * Version: 2.0.0 + */ + +// ============================================ +// CONFIGURATION +// ============================================ +const CONFIG = { + // API Endpoints + API: { + YOUTUBE_WORKER: window.__API_CONFIG__?.YOUTUBE_WORKER || 'https://yt-studio-youtube-api.ruhdevopsytstudio.workers.dev', + FALLBACK_DATA: '/data/demo.json' + }, + + // Storage Keys + STORAGE: { + CHANNEL_KEY: 'yt_studio_channel_id', + CACHE_KEY: 'yt_studio_videos_cache_v4', + CACHE_EXPIRY: 86400000, // 24 hours + PROJECTS_KEY: 'yt_studio_projects', + RESEARCH_KEY: 'yt_studio_research', + WATCH_LATER_KEY: 'watch_later_list', + THEME_KEY: 'ui_theme', + SEARCH_HISTORY_KEY: 'search_history', + PROGRESS_KEY: 'watch_progress' + }, + + // UI Settings + UI: { + ITEMS_PER_PAGE: 15, + LAZY_LOAD_THRESHOLD: 400 + }, + + // API Retry Config + API_CONFIG: { + timeout: 10000, + retries: 3, + backoff: 1.5, + delay: 500 + } +}; +// ============================================ +// CATEGORIES +// ============================================ +const CATEGORIES = [ + { key: 'quran', label: 'Quran', terms: ['quran', 'surah', 'ayah', 'allah', 'tafsir', 'islam'] }, + { key: 'prophecy', label: 'Prophecy', terms: ['prophecy', 'dajjal', 'gog', 'magog', 'end times'] }, + { key: 'discussion', label: 'Discussion', terms: ['podcast', 'debate', 'interview', 'conversation'] }, + { key: 'educational', label: 'Educational', terms: ['lesson', 'guide', 'explained', 'documentary'] }, + { key: 'history', label: 'History', terms: ['history', 'empire', 'caliph', 'war', 'civilization'] } +]; + +// ============================================ +// DOM ELEMENTS +// ============================================ +const DOM = { + // Core elements + body: document.body, + grid: document.getElementById('grid'), + modal: document.getElementById('modal'), + modalSaveBtn: document.getElementById('modalSaveBtn'), + player: document.getElementById('player'), + closeModal: document.getElementById('close'), + toast: document.getElementById('toast'), + + // Hero section + heroTitle: document.getElementById('hero-title'), + heroDesc: document.getElementById('hero-desc'), + heroBtn: document.getElementById('hero-btn'), + heroSave: document.getElementById('hero-save'), + heroCategory: document.getElementById('hero-category'), + heroDate: document.getElementById('hero-date'), + bg: document.getElementById('bg'), + + // Search + search: document.getElementById('searchInput'), + searchToggle: document.getElementById('searchToggleBtn'), + searchSection: document.getElementById('searchSection'), + clearSearch: document.getElementById('clearSearch'), + resultsMeta: document.getElementById('results-meta'), + + // Pagination & Loading + loadMore: document.getElementById('loadMoreBtn'), + loadMoreContainer: document.getElementById('loadMoreContainer'), + loading: document.getElementById('loading'), + error: document.getElementById('error'), + errorMsg: document.getElementById('error-msg'), + retryBtn: document.getElementById('retryBtn'), + + // Theme & UI + themeToggle: document.getElementById('themeToggleBtn'), + themeMenu: document.getElementById('themeMenu'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + episodesNavBtn: document.querySelector('[data-action="scroll-to-episodes"]'), + episodesSection: document.getElementById('episodesSection'), + menuToggle: document.getElementById('menuToggleBtn'), + scrollToTop: document.getElementById('scrollToTop'), + + // Watch Later + watchLaterBtn: document.getElementById('watchLaterBtn'), + watchLaterBadge: document.getElementById('watchLaterBadge'), + watchLaterCount: document.getElementById('watchLaterCount'), + watchLaterPage: document.getElementById('watchLaterPage'), + watchLaterContainer: document.getElementById('watchLaterContainer'), + closeWatchLater: document.getElementById('closeWatchLater'), + + // Dashboard + dashboardBtn: document.getElementById('dashboardBtn'), + dashboardModal: document.getElementById('dashboardModal'), + closeDashboard: document.getElementById('closeDashboard'), + dashTotal: document.getElementById('dashboard-total'), + dashSaved: document.getElementById('dashboard-saved'), + dashProgress: document.getElementById('dashboard-progress'), + dashHours: document.getElementById('dashboard-hours'), + dashCategories: document.getElementById('dashboardCategories'), + dashResumeList: document.getElementById('dashboardResumeList'), + + // Studio Mode + modeSwitcher: document.getElementById('modeSwitcher'), + modeBtns: document.querySelectorAll('.mode-btn'), + studioRoot: document.getElementById('studio-root'), + appRoot: document.getElementById('app-root'), + heroSection: document.getElementById('hero'), + continueBlock: document.getElementById('continue-block'), + continueRow: document.getElementById('continue-row'), + emptyHistory: document.getElementById('empty-history'), + recommendedRow: document.getElementById('recommended-row'), + recommendedBlockSec: document.getElementById('recommended-block'), + continueBlockSec: document.getElementById('continue-block'), + + // Studio Navigation + studioNavBtns: document.querySelectorAll('.studio-nav-btn'), + studioViews: document.querySelectorAll('.studio-view'), + studioBreadcrumbs: document.getElementById('studioBreadcrumbs'), + studioViewProjects: document.getElementById('studio-view-projects'), + activeProjectView: document.getElementById('active-project-view'), + newProjectBtn: document.getElementById('newProjectBtn'), + backToProjectsBtn: document.getElementById('backToProjectsBtn'), + projectTabBtns: document.querySelectorAll('.project-tab-btn'), + ptabContents: document.querySelectorAll('.ptab-content'), + + // Misc + channelInput: document.getElementById('channelIdInput'), + connectBtn: document.getElementById('connectChannelBtn'), + clearFilters: document.getElementById('clearFilters') +}; + +// ============================================ +// APPLICATION STATE +// ============================================ +const AppState = { + videos: [], + filtered: [], + hero: null, + current: null, + categories: ['all'], + search: '', + page: 0, + watchLater: [], + theme: 'dark', + debounceTimer: null, + searchHistory: [], + progress: {}, + ytPlayer: null, + isPlaying: false, + isMuted: false, + currentView: 'list', lastFocused: null +}; + +// ============================================ +// UTILITY FUNCTIONS +// ============================================ +const Utils = { + sanitize(str) { + return String(str ?? '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + + truncate(str, maxLength) { + if (!str) return ''; + return str.length > maxLength ? str.slice(0, maxLength) + '...' : str; + }, + + formatDate(dateStr) { + try { + return new Intl.DateTimeFormat('en', { + month: 'short', + day: 'numeric', + year: 'numeric' + }).format(new Date(dateStr)); + } catch { + return ''; + } + }, + + highlight(text, search) { + if (!search) return text; + const regex = new RegExp('(' + this.escapeRegex(search) + ')', 'gi'); + return text.replace(regex, '$1'); + }, + + escapeRegex(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + + saveLS(key, data) { + try { + localStorage.setItem(key, JSON.stringify(data)); + } catch (e) { + console.error('LS Save Error:', e); + } + }, + + getLS(key, defaultValue = null) { + try { + const item = localStorage.getItem(key); + return item ? JSON.parse(item) : defaultValue; + } catch { + return defaultValue; + } + }, + + async fetchWithRetry(url, config = CONFIG.API_CONFIG) { + let delay = config.delay; + for (let i = 0; i < config.retries; i++) { + try { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), config.timeout); + const response = await fetch(url, { signal: controller.signal }); + clearTimeout(timeout); + if (response.ok) return response; + throw new Error(`API Error: ${response.status}`); + } catch (error) { + if (i === config.retries - 1) throw error; + await new Promise(resolve => setTimeout(resolve, delay)); + delay *= config.backoff; + } + } + }, + + showToast(message, type = "info") { + if (DOM.toast) { + DOM.toast.textContent = message; + DOM.toast.className = "toast show"; + if (type !== "info") DOM.toast.classList.add(type); + setTimeout(() => DOM.toast.classList.remove("show"), 3000); + } + }, + + async copyToClipboard(text, element) { + try { + await navigator.clipboard.writeText(text); + this.showToast("Copied to clipboard!", "success"); + if (element) { + const feedback = document.createElement("span"); + feedback.className = "copy-feedback"; + feedback.textContent = "Copied!"; + element.style.position = "relative"; + element.appendChild(feedback); + setTimeout(() => feedback.classList.add("show"), 10); + setTimeout(() => { + feedback.classList.remove("show"); + setTimeout(() => feedback.remove(), 200); + }, 2000); + } + } catch (err) { + console.error("Failed to copy: ", err); + this.showToast("Failed to copy", "error"); + } + }, + + trapFocus(element) { + AppState.lastFocused = document.activeElement; + const focusableEls = element.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); + const firstFocusableEl = focusableEls[0]; + const lastFocusableEl = focusableEls[focusableEls.length - 1]; + + element.addEventListener('keydown', (e) => { + const isTabPressed = (e.key === 'Tab' || e.keyCode === 9); + if (!isTabPressed) return; + + if (e.shiftKey) { + if (document.activeElement === firstFocusableEl) { + lastFocusableEl.focus(); + e.preventDefault(); + } + } else { + if (document.activeElement === lastFocusableEl) { + firstFocusableEl.focus(); + e.preventDefault(); + } + } + }); + + if (firstFocusableEl) firstFocusableEl.focus(); + } +}; + +// ============================================ +// CATEGORY HELPERS +// ============================================ +function getCategoryLabel(key) { + const cat = CATEGORIES.find(c => c.key === key); + return cat ? cat.label : 'History'; +} + +function detectCategory(title) { + const text = (title || '').toLowerCase(); + const cat = CATEGORIES.find(c => c.terms.some(term => text.includes(term))); + return cat ? cat.key : 'history'; +} + +// ============================================ +// PROGRESS TRACKING +// ============================================ +function getProgress(videoId) { + return AppState.progress[videoId] || null; +} + +// ============================================ +// YOUTUBE API INTEGRATION (via Cloudflare Worker) +// ============================================ +async function fetchYouTubeChannelData() { + try { + const response = await fetch(`${CONFIG.API.YOUTUBE_WORKER}/api/channel`); + if (!response.ok) throw new Error('Failed to fetch channel data'); + return await response.json(); + } catch (error) { + console.error('YouTube Worker Error:', error); + return null; + } +} + +async function loadVideos() { + if (DOM.loading) DOM.loading.style.display = 'block'; + + // Check cache first + const cached = Utils.getLS(CONFIG.STORAGE.CACHE_KEY); + if (cached && cached.data && cached.data.length && Date.now() - cached.time < CONFIG.STORAGE.CACHE_EXPIRY) { + if (DOM.loading) DOM.loading.style.display = 'none'; + return cached.data; + } + + try { + // Use the correct worker endpoint: /api/videos + const channelId = Utils.getLS(CONFIG.STORAGE.CHANNEL_KEY); + let url = `${CONFIG.API.YOUTUBE_WORKER}/api/videos`; + if (channelId) { + url += `?channelId=${channelId}`; + } + + const response = await Utils.fetchWithRetry(url); + const data = await response.json(); + console.log('API Fetch Success:', data); + + const videos = (data.videos || []).map(video => ({ + id: video.id || video.videoId, + title: video.title || 'Untitled', + thumbnail: video.thumbnail || `https://i.ytimg.com/vi/${video.id}/hqdefault.jpg`, + publishedAt: video.publishedAt || new Date().toISOString(), + category: detectCategory(video.title), + description: video.description || 'Deep dive into Islamic history and theology.' + })); + + console.log('Processed Videos:', videos.length); + + if (videos.length) { + Utils.saveLS(CONFIG.STORAGE.CACHE_KEY, { data: videos, time: Date.now() }); + } + + if (DOM.loading) DOM.loading.style.display = 'none'; + return videos; + + } catch (error) { + if (DOM.loading) DOM.loading.style.display = 'none'; + console.error('Worker fetch failed, using fallback:', error); + try { + const fallback = await fetch(CONFIG.API.FALLBACK_DATA); + if (!fallback.ok) throw new Error(`Fallback HTTP error: ${fallback.status}`); + const data = await fallback.json(); + console.log('Fallback Data Loaded:', data); + + document.body.classList.add('demo-mode'); + const videos = (data.videos || []).map(video => ({ + ...video, + category: video.category || 'history', + description: video.description || 'Deep dive into Islamic history and theology.' + })); + console.log('Processed Fallback Videos:', videos.length); + return videos; + } catch (fallbackError) { + console.error('Fallback fetch also failed:', fallbackError); + return []; + } + } +} + +// ============================================ +// RENDER FUNCTIONS +// ============================================ +function renderCard(video, index = 0) { + const isSaved = AppState.watchLater.some(v => v.id === video.id); + const thumbnail = video.thumbnail || `https://i.ytimg.com/vi/${video.id}/hqdefault.jpg`; + const progress = getProgress(video.id); + + return ` +Try different keywords or browse by category to find what you're looking for.
+No saved episodes.
')}}function updateStats(){DOM.statTotal&&(DOM.statTotal.textContent=AppState.videos.length),DOM.statSaved&&(DOM.statSaved.textContent=AppState.watchLater.length),DOM.statProgress&&(DOM.statProgress.textContent=Object.keys(AppState.progress).length),DOM.watchLaterCount&&(DOM.watchLaterCount.textContent=AppState.watchLater.length),DOM.watchLaterBadge&&DOM.watchLaterBadge.setAttribute("aria-label",`Open watch later list (${AppState.watchLater.length} episodes)`)}function openVideo(e){var t;DOM.modal&&DOM.player&&(t=getProgress((AppState.current=e).id)?.time||0,DOM.player.src=`https://www.youtube.com/embed/${e.id}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1&start=`+Math.floor(t),DOM.modal.style.display="flex",DOM.modal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.modal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"),(t=document.getElementById("video-title"))&&(t.textContent=e.title),DOM.transcriptPanel&&DOM.transcriptPanel.setAttribute("aria-hidden","true"),DOM.sharePanel)&&DOM.sharePanel.setAttribute("aria-hidden","true")}function closeVideo(){DOM.modal&&DOM.player&&(DOM.player.src="",DOM.modal.style.display="none",DOM.modal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.current=null,clearInterval(AppState.progressTimer),renderContinueWatching(),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function navigateVideo(t){if(AppState.current&&AppState.filtered.length){var a=AppState.filtered.findIndex(e=>e.id===AppState.current.id);if(-1!==a){let e=a+t;(e=e<0?AppState.filtered.length-1:e)>=AppState.filtered.length&&(e=0),openVideo(AppState.filtered[e])}}}function toggleWatchLater(t){var e=AppState.watchLater.findIndex(e=>e.id===t.id);-1===e?(AppState.watchLater.push(t),Utils.showToast("Added to Watch Later")):(AppState.watchLater.splice(e,1),Utils.showToast("Removed from Watch Later")),Utils.saveLS(CONFIG.STORAGE.WATCH_LATER_KEY,AppState.watchLater),updateStats(),AppState.hero&&AppState.hero.id===t.id&&renderHero(AppState.hero),renderGrid(),DOM.watchLaterContainer&&renderWatchLater()}function openWatchLater(){DOM.watchLaterPage&&(renderWatchLater(),DOM.watchLaterPage.style.display="block",DOM.watchLaterPage.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.watchLaterPage),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeWatchLater(){DOM.watchLaterPage&&(DOM.watchLaterPage.style.display="none",DOM.watchLaterPage.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}let initAnalyticsChart=async()=>{var e=document.getElementById("analyticsChart");e&&(window.Chart||await new Promise(e=>{var t=document.createElement("script");t.src="https://cdn.jsdelivr.net/npm/chart.js",t.onload=e,document.head.appendChild(t)}),e=e.getContext("2d"),window.myChart&&window.myChart.destroy(),window.myChart=new Chart(e,{type:"line",data:{labels:["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],datasets:[{label:"Subscriber Growth",data:[12,19,3,5,2,3,9],borderColor:"#e50914",backgroundColor:"rgba(229, 9, 20, 0.1)",fill:!0,tension:.4}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{y:{display:!1},x:{grid:{display:!1},ticks:{color:"#808080",font:{size:10}}}}}}))},initAIAssistant=()=>{var e;if(isFeatureEnabled("AI_ASSISTANT")){let s=document.getElementById("ai-score-title"),a=document.getElementById("ai-generate-hook"),o=document.getElementById("ai-title-input");s&&s.addEventListener("click",()=>{o.value.trim()?(s.disabled=!0,s.innerHTML='',setTimeout(()=>{var e=Math.floor(30*Math.random()+65),t=document.getElementById("ai-score-result"),a=document.getElementById("ai-score-value"),o=document.getElementById("ai-score-bar");t.classList.remove("hidden"),a.textContent=e+"/100",o.style.width=e+"%",s.disabled=!1,s.textContent="Score",Utils.showToast("Title analyzed!","success")},1e3)):Utils.showToast("Please enter a title","warning")}),a&&a.addEventListener("click",()=>{a.disabled=!0,a.innerHTML=' Analyzing...';let t=["What if everything you knew about the fall of Andalusia was wrong?","Behind the silence of history lies a truth far more cinematic than fiction.","The year was 1492. The world was changing. And at the center of it all?","How did one decision change the course of human history forever?"];setTimeout(()=>{var e=document.getElementById("ai-hook-result");e.classList.remove("hidden"),e.textContent=t[Math.floor(Math.random()*t.length)],a.disabled=!1,a.innerHTML=' Generate Viral Hook',Utils.showToast("Hook generated!","success")},1200)}),document.querySelectorAll("#ai-topics span").forEach(t=>{t.addEventListener("click",()=>{var e=t.textContent;o&&(o.value=e,Utils.showToast("Selected: "+e,"info"))}),t.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),t.click())})})}else(e=document.getElementById("ai-assistant-panel"))&&(e.style.display="none")};function openDashboard(){DOM.dashboardModal&&(renderDashboard(),initAnalyticsChart(),initAIAssistant(),DOM.dashboardModal.style.display="block",DOM.dashboardModal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.dashboardModal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeDashboard(){DOM.dashboardModal&&(DOM.dashboardModal.style.display="none",DOM.dashboardModal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function setTheme(t){AppState.theme=t,Utils.saveLS(CONFIG.STORAGE.THEME_KEY,t),DOM.body.classList.remove("light-mode","theme-neon"),"light"===t?DOM.body.classList.add("light-mode"):"neon"===t&&DOM.body.classList.add("theme-neon");var e=DOM.themeToggle?.querySelector("i");e&&(e.className="dark"===t?"fa-regular fa-moon":"neon"===t?"fa-solid fa-bolt":"fa-regular fa-sun"),document.querySelectorAll(".theme-opt").forEach(e=>{e.classList.toggle("active",e.dataset.theme===t)})}function toggleTheme(){setTheme("dark"===AppState.theme?"light":"dark")}function switchMode(e){"creator"===e?(DOM.studioRoot&&(DOM.studioRoot.style.display="block"),DOM.appRoot&&(DOM.appRoot.style.display="none"),DOM.heroSection&&(DOM.heroSection.style.display="none"),DOM.continueBlockSec&&(DOM.continueBlockSec.style.display="none"),updateBreadcrumbs("Studio > Projects")):(DOM.studioRoot&&(DOM.studioRoot.style.display="none"),DOM.appRoot&&(DOM.appRoot.style.display="block"),DOM.heroSection&&(DOM.heroSection.style.display="block"),DOM.continueBlockSec&&AppState.videos.some(e=>getProgress(e.id))&&(DOM.continueBlockSec.style.display="block"))}function updateBreadcrumbs(e){if(DOM.studioBreadcrumbs){let a=e.split(" > ");DOM.studioBreadcrumbs.innerHTML=a.map((e,t)=>t===a.length-1?`${e}`:`${e}`).join(' ')}}function renderProjects(){var e=document.getElementById("studioProjectsList");if(e){let o=Utils.getLS(CONFIG.STORAGE.PROJECTS_KEY,[{id:"p1",title:"The Fall of the Abbasids",status:"Writing",progress:65,date:"2024-05-10"},{id:"p2",title:"Prophecy & Modernity",status:"Researching",progress:30,date:"2024-05-12"},{id:"p3",title:"The Silent Silk Road",status:"Editing",progress:90,date:"2024-05-08"},{id:"p4",title:"The Golden Age",status:"Published",progress:100,date:"2024-05-01"}]);"list"===AppState.currentView?(e.className="studio-projects-grid",e.innerHTML=o.map(e=>` + `).join('') + : 'No saved episodes.
'; + } +} + +function updateStats() { diff --git a/js/app.js.part2 b/js/app.js.part2 new file mode 100644 index 0000000..a95b818 --- /dev/null +++ b/js/app.js.part2 @@ -0,0 +1,1104 @@ +function updateStats() { + if (DOM.statTotal) DOM.statTotal.textContent = AppState.videos.length; + if (DOM.statSaved) DOM.statSaved.textContent = AppState.watchLater.length; + if (DOM.statProgress) DOM.statProgress.textContent = Object.keys(AppState.progress).length; + if (DOM.watchLaterCount) DOM.watchLaterCount.textContent = AppState.watchLater.length; + if (DOM.watchLaterBadge) { + DOM.watchLaterBadge.setAttribute('aria-label', `Open watch later list (${AppState.watchLater.length} episodes)`); + } +} + +// ============================================ +// VIDEO PLAYER FUNCTIONS +// ============================================ +function openVideo(video) { + if (!DOM.modal || !DOM.player) return; + + AppState.current = video; + const progress = getProgress(video.id); + const startTime = progress?.time || 0; + + DOM.player.src = `https://www.youtube.com/embed/${video.id}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1&start=${Math.floor(startTime)}`; + + DOM.modal.style.display = 'flex'; + DOM.modal.setAttribute('aria-hidden', 'false'); + Utils.trapFocus(DOM.modal); + DOM.body.style.overflow = 'hidden'; + DOM.body.classList.add('modal-open'); + + const titleEl = document.getElementById('video-title'); + if (titleEl) titleEl.textContent = video.title; + + if (DOM.transcriptPanel) DOM.transcriptPanel.setAttribute('aria-hidden', 'true'); + if (DOM.sharePanel) DOM.sharePanel.setAttribute('aria-hidden', 'true'); + + // Update modal save button state + if (DOM.modalSaveBtn) { + const isSaved = AppState.watchLater.some(v => v.id === video.id); + DOM.modalSaveBtn.classList.toggle('active', isSaved); + const icon = DOM.modalSaveBtn.querySelector('i'); + if (icon) { + icon.className = isSaved ? 'fas fa-bookmark' : 'far fa-bookmark'; + } + DOM.modalSaveBtn.setAttribute('aria-label', isSaved ? 'Remove from Watch Later' : 'Save for later'); + } +} + +function closeVideo() { + if (!DOM.modal || !DOM.player) return; + DOM.player.src = ""; + DOM.modal.style.display = "none"; + DOM.modal.setAttribute("aria-hidden", "true"); + DOM.body.style.overflow = ""; + DOM.body.classList.remove("modal-open"); + AppState.current = null; + clearInterval(AppState.progressTimer); + renderContinueWatching(); + if (AppState.lastFocused) { AppState.lastFocused.focus(); AppState.lastFocused = null; } +} + +function navigateVideo(direction) { + if (!AppState.current || !AppState.filtered.length) return; + + const currentIndex = AppState.filtered.findIndex(v => v.id === AppState.current.id); + if (currentIndex === -1) return; + + let newIndex = currentIndex + direction; + if (newIndex < 0) newIndex = AppState.filtered.length - 1; + if (newIndex >= AppState.filtered.length) newIndex = 0; + + openVideo(AppState.filtered[newIndex]); +} + +// ============================================ +// WATCH LATER FUNCTIONS +// ============================================ +function toggleWatchLater(video) { + const index = AppState.watchLater.findIndex(v => v.id === video.id); + + if (index === -1) { + AppState.watchLater.push(video); + Utils.showToast('Added to Watch Later'); + } else { + AppState.watchLater.splice(index, 1); + Utils.showToast('Removed from Watch Later'); + } + + Utils.saveLS(CONFIG.STORAGE.WATCH_LATER_KEY, AppState.watchLater); + updateStats(); + + // Re-render affected components + if (AppState.hero && AppState.hero.id === video.id) renderHero(AppState.hero); + renderGrid(); + if (DOM.watchLaterContainer) renderWatchLater(); + + // Sync modal save button if open + if (DOM.modalSaveBtn && AppState.current && AppState.current.id === video.id) { + const isSaved = AppState.watchLater.some(v => v.id === video.id); + DOM.modalSaveBtn.classList.toggle('active', isSaved); + const icon = DOM.modalSaveBtn.querySelector('i'); + if (icon) { + icon.className = isSaved ? 'fas fa-bookmark' : 'far fa-bookmark'; + } + DOM.modalSaveBtn.setAttribute('aria-label', isSaved ? 'Remove from Watch Later' : 'Save for later'); + } +} + +function openWatchLater() { + if (!DOM.watchLaterPage) return; + renderWatchLater(); + DOM.watchLaterPage.style.display = 'block'; + DOM.watchLaterPage.setAttribute('aria-hidden', 'false'); + if (DOM.watchLaterBtn) DOM.watchLaterBtn.setAttribute('aria-expanded', 'true'); + Utils.trapFocus(DOM.watchLaterPage); + DOM.body.style.overflow = 'hidden'; + DOM.body.classList.add('modal-open'); +} + +function closeWatchLater() { + if (!DOM.watchLaterPage) return; + DOM.watchLaterPage.style.display = "none"; + DOM.watchLaterPage.setAttribute("aria-hidden", "true"); + if (DOM.watchLaterBtn) DOM.watchLaterBtn.setAttribute('aria-expanded', 'false'); + DOM.body.style.overflow = ""; + DOM.body.classList.remove("modal-open"); + if (AppState.lastFocused) { AppState.lastFocused.focus(); AppState.lastFocused = null; } +} + +// ============================================ +// DASHBOARD FUNCTIONS +// ============================================ +const initAnalyticsChart = async () => { + const canvas = document.getElementById('analyticsChart'); + if (!canvas) return; + + if (!window.Chart) { + await new Promise((resolve) => { + const script = document.createElement('script'); + script.src = 'https://cdn.jsdelivr.net/npm/chart.js'; + script.onload = resolve; + document.head.appendChild(script); + }); + } + + const ctx = canvas.getContext('2d'); + if (window.myChart) window.myChart.destroy(); + + window.myChart = new Chart(ctx, { + type: 'line', + data: { + labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], + datasets: [{ + label: 'Subscriber Growth', + data: [12, 19, 3, 5, 2, 3, 9], + borderColor: '#e50914', + backgroundColor: 'rgba(229, 9, 20, 0.1)', + fill: true, + tension: 0.4 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + plugins: { legend: { display: false } }, + scales: { + y: { display: false }, + x: { grid: { display: false }, ticks: { color: '#808080', font: { size: 10 } } } + } + } + }); +}; + +const initAIAssistant = () => { + if (!isFeatureEnabled('AI_ASSISTANT')) { + const aiPanel = document.getElementById('ai-assistant-panel'); + if (aiPanel) aiPanel.style.display = 'none'; + return; + } + const scoreBtn = document.getElementById('ai-score-title'); + const hookBtn = document.getElementById('ai-generate-hook'); + const titleInput = document.getElementById('ai-title-input'); + + if (scoreBtn) { + scoreBtn.addEventListener('click', () => { + const title = titleInput.value.trim(); + if (!title) { + Utils.showToast('Please enter a title', 'warning'); + return; + } + + scoreBtn.disabled = true; + scoreBtn.innerHTML = ''; + + setTimeout(() => { + const score = Math.floor(Math.random() * (95 - 65) + 65); + const result = document.getElementById('ai-score-result'); + const scoreVal = document.getElementById('ai-score-value'); + const scoreBar = document.getElementById('ai-score-bar'); + + result.classList.remove('hidden'); + scoreVal.textContent = `${score}/100`; + scoreBar.style.width = `${score}%`; + + scoreBtn.disabled = false; + scoreBtn.textContent = 'Score'; + Utils.showToast('Title analyzed!', 'success'); + }, 1000); + }); + } + + if (hookBtn) { + hookBtn.addEventListener('click', () => { + hookBtn.disabled = true; + hookBtn.innerHTML = ' Analyzing...'; + + const hooks = [ + "What if everything you knew about the fall of Andalusia was wrong?", + "Behind the silence of history lies a truth far more cinematic than fiction.", + "The year was 1492. The world was changing. And at the center of it all?", + "How did one decision change the course of human history forever?" + ]; + + setTimeout(() => { + const result = document.getElementById('ai-hook-result'); + result.classList.remove('hidden'); + result.textContent = hooks[Math.floor(Math.random() * hooks.length)]; + + hookBtn.disabled = false; + hookBtn.innerHTML = ' Generate Viral Hook'; + Utils.showToast('Hook generated!', 'success'); + }, 1200); + }); + } + + const topicChips = document.querySelectorAll('#ai-topics span'); + topicChips.forEach(chip => { + chip.addEventListener('click', () => { + const topic = chip.textContent; + if (titleInput) { + titleInput.value = topic; + Utils.showToast(`Selected: ${topic}`, 'info'); + } + }); + chip.addEventListener("keydown", (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); chip.click(); } }); + }); +}; + +function openDashboard() { + if (!DOM.dashboardModal) return; + renderDashboard(); + initAnalyticsChart(); + initAIAssistant(); + DOM.dashboardModal.style.display = 'block'; + DOM.dashboardModal.setAttribute('aria-hidden', 'false'); + Utils.trapFocus(DOM.dashboardModal); + DOM.body.style.overflow = 'hidden'; + DOM.body.classList.add('modal-open'); +} + +function closeDashboard() { + if (!DOM.dashboardModal) return; + DOM.dashboardModal.style.display = "none"; + DOM.dashboardModal.setAttribute("aria-hidden", "true"); + DOM.body.style.overflow = ""; + DOM.body.classList.remove("modal-open"); + if (AppState.lastFocused) { AppState.lastFocused.focus(); AppState.lastFocused = null; } +} + +// ============================================ +// THEME FUNCTIONS +// ============================================ +function setTheme(theme) { + AppState.theme = theme; + Utils.saveLS(CONFIG.STORAGE.THEME_KEY, theme); + + DOM.body.classList.remove('light-mode', 'theme-neon'); + if (theme === 'light') { + DOM.body.classList.add('light-mode'); + } else if (theme === 'neon') { + DOM.body.classList.add('theme-neon'); + } + + const icon = DOM.themeToggle?.querySelector('i'); + if (icon) { + icon.className = theme === 'dark' ? 'fa-regular fa-moon' : theme === 'neon' ? 'fa-solid fa-bolt' : 'fa-regular fa-sun'; + } + + document.querySelectorAll('.theme-opt').forEach(opt => { + opt.classList.toggle('active', opt.dataset.theme === theme); + }); +} + +function toggleTheme() { + const newTheme = AppState.theme === 'dark' ? 'light' : 'dark'; + setTheme(newTheme); +} + +// ============================================ +// STUDIO MODE & PROJECTS +// ============================================ +function switchMode(mode) { + if (mode === 'creator') { + if (DOM.studioRoot) DOM.studioRoot.style.display = 'block'; + if (DOM.appRoot) DOM.appRoot.style.display = 'none'; + if (DOM.heroSection) DOM.heroSection.style.display = 'none'; + if (DOM.continueBlockSec) DOM.continueBlockSec.style.display = 'none'; + updateBreadcrumbs('Studio > Projects'); + } else { + if (DOM.studioRoot) DOM.studioRoot.style.display = 'none'; + if (DOM.appRoot) DOM.appRoot.style.display = 'block'; + if (DOM.heroSection) DOM.heroSection.style.display = 'block'; + if (DOM.continueBlockSec && AppState.videos.some(v => getProgress(v.id))) { + DOM.continueBlockSec.style.display = 'block'; + } + } +} + +function updateBreadcrumbs(path) { + if (!DOM.studioBreadcrumbs) return; + + const parts = path.split(' > '); + DOM.studioBreadcrumbs.innerHTML = parts.map((part, i) => + i === parts.length - 1 + ? `${part}` + : `${part}` + ).join(' '); +} + +function renderProjects() { + const projectsList = document.getElementById('studioProjectsList'); + if (!projectsList) return; + + const projects = Utils.getLS(CONFIG.STORAGE.PROJECTS_KEY, [ + { id: 'p1', title: 'The Fall of the Abbasids', status: 'Writing', progress: 65, date: '2024-05-10' }, + { id: 'p2', title: 'Prophecy & Modernity', status: 'Researching', progress: 30, date: '2024-05-12' }, + { id: 'p3', title: 'The Silent Silk Road', status: 'Editing', progress: 90, date: '2024-05-08' }, + { id: 'p4', title: 'The Golden Age', status: 'Published', progress: 100, date: '2024-05-01' } + ]); + + if (AppState.currentView === 'list') { + projectsList.className = 'studio-projects-grid'; + projectsList.innerHTML = projects.map(project => ` +Try different keywords or browse by category to find what you're looking for.
+No saved episodes.
')}}function updateStats(){DOM.statTotal&&(DOM.statTotal.textContent=AppState.videos.length),DOM.statSaved&&(DOM.statSaved.textContent=AppState.watchLater.length),DOM.statProgress&&(DOM.statProgress.textContent=Object.keys(AppState.progress).length),DOM.watchLaterCount&&(DOM.watchLaterCount.textContent=AppState.watchLater.length),DOM.watchLaterBadge&&DOM.watchLaterBadge.setAttribute("aria-label",`Open watch later list (${AppState.watchLater.length} episodes)`)}function openVideo(e){var t;DOM.modal&&DOM.player&&(t=getProgress((AppState.current=e).id)?.time||0,DOM.player.src=`https://www.youtube.com/embed/${e.id}?autoplay=1&rel=0&modestbranding=1&enablejsapi=1&start=`+Math.floor(t),DOM.modal.style.display="flex",DOM.modal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.modal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"),(t=document.getElementById("video-title"))&&(t.textContent=e.title),DOM.transcriptPanel&&DOM.transcriptPanel.setAttribute("aria-hidden","true"),DOM.sharePanel)&&DOM.sharePanel.setAttribute("aria-hidden","true")}function closeVideo(){DOM.modal&&DOM.player&&(DOM.player.src="",DOM.modal.style.display="none",DOM.modal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.current=null,clearInterval(AppState.progressTimer),renderContinueWatching(),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function navigateVideo(t){if(AppState.current&&AppState.filtered.length){var a=AppState.filtered.findIndex(e=>e.id===AppState.current.id);if(-1!==a){let e=a+t;(e=e<0?AppState.filtered.length-1:e)>=AppState.filtered.length&&(e=0),openVideo(AppState.filtered[e])}}}function toggleWatchLater(t){var e=AppState.watchLater.findIndex(e=>e.id===t.id);-1===e?(AppState.watchLater.push(t),Utils.showToast("Added to Watch Later")):(AppState.watchLater.splice(e,1),Utils.showToast("Removed from Watch Later")),Utils.saveLS(CONFIG.STORAGE.WATCH_LATER_KEY,AppState.watchLater),updateStats(),AppState.hero&&AppState.hero.id===t.id&&renderHero(AppState.hero),renderGrid(),DOM.watchLaterContainer&&renderWatchLater()}function openWatchLater(){DOM.watchLaterPage&&(renderWatchLater(),DOM.watchLaterPage.style.display="block",DOM.watchLaterPage.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.watchLaterPage),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeWatchLater(){DOM.watchLaterPage&&(DOM.watchLaterPage.style.display="none",DOM.watchLaterPage.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}let initAnalyticsChart=async()=>{var e=document.getElementById("analyticsChart");e&&(window.Chart||await new Promise(e=>{var t=document.createElement("script");t.src="https://cdn.jsdelivr.net/npm/chart.js",t.onload=e,document.head.appendChild(t)}),e=e.getContext("2d"),window.myChart&&window.myChart.destroy(),window.myChart=new Chart(e,{type:"line",data:{labels:["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],datasets:[{label:"Subscriber Growth",data:[12,19,3,5,2,3,9],borderColor:"#e50914",backgroundColor:"rgba(229, 9, 20, 0.1)",fill:!0,tension:.4}]},options:{responsive:!0,maintainAspectRatio:!1,plugins:{legend:{display:!1}},scales:{y:{display:!1},x:{grid:{display:!1},ticks:{color:"#808080",font:{size:10}}}}}}))},initAIAssistant=()=>{var e;if(isFeatureEnabled("AI_ASSISTANT")){let s=document.getElementById("ai-score-title"),a=document.getElementById("ai-generate-hook"),o=document.getElementById("ai-title-input");s&&s.addEventListener("click",()=>{o.value.trim()?(s.disabled=!0,s.innerHTML='',setTimeout(()=>{var e=Math.floor(30*Math.random()+65),t=document.getElementById("ai-score-result"),a=document.getElementById("ai-score-value"),o=document.getElementById("ai-score-bar");t.classList.remove("hidden"),a.textContent=e+"/100",o.style.width=e+"%",s.disabled=!1,s.textContent="Score",Utils.showToast("Title analyzed!","success")},1e3)):Utils.showToast("Please enter a title","warning")}),a&&a.addEventListener("click",()=>{a.disabled=!0,a.innerHTML=' Analyzing...';let t=["What if everything you knew about the fall of Andalusia was wrong?","Behind the silence of history lies a truth far more cinematic than fiction.","The year was 1492. The world was changing. And at the center of it all?","How did one decision change the course of human history forever?"];setTimeout(()=>{var e=document.getElementById("ai-hook-result");e.classList.remove("hidden"),e.textContent=t[Math.floor(Math.random()*t.length)],a.disabled=!1,a.innerHTML=' Generate Viral Hook',Utils.showToast("Hook generated!","success")},1200)}),document.querySelectorAll("#ai-topics span").forEach(t=>{t.addEventListener("click",()=>{var e=t.textContent;o&&(o.value=e,Utils.showToast("Selected: "+e,"info"))}),t.addEventListener("keydown",e=>{"Enter"!==e.key&&" "!==e.key||(e.preventDefault(),t.click())})})}else(e=document.getElementById("ai-assistant-panel"))&&(e.style.display="none")};function openDashboard(){DOM.dashboardModal&&(renderDashboard(),initAnalyticsChart(),initAIAssistant(),DOM.dashboardModal.style.display="block",DOM.dashboardModal.setAttribute("aria-hidden","false"),Utils.trapFocus(DOM.dashboardModal),DOM.body.style.overflow="hidden",DOM.body.classList.add("modal-open"))}function closeDashboard(){DOM.dashboardModal&&(DOM.dashboardModal.style.display="none",DOM.dashboardModal.setAttribute("aria-hidden","true"),DOM.body.style.overflow="",DOM.body.classList.remove("modal-open"),AppState.lastFocused)&&(AppState.lastFocused.focus(),AppState.lastFocused=null)}function setTheme(t){AppState.theme=t,Utils.saveLS(CONFIG.STORAGE.THEME_KEY,t),DOM.body.classList.remove("light-mode","theme-neon"),"light"===t?DOM.body.classList.add("light-mode"):"neon"===t&&DOM.body.classList.add("theme-neon");var e=DOM.themeToggle?.querySelector("i");e&&(e.className="dark"===t?"fa-regular fa-moon":"neon"===t?"fa-solid fa-bolt":"fa-regular fa-sun"),document.querySelectorAll(".theme-opt").forEach(e=>{e.classList.toggle("active",e.dataset.theme===t)})}function toggleTheme(){setTheme("dark"===AppState.theme?"light":"dark")}function switchMode(e){"creator"===e?(DOM.studioRoot&&(DOM.studioRoot.style.display="block"),DOM.appRoot&&(DOM.appRoot.style.display="none"),DOM.heroSection&&(DOM.heroSection.style.display="none"),DOM.continueBlockSec&&(DOM.continueBlockSec.style.display="none"),updateBreadcrumbs("Studio > Projects")):(DOM.studioRoot&&(DOM.studioRoot.style.display="none"),DOM.appRoot&&(DOM.appRoot.style.display="block"),DOM.heroSection&&(DOM.heroSection.style.display="block"),DOM.continueBlockSec&&AppState.videos.some(e=>getProgress(e.id))&&(DOM.continueBlockSec.style.display="block"))}function updateBreadcrumbs(e){if(DOM.studioBreadcrumbs){let a=e.split(" > ");DOM.studioBreadcrumbs.innerHTML=a.map((e,t)=>t===a.length-1?`${e}`:`${e}`).join(' ')}}function renderProjects(){var e=document.getElementById("studioProjectsList");if(e){let o=Utils.getLS(CONFIG.STORAGE.PROJECTS_KEY,[{id:"p1",title:"The Fall of the Abbasids",status:"Writing",progress:65,date:"2024-05-10"},{id:"p2",title:"Prophecy & Modernity",status:"Researching",progress:30,date:"2024-05-12"},{id:"p3",title:"The Silent Silk Road",status:"Editing",progress:90,date:"2024-05-08"},{id:"p4",title:"The Golden Age",status:"Published",progress:100,date:"2024-05-01"}]);"list"===AppState.currentView?(e.className="studio-projects-grid",e.innerHTML=o.map(e=>` + `).join('') + : 'No saved episodes.
'; + } +} + +function updateStats() { if (DOM.statTotal) DOM.statTotal.textContent = AppState.videos.length; if (DOM.statSaved) DOM.statSaved.textContent = AppState.watchLater.length; if (DOM.statProgress) DOM.statProgress.textContent = Object.keys(AppState.progress).length; @@ -953,11 +1329,13 @@ function bindEvents() { // Theme toggle if (key === 't') { + e.preventDefault(); toggleTheme(); } // Watch Later toggle if (key === 'b') { + e.preventDefault(); if (AppState.current) { toggleWatchLater(AppState.current); } else if (DOM.watchLaterPage) { @@ -968,6 +1346,16 @@ function bindEvents() { } } } + + // Dashboard toggle + if (key === 'd' && DOM.dashboardModal) { + e.preventDefault(); + if (DOM.dashboardModal.style.display === 'block') { + closeDashboard(); + } else { + openDashboard(); + } + } }); // Mouse move effect for cards From 756a21a8a35b9eb224281c6e2f12f5058686967a Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 14 Jun 2026 04:03:25 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Add=20Dashboard?= =?UTF-8?q?=20keyboard=20shortcut=20and=20UI=20hints?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Implement 'D' keyboard shortcut to toggle Dashboard panel. - Update #dashboardBtn title with '(D)' hint for discoverability. - Add e.preventDefault() to 'T', 'B', and 'D' shortcuts to prevent default browser actions. - Ensure 'B' shortcut is context-aware (toggles save state when modal is open). Co-authored-by: ruhdevops <203426218+ruhdevops@users.noreply.github.com>