From 9095d27ffbba3173ae48eb77f6eebe0d7fc89167 Mon Sep 17 00:00:00 2001 From: Aaron <44r0n7+Claude@pm.me> Date: Sat, 23 May 2026 11:50:46 -0400 Subject: [PATCH] Fix ticker clipping, remove gear icon, add ENDED fallback, persist shuffle, add API key guide modal - Remove overflow:hidden from .bb-top; give .np-wrap explicit 26px height to stop ticker text being vertically clipped - Remove redundant gear icon from top bar (settings accessible via hamburger menu) - ENDED state: try nextVideo(), fallback to playVideoAt(0) after 1.2s so last-video playlists loop - Persist shuffle state to localStorage from both the toolbar button and S keyboard shortcut - Replace setup hint with direct YouTube API link + "What's an API key?" modal with 4-step guide for first-time users Co-Authored-By: claude-flow --- vidflow.html | 54 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/vidflow.html b/vidflow.html index d61ecb5..1b80a8f 100644 --- a/vidflow.html +++ b/vidflow.html @@ -118,11 +118,11 @@ html,body{width:100%;height:100%;overflow:hidden;background:var(--bg);font-famil /* Bottom bar */ #bb{position:absolute;bottom:0;left:0;right:0;height:var(--bot-h);z-index:5;display:flex;flex-direction:column;justify-content:center;align-items:stretch;padding:5px 14px;gap:2px} -.bb-top{display:flex;align-items:center;gap:8px;overflow:hidden} +.bb-top{display:flex;align-items:center;gap:8px;} .bb-bot{display:flex;justify-content:center;align-items:center} .np-lbl{font-family:var(--font-d);font-size:12px;letter-spacing:2.5px;color:var(--accent);flex-shrink:0;display:flex;align-items:center;gap:5px;white-space:nowrap} .np-lbl-dot{width:5px;height:5px;background:var(--accent);border-radius:50%;animation:dot-p 1s ease-in-out infinite} -.np-wrap{flex:1;overflow:hidden;height:100%;display:flex;align-items:center;position:relative} +.np-wrap{flex:1;overflow:hidden;height:26px;position:relative} #np-ticker{white-space:nowrap;font-size:15px;font-weight:600;letter-spacing:.4px;will-change:transform;position:absolute;left:0;top:50%;transform-origin:left center;margin-top:-10px} .np-art{color:var(--accent2)} .ctrls{display:flex;align-items:center;gap:3px} @@ -284,9 +284,9 @@ body.idle .gt,body.idle .gb,body.idle .gr,body.idle .gl{opacity:0;transition:opa
YouTube Data API v3 Key
- Get a free key at Google Cloud Console → - Enable YouTube Data API v3 → Credentials → Create API Key.
- No Google login needed to watch videos. + A free YouTube API key lets VidFlow search playlists.
+ Get your key here — + What's an API key?
@@ -294,6 +294,26 @@ body.idle .gt,body.idle .gb,body.idle .gr,body.idle .gl{opacity:0;transition:opa + +
@@ -342,7 +362,6 @@ body.idle .gt,body.idle .gb,body.idle .gr,body.idle .gl{opacity:0;transition:opa
-
@@ -460,7 +479,12 @@ function onPState(e){ // browser blocked autoplay — prompt user to tap pb.textContent='▶'; A.playing=false; showTap(); } else if(e.data===S.ENDED){ - A.player?.nextVideo?.(); + try{ A.player.nextVideo(); }catch(e){} + setTimeout(()=>{ + try{ + if(A.player?.getPlayerState?.()===YT.PlayerState.ENDED) A.player.playVideoAt(0); + }catch(e){} + }, 1200); } } @@ -882,6 +906,16 @@ const closeSB=()=>qs('#sb').classList.remove('on'); let toastT; function toast(msg){ const t=qs('#toast'); t.textContent=msg; t.classList.add('on'); clearTimeout(toastT); toastT=setTimeout(()=>t.classList.remove('on'),2800); } +function initApiGuide(){ + const guide=qs('#api-guide'); + const show=()=>{ guide.style.display='flex'; }; + const hide=()=>{ guide.style.display='none'; }; + const btn=qs('#api-guide-btn'); + if(btn) btn.onclick=show; + qs('#api-guide-close').onclick=hide; + qs('#api-guide-close2').onclick=hide; + guide.onclick=e=>{ if(e.target===guide) hide(); }; +} // ── Persist ── function save(){ @@ -941,6 +975,7 @@ function wireControls(){ A.shuffled=!A.shuffled; qs('#b-shuf').classList.toggle('act',A.shuffled); try{ A.player?.setShuffle?.(A.shuffled); }catch(e){} + localStorage.setItem('vf_shuffle', A.shuffled); toast(A.shuffled?'\u21cc Shuffle ON':'Shuffle OFF'); }; qs('#b-sb').onclick=openSB; @@ -957,7 +992,6 @@ function wireControls(){ if(!document.fullscreenElement) document.documentElement.requestFullscreen?.().catch(()=>{}); else document.exitFullscreen?.(); }; - qs('#cfg-btn').onclick=()=>{ openSB(); renderTab('settings'); }; qs('#pb-wrap').onclick=e=>{ try{ if(!A.player?.getDuration) return; @@ -976,7 +1010,7 @@ function wireControls(){ document.addEventListener('click',e=>{ if(!document.contains(e.target)) return; // guard: element may have been removed by innerHTML replacement const sb=qs('#sb'); - if(sb.classList.contains('on')&&!sb.contains(e.target)&&!qs('#b-sb').contains(e.target)&&!e.target.closest('#add-ch-btn')&&!e.target.closest('#cfg-btn')) closeSB(); + if(sb.classList.contains('on')&&!sb.contains(e.target)&&!qs('#b-sb').contains(e.target)&&!e.target.closest('#add-ch-btn')) closeSB(); }); document.addEventListener('keydown',e=>{ if(['INPUT','TEXTAREA'].includes(e.target.tagName)) return; @@ -1005,6 +1039,7 @@ function wireControls(){ A.shuffled=!A.shuffled; qs('#b-shuf').classList.toggle('act',A.shuffled); try{A.player?.setShuffle?.(A.shuffled);}catch(err){} + localStorage.setItem('vf_shuffle', A.shuffled); toast(A.shuffled?'⇌ Shuffle ON':'Shuffle OFF'); } else if(e.code==='KeyC'){ openSB(); } else if(e.code==='KeyP'){ window.opener ? window.close() : popOut(); @@ -1111,6 +1146,7 @@ if(!window.opener){ loadState(); wireControls(); initSetup(); +initApiGuide();