From c5844ac7122b351a9d58dea0585b48c0cb70b719 Mon Sep 17 00:00:00 2001 From: xpltd Date: Wed, 18 Mar 2026 21:16:24 -0500 Subject: [PATCH] =?UTF-8?q?GSD:=20M002/S01=20complete=20=E2=80=94=20Bug=20?= =?UTF-8?q?fixes=20+=20header/footer=20rework?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix cancel download bug: add @click.stop, debounce with cancelling ref - Rework header: remove nav tabs, replace ThemePicker with DarkModeToggle - Add isDark computed + toggleDarkMode() to theme store - Add WelcomeMessage component above URL input, reads from public config - Add welcome_message to UIConfig and public config endpoint - Add AppFooter with app version, yt-dlp version, GitHub link - Remove SSE status dot from header - Remove connectionStatus prop from AppLayout - 5 new theme toggle tests (34 frontend tests total) - 179 backend tests still passing --- .gsd/milestones/M001/UI-REVIEW-FINDINGS.md | 133 ++++++++++++++++++++ .gsd/milestones/M002/M002-ROADMAP.md | 89 +++++++++++++ .gsd/milestones/M002/slices/S01/S01-PLAN.md | 76 +++++++++++ backend/app/core/config.py | 1 + backend/app/routers/system.py | 1 + frontend/src/App.vue | 34 +---- frontend/src/api/types.ts | 1 + frontend/src/components/AppFooter.vue | 57 +++++++++ frontend/src/components/AppHeader.vue | 35 +----- frontend/src/components/AppLayout.vue | 5 - frontend/src/components/DarkModeToggle.vue | 54 ++++++++ frontend/src/components/DownloadItem.vue | 18 ++- frontend/src/components/MainView.vue | 4 +- frontend/src/components/WelcomeMessage.vue | 44 +++++++ frontend/src/stores/theme.ts | 23 ++++ frontend/src/tests/stores/config.test.ts | 1 + frontend/src/tests/stores/theme.test.ts | 45 +++++++ 17 files changed, 547 insertions(+), 74 deletions(-) create mode 100644 .gsd/milestones/M001/UI-REVIEW-FINDINGS.md create mode 100644 .gsd/milestones/M002/M002-ROADMAP.md create mode 100644 .gsd/milestones/M002/slices/S01/S01-PLAN.md create mode 100644 frontend/src/components/AppFooter.vue create mode 100644 frontend/src/components/DarkModeToggle.vue create mode 100644 frontend/src/components/WelcomeMessage.vue diff --git a/.gsd/milestones/M001/UI-REVIEW-FINDINGS.md b/.gsd/milestones/M001/UI-REVIEW-FINDINGS.md new file mode 100644 index 0000000..1e2dd63 --- /dev/null +++ b/.gsd/milestones/M001/UI-REVIEW-FINDINGS.md @@ -0,0 +1,133 @@ +# UI/UX Review Findings — M001 Post-Completion Walkthrough + +**Date:** 2026-03-18 +**Participants:** User (product owner) + GSD (agent) +**Method:** Live app walkthrough at localhost:5173, guided interview + +--- + +## Summary + +M001 is functionally complete (208 tests, all slices done) but the UI needs a significant UX pass before v0.1.0 tag. The app works but doesn't feel polished — unclear affordances, missing user flows, and several functional gaps identified during live testing. + +--- + +## Findings + +### 1. Welcome / Informational Block +**Priority: HIGH** +- Add a configurable welcome message block above the URL input +- Default text: "Paste any video or audio URL. We rip it, you download it. No accounts, no tracking. Files auto-purge after 24h." (or similar) +- Admin-configurable from admin panel (can override text or hide entirely) +- Should look clean and integrated, not a banner bar — a styled text block above the input area + +### 2. Theme System Rework +**Priority: HIGH** +- **Current:** 3 radio-button dots (Cyberpunk/Dark/Light) in header +- **Target:** Admin sets the theme (cyberpunk default). Users get a sun/moon toggle for light ↔ dark variant only +- Backend implication: each theme needs a light variant, or the light mode is a modifier on any theme +- The "dark" and "light" themes become _modes_ rather than separate themes +- Theme picker (full theme selection) moves to admin panel + +### 3. SSE Connection Indicator (Green Dot) +**Priority: LOW** +- Currently unlabeled, looks like a 4th theme option +- **Decision:** Hide in production. Keep available for debug/development mode only + +### 4. Remove ADMIN Tab from Main Nav +**Priority: HIGH** +- Admin panel accessible only via `/admin` URL — no visual link from main app +- Security by obscurity layer (auth still required, but no invitation to probe) +- Consequence: since ADMIN tab is removed, the DOWNLOADS tab is also unnecessary (only one view) +- Remove the entire DOWNLOADS/ADMIN tab bar + +### 5. Footer with Version Info +**Priority: MEDIUM** +- Centered footer showing: `media.rip() v0.1.0 | yt-dlp 2026.03.17 | GitHub` +- Pipe-delimited, clean typography +- GitHub link goes to repo +- Version info pulled from health endpoint data + +### 6. Download Flow Rework +**Priority: HIGH** +- **Current:** URL input → "Get Formats" button → format picker appears → "Download" button +- **Target:** URL input → "Download" button (auto-best quality) with optional format picker as expandable section +- Add audio/video toggle glyph — clean, intuitive icon to switch between audio-only and video download +- Format picker becomes "Advanced" or expandable area, not the primary flow +- Must handle playlist URLs intuitively — multi-file links should dynamically show appropriate UI + +### 7. Download Queue → Table-Style Display +**Priority: HIGH** +- **Current:** Card-based list with title, progress bar, speed, ETA, cancel +- **Target:** Table-like display that maintains the card aesthetic (not Excel — keep the cyberpunk vibes) +- Columns to add: started timestamp, file size (if available) +- Admin-configurable visible columns (enable/disable from admin panel) +- Sorting: by ETA, % complete, alphabetical, download status +- Keep filter tabs (All/Active/Completed/Failed) with counts + +### 8. Download Item Actions (Glyphs, Not Words) +**Priority: HIGH** +- Use intuitive glyphs/icons instead of text labels +- **Active downloads:** Cancel (✕) +- **Completed downloads:** Download to local machine (↓), Copy share link (🔗), Clear from queue (✕) +- Cancel and clear should use the same position/interface pattern +- Single-click copy for share link + +### 9. Cancel Download Bug +**Priority: HIGH (Functional Bug)** +- Cancel button (✕) on active downloads does not work — clicking does not cancel the download +- Network logs show no request is sent when clicking cancel +- Likely a click handler or z-index/event propagation issue in the grid layout +- Must investigate and fix + +### 10. Session Management UI Missing +**Priority: MEDIUM** +- R017 (Session export/import) has no visible UI elements +- No export, import, or delete session buttons anywhere in the app +- Needs UI surface — likely in a settings area or as part of the header/footer + +### 11. Admin Panel — Deferred to Next Review +**Priority: MEDIUM (deferred)** +- Admin panel needs review after UI changes are applied +- Current state: login form shows even when admin is disabled (no credentials configured) +- New admin features needed: welcome message editor, theme selection, column visibility toggles +- Default credentials / first-boot setup flow needs work +- Will review in next walkthrough round + +### 12. Mobile View +**Priority: MEDIUM** +- Bottom tab bar (SUBMIT/QUEUE) appears at <768px +- If table-style download display makes mobile too complex, recommend most elegant fallback +- Needs reassessment after desktop changes land + +--- + +## Bugs Found + +| # | Description | Severity | +|---|---|---| +| B1 | Cancel button on active downloads doesn't fire network request | High | +| B2 | Admin login form shown when admin is disabled (no credentials configured) | Medium | +| B3 | Format picker only shows "Completed" text match from filter tab label (false text match, cosmetic) | Low | + +--- + +## Proposed Execution Order + +1. **Cancel bug fix** (B1) — functional blocker +2. **Header rework** — remove tabs, add welcome message block, simplify theme to sun/moon toggle +3. **Footer** — version info display +4. **Download flow** — quick download + optional format picker, audio/video toggle +5. **Queue table redesign** — table-style with sorting, timestamps, file size +6. **Action glyphs** — download/copy/clear icons on completed items +7. **Admin panel improvements** — welcome message editor, theme selection, column config +8. **Session management UI** — export/import/delete +9. **Mobile reassessment** — after desktop changes + +--- + +## Out of Scope for This Pass + +- Full admin panel redesign (deferred to next review round) +- Playlist-specific UI (parent/child collapse) — will be designed during execution if time permits +- Visual polish / animation refinement diff --git a/.gsd/milestones/M002/M002-ROADMAP.md b/.gsd/milestones/M002/M002-ROADMAP.md new file mode 100644 index 0000000..e58d90f --- /dev/null +++ b/.gsd/milestones/M002/M002-ROADMAP.md @@ -0,0 +1,89 @@ +# M002: UI/UX Polish — Ship-Ready Frontend + +**Vision:** Transform the functional-but-rough v1 frontend into a polished, intuitive experience. Fix functional bugs, rework the download flow, redesign the queue display, and clean up navigation so the app feels intentional rather than assembled. + +## Success Criteria + +- User can paste a URL and download with one click (best quality auto-selected) +- Completed downloads show download/copy-link/clear actions as intuitive glyphs +- Cancel button on active downloads actually cancels +- Download queue displays as a styled table with sorting by ETA, %, name, status +- Welcome message is visible above the URL input and configurable by admin +- Theme toggle is a sun/moon icon (light/dark mode), not a 3-option picker +- Admin panel is only accessible at `/admin` — no link from main app +- Footer shows app version, yt-dlp version, and GitHub link +- Mobile view remains functional after desktop changes + +## Key Risks / Unknowns + +- Cancel bug root cause — could be event propagation, could be deeper API issue +- Table-style queue on mobile — may need a different layout strategy below 768px +- Theme light/dark variant architecture — each theme needs a light mode modifier + +## Proof Strategy + +- Cancel bug → retire in S01 by verifying network request fires and download stops +- Table mobile layout → retire in S03 by visual verification on mobile viewport + +## Verification Classes + +- Contract verification: frontend tests (vitest), backend tests (pytest) for any API changes +- Integration verification: live browser verification of all changed UI flows +- Operational verification: none (no backend architecture changes) +- UAT / human verification: walkthrough with user after S03 + +## Milestone Definition of Done + +This milestone is complete only when all are true: + +- All UI changes are implemented and visually verified in browser +- Cancel downloads works end-to-end +- Download flow (paste → download → completed → download file) works +- Mobile view is functional +- Frontend tests pass +- Backend tests pass (no regressions) +- User walkthrough confirms satisfaction + +## Requirement Coverage + +- Covers: R005 (queue view), R013 (mobile responsive), R018 (link sharing) +- Partially covers: R010 (themes — light/dark toggle rework), R014 (admin panel — welcome message config) +- Leaves for later: R017 (session export/import UI), R011 (custom theme system — admin theme picker) +- Orphan risks: none + +## Slices + +- [ ] **S01: Bug Fixes + Header/Footer Rework** `risk:high` `depends:[]` + > After this: Cancel button works, header has no tabs, footer shows version info, welcome message block is visible with default text, theme is sun/moon toggle + +- [ ] **S02: Download Flow + Queue Redesign** `risk:medium` `depends:[S01]` + > After this: Single "Download" button with optional format picker, audio/video toggle, queue displays as styled table with sorting, completed items show download/copy/clear glyphs + +- [ ] **S03: Mobile + Integration Polish** `risk:low` `depends:[S02]` + > After this: Mobile layout works with new table design, admin welcome message editor functional, all flows verified end-to-end + +## Boundary Map + +### S01 → S02 + +Produces: +- Simplified header component (no tabs, sun/moon toggle) +- Footer component with version data from `/api/health` +- Welcome message block component reading from `/api/config/public` +- Working cancel endpoint (DELETE `/api/downloads/{id}` verified) +- `--color-bg-light` / `--color-text-light` CSS variable pattern for light mode + +Consumes: +- nothing (first slice) + +### S02 → S03 + +Produces: +- Refactored UrlInput with "Download" primary action + collapsible format picker +- Audio/video toggle component +- Table-based DownloadQueue with sortable columns +- Action glyph components (download, copy-link, clear) + +Consumes: +- S01 header/footer/welcome components stable +- S01 cancel bug fixed diff --git a/.gsd/milestones/M002/slices/S01/S01-PLAN.md b/.gsd/milestones/M002/slices/S01/S01-PLAN.md new file mode 100644 index 0000000..e36a3f7 --- /dev/null +++ b/.gsd/milestones/M002/slices/S01/S01-PLAN.md @@ -0,0 +1,76 @@ +# S01: Bug Fixes + Header/Footer Rework + +**Goal:** Fix the cancel download bug, rework the header (remove tabs, add welcome message, simplify theme toggle to sun/moon), add version footer, and hide the SSE status dot in production. +**Demo:** User sees a clean header with logo + sun/moon toggle, welcome message block above URL input, version footer at bottom. Cancel button on active downloads fires the DELETE request and removes the item. + +## Must-Haves + +- Cancel button on active downloads actually cancels (fires DELETE request, item removed) +- Header has no DOWNLOADS/ADMIN tabs +- Sun/moon toggle replaces 3-theme picker (switches current theme between its dark and light variant) +- Welcome message block above URL input with sensible default text +- Footer shows app version, yt-dlp version, GitHub link (pipe-delimited) +- SSE green dot hidden (dev-mode only) +- Admin panel still accessible at `/admin` but no nav link from main app + +## Verification + +- `cd frontend && npx vitest run` — all tests pass (update theme tests for new toggle behavior) +- `cd backend && python -m pytest tests/ -q -m "not integration"` — no regressions +- Browser: cancel button on active download fires network request and item disappears +- Browser: header shows logo + sun/moon toggle, no tabs, no green dot +- Browser: welcome message visible above URL input +- Browser: footer visible with version info +- Browser: `/admin` still loads the login form + +## Tasks + +- [x] **T01: Fix cancel download bug** `est:30m` + - Why: Cancel button clicks don't fire a network request — functional blocker + - Files: `frontend/src/components/DownloadItem.vue`, `frontend/src/components/DownloadQueue.vue` + - Do: Investigate why the cancel button click doesn't reach the handler. Check for event propagation issues in the grid layout, z-index conflicts, or pointer-events CSS. Verify the DELETE endpoint works via curl. Fix the event wiring. Add `@click.stop` if needed. + - Verify: Start a download, click cancel, confirm DELETE request in network tab, item disappears from queue + - Done when: Cancel button reliably cancels active downloads + +- [x] **T02: Rework header — remove tabs, simplify theme toggle** `est:45m` + - Why: DOWNLOADS/ADMIN tabs are unnecessary (admin moves to URL-only access). Theme picker needs to be a simple sun/moon toggle instead of 3 radio buttons. + - Files: `frontend/src/components/AppHeader.vue`, `frontend/src/components/ThemePicker.vue`, `frontend/src/stores/theme.ts`, `frontend/src/components/AppLayout.vue`, `frontend/src/App.vue`, `frontend/src/router.ts` + - Do: Remove the nav tab bar from AppLayout/MainView. Replace ThemePicker with a DarkModeToggle component — a sun/moon icon button that toggles between the current theme's dark and light variants. For cyberpunk, "light" mode uses the light theme CSS. Remove the SSE status dot from the header (or gate behind a `DEV` flag). Keep `/admin` route in router but remove any nav link to it. Update theme store: `toggleDarkMode()` method that swaps between `cyberpunk`↔`light` (or `dark`↔`light`). + - Verify: Header shows only logo + sun/moon toggle. Clicking toggle switches between dark/light appearance. No tabs visible. Green dot hidden. + - Done when: Header is clean with logo left, sun/moon toggle right, nothing else + +- [x] **T03: Add welcome message block** `est:30m` + - Why: Users need context about what the app does when they first land + - Files: `frontend/src/components/WelcomeMessage.vue` (new), `frontend/src/components/MainView.vue`, `backend/app/routers/system.py` + - Do: Create WelcomeMessage component that displays a styled text block above the URL input. Default text: "Paste any video or audio URL. We rip it, you download it. No accounts, no tracking." Make it read from the public config endpoint. Add `welcome_message` field to the public config response (with default value). Style it to integrate cleanly — not a banner, but a subtle informational block with proper typography. + - Verify: Welcome message visible above URL input on page load. Text matches default or config override. + - Done when: Welcome message block renders with default text, reads from config + +- [x] **T04: Add version footer** `est:20m` + - Why: Users/operators want to see app version, yt-dlp version, and find the GitHub repo + - Files: `frontend/src/components/AppFooter.vue` (new), `frontend/src/App.vue` + - Do: Create AppFooter component. Fetch version data from `/api/health` on mount. Display: `media.rip() v0.1.0 | yt-dlp 2026.03.17 | GitHub`. GitHub links to repo. Pipe-delimited, centered, subtle typography matching the theme. Place it at the bottom of the page (not fixed — scrolls with content). + - Verify: Footer visible at bottom of page with correct version numbers. GitHub link works. + - Done when: Footer renders with live version data from health endpoint + +- [x] **T05: Update tests and verify** `est:20m` + - Why: Theme store tests need updating for the new toggle behavior. Ensure no regressions. + - Files: `frontend/src/tests/stores/theme.test.ts`, `frontend/src/tests/stores/downloads.test.ts` + - Do: Update theme store tests to reflect new `toggleDarkMode()` method. Remove tests for 3-theme picker behavior. Add test for dark/light toggle. Run full test suites for both frontend and backend. + - Verify: `npx vitest run` all pass, `python -m pytest tests/ -q -m "not integration"` all pass + - Done when: All tests green, no regressions + +## Files Likely Touched + +- `frontend/src/components/AppHeader.vue` +- `frontend/src/components/ThemePicker.vue` (replaced by DarkModeToggle) +- `frontend/src/components/DarkModeToggle.vue` (new) +- `frontend/src/components/WelcomeMessage.vue` (new) +- `frontend/src/components/AppFooter.vue` (new) +- `frontend/src/components/AppLayout.vue` +- `frontend/src/components/MainView.vue` +- `frontend/src/App.vue` +- `frontend/src/stores/theme.ts` +- `frontend/src/tests/stores/theme.test.ts` +- `backend/app/routers/system.py` +- `backend/app/core/config.py` diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 70b521b..ae5a23e 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -73,6 +73,7 @@ class UIConfig(BaseModel): """UI preferences.""" default_theme: str = "dark" + welcome_message: str = "Paste any video or audio URL. We rip it, you download it. No accounts, no tracking." class AdminConfig(BaseModel): diff --git a/backend/app/routers/system.py b/backend/app/routers/system.py index f1b50c7..8aa6004 100644 --- a/backend/app/routers/system.py +++ b/backend/app/routers/system.py @@ -23,6 +23,7 @@ async def public_config(request: Request) -> dict: return { "session_mode": config.session.mode, "default_theme": config.ui.default_theme, + "welcome_message": config.ui.welcome_message, "purge_enabled": config.purge.enabled, "max_concurrent_downloads": config.downloads.max_concurrent, } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 3a8b4e4..e8926ff 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,10 +4,11 @@ import { useSSE } from '@/composables/useSSE' import { useConfigStore } from '@/stores/config' import { useThemeStore } from '@/stores/theme' import AppHeader from '@/components/AppHeader.vue' +import AppFooter from '@/components/AppFooter.vue' const configStore = useConfigStore() const themeStore = useThemeStore() -const { connectionStatus, connect } = useSSE() +const { connect } = useSSE() onMounted(async () => { themeStore.init() @@ -18,34 +19,7 @@ onMounted(async () => { - - diff --git a/frontend/src/api/types.ts b/frontend/src/api/types.ts index 8901605..6ad99c3 100644 --- a/frontend/src/api/types.ts +++ b/frontend/src/api/types.ts @@ -67,6 +67,7 @@ export interface FormatInfo { export interface PublicConfig { session_mode: string default_theme: string + welcome_message: string purge_enabled: boolean max_concurrent_downloads: number } diff --git a/frontend/src/components/AppFooter.vue b/frontend/src/components/AppFooter.vue new file mode 100644 index 0000000..cd49d8e --- /dev/null +++ b/frontend/src/components/AppFooter.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue index 31b9ab6..bb9b215 100644 --- a/frontend/src/components/AppHeader.vue +++ b/frontend/src/components/AppHeader.vue @@ -1,17 +1,5 @@