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

10 KiB
Raw Blame History

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.