Skip to content

Commit 194ad6f

Browse files
fix(studio): timeline seekbar focus blocks NLE keyboard shortcuts (#1137)
* fix(studio): blur seekbar after seek so NLE shortcuts resume Clicking the timeline seekbar (role=slider) explicitly called e.currentTarget.focus(), leaving focus on the slider element. shouldIgnorePlaybackShortcutTarget filters out [role='slider'] targets, so all playback shortcuts (Space/J/K/L/arrows) were silently blocked until the user clicked away. - blur() the seekbar in cleanup() so focus returns after pointer release - replace the default white focus ring with a focus-visible ring (keyboard-only) - add tabIndex={-1} + outline-none to the NLE timeline scroll div, which Chrome auto-focuses for overflow:auto elements Fixes #1136 * fix(studio): blur color slider on pointer release (sister bug) Same pattern as the seekbar: role=slider + tabIndex=0 receives natural browser focus on click, blocking playback shortcuts while focused. ColorSlider never had an onPointerUp handler; adding one to blur immediately after release matches the seekbar's cleanup() blur.
1 parent 100d355 commit 194ad6f

4 files changed

Lines changed: 7 additions & 2 deletions

File tree

packages/studio/src/components/editor/propertyPanelColor.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ function ColorSlider({
8080
event.currentTarget.setPointerCapture(event.pointerId);
8181
commitFromClientX(event.clientX);
8282
}}
83+
onPointerUp={(event) => {
84+
event.currentTarget.blur();
85+
}}
8386
onPointerMove={(event) => {
8487
if (disabled || event.buttons !== 1) return;
8588
commitFromClientX(event.clientX);

packages/studio/src/player/components/PlayerControls.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ const SeekBar = memo(function SeekBar({
282282
aria-valuemin={0}
283283
aria-valuemax={Math.round(duration)}
284284
aria-valuenow={0}
285-
className={`min-w-[96px] flex-1 h-6 flex items-center group ${
285+
className={`min-w-[96px] flex-1 h-6 flex items-center group outline-none focus-visible:ring-1 focus-visible:ring-white/30 focus-visible:rounded ${
286286
disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer"
287287
}`}
288288
style={{ touchAction: "none" }}

packages/studio/src/player/components/Timeline.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ export const Timeline = memo(function Timeline({
428428
>
429429
<div
430430
ref={scrollRef}
431-
className={`${zoomMode === "fit" ? "overflow-x-hidden" : "overflow-x-auto"} overflow-y-auto h-full`}
431+
tabIndex={-1}
432+
className={`${zoomMode === "fit" ? "overflow-x-hidden" : "overflow-x-auto"} overflow-y-auto h-full outline-none`}
432433
onDragOver={handleAssetDragOver}
433434
onDragLeave={() => setIsDragOver(false)}
434435
onDrop={handleAssetDrop}

packages/studio/src/player/components/useSeekBarDrag.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ export function useSeekBarDrag(
110110
window.removeEventListener("pointercancel", onUp);
111111
document.removeEventListener("visibilitychange", onVisibilityChange);
112112
window.removeEventListener("blur", cleanup);
113+
target.blur();
113114
};
114115
const onUp = (ev: PointerEvent) => {
115116
if (ev.pointerId !== pointerId) return;

0 commit comments

Comments
 (0)