Files
readest/apps/readest-app/src/utils/shortcutKeys.ts
T
Huang Xin 3d4d1482aa feat: add keyboard shortcuts help dialog (#3653)
Add a keyboard shortcuts help dialog toggled with '?' that displays all
shortcuts grouped by section with platform-appropriate key rendering.

Restructure DEFAULT_SHORTCUTS to include i18n descriptions and section
metadata. Translate 37 new keys across 29 locales.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 14:59:38 +08:00

89 lines
2.2 KiB
TypeScript

const MODIFIER_MAP_MAC: Record<string, string> = {
ctrl: '⌘',
cmd: '⌘',
alt: '⌥',
opt: '⌥',
shift: '⇧',
meta: '⌘',
};
const MODIFIER_MAP_OTHER: Record<string, string> = {
ctrl: 'Ctrl',
cmd: 'Ctrl',
alt: 'Alt',
opt: 'Alt',
shift: 'Shift',
meta: 'Win',
};
const MODIFIERS = new Set(['ctrl', 'cmd', 'alt', 'opt', 'shift', 'meta']);
const SPECIAL_KEYS: Record<string, string> = {
arrowleft: '←',
arrowright: '→',
arrowup: '↑',
arrowdown: '↓',
escape: 'Esc',
pagedown: 'PgDn',
pageup: 'PgUp',
' ': 'Space',
tab: 'Tab',
enter: 'Enter',
};
export const formatKeyForDisplay = (key: string, isMac: boolean): string => {
const parts = key.split('+');
const modMap = isMac ? MODIFIER_MAP_MAC : MODIFIER_MAP_OTHER;
const modifiers: string[] = [];
let baseKey = '';
for (const part of parts) {
const lower = part.toLowerCase();
if (MODIFIERS.has(lower)) {
modifiers.push(modMap[lower]!);
} else {
baseKey = part;
}
}
// Map special keys or capitalize single characters
const lowerBase = baseKey.toLowerCase();
let displayKey: string;
if (SPECIAL_KEYS[lowerBase]) {
displayKey = SPECIAL_KEYS[lowerBase]!;
} else if (baseKey.length === 1 && baseKey >= 'a' && baseKey <= 'z') {
displayKey = baseKey.toUpperCase();
} else {
displayKey = baseKey;
}
if (isMac) {
return [...modifiers, displayKey].join('');
}
return [...modifiers, displayKey].join('+');
};
const MAC_MODIFIERS = new Set(['cmd', 'opt']);
const OTHER_MODIFIERS = new Set(['ctrl', 'alt']);
const hasModifier = (key: string, modifiers: Set<string>): boolean => {
const parts = key.split('+');
return parts.some((p) => modifiers.has(p.toLowerCase()));
};
export const filterPlatformKeys = (keys: string[], isMac: boolean): string[] => {
const preferred = isMac ? MAC_MODIFIERS : OTHER_MODIFIERS;
const excluded = isMac ? OTHER_MODIFIERS : MAC_MODIFIERS;
const platformKeys = keys.filter((k) => hasModifier(k, preferred));
const agnosticKeys = keys.filter((k) => !hasModifier(k, preferred) && !hasModifier(k, excluded));
if (platformKeys.length > 0 || agnosticKeys.length > 0) {
return [...agnosticKeys, ...platformKeys];
}
// Fallback: return all keys if none match
return keys;
};