fix(reader): add Alt+P proofread shortcut and let Shift+P exit paragraph mode (#4717) (#4723)

On Windows/Linux, Ctrl+P opens the proofread/replace rules but also
triggers the browser print dialog, since the selection shortcut handlers
return undefined and never preventDefault. Add a print-free `alt+p`
binding for Proofread Selection alongside ctrl+p/cmd+p.

Also fix Shift+P being unable to exit paragraph mode: the paragraph
overlay attaches a capture-phase keydown listener that calls
stopImmediatePropagation() on every key while visible, so the global
toggle shortcut never reached useShortcuts. Honor the configured
"Toggle Paragraph Mode" shortcut directly in the overlay so the same
shortcut that enters paragraph mode also exits it.

Extract the shared shortcut event-matching into matchesShortcut() in
utils/shortcutKeys.ts and reuse it from useShortcuts instead of its
private duplicate.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Huang Xin
2026-06-22 10:57:04 +08:00
committed by GitHub
parent b87c735c1e
commit a6d28ffcdf
7 changed files with 134 additions and 48 deletions
@@ -13,6 +13,8 @@ import {
ParagraphPresentation,
} from '@/utils/paragraphPresentation';
import { getTextSubRange } from '@/services/tts/wordHighlight';
import { loadShortcuts } from '@/helpers/shortcuts';
import { matchesShortcut } from '@/utils/shortcutKeys';
import TTSFollowIndicator, { TtsSyncStatus } from '../tts/TTSFollowIndicator';
import { buildTtsHighlightCssText } from './paragraphTts';
@@ -342,6 +344,16 @@ const ParagraphOverlay: React.FC<ParagraphOverlayProps> = ({
return;
}
// The overlay swallows every keydown in the capture phase, so the global
// toggle shortcut (Shift+P by default) never reaches useShortcuts. Honor
// it here so the same shortcut that enters paragraph mode also exits it
// (#4717).
if (matchesShortcut(e, loadShortcuts().onToggleParagraphMode.keys)) {
e.preventDefault();
onCloseRef.current?.();
return;
}
const action = getParagraphActionForKey(e.key, activePresentation ?? viewSettings);
if (action === 'next') {
e.preventDefault();