Shortcut cheat sheet
A dialog listing every shortcut that works right now, grouped, built from the registry.
<script setup lang="ts">
import ShortcutCheatSheet from '@shared/components/common/ShortcutCheatSheet.vue'
import { Button } from '@shared/components/ui/button'
import { useShortcutCheatSheet, useShortcuts } from '@shared/composables/useShortcuts'
import { ref } from 'vue'
const { open } = useShortcutCheatSheet()
const fired = ref('')
useShortcuts(
{
'Ctrl+S': () => { fired.value = 'Saved' },
'g d': () => { fired.value = 'Went to the dashboard' },
'g c': () => { fired.value = 'Went to customers' },
'Shift+N': () => { fired.value = 'New record' },
},
{
'Ctrl+S': { description: 'Save the current record' },
'g d': { description: 'Go to the dashboard', group: 'common.navigation' },
'g c': { description: 'Go to customers', group: 'common.navigation' },
'Shift+N': { description: 'Create a record' },
},
)
</script>
<template>
<div class="space-y-3">
<Button @click="open = true">
Show the shortcuts
</Button>
<ShortcutCheatSheet />
<p class="text-sm text-muted-foreground">
{{ fired || 'Press Ctrl+S, Shift+N, or g then d' }}
</p>
</div>
</template>Usage
The component takes no props. Mount it once near the root and open it by setting the singleton ref from useShortcutCheatSheet(). In the template the global ? key does that, from main.ts.
The list is not written anywhere. It is the registry that useShortcuts(map, meta) fills, so a shortcut appears here because it is bound, not because someone remembered to document it.
import { useShortcuts } from '@shared/composables/useShortcuts'
useShortcuts(
{ 'Ctrl+S': save },
{ 'Ctrl+S': { description: 'Save the current record' } },
)A shortcut with no meta entry still works; it just does not appear in the sheet. A meta entry with no group lands in General.
What it shows
The app-wide groups come first, in this order: General, Navigation, View. Anything else, such as a page's own shortcuts registered while you are on it, follows in the order it arrived, so a page's keys sit under the ones that work everywhere.
A combo can be registered more than once. A page binding g s shadows the app's own for as long as that page is mounted, and the sheet shows the last registration for each combo, so it describes what the key does right here, right now.
Each combo is split into its own key caps: Ctrl+K becomes Ctrl and K, g d becomes g and d.
API
Source: src/shared/components/common/ShortcutCheatSheet.vue
Props
None.
Emits
None.
Slots
None.
ShortcutMeta
| Key | Type | Description |
|---|---|---|
description | string | The line beside the keys. A translation key or a literal. |
group | string | The section. Defaults to common.general, which renders as "General". |
Accessibility
- It is a dialog: focus moves into it, Tab is trapped, and Escape closes it.
- Each key is a
<kbd>, so it is announced as a key rather than as loose text. - The description carries
dir="auto". English copy inside a right-to-left dialog would otherwise have its full stop resolved as a neutral character and moved to the front of the line. - Shortcuts do not fire while focus is in an input, a textarea, a select or a contenteditable element, so typing "g" in a search box never navigates away.
- A sequence such as
g dtimes out after 800 milliseconds, so a stray key press does not sit half-armed.