Files
tutorialvault/docs/plans/2026-02-19-15-enhancements-design.md
Your Name c2533e8a76 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.
2026-02-19 16:51:54 +02:00

257 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.