Repository navigation
Conversation
The native bar handled keys inside itself, differently per browser, and the page could not reliably intercept them, so Space played and paused the recording with one thing focused and typed or pressed with another. The editor's video now has no controls attribute, and a PlayerControls pill under the picture replaces the bar: play/pause, "Play with context", speed (0.5-2x, a menu), a position bar with the time beside it, and the volume. The rule is now the same everywhere: a bare Space never plays or pauses the recording from the page (the head script's handler is removed), and Ctrl+Space always does. A focused button is kept from also being pressed by Ctrl+Space, which would cancel the toggle out. "Play with context" plays the block the caret was last in, or the one under the playhead before it has been anywhere. A recording that is sound alone drops the picture frame and shows the file name under the controls. The subtitle overlay no longer has a bar to clear, so the script that raised it is removed. The icon for "Play with context" is a standard Material Symbols stand-in, one constant to change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
A container with inline-size containment has no width of its own to offer. In the editor's column, a flex column that aligns its children to the start, the wrap came out 0 wide and the strip and legend with it. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
The controls move from a pill under the picture into the video frame, laid over its foot on a dark tone as the browser's own bar was, with the position bar above and the buttons below. They go while the picture plays and nothing is being done to them, and come back for the pointer, for keyboard focus, on pause, and always where there is no hover. The subtitle preview rises clear of them while they are up. A recording with no picture turns the frame into the player's own dark surface with the file name above the same rows. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
Measured in headless Chromium, the animation-frame loop that moved the thumb cost about a tenth of a core while playing, against about a hundredth on timeupdate (four times a second), for movement nobody can see: the time changes once a second. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
A tap that wakes the faded controls no longer also presses the button under the finger, and the controls are out of reach while asleep. The position bar no longer freezes after a click on its thumb that does not move it, and the controls do not fall asleep mid-drag. The volume slider is left out where the browser cannot set the volume (iOS). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
…ting shortcuts only) Spell out that the player carries only what the video element does itself plus buttons that fire an existing shortcut, and that the waveform idea was decided against, so later changes are weighed against upkeep. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
… away The button now plays around where the recording is: the caption under the playhead with its context, or, in a pause, in a stretch not marked as speech or in a recording without word timings, the words around the moment or the plain window of five seconds either side. It no longer reads the caret (which stays "last settled in" and would have beaten the playhead for good) and no longer answers with a message. A press from where the last one stopped is the same stretch again, so the stretch does not walk forward a caption at a time. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
…der's own A press plays the same stretch again if it is running, or has stopped and neither the playhead nor the caret has been moved since. The player keeps that state next to the video (set by the range script, cleared on a pause, a seek or playing on), the editor clears it when the caret settles in another caption. This replaces the position tolerance, which could not tell a stretch still running from one the reader had moved to. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
The overlay shows what a viewer sees while the recording plays, so its
on/off belongs with the player rather than among the editor's own
markings, and the player's own controls make that possible.
The button sits between "Play with context" and the speed, for
subtitles only (a transcription has no overlay and gets no button). It
is a toggle button: one name ("Subtitles") with aria-pressed, the
closed-captions box when on and the struck-through one when off, so the
state is in the shape and not only in a colour. It flips at once and
emits "captions"; the page keeps the setting as before and redraws the
overlay without waiting for a timeupdate. The "Subtitle overlay" switch
is removed.
AGENTS.md: the player's scope now reads "a shortcut or a setting the
service already has", and the CC button is described.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
The audio surface hides the subtitle overlay (there is no picture to lay it over), so the button would change a setting with no visible effect. It is now only drawn for subtitles over a picture. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCjpD3ehigLiUGH1oVQQAW
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Own player controls in place of the browser's bar
Base branch:
develop.Space sometimes played and paused the recording and sometimes did not. The cause was the native
<video controls>bar: it handles keys inside itself, differently in each browser, and the page cannot reliably intercept them. Which thing had focus decided whether Space meant "play", "type a space" or "press this button".The rule
A bare Space never plays or pauses the recording from the page. Ctrl+Space always does. This is now true because of how the page is built, not because something has to be watched for:
<video>has nocontrolsattribute and so no tab stop;PlayerControlsreplaces the bar.SRTEditor.handle_key_event's. Because a focused button is also pressed when Space is released, the controls stop the browser's own action on Ctrl+Space (preventDefaultonly; the event still reaches the page), or the two toggles would cancel out.What the controls are
Laid over the foot of the picture, inside the video frame, on a dark tone, the way the browser's own bar is. Two rows: the position bar with the time on either side, and under it the buttons. All white, in both themes, because what they sit on is a video.
auto_read_play, in the font NiceGUI already ships). Tooltip "Play with context (Ctrl + Shift + Space)".menu/menuitemradio, arrow keys, Escape returns focus). Not remembered between sessions. It follows into play with context, whose stop is decided on position, never on time.The times, the speed and the menu are in the page's own type (14px, weight 500, as the "Uncertain words" choices beside the video), with tabular numerals so the figures do not jitter.
No fullscreen, picture-in-picture or download. The bars are native range inputs, so the keyboard, the pointer and screen readers get what the browser does for a slider: arrows move 5 s, Page Up/Down 30 s, Home/End the ends; each has a name and a spoken value (
12 minutes 34 seconds of 1 hour 23 minutes), and the figures beside the bar arearia-hiddenso they are not read twice.Everything the controls show is read off the video element, so it is right whoever started playback (the button, Ctrl+Space, the timeline, a caption played with its context). Nothing is sent to the server for it.
The position follows
timeupdate(about four times a second), not an animation-frame loop. That was tried first and measured at about 11% of a core in headless Chromium while playing, against about 1% ontimeupdateand 0.1% for the bare video; the time changes once a second and the thumb, over a recording of any length, a fraction of a pixel, so nothing visible was lost.The volume slider is only drawn where the browser lets a page set the volume (iOS never does; the mute button stays).
A mouse press on the buttons does not take focus (the same reason the Highlight button does it), so the reader can click Play and keep typing, and a Space after the click is a space in the text.
Going away and coming back
While the video plays and nothing is being done to the controls they fade out (3 s after the pointer last moved over the frame, 1 s after it leaves, not while the speed menu is open, never for a recording without video). The pointer, a tap on the video, keyboard focus anywhere in them (
:focus-within) or pause brings them back. Asleep they are alsopointer-events: none, and the click that follows a tap which woke them is swallowed (touch and pen only), so waking them never presses the button that was under the finger. The subtitle preview rises clear of them while they are up and drops back to the foot of the video when they go (:has(), short transition, none forprefers-reduced-motion).Play with context
The button has no caret to read the block from, unlike the shortcut.
TranscriptEditor.play_with_context_here()plays the block the caret last settled in (caret_block, kept when the caret leaves); before the caret has been anywhere, or if that block has gone, the caption under the playhead; with neither it says so. The icon is the singleCONTEXT_ICONconstant inplayer_controls.js.Audio files
A recording that is sound alone (
videoWidth === 0once the header is read) turns the frame into the player's own dark surface: no video and no subtitle preview, the file name where the video was, and the same two rows under it. The video's controls are the audio player's laid over a video, which is what makes them siblings. The speed menu opens downward there.Colours and contrast
!importantin@layer theme, so this one has to be in that layer and specific enough to win.Also in this branch
fix/timeline-strip-width(4382ac5, also open as its own change):.speech-timeline-wrapgotcontainer-type: inline-sizein the tray change, which gives it no width of its own; in the editor's column (a flex column aligned to the start) it came out 0 wide and the strip and legend with it. It is givenwidth: 100%. If that change is merged first this branch's diff is clean of it.How it was checked
tests/test_player_controls.py, 40 tests: the video has no controls, the controls' place in the frame, the removed Space handler and overlay script, the Ctrl+Space guard on both halves of the key, names/roles/values of every control, the speeds, the audio-only path, the look and the type, the fade (both ways), the white ring, the menu fitting a short frame, andplay_with_context_here(caret block, a block that has gone, the playhead fallback, nothing to play, no edit, nothing on screen). Existing tests for the old Space handler and the overlay script were removed or rewritten. Full suite: 1805 passed (tests/test_recording.pyexcluded).In Chromium against a real video, a white video and the Material icon fonts NiceGUI ships, with real key presses: Space on the focused Play button plays and pauses; Ctrl+Space on it toggles exactly once; Space in a text area leaves the player alone; Enter/Space/click on Play with context emit once, Ctrl+Shift+Space on it emits none; the speed menu opens on the speed in force, arrows and Enter pick, Escape returns focus; the bar moves by 5 s / 30 s / End; the controls go while playing and come back for the pointer and for Tab; narrow (360 px), dark, and an audio file.
In the real app, in Chrome, light and dark: the controls sit in the frame with the frame's rounded corners; Ctrl+Space from the text pauses once; Space on the focused Play button plays; Ctrl+Space on it toggles once; Play with context from a click plays the block's context; the speed menu opens upward with the right item marked; the controls fade while playing and the preview drops.
With the timeline docked at the foot of the page: the controls are unaffected, the in-column strip's place closes up, and the column reads video, then the switches.
Not checked
pages/view.py) is a different page and keeps its native bar.pages/srt.py,utils/common.py,utils/srt_render.py) describe the F-74 workaround as guarding against the native Space shortcut, which no longer exists. The workaround is harmless and was left alone.