fix(clip): typed times belong to their value, and every mark on the player chrome can be seen

TimecodeField drops a draft when the value moves under it (another segment selected, a nudge), so
leaving the field never commits it to another cut; it is invalid only once offered, with a line
under it saying how to type a time. On the always-dark chrome media-scope points every focus ring
at --media-mark and errors at the new --media-danger (a light theme's accent and danger were under
3:1 there). A track whose dragged handle unmounts ends the drag on the pointer's release instead of
locking up; a remove refused while clips are queued leaves no focus to take later; each remove-cut
button names its cut and is a 24px target. DESIGN.md records the media tokens; stale comments fixed.
This commit is contained in:
2026-10-10 20:28:52 +02:00
parent a867145bcb
commit c27cfdcb9c
18 changed files with 303 additions and 71 deletions
@@ -150,18 +150,35 @@ describe("RangeTrack", () => {
expect(h.onChange).not.toHaveBeenCalled();
});
it("rings a focused handle in the caller's colour, for a track on the dark media chrome", () => {
render(
it("ends a drag whose handle leaves the window under it, so the track takes the next press", () => {
const onChange = vi.fn();
const onSeek = vi.fn();
const { rerender } = render(
<RangeTrack duration={200} label="Clip" handles={[handle({ onChange })]} onSeek={onSeek} />,
);
const track = screen.getByRole("group", { name: "Clip" });
box(track);
fireEvent.pointerDown(screen.getByRole("slider", { name: "In" }), {
button: 0,
clientX: 250,
pointerId: 7,
});
// A key moves the mark out of the window while the finger is still down: the handle unmounts.
rerender(
<RangeTrack
duration={100}
duration={200}
label="Clip"
handles={[handle()]}
handleRingClassName="focus-visible:outline-warn"
handles={[handle({ onChange, time: 20 })]}
view={{ from: 100, to: 200 }}
onSeek={onSeek}
/>,
);
expect(screen.getByRole("slider", { name: "In" }).className).toContain(
"focus-visible:outline-warn",
);
expect(screen.queryByRole("slider", { name: "In" })).toBeNull();
// Released where the handle no longer is: the drag ends, as final, where the mark now is.
fireEvent.pointerUp(document.body, { pointerId: 7 });
expect(onChange).toHaveBeenLastCalledWith(20, true);
fireEvent.pointerDown(track, { button: 0, clientX: 500, pointerId: 8 });
expect(onSeek).toHaveBeenCalledWith(150, false);
});
it("seeks on a press of the bare track, following the drag, final on release", () => {