Skip to content

Keyboard Shortcuts

Add in-app and global keyboard shortcuts to your LightShell app.

LightShell supports two kinds of keyboard shortcuts: in-app shortcuts that work when your window is focused, and global shortcuts that work system-wide, even when your app is in the background.

For shortcuts that only need to work while your app is focused, use standard DOM keyboard event listeners. This is the simplest approach and requires no special API.

document.addEventListener('keydown', (e) => {
// Cmd+S on macOS, Ctrl+S on Linux
if ((e.metaKey || e.ctrlKey) && e.key === 's') {
e.preventDefault()
saveFile()
}
// Cmd+O / Ctrl+O
if ((e.metaKey || e.ctrlKey) && e.key === 'o') {
e.preventDefault()
openFile()
}
// Cmd+Shift+P / Ctrl+Shift+P
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'P') {
e.preventDefault()
toggleCommandPalette()
}
// Escape
if (e.key === 'Escape') {
closeModal()
}
})

On macOS, e.metaKey is the Command key. On Linux, e.ctrlKey is the Ctrl key. To handle both platforms with one check, test for either:

function isModKey(e) {
return e.metaKey || e.ctrlKey
}
document.addEventListener('keydown', (e) => {
if (isModKey(e) && e.key === 'n') {
e.preventDefault()
newFile()
}
})

For apps with many shortcuts, a lookup table is cleaner than a chain of if/else:

const shortcuts = {
'mod+s': saveFile,
'mod+o': openFile,
'mod+n': newFile,
'mod+shift+p': toggleCommandPalette,
'mod+,': openSettings,
'mod+w': closeTab,
'escape': closeModal,
'f11': toggleFullscreen,
}
document.addEventListener('keydown', (e) => {
const parts = []
if (e.metaKey || e.ctrlKey) parts.push('mod')
if (e.shiftKey) parts.push('shift')
if (e.altKey) parts.push('alt')
parts.push(e.key.toLowerCase())
const combo = parts.join('+')
const handler = shortcuts[combo]
if (handler) {
e.preventDefault()
handler()
}
})

Global shortcuts work even when your app window is not focused. They are registered through lightshell.shortcuts and use the system’s global hotkey mechanism.

This is essential for productivity tools, clipboard managers, quick launchers, and screenshot utilities.

lightshell.shortcuts.register('CommandOrControl+Shift+Space', () => {
lightshell.window.restore() // bring window to front
document.getElementById('search').focus()
})

Use these cross-platform modifier names in your shortcut strings:

Key StringmacOSLinux
CommandOrControlCmdCtrl
CommandCmd(ignored on Linux)
ControlCtrlCtrl
AltOptionAlt
ShiftShiftShift
SuperCmdSuper/Win

CommandOrControl is the recommended modifier for most shortcuts. It maps to the primary modifier key on each platform.

Combine modifiers and a key with +. The key part can be a letter, number, function key, or special key:

CommandOrControl+Shift+P
Alt+Space
Control+F12
CommandOrControl+1
// Remove a specific shortcut
lightshell.shortcuts.unregister('CommandOrControl+Shift+Space')
// Remove all global shortcuts
lightshell.shortcuts.unregisterAll()
const registered = await lightshell.shortcuts.isRegistered('CommandOrControl+Shift+Space')
console.log(registered) // true or false

A clipboard manager or quick launcher that activates with a global hotkey:

// Register global hotkey to show/hide the app
lightshell.shortcuts.register('CommandOrControl+Shift+Space', () => {
toggleWindow()
})
async function toggleWindow() {
// Bring window to front and focus the search input
await lightshell.window.restore()
document.getElementById('search-input').focus()
document.getElementById('search-input').select()
}
// Escape hides the window
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
lightshell.window.minimize()
}
})

A full set of shortcuts for a text editing app:

document.addEventListener('keydown', (e) => {
const mod = e.metaKey || e.ctrlKey
if (mod && e.key === 's') {
e.preventDefault()
if (e.shiftKey) {
saveFileAs()
} else {
saveFile()
}
}
if (mod && e.key === 'o') {
e.preventDefault()
openFile()
}
if (mod && e.key === 'n') {
e.preventDefault()
newFile()
}
if (mod && e.key === 'f') {
e.preventDefault()
showFindBar()
}
if (mod && e.key === 'z') {
e.preventDefault()
if (e.shiftKey) {
redo()
} else {
undo()
}
}
})

Always unregister global shortcuts when your app exits. Global shortcuts remain active at the OS level until explicitly removed:

// Register shortcuts on startup
function initShortcuts() {
lightshell.shortcuts.register('CommandOrControl+Shift+L', showApp)
lightshell.shortcuts.register('CommandOrControl+Shift+K', quickCapture)
}
// Clean up before quitting
window.addEventListener('beforeunload', () => {
lightshell.shortcuts.unregisterAll()
})
initShortcuts()

Let users customize their shortcuts by storing the bindings in lightshell.store:

const defaultBindings = {
save: 'mod+s',
open: 'mod+o',
find: 'mod+f',
newFile: 'mod+n',
}
async function loadShortcuts() {
const saved = await lightshell.store.get('keybindings')
return saved || defaultBindings
}
async function saveShortcuts(bindings) {
await lightshell.store.set('keybindings', bindings)
}

Do not conflict with system shortcuts. Avoid overriding shortcuts the OS or desktop environment already uses:

ShortcutReserved By
Cmd+Q (macOS)Quit application
Cmd+H (macOS)Hide application
Cmd+TabApp switcher
Ctrl+Alt+Delete (Linux)System menu
Super (Linux)Activities/launcher
Alt+F4 (Linux)Close window

Use CommandOrControl instead of platform-specific modifiers. This makes your shortcuts work on both macOS and Linux without platform checks.

Keep global shortcuts minimal. Only register global shortcuts for actions that genuinely need to work when your app is not focused. Overusing global shortcuts can conflict with other apps.

Always call e.preventDefault() for in-app shortcuts. Without it, the browser default action (like Cmd+S triggering a save dialog in the webview) may fire alongside your handler.

Show shortcuts in your UI. Display the keyboard shortcut next to menu items and buttons so users can discover them. Use lightshell.system.platform() to show the correct modifier symbol:

const platform = await lightshell.system.platform()
const modLabel = platform === 'darwin' ? 'Cmd' : 'Ctrl'
document.getElementById('save-btn').title = `Save (${modLabel}+S)`
FeaturemacOSLinux
Global hotkey APICGEventTap / NSEventX11 XGrabKey
CommandOrControlMaps to CmdMaps to Ctrl
Function keysMay need Fn keyWork directly
Media keysTypically reserved by OSVaries by DE
Global shortcut limitNo hard limitNo hard limit

Global shortcuts are registered at the OS level. If another application has already claimed a shortcut, registration may fail silently or the other app may take priority. Choose distinctive key combinations to minimize conflicts.