export function initCockpit({ storage, toast }) { const $ = s => document.querySelector(s), audio = $('#audio'); const dedicated = new URLSearchParams(location.search).get('display') === 'drive'; let collapsed = dedicated || storage.get('railCollapsed') === 'true'; function rail() { document.body.classList.toggle('rail-collapsed', collapsed); $('#rail-toggle').setAttribute('aria-expanded', String(!collapsed)); $('#rail-toggle').setAttribute('aria-label', collapsed ? 'Afficher les favoris' : 'Masquer les favoris'); document.dispatchEvent(new Event('caria-layout')); } $('#rail-toggle').onclick = () => { collapsed = !collapsed; storage.set('railCollapsed', String(collapsed)); rail(); }; document.querySelectorAll('.screen-links a').forEach(link => link.onclick = () => $('#settings').close()); rail(); if (dedicated && !location.hash) location.hash = 'drive'; const time = seconds => Number.isFinite(seconds) ? `${Math.floor(seconds / 60)}:${String(Math.floor(seconds % 60)).padStart(2,'0')}` : '0:00'; function updatePlayer() { const duration = Number.isFinite(audio.duration) ? audio.duration : 0, progress = duration ? audio.currentTime / duration : 0; $('#track-arc').style.strokeDashoffset = String(100 * (1 - progress)); $('#immersive-current').textContent = time(audio.currentTime); $('#immersive-duration').textContent = time(duration); $('#immersive-title').textContent = $('#track-title').textContent; $('#immersive-subtitle').textContent = $('#track-subtitle').textContent; $('#immersive-play').innerHTML = audio.paused ? '' : ''; $('#immersive-play').setAttribute('aria-label', audio.paused ? 'Lire la musique' : 'Mettre en pause'); $('#now-playing').classList.toggle('playing', !audio.paused); $('#immersive-volume').value = audio.muted ? '0' : String(audio.volume); const silent = audio.muted || audio.volume === 0; $('#immersive-mute').innerHTML = silent ? '' : ''; $('#immersive-mute').setAttribute('aria-label', silent ? 'Activer le son' : 'Couper le son'); $('#immersive-mute').title = silent ? 'Activer le son' : 'Couper le son'; $('#immersive-mute').setAttribute('aria-pressed', String(silent)); $('#immersive-volume-value').textContent = Math.round((silent ? 0 : audio.volume) * 100) + ' %'; document.querySelectorAll('[data-audio-control]').forEach(button => button.disabled = !audio.getAttribute('src')); } $('#immersive-play').onclick = () => $('#play').click(); $('#immersive-previous').onclick = () => $('#previous').click(); $('#immersive-next').onclick = () => $('#next').click(); $('#immersive-volume').oninput = e => { audio.volume = Number(e.target.value); audio.muted = false; }; $('#immersive-mute').onclick = () => { if (audio.volume === 0) { audio.volume = .5; audio.muted = false; } else audio.muted = !audio.muted; }; $('#immersive-repeat').onclick = () => { audio.loop = !audio.loop; $('#immersive-repeat').setAttribute('aria-pressed', String(audio.loop)); toast(audio.loop ? 'Répétition du morceau activée' : 'Répétition désactivée'); }; for (const event of ['timeupdate','loadedmetadata','durationchange','play','pause','volumechange','emptied','ended']) audio.addEventListener(event,updatePlayer); new MutationObserver(updatePlayer).observe($('#track-title'),{childList:true,subtree:true}); audio.addEventListener('error', () => $('#immersive-subtitle').textContent = 'Lecture impossible. Choisissez un autre morceau dans la bibliothèque.'); updatePlayer(); // Keep lengthy setup panels reachable with large page buttons. for (const panel of document.querySelectorAll('.journey-panel')) { const content = document.createElement('div'), nav = document.createElement('nav'); content.className = 'panel-content'; nav.className = 'collection-pages panel-pages'; content.append(...panel.childNodes); panel.append(content,nav); const back = document.createElement('button'), next = document.createElement('button'), label = document.createElement('span'); back.type = next.type = 'button'; back.textContent = '‹'; next.textContent = '›'; back.setAttribute('aria-label','Page précédente du panneau'); next.setAttribute('aria-label','Page suivante du panneau'); nav.append(back,label,next); function refresh() { if (!panel.getClientRects().length) return; const max = content.scrollHeight - content.clientHeight; nav.hidden = max <= 2; back.disabled = content.scrollTop < 2; next.disabled = content.scrollTop >= max - 2; label.textContent = max <= 2 ? '' : `${Math.floor(content.scrollTop / Math.max(1,content.clientHeight - 60)) + 1} / ${Math.ceil(max / Math.max(1,content.clientHeight - 60)) + 1}`; } back.onclick = () => { content.scrollTop -= Math.max(44,content.clientHeight - 60); refresh(); }; next.onclick = () => { content.scrollTop += Math.max(44,content.clientHeight - 60); refresh(); }; new ResizeObserver(refresh).observe(content); new MutationObserver(refresh).observe(content,{childList:true,subtree:true,attributes:true,attributeFilter:['open','hidden']}); new MutationObserver(refresh).observe(panel,{attributes:true,attributeFilter:['open']}); content.addEventListener('scroll',refresh); document.addEventListener('caria-view',refresh); } // Paginate collections rather than shrinking touch targets or scrolling the screen. for (const selector of ['#playlist', '.detail-grid', '.home-grid']) { const container = $(selector), pager = document.createElement('nav'); pager.className = 'collection-pages'; pager.setAttribute('aria-label', 'Pages de contenu'); const back = document.createElement('button'), next = document.createElement('button'), label = document.createElement('span'); back.textContent = '‹'; next.textContent = '›'; back.setAttribute('aria-label', 'Page précédente'); next.setAttribute('aria-label', 'Page suivante'); pager.append(back,label,next); container.after(pager); let page = 0; function layout() { if (!container.getClientRects().length) return; const items = [...container.children], small = innerWidth < 760; let count = items.length || 1; if (selector === '#playlist') count = Math.max(1, Math.floor(container.clientHeight / 58)); if (selector === '.detail-grid') count = small || innerHeight < 750 ? 4 : 8; if (selector === '.home-grid') count = small ? 2 : 4; const pages = Math.max(1,Math.ceil(items.length / count)); page = Math.min(page,pages - 1); items.forEach((item,i) => item.hidden = i < page * count || i >= (page + 1) * count); pager.hidden = pages === 1; label.textContent = `${page + 1} / ${pages}`; back.disabled = page === 0; next.disabled = page === pages - 1; } back.onclick = () => { page--; layout(); }; next.onclick = () => { page++; layout(); }; new ResizeObserver(layout).observe(container); new MutationObserver(() => { page = 0; layout(); }).observe(container,{childList:true}); document.addEventListener('caria-view',layout); layout(); } }