docs: add design for 15 UI enhancements

Mute toggle, fullscreen shortcut, seek feedback overlay, playlist
search, scroll-to-current, PiP, timestamp insertion, keyboard help,
speed shortcuts, reset confirmation, error state, double-click
fullscreen, playlist stats, per-item progress bars, collapsible docks.
This commit is contained in:
Your Name
2026-02-19 16:51:54 +02:00
parent a6f1aef489
commit c2533e8a76

View File

@@ -0,0 +1,256 @@
# TutorialVault — 15 UI Enhancements Design
**Goal:** Add 15 real, polished enhancements to the TutorialVault frontend — all WCAG 2.2 AAA compliant.
**Architecture:** All changes are frontend-only (TypeScript + CSS). No Rust/backend changes needed. Each enhancement is independent — no cross-dependencies between them.
**Tech Stack:** TypeScript, CSS, HTML (Tauri v2 webview frontend)
---
## Enhancement 1: Mute Toggle (M key + volume icon click)
**What:** Press `M` to toggle mute. Click the volume icon to toggle mute. Muted state shows `fa-volume-xmark`, grays out the slider. Unmuting restores previous volume.
**Files:** `main.ts`, `player.ts`, `player.css`
**Behavior:**
- Store `lastVolume` before muting. Set volume to 0 on mute.
- On unmute, restore `lastVolume` (default 1.0 if none stored).
- Volume icon dynamically swaps: `fa-volume-high` (>50%), `fa-volume-low` (>0%), `fa-volume-xmark` (0/muted).
- Dragging the volume slider while muted unmutes automatically.
**WCAG:** Volume icon gets `aria-label="Mute"/"Unmute"` dynamically. Muted visual uses icon change + reduced opacity (not color alone).
---
## Enhancement 2: Fullscreen Shortcut (F key)
**What:** Press `F` to toggle fullscreen. Mirrors the existing fullscreen button behavior.
**Files:** `main.ts`
**Behavior:** Add `case 'f':` to the keyboard switch. Calls the same fullscreen toggle as `fsBtn.onclick`.
**WCAG:** No visual changes needed.
---
## Enhancement 3: Seek Feedback Overlay
**What:** Flash `5s` / `+5s` text in the video overlay when seeking via arrow keys. Fades after 600ms.
**Files:** `main.ts`, `player.ts`, `player.css`
**Behavior:**
- New `showSeekFeedback(delta: number)` function in player.ts.
- Reuses the existing video overlay area. Shows text like "5s" or "+5s" centered.
- Uses a CSS class `.seekFeedback` that fades in/out.
- Consecutive presses accumulate: pressing ArrowRight 3 times quickly shows "+15s".
**WCAG:** Overlay area has `aria-live="assertive"` so screen readers announce "Seeked forward 5 seconds". Text meets 7:1 contrast (white on semi-transparent dark).
---
## Enhancement 4: Playlist Search/Filter
**What:** Search input above the playlist. Typing filters by video name or relative path in real-time. Shows "X of Y" while filtering. Clear button (x) resets.
**Files:** `index.html`, `playlist.ts`, `playlist.css`
**Behavior:**
- Add `<input id="plistSearch">` in the playlist `.panelHeader`.
- `renderList()` checks the filter value and skips non-matching items.
- Filter is case-insensitive, matches against `it.title`, `it.name`, `it.relpath`.
- Clear button appears only when input has text.
- Pressing Escape in the search input clears it.
**WCAG:** `aria-label="Search playlist"`. Filtered count via `aria-live="polite"` region. Clear button has 44x44 target and `aria-label="Clear search"`.
---
## Enhancement 5: Scroll-to-Current Button
**What:** Button in playlist header that scrolls the active item into view. Only visible when the active item is off-screen.
**Files:** `index.html`, `playlist.ts`, `playlist.css`
**Behavior:**
- Uses `IntersectionObserver` on the `.row.active` element.
- Button appears (fades in) when active row is not visible.
- Clicking calls `activeRow.scrollIntoView({ block: 'center', behavior: 'smooth' })`.
- Icon: `fa-crosshairs` or `fa-location-dot`.
**WCAG:** `aria-label="Scroll to current video"`. 44x44 touch target. Hidden with `display:none` when not actionable (removed from a11y tree).
---
## Enhancement 6: Picture-in-Picture Button
**What:** PiP toggle button next to the fullscreen button. Uses native `requestPictureInPicture()` API.
**Files:** `index.html`, `player.ts`
**Behavior:**
- New `iconBtn` after fsBtn: `<button class="iconBtn" id="pipBtn">`.
- Icon swaps: `fa-up-right-from-square` (enter PiP) / `fa-down-left-and-up-right-to-center` (exit PiP).
- Listen to `enterpictureinpicture` / `leavepictureinpicture` events to update icon.
- Feature-detect: hide button if `document.pictureInPictureEnabled` is false.
**WCAG:** Dynamic `aria-label="Enter picture-in-picture"/"Exit picture-in-picture"`. Same focus-visible ring as all icon buttons.
---
## Enhancement 7: Timestamp Insertion in Notes
**What:** Button in the notes dock header that inserts `[MM:SS] ` at the textarea cursor position using the current video time.
**Files:** `index.html`, `ui.ts`, `panels.css`
**Behavior:**
- Small button in `#notesHeader`: `<button id="insertTimestamp" aria-label="Insert timestamp">`.
- Icon: `fa-clock` or `fa-stopwatch`.
- On click: read `player.currentTime`, format as `[M:SS]`, insert at `notesBox.selectionStart`.
- After insert, focus the textarea and place cursor after the inserted text.
**WCAG:** `aria-label="Insert timestamp"`. 44x44 target via `::before` expansion. `data-tooltip` for hover discoverability.
---
## Enhancement 8: Keyboard Shortcut Help Panel
**What:** Press `?` to open a help overlay listing all keyboard shortcuts. Press `?` or `Escape` to close.
**Files:** `index.html`, `main.ts`, `main.css` or `components.css`
**Behavior:**
- Hidden `<div id="shortcutHelp" role="dialog" aria-label="Keyboard shortcuts">` in HTML.
- Two-column grid showing key + description pairs.
- Shortcuts shown: Space (play/pause), Left/Right (seek ±5s), Up/Down (volume), M (mute), F (fullscreen), [ / ] (speed), Alt+Arrow (reorder playlist), ? (this help).
- Focus trap inside the dialog while open.
- Semi-transparent dark backdrop.
**WCAG:** `role="dialog"` with `aria-label`. Focus trapped. Escape closes. All text 7:1 contrast. Backdrop doesn't remove background content from a11y tree (uses `aria-hidden="true"` on app root while dialog is open).
---
## Enhancement 9: Playback Speed Shortcuts ([ and ])
**What:** Press `[` to decrease speed one step, `]` to increase speed one step through the SPEEDS array.
**Files:** `main.ts`, `player.ts`
**Behavior:**
- New `cycleSpeed(delta: number)` exported from player.ts.
- Finds current speed in SPEEDS array, moves by delta (clamped to bounds).
- Updates speed button text, gauge icon, and saves to backend.
- Shows toast notification: "Speed: 1.25x".
**WCAG:** Speed change announced via toast (existing `aria-live="polite"` region).
---
## Enhancement 10: Reset Progress Confirmation
**What:** Two-click confirmation before resetting progress. First click changes button to "Confirm?" state with red tint. Second click within 3 seconds actually resets. Timeout or click-away cancels.
**Files:** `ui.ts`, `main.css`
**Behavior:**
- First click: add `.confirming` class, change icon to `fa-exclamation-triangle`, start 3s timer.
- Second click while `.confirming`: execute the actual reset.
- Timer expiry or blur: remove `.confirming`, restore original icon.
- `.confirming` state: red-tinted background (`rgba(255,70,70,.14)`).
**WCAG:** `aria-label` changes to "Confirm reset progress" in confirm state. Visual change uses both color AND icon change (not color alone).
---
## Enhancement 11: Video Load Error State
**What:** When the `<video>` fires an error event, show a centered overlay with warning icon, error message, and "Try next" button.
**Files:** `player.ts`, `player.css`
**Behavior:**
- Listen to `player.addEventListener('error', ...)`.
- Create/show error overlay inside `.videoWrap` with `fa-triangle-exclamation` icon.
- Message: "This file format may not be supported. Try MP4 (H.264/AAC) or WebM."
- "Try next" button calls `nextPrev(+1)`.
- Overlay clears on next successful `loadedmetadata`.
**WCAG:** `role="alert"` for screen reader announcement. "Try next" button has 44x44 target. Error text meets 7:1 contrast.
---
## Enhancement 12: Double-Click to Fullscreen
**What:** Double-clicking the video toggles fullscreen. Single click still toggles play/pause.
**Files:** `player.ts`
**Behavior:**
- Replace simple click handler with a click/double-click discriminator.
- On click: set a 300ms timer. If no second click arrives, toggle play/pause.
- On double-click: cancel the timer, toggle fullscreen instead.
- `dblclick` event on the video overlay area.
**WCAG:** Mouse/touch enhancement only. Fullscreen remains keyboard-accessible via F key and button.
---
## Enhancement 13: Playlist Stats in Header
**What:** Show video count and completion info next to "Playlist" header. E.g., "24 videos · 8 done".
**Files:** `index.html`, `playlist.ts`
**Behavior:**
- Add `<span class="plistStats" id="plistStats"></span>` in the panelHeader.
- In `renderList()`: compute total count, done count, update the span.
- Styled as `--textMuted`, smaller font (11px), monospace.
**WCAG:** Stats text meets 7:1 contrast. Updated via DOM so screen readers can read it on demand.
---
## Enhancement 14: Mini Progress Bars Per Playlist Item
**What:** Thin 2px progress bar at the bottom of each playlist row showing watched/total. Green for done, accent blue for in-progress.
**Files:** `playlist.ts`, `playlist.css`
**Behavior:**
- For each row in `renderList()`, create a `.rowProgress` div.
- Width: `(watched / duration) * 100%`. If duration is null, hide.
- Color: `--success` for done items, `--accent` for in-progress.
- Positioned absolutely at bottom of the row.
**WCAG:** `aria-hidden="true"` — numeric info already in the row's `aria-label`. Colors pass 3:1 minimum for non-text contrast per WCAG 2.2.
---
## Enhancement 15: Collapsible Dock Panes
**What:** Clicking a dock header collapses that pane, giving the other pane full height. Collapsed state shows just the header with a chevron. Persisted via prefs.
**Files:** `ui.ts`, `panels.css`, `index.html`
**Behavior:**
- Click on `.dockHeader` toggles `.collapsed` on the parent `.dockPane`.
- Collapsed: `flex:0 0 auto`, content inside has `display:none`.
- Chevron icon in header rotates to indicate collapsed/expanded.
- Collapsing one pane doesn't collapse the other.
- State saved in prefs as `notes_collapsed` / `info_collapsed` booleans.
**WCAG:** Headers get `aria-expanded="true"/"false"`. Chevron is `aria-hidden="true"`. Enter/Space on header toggles. Collapsed content is `display:none` (removed from a11y tree).
---
## Approach
**Single-pass, all frontend:** Every enhancement modifies only TypeScript and CSS files. No backend/Rust changes. Each enhancement is independent and can be implemented in any order.
**Implementation strategy:** Direct edits to existing files using Edit/Write tools. No subagents for file editing. Each enhancement touches 1-3 files. Build verification after each enhancement or batch.
**Testing:** `npm run build` (TypeScript compilation + Vite build) after each enhancement to catch errors immediately.