diff --git a/BACKLOG.md b/BACKLOG.md new file mode 100644 index 0000000..0887d95 --- /dev/null +++ b/BACKLOG.md @@ -0,0 +1,39 @@ +# YT Player — Improvement Backlog + +The `/loop` works through this top-to-bottom, **one item per iteration**. Tick an item +(`[x]`) only after it builds, verifies, and is committed + pushed. Add new ideas to the +bottom as they come up. + +## Build & verify protocol (every iteration) +1. Edit the **canonical source** in WSL (`~/development/personal/ytplayer`). +2. Sync changed files **individually** into `C:\ytbuild\ytplayer` — never + `Copy-Item -Recurse` onto an existing dir (it nests as `frontend\frontend`). +3. `npx tauri build` in `C:\ytbuild\ytplayer`. +4. Launch with `--remote-debugging-port=922x` and verify via CDP/screenshot + (retry the launch a few times — Smart App Control is intermittent). +5. **Only if it builds and verifies:** refresh `releases/`, commit (no AI attribution), + `git push origin main`, then tick the item here. + +## UX / polish +- [x] Empty-state designs for empty playlists and empty history (match the new hero). +- [ ] Loading skeletons for search results instead of a bare "Searching…". +- [ ] Keyboard-shortcut help overlay (press `?`), listing space / arrows / f / m. +- [ ] Download/preload progress indicator on cards (percent or a bar, not just a pulse). +- [ ] Remember and restore the last playback position per video. +- [ ] Toast queue (stack multiple toasts instead of replacing). + +## Features +- [ ] Drag-to-reorder videos within a playlist. +- [ ] "Up next" queue panel showing the autoplay queue. +- [ ] Persistent mini now-playing bar when browsing other views. +- [ ] Cache size cap in Settings (auto-evict oldest past a limit). +- [ ] Export / import playlists as JSON. +- [ ] More / trending quick-search chips on the hero, rotated. + +## Robustness +- [ ] Sanitize `video_id` in `cache_download` (reject path separators). +- [ ] Gate the embedded yt-dlp behind a build flag so debug builds compile faster. +- [ ] Surface yt-dlp extraction failures with a retry button. + +## Done + diff --git a/frontend/app.js b/frontend/app.js index 8d6d721..ec23613 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -596,11 +596,44 @@ function renderList() { } if (!list.length) { - els.status.classList.remove('hidden'); - els.status.textContent = - view.type === 'search' ? 'Search for something to begin.' - : view.type === 'history' ? 'Nothing watched yet.' - : 'This playlist is empty. Add videos from search.'; + els.status.classList.add('hidden'); + els.cards.innerHTML = ''; + const empty = document.createElement('div'); + empty.className = 'empty-state'; + + if (view.type === 'search') { + // Hero landing is already in the player pane — list pane stays bare. + els.status.classList.remove('hidden'); + els.status.textContent = 'Search for something to begin.'; + return; + } + + if (view.type === 'history') { + empty.innerHTML = ` +
🕘
+

Nothing watched yet

+

Your viewing history shows up here once you start playing videos.

+ `; + const cta = document.createElement('button'); + cta.className = 'empty-cta'; + cta.textContent = '🔍 Search videos'; + cta.addEventListener('click', () => { view = { type: 'search' }; render(); }); + empty.appendChild(cta); + } else { + // Playlist view — empty + empty.innerHTML = ` +
🎵
+

This playlist is empty

+

Add videos from search results or use the + Playlist button while playing.

+ `; + const cta = document.createElement('button'); + cta.className = 'empty-cta'; + cta.textContent = '🔍 Browse videos'; + cta.addEventListener('click', () => { view = { type: 'search' }; render(); }); + empty.appendChild(cta); + } + + els.cards.appendChild(empty); return; } els.status.classList.add('hidden'); diff --git a/frontend/styles.css b/frontend/styles.css index 52f35ab..2288350 100644 --- a/frontend/styles.css +++ b/frontend/styles.css @@ -557,6 +557,66 @@ input[type="range"]::-webkit-slider-thumb:hover { transform: scale(1.25); } .list-actions button:hover { color: var(--text); border-color: var(--accent); background: var(--bg-3); } .status { padding: 16px 20px; color: var(--text-dim); font-size: 13px; } + +/* ===================== Empty state ===================== */ +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 40px 32px 60px; + gap: 8px; + animation: heroIn 0.5s var(--ease) both; +} +.empty-icon { + font-size: 42px; + width: 80px; height: 80px; + display: grid; place-items: center; + border-radius: 24px; + background: var(--bg-2); + border: 1px solid var(--line); + margin-bottom: 10px; + transition: transform 0.3s var(--ease), box-shadow 0.3s; +} +.empty-state:hover .empty-icon { + transform: translateY(-4px); + box-shadow: 0 12px 32px -12px var(--accent-glow); +} +.empty-title { + font-family: var(--display); + font-weight: 700; + font-size: 18px; + letter-spacing: -0.01em; + margin: 0; + color: var(--text); +} +.empty-desc { + margin: 4px 0 0; + color: var(--text-dim); + font-size: 13.5px; + line-height: 1.5; + max-width: 300px; +} +.empty-desc strong { color: var(--text-2); font-weight: 600; } +.empty-cta { + margin-top: 16px; + background: linear-gradient(145deg, var(--accent-bright), var(--accent-deep)); + color: #fff; + border: none; + padding: 10px 22px; + border-radius: 11px; + font-family: var(--ui); + font-weight: 700; + font-size: 13px; + letter-spacing: 0.01em; + cursor: pointer; + box-shadow: 0 8px 22px -8px var(--accent-glow); + transition: transform 0.16s var(--ease), box-shadow 0.16s; +} +.empty-cta:hover { transform: translateY(-2px); box-shadow: 0 12px 28px -8px var(--accent-glow); } +.empty-cta:active { transform: translateY(0); } + .cards { flex: 1; overflow-y: auto; padding: 6px 14px 28px; display: flex; flex-direction: column; gap: 5px; } .card { diff --git a/releases/YT Player_1.0.0_x64-setup.exe b/releases/YT Player_1.0.0_x64-setup.exe new file mode 100755 index 0000000..d00d091 Binary files /dev/null and b/releases/YT Player_1.0.0_x64-setup.exe differ diff --git a/releases/YT Player_1.0.0_x64_en-US.msi b/releases/YT Player_1.0.0_x64_en-US.msi new file mode 100755 index 0000000..f787171 Binary files /dev/null and b/releases/YT Player_1.0.0_x64_en-US.msi differ diff --git a/releases/ytplayer.exe b/releases/ytplayer.exe index 738a4ce..2a09f10 100644 Binary files a/releases/ytplayer.exe and b/releases/ytplayer.exe differ