feat(clip): crop a clip to a frame drawn on the picture, and give the clip panel the room it needs

An optional crop in clip mode: a Crop toggle puts a frame on the picture (free, 16:9, 9:16 or 1:1;
the whole picture stays the default). The frame is sent in fractions of the box it was drawn in,
with that box's shape; the worker fits the real picture into the box by its display aspect (sample
aspect and rotation aware), drops the part over the bars, and crops first in the filter chain. A
frame covering the whole box is no crop at all, and one off the picture is refused by name.

The crop frame is a ui primitive (CropFrame) shared with the video editor, keyboard-operable.
YouTube: the wide side panel shuts while clipping and comes back after; maximised, the clip panel
docks in the stage. Plex: the picture sits above the clip panel so the frame lies on it.
This commit is contained in:
2026-10-10 12:52:59 +02:00
parent e301632765
commit 520b0afb90
24 changed files with 1260 additions and 153 deletions
+12 -4
View File
@@ -548,16 +548,23 @@ of a full-width panel and nothing on the right.
- **Clip mode (both players, owner's picks A + P1 + a):** a scissors in the control row (Plex: the C5 row,
before the ⋯; YouTube: the actions beside Download, and the docked bar) or `C` opens a clip panel IN the
player; `[` / `]` mark the start / end at the playhead and open it when shut. The video being watched is the
preview — no second stream. Plex: the panel takes the seek bar's and the row's place. YouTube: it sits under
the video (the card's fit counts it, so the video shrinks to make room), and a big view steps back to the
card. The panel: an overview of the whole item (the selection and the zoomed window on it), a zoomed strip
preview — no second stream. Plex: the panel takes the seek bar's and the row's place, and the picture stands
above it. YouTube: it sits under the video (the card's fit counts it, so the video shrinks to make room)
and the wide side panel shuts to its strip while the clip panel is open (owner; it opens again after, unless
the user opened it meanwhile); maximised, the panel takes the docked bar's place; the browser's fullscreen
steps back to maximised. The panel: an overview of the whole item (the selection and the zoomed window on it), a zoomed strip
around the selection with the two ends (`ui/RangeTrack`: each end a slider — arrows step a tenth,
Shift a second; drawn 4px, hit 1.5rem), Start / End fields with a tenth back / forward and "to the
playhead", the length, Play selection (loops), a name, Cancel / Cut clip. On the always-dark chrome the
ends are white and the playhead amber (a light theme's accent all but vanished there). The strip's window
follows the selection when it settles, never under a dragging pointer. Plex seeks once a drag is
released (a seek may restart the transcode); YouTube follows the drag. Escape closes the panel before the
player; the scissors gets the focus back. A cut is a toast with a Downloads link and the usual
player; the scissors gets the focus back. **Crop (owner's picks K1 + a):** a Crop toggle in the panel puts a
frame over the picture (`ui/CropFrame`: on the picture, not its bars; the outside dimmed; drag to move, edges
and corners to resize, thirds while dragging; the arrows move it, a focused corner's resize it, Shift
further; focus ring amber) with a shape — Free, 16:9, 9:16, 1:1 (the largest of it the picture holds) — and
Whole picture; off, the clip is the whole picture. Escape turns the crop off before it closes the panel. A
press outside the frame is still the video's. A cut is a toast with a Downloads link and the usual
"Ready to watch" notification. Limits are the server's: 1 s to 10 min (`lib/clipRange`).
## Dialogs — _Decided_ (C4, built in S3f)
@@ -745,3 +752,4 @@ of a full-width panel and nothing on the right.
| 2026-10-08 | The wheel over the YouTube modal | Over the video: volume. Over a panel with something to scroll: scroll only, never volume, not even at its end. Over a panel with nothing to scroll: volume (owner) | Volume everywhere; a scroll that hands over to the volume at its end | Reading a long description turned the volume down once it hit the end |
| 2026-10-09 | A Plex show card plays | Its picture plays the show's next episode everywhere (an episode with a resume point → Continue + Restart, else the first unwatched, else S1·E1), resolved on the click; the preview names it once up; title and ⓘ open the show (option A + hover ask) | The picture opens the show page (as before); every card carrying `next` | A show card on Discover had no Play while Home's had one; carrying `next` costs a grid page up to 180 queries for the few cards anyone plays |
| 2026-10-10 | Clip mode in the players (E38) | Scissors in the control row + `C`, `[` `]`; the panel in the player with the watched video as preview; overview + zoomed strip on `ui/RangeTrack` (ends are sliders); a toast + the Ready notification after Cut | A ⋯ menu entry; permanent In / Out buttons; a big dialog with its own preview (a second Plex transcode / YouTube iframe) | Owner's picks A, P1, a; a film is hours and a clip seconds, so only a zoomed strip gives a hand the precision. The Downloads editor's cuts moved onto the same track (keyboard-steppable now) |
| 2026-10-10 | Crop in clip mode (E38) | A Crop toggle in the clip panel: a frame over the playing picture (`ui/CropFrame`, the editor's crop too), Free / 16:9 / 9:16 / 1:1, Whole picture; maximised YouTube keeps the panel in the bar's place; the wide side panel shuts while clipping | A frame always on the picture in clip mode (K2); a free shape only | Owner's K1 + a: a crop is rare, so it never covers the picture unasked; a phone or square clip needs a held shape. The iframe hides its picture's size: drawn on the 16:9 box, the cut fits it to the real picture |