- Guard drawPlayhead on session.duration > 0 in PlayerTimeline - Move floating controls pill from bottom: 24px to 48px - Add playerControlsVisible prop to VideoPlayer; captions slide up 160px when controls visible - Replace web Fullscreen API with Tauri getCurrentWindow().setFullscreen() (WKWebView compat) - Add core:window:allow-set-fullscreen and core:window:allow-is-fullscreen permissions - Tie PlayerTimeline visibility to showPlayerControls; remove unused proximity logic Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
4.2 KiB
Markdown
74 lines
4.2 KiB
Markdown
# v0.2.0 Player Mode — Implementation Summary
|
||
|
||
## Task Description
|
||
|
||
Implemented all 9 tasks from the v0.2.0 Player Mode implementation plan using subagent-driven development (SDD). This is the execution phase following the design spec and plan created in an earlier session.
|
||
|
||
## Changes Made
|
||
|
||
### 11 commits (bd96da4..7e838dc)
|
||
|
||
| Commit | Description |
|
||
|--------|-------------|
|
||
| `bd96da4` | Task 1: `appMode` preference (clipper/player) with persistence |
|
||
| `d9f5874` | Task 2: PiP, fullscreen, playbackRate helpers in playback module |
|
||
| `bb5fa4b` | Task 3: SpeedSelector popup component |
|
||
| `f717687` | Task 4: PlayerControls floating overlay (frosted glass, seek bar, all controls) |
|
||
| `56686a3` | Task 5: PlayerTimeline simplified waveform component |
|
||
| `d36138c` | Task 6: Player/Clipper mode switching with auto-hide UI in App.svelte |
|
||
| `199ad1c` | Task 6 fix: transport shortcuts fallback via `runTransportAction` for Player mode |
|
||
| `3e219b7` | Task 7: Conditional CC rendering, click-to-play, bindable captionsEnabled |
|
||
| `2472dec` | Task 7 fix: bind captionsEnabled between App and VideoPlayer |
|
||
| `1a86a34` | Task 8: FLIP transitions (fly/fade + CSS grid transition) |
|
||
| `7e838dc` | Task 9: Version bump 0.1.3 → 0.2.0 |
|
||
|
||
### Files (17 changed, +1327 −92)
|
||
|
||
**New files:**
|
||
- `src/lib/components/PlayerControls.svelte` — floating overlay with seek bar, play/pause, skip, volume, speed, CC, PiP, fullscreen
|
||
- `src/lib/components/PlayerTimeline.svelte` — simplified waveform-only timeline
|
||
- `src/lib/components/SpeedSelector.svelte` — playback speed popup (0.5x–2x)
|
||
- `tests/lib/components/SpeedSelector.test.ts` — 3 component tests
|
||
- `tests/lib/stores/preferences.test.ts` — 3 preference tests
|
||
- `tests/lib/transport/playback.test.ts` — 10 playback helper tests
|
||
|
||
**Modified files:**
|
||
- `src/App.svelte` — mode switching, auto-hide, keyboard shortcuts, layout conditionals
|
||
- `src/app.css` — grid transitions, player-mode positioning
|
||
- `src/lib/components/VideoPlayer.svelte` — bindable captionsEnabled, click-to-play, conditional CC
|
||
- `src/lib/stores/preferences.svelte.ts` — appMode field + persistence
|
||
- `src/lib/transport/playback.ts` — PiP, fullscreen, playbackRate exports
|
||
- `vite.config.ts` — svelteTesting() plugin for Svelte 5 component tests
|
||
|
||
## Key Architecture Decisions
|
||
|
||
1. **Single always-mounted VideoPlayer** — video element stays in DOM across mode switches, avoiding destruction/recreation
|
||
2. **Proximity-based auto-hide** — controls: any mouse move + 3s timeout; toolbar: 50px from top; timeline: 80px from bottom
|
||
3. **Transport fallback** — `dispatchTransport()` falls back to `runTransportAction()` when TransportControls is null (Player mode)
|
||
4. **CSS Grid transitions** — `.content` animates grid-template changes for smooth layout shifts
|
||
5. **Polling for PiP/fullscreen state** — PlayerControls polls `isPiPActive()`/`isFullscreenActive()` at 500ms
|
||
|
||
## Bugs Found & Fixed
|
||
|
||
1. **Transport shortcuts no-op in Player mode** — TransportControls not rendered → `transportControls` null → all playback shortcuts broken. Fixed with `runTransportAction` fallback.
|
||
2. **captionsEnabled not bound** — App.svelte and VideoPlayer had separate `captionsEnabled` states. Fixed with `bind:captionsEnabled`.
|
||
|
||
## Lessons Learned
|
||
|
||
- When a component that handles keyboard dispatch is conditionally rendered, always provide a fallback path for the keyboard handler
|
||
- Svelte 5 bindable props require explicit `$bindable()` wrapper and typed `$props()` destructuring
|
||
- Svelte 5 component testing in vitest/jsdom needs `svelteTesting()` plugin in vite config (not documented in the plan)
|
||
- Timer-based click/double-click discrimination (250ms delay) is the standard pattern for video players
|
||
|
||
## Test Results
|
||
|
||
- **8 test files, 47 tests passing**
|
||
- **svelte-check: 0 errors** (1 pre-existing a11y warning — missing tabindex on slider role)
|
||
|
||
## Follow-up Items
|
||
|
||
- Add `tabindex="0"` to PlayerControls seek bar slider role (a11y warning)
|
||
- Remove dead `handleVideoClick()` function in VideoPlayer.svelte (unused, superseded by `handleVideoClickWithDelay`)
|
||
- Consider event-based PiP/fullscreen state updates instead of 500ms polling
|
||
- Visual QA with `npm run tauri dev` for transition smoothness
|