feat: make shortcuts easier to add across the app

This commit is contained in:
Zoe
2026-02-25 19:47:33 -06:00
parent 50cb195486
commit d22d08e99f
4 changed files with 57 additions and 21 deletions
+12 -5
View File
@@ -107,13 +107,20 @@ const handleKeyDown = async (event: KeyboardEvent) => {
}; };
const handleWindowKeyDown = async (event: KeyboardEvent) => { const handleWindowKeyDown = async (event: KeyboardEvent) => {
if (event.ctrlKey || event.metaKey) { if (event.defaultPrevented) return;
return; if (event.ctrlKey || event.metaKey || event.altKey) return;
}
if (document.activeElement === document.body) { if (document.activeElement === document.body) {
// redirect all standard keyboard input to the input field const isPrintable = event.key.length === 1;
inputRef.value?.focus();
if (isPrintable) {
inputRef.value?.focus();
} else if (event.key === 'Enter') {
event.preventDefault();
handleSubmit();
inputRef.value?.focus();
}
} }
}; };
+7 -1
View File
@@ -4,7 +4,12 @@ import Settings from './Settings.vue';
import { DialogType } from '~/composables/useDialog'; import { DialogType } from '~/composables/useDialog';
const { open, page, data, close, confirm } = useDialog(); const { open, page, data, close, confirm, openDialog } = useDialog();
const { addShortcut } = useKeyboardShortcuts();
const unbindShortcut = addShortcut(['ctrl', ','], () => {
openDialog(DialogType.Settings);
});
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') { if (event.key === 'Escape') {
@@ -24,6 +29,7 @@ onUnmounted(() => {
if (open.value) { if (open.value) {
document.body.removeEventListener('keydown', handleKeyDown); document.body.removeEventListener('keydown', handleKeyDown);
} }
unbindShortcut();
}); });
</script> </script>
+32 -13
View File
@@ -1,23 +1,41 @@
export const useKeyboardShortcuts = () => { export const useKeyboardShortcuts = () => {
const { toggle: toggleSidebar } = useSidebar(); const shortcutsMap = new Map<string, () => void>();
const { toggle: openSettings, open: isSettingsOpen } = useSettings();
const normalize = (combo: string[]) =>
combo.map(k => k.toLowerCase()).sort().join('+');
const addShortcut = (combo: string[], callback: () => void) => {
const normalizedKey = normalize(combo);
shortcutsMap.set(normalizedKey, callback);
return () => {
shortcutsMap.delete(normalizedKey);
};
};
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
// Ctrl+[ to collapse sidebar const isInput = ['INPUT', 'TEXTAREA'].includes((event.target as HTMLElement).tagName);
if (event.ctrlKey && event.key === '[') { if (isInput && !event.ctrlKey && !event.metaKey && event.key !== 'Escape') {
event.preventDefault();
toggleSidebar();
return; return;
} }
if (event.ctrlKey && event.key === ',') { const keys: string[] = [];
event.preventDefault(); if (event.ctrlKey) keys.push('ctrl');
if (isSettingsOpen.value) { if (event.metaKey) keys.push('meta');
return; if (event.shiftKey) keys.push('shift');
} if (event.altKey) keys.push('alt');
openSettings(); const mainKey = event.key.toLowerCase();
return; if (!['control', 'shift', 'meta', 'alt'].includes(mainKey)) {
keys.push(mainKey);
}
const pressedCombo = normalize(keys);
if (shortcutsMap.has(pressedCombo)) {
event.preventDefault();
event.stopPropagation();
shortcutsMap.get(pressedCombo)!();
} }
}; };
@@ -30,6 +48,7 @@ export const useKeyboardShortcuts = () => {
}); });
return { return {
addShortcut,
handleKeyDown, handleKeyDown,
}; };
}; };
+6 -2
View File
@@ -2,14 +2,18 @@
import Dialog from '~/components/Dialog/index.vue'; import Dialog from '~/components/Dialog/index.vue';
const { colorScheme } = await useUserSettings(); const { colorScheme } = await useUserSettings();
const { toggle: toggleSidebar } = useSidebar();
const { addShortcut } = useKeyboardShortcuts();
addShortcut(['ctrl', '['], () => {
toggleSidebar();
});
useHead({ useHead({
htmlAttrs: { htmlAttrs: {
class: colorScheme.class class: colorScheme.class
} }
}); });
useKeyboardShortcuts();
</script> </script>
<template> <template>