From 66a7adb5b446a5f5e76b01670fe8a90754f27b79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 30 May 2026 16:59:54 +0000 Subject: [PATCH 1/2] 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 --- packages/studio/src/player/components/PlayerControls.tsx | 2 +- packages/studio/src/player/components/Timeline.tsx | 3 ++- packages/studio/src/player/components/useSeekBarDrag.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/studio/src/player/components/PlayerControls.tsx b/packages/studio/src/player/components/PlayerControls.tsx index 50628ce7d..488086800 100644 --- a/packages/studio/src/player/components/PlayerControls.tsx +++ b/packages/studio/src/player/components/PlayerControls.tsx @@ -282,7 +282,7 @@ const SeekBar = memo(function SeekBar({ aria-valuemin={0} aria-valuemax={Math.round(duration)} aria-valuenow={0} - className={`min-w-[96px] flex-1 h-6 flex items-center group ${ + 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 ${ disabled ? "cursor-not-allowed opacity-50" : "cursor-pointer" }`} style={{ touchAction: "none" }} diff --git a/packages/studio/src/player/components/Timeline.tsx b/packages/studio/src/player/components/Timeline.tsx index 017051a05..7e61b10b8 100644 --- a/packages/studio/src/player/components/Timeline.tsx +++ b/packages/studio/src/player/components/Timeline.tsx @@ -428,7 +428,8 @@ export const Timeline = memo(function Timeline({ >
setIsDragOver(false)} onDrop={handleAssetDrop} diff --git a/packages/studio/src/player/components/useSeekBarDrag.ts b/packages/studio/src/player/components/useSeekBarDrag.ts index 81c78d1f1..5fd0f054c 100644 --- a/packages/studio/src/player/components/useSeekBarDrag.ts +++ b/packages/studio/src/player/components/useSeekBarDrag.ts @@ -110,6 +110,7 @@ export function useSeekBarDrag( window.removeEventListener("pointercancel", onUp); document.removeEventListener("visibilitychange", onVisibilityChange); window.removeEventListener("blur", cleanup); + target.blur(); }; const onUp = (ev: PointerEvent) => { if (ev.pointerId !== pointerId) return; From 3866214e2e835518ce1f48b0749a80a185c389b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 30 May 2026 17:05:36 +0000 Subject: [PATCH 2/2] 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. --- packages/studio/src/components/editor/propertyPanelColor.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/studio/src/components/editor/propertyPanelColor.tsx b/packages/studio/src/components/editor/propertyPanelColor.tsx index bb5fedbb3..2a8cb0fd5 100644 --- a/packages/studio/src/components/editor/propertyPanelColor.tsx +++ b/packages/studio/src/components/editor/propertyPanelColor.tsx @@ -80,6 +80,9 @@ function ColorSlider({ event.currentTarget.setPointerCapture(event.pointerId); commitFromClientX(event.clientX); }} + onPointerUp={(event) => { + event.currentTarget.blur(); + }} onPointerMove={(event) => { if (disabled || event.buttons !== 1) return; commitFromClientX(event.clientX);