Commit 20bbd6e
feat: zoom-to-video, mute, YouTube Trusted Types fix, recovery paths
## Zoom to video
New global hotkey (Alt+Shift+V) and strip button that finds the largest
<video> on the page and crops to its bounding rect. If the player is
offscreen the page smooth-scrolls to center it first, then the crop
animates in. Ephemeral (doesn't persist) so a missing/relocated video
on the next visit doesn't strand a stale crop; on toggle-off any
manual crop the user had saved is restored intact. If no usable
video exists the button flashes red for 700ms.
## Mute
Alt+Shift+M and a strip speaker button that mutes every <video> and
<audio> on the page. Icon reflects real state — a volumechange
listener on media elements (attached via MutationObserver so it
catches lazy-loaded players too) keeps the UI in sync with page-side
mute toggles like YouTube's `M` key.
## Trusted Types / YouTube compatibility
YouTube (and much of Google, GitHub, many banks) ships
`Content-Security-Policy: require-trusted-types-for 'script'`, which
throws a TypeError on every `element.innerHTML = ...` assignment.
Our IIFE died on the first such assignment, leaving the page with no
strip, no hotzone, and no recovery affordances. Fix: new `setInner`
helper with a two-tier strategy — registers a named Trusted Types
policy if the host CSP allows it, otherwise uses DOMParser (whose
output document isn't subject to the page's CSP). All 15 innerHTML
assignments routed through the helper.
## Recovery affordances
- `Alt+Shift+S` global hotkey: force-shows the strip. Reparents the
container into the current home, un-hides it, cancels any pending
hide timer, releases stuck click-through, and calls showStrip().
- Tray: new "Reload Page" item (hard Rust-side reload, the nuclear
option when JS is hung) and "Show Control Strip" item (mirrors the
hotkey via Rust eval).
- DevTools enabled in release builds (Tauri `devtools` feature) so
power users can inspect their own sessions.
## Fullscreen awareness
New `fullscreenchange` listener reparents the container into the
active fullscreen element. The Fullscreen API bypasses z-index
(only the fullscreen element's descendants render), so without this
the strip is invisible during YouTube/Plex fullscreen.
## Supporting refactors
- Mute observer only calls `updateMuteIcon` when video/audio nodes
are actually added or removed, not on every DOM mutation — saves
significant main-thread CPU on YouTube-scale churn.
- applyCrop/removeCrop now take an explicit `persist` flag so
ephemeral (zoom-to-video) and saved (manual) crops don't step on
each other's config state.
## Version bump
1.2.0 → 1.3.0 across Cargo.toml, tauri.conf.json, package.json.
Verified: cargo test (41 pass), clippy clean, release build clean,
runtime smoke-tested on YouTube including the previously-broken
hijack case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent e98084f commit 20bbd6e
9 files changed
Lines changed: 621 additions & 34 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
55 | 55 | | |
56 | 56 | | |
57 | 57 | | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
58 | 81 | | |
59 | 82 | | |
60 | 83 | | |
| |||
68 | 91 | | |
69 | 92 | | |
70 | 93 | | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
71 | 97 | | |
72 | 98 | | |
73 | 99 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
99 | 102 | | |
100 | 103 | | |
101 | 104 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | | - | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
145 | 148 | | |
146 | 149 | | |
147 | 150 | | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
148 | 166 | | |
149 | 167 | | |
150 | 168 | | |
| |||
0 commit comments