fix: stop reader key navigation leaking behind open overlays

Arrows/j/k/Home/End bubbled to the reader's window handler while a
jump/settings/action/collections/tags/context overlay was open, paging the
chapter behind it (FE-1); native select/contenteditable weren't exempt (FE-4).
New unit-tested isTypingTarget() + an overlay-open early return fix both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-07-13 21:28:57 +02:00
parent c31830468c
commit cd5358dc9b
6 changed files with 63 additions and 6 deletions

View File

@@ -3,6 +3,7 @@
import { browser } from '$app/environment';
import { afterNavigate, goto, invalidateAll } from '$app/navigation';
import { fileUrl, ApiError } from '$lib/api/client';
import { isTypingTarget } from '$lib/readerKeys';
import { GAP_PX, type ReaderPageGap } from '$lib/api/preferences';
import { preferences } from '$lib/preferences.svelte';
import { updateReadProgress } from '$lib/api/read_progress';
@@ -675,9 +676,9 @@
);
function onKeydown(e: KeyboardEvent) {
// Don't hijack keys while the user is typing in an input.
const target = e.target as HTMLElement | null;
if (target && (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA')) return;
// Don't hijack keys while the user is typing or focused on a native
// control (input/textarea/select/contenteditable own their own keys).
if (isTypingTarget(e.target)) return;
// `?` opens the keyboard-shortcut help overlay. While it's open,
// swallow every key here so paging can't happen behind it — the
// Sheet owns its own Escape/close.
@@ -687,6 +688,20 @@
return;
}
if (shortcutsOpen) return;
// Any other overlay (jump/settings/action/collections/tags/context menu)
// owns its own keys; swallow reader paging so arrows/j/k/Home/End can't
// act on the chapter *behind* the overlay. Those overlays only
// stopPropagation() on Escape, so navigation keys otherwise bubble here.
if (
chapterJumpOpen ||
settingsOpen ||
contextMenuOpen ||
actionSheetOpen ||
collectionsModalOpen ||
tagsModalOpen
) {
return;
}
// Esc always exits fullscreen if active — applies in both
// modes, including when the bars are hidden. Handled before
// the per-mode switches so it doesn't get shadowed.