diff --git a/docs/superpowers/specs/2026-09-23-v021-polish-and-cut-clip-design.md b/docs/superpowers/specs/2026-09-23-v021-polish-and-cut-clip-design.md new file mode 100644 index 0000000..87b2ecd --- /dev/null +++ b/docs/superpowers/specs/2026-09-23-v021-polish-and-cut-clip-design.md @@ -0,0 +1,159 @@ +# v0.2.1 โ€” Polish, Icons, Cut Clip & Shortcuts Reference + +## Overview + +Incremental polish release building on v0.2.0 Player mode. Six changes: controls position tweak, Lucide icon migration, blur animation fix, mode toggle shortcuts, cut-clip feature, and keyboard shortcuts reference in the About dialog. + +--- + +## 1. Floating Controls Position & Blur Fix + +### Position + +Raise `.player-controls-overlay` from `bottom: 48px` to `bottom: 64px` (+16px). + +### Blur Pop-in Fix + +**Problem:** `backdrop-filter: blur(8px)` is on `.player-controls-panel` while the parent `.player-controls-overlay` transitions `opacity: 0 โ†’ 1`. WebKit doesn't smoothly composite `backdrop-filter` through an ancestor `opacity` transition โ€” the blur snaps on once opacity crosses a rendering threshold. + +**Fix:** Move the blur background into a `::before` pseudo-element on `.player-controls-panel`. The pseudo-element handles `background` + `backdrop-filter` and has its own `opacity` transition driven by the `.visible` class on the overlay ancestor. The panel itself becomes `position: relative` with no background/backdrop-filter of its own. + +```css +.player-controls-panel { + position: relative; + /* background and backdrop-filter removed */ +} + +.player-controls-panel::before { + content: ''; + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.75); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border-radius: 12px; + z-index: -1; + opacity: 0; + transition: opacity 0.25s ease; +} + +.player-controls-overlay.visible .player-controls-panel::before { + opacity: 1; +} +``` + +--- + +## 2. Lucide Icon Migration + +Install `lucide-svelte` as a dependency. Replace all emoji and text-glyph icons with Lucide SVG components across `PlayerControls.svelte`, `TransportControls.svelte`, and the `App.svelte` toolbar. + +### Icon Mapping + +| Current | Lucide Component | Location | +|---|---|---| +| ๐Ÿ”‡ / ๐Ÿ”‰ / ๐Ÿ”Š | `VolumeX` / `Volume1` / `Volume2` | PlayerControls, TransportControls | +| โช | `SkipBack` | PlayerControls | +| โšโš / โ–ถ | `Pause` / `Play` | PlayerControls, TransportControls | +| โฉ | `SkipForward` | PlayerControls | +| CC (text) | `Captions` | PlayerControls | +| โง‰ | `PictureInPicture2` | PlayerControls | +| โ›ถ / โค“ | `Maximize` / `Minimize` | PlayerControls | +| โš™ | `Settings` | App toolbar | +| โ„น | `Info` | App toolbar | +| โฌ’ / โฌ“ | `PanelLeft` / `PanelBottom` | App toolbar | +| โœ‚๏ธ (new) | `Scissors` | PlayerControls (cut clip button) | +| โ—„K / Kโ–บ | `ChevronFirst` / `ChevronLast` | TransportControls | +| โ—„\| / \|โ–บ | `StepBack` / `StepForward` | TransportControls | + +Icons render inline as SVGs. Size: 16โ€“18px to match existing button dimensions. + +--- + +## 3. Mode Toggle Shortcuts + +Both `C` and `P` keys become bidirectional toggles. Pressing either key switches from the current mode to the other mode. + +Implementation: both the `'c'/'C'` and `'p'/'P'` cases in `handleGlobalKeydown` call `toggleMode()` instead of hard-coding a target mode via `setAppMode()`. + +--- + +## 4. Cut Clip Feature + +### Button + +A scissors icon (`Scissors` from Lucide) in the `PlayerControls` right control group, positioned between the speed selector and the CC button. + +### Behavior + +- **Left-click / `X` key:** Create a 10-second clip starting at the current playhead position (`currentTime` โ†’ `currentTime + 10`). Clamp end to `session.duration`. Switch to Clipper mode. Playhead stays at its current position. +- **Right-click / `Z` key:** Create a 10-second clip ending at the current playhead position (`currentTime - 10` โ†’ `currentTime`). Clamp start to `0`. Switch to Clipper mode. Playhead stays at its current position. + +### Implementation + +- Add a `cutClip(position: 'at' | 'before')` helper. It calls `addClip(start, end)` from the clips store, then `setAppMode('clipper')`. +- Pass an `onCutClip` callback prop to `PlayerControls`. The button's `onclick` calls `onCutClip('at')`, `oncontextmenu` prevents default and calls `onCutClip('before')`. +- In `handleGlobalKeydown`: `X`/`x` โ†’ `cutClip('at')`, `Z`/`z` โ†’ `cutClip('before')`. Only active in Player mode. +- **Tooltip:** "Cut clip at playhead (X) ยท Right-click: 10s before (Z)" + +### Edge Cases + +- No video loaded (`session.duration === 0`): no-op. +- Playhead < 10s from start: `Z`/right-click clamps start to 0. +- Playhead < 10s from end: `X`/left-click clamps end to duration. + +--- + +## 5. Tabbed About Dialog with Keyboard Shortcuts + +### Tab Structure + +Two tabs: **About** (default) and **Shortcuts**. + +### About Tab + +Identical to current content (icon, app name, version, author, license, repo link, close button). Dialog stays at ~360px wide. + +### Shortcuts Tab + +Two-column layout (key on left, description on right) grouped by section headers. Dialog expands to ~520px wide with `max-height: 70vh` and vertical scroll. Width change transitions smoothly (`transition: max-width 0.2s ease`). + +Tab switcher: text tabs at the top of the dialog, active tab gets accent-color underline. + +### Complete Shortcut Reference + +**Global (both modes):** + +| Key | Action | +|---|---| +| `Space` | Play / Pause | +| `K` | Play / Pause | +| `J` | Shuttle slower (โˆ’0.25ร—) | +| `L` | Shuttle faster (+0.25ร—) | +| `โ†` | Seek back 5s | +| `โ†’` | Seek forward 5s | +| `Shift+โ†` | Seek back 1s | +| `Shift+โ†’` | Seek forward 1s | +| `,` | Previous frame | +| `.` | Next frame | +| `<` (Shift+,) | Previous keyframe | +| `>` (Shift+.) | Next keyframe | +| `C` / `P` | Toggle mode | +| `Cmd+/` | About | + +**Player mode:** + +| Key | Action | +|---|---| +| `F` | Toggle fullscreen | +| `X` | Cut clip at playhead โ†’ Clipper | +| `Z` | Cut clip 10s before playhead โ†’ Clipper | + +**Clipper mode:** + +| Key | Action | +|---|---| +| `I` | Set in-point | +| `O` | Set out-point | +| `Delete` / `Backspace` | Remove selected clip | +| `Cmd+E` | Export |