Skip to content

A tiny, type-safe, cross-platform keyboard shortcuts library with ctrl→Command mapping on macOS.

License

Notifications You must be signed in to change notification settings

tech-zjf/kbd-shortcuts

Repository files navigation

kbd-shortcuts

A tiny, type-safe, cross-platform keyboard shortcuts library with sensible defaults.

  • Case-insensitive: Ctrl, CTRL, ctrl are the same.
  • Cross-platform mapping: by default, ctrl in hotkey strings is treated as Command (⌘) on macOS (event side remains real keys).
  • Zero dependencies, tree-shakable, ESM/CJS, full TypeScript types.

Install

npm install kbd-shortcuts
# or
pnpm add kbd-shortcuts
# or
yarn add kbd-shortcuts

Quick Start

import { KeyboardShortcuts } from 'kbd-shortcuts';

const kbd = new KeyboardShortcuts({
  // enabled: true,
  // eventType: 'keydown',
  // target: window,
  // ctrlMapsToCommandOnMac: true,
});

// Register a binding (macOS: ⌘S, Windows/Linux: Ctrl+S)
const id = kbd.register(
  'ctrl+s',
  ({ platform }) => {
    console.log('Save triggered on', platform);
  },
  {
    preventDefault: true,
    description: 'Save',
  }
);

// Another example
kbd.register('ctrl+shift+p', () => console.log('Command Palette'));

// Controls
kbd.disable();
kbd.enable();

// Single binding controls
kbd.disableBinding(id);
kbd.enableBinding(id);

// Update binding
kbd.updateBinding(id, { once: true, description: 'Save (once)' });

// List for UI
console.log(kbd.listBindings());

// Display-friendly string
console.log(kbd.formatHotkey('ctrl+s')); // mac: ⌘S, win: Ctrl+S

API Reference

Constructor

new KeyboardShortcuts(options?: CreateOptions)

Methods

  • register(hotkeys: KeyInput, handler: Handler, options?: AddHandleOptions): string
  • unregister(id: string): void
  • updateBinding(id: string, patch: Partial<HandleRecord> & Partial<AddHandleOptions>): void
  • enable(): void / disable(): void
  • enableBinding(id: string): void / disableBinding(id: string): void
  • listBindings(): HandleView[]
  • formatHotkey(hotkey: string): string
  • clear(): void

Note: First-match-wins. Bindings are sorted by priority (higher first). Once a binding is matched, processing stops.

Configuration

CreateOptions (Global Config)

Name Type Default Description
enabled boolean true Global enable switch
eventType 'keydown' | 'keyup' 'keydown' Event type to listen
target Window | Document | HTMLElement window Event target
ctrlMapsToCommandOnMac boolean true When parsing hotkey strings on macOS, map ctrl to Command(⌘)

AddHandleOptions (Per Binding Config)

Name Type Default Description
preventDefault boolean true Call event.preventDefault() on match
stopPropagation boolean true Call event.stopPropagation() on match
allowInEditable boolean false Allow triggers in inputs/contenteditable
enabled boolean true Binding is active
once boolean false Trigger once then disable binding
priority number 0 Higher first; first match wins
ctrlMapsToCommandOnMac boolean inherit global Override global mapping for this binding
description string '' For UI display

Features

Hotkey Syntax

  • KeyInput: string | string[], e.g., "ctrl+s" or ["ctrl+s", "cmd+s"]
  • Syntax: modifier+...+key
  • Modifiers: ctrl, meta, cmd, command, win, super, alt, option, opt, shift
  • Case insensitive: Ctrl+S === ctrl+s
  • Symbol support: , , , are recognized

Wildcard Support

Use * or any as the main key to match any key:

kbd.register('shift+*', () => {
  console.log('Shift + any key pressed');
});

Cross-Platform Behavior

  • Hotkey strings: ctrl maps to on macOS, Ctrl on Windows/Linux
  • Event matching: Uses actual pressed keys (strict matching)
  • Display: formatHotkey() shows platform-appropriate symbols

Priority and Matching

  • Bindings are sorted by priority (descending)
  • First match wins and stops processing
  • Higher priority = processed first

Examples

Basic Usage

const kbd = new KeyboardShortcuts();

// Save shortcut
kbd.register('ctrl+s', () => {
  console.log('Save!');
});

// Copy with multiple variants
kbd.register(['ctrl+c', 'ctrl+insert'], () => {
  console.log('Copy!');
});

Advanced Configuration

const kbd = new KeyboardShortcuts({
  target: document.querySelector('.editor'),
  ctrlMapsToCommandOnMac: false, // Use real Ctrl on Mac
});

// High priority shortcut
kbd.register(
  'escape',
  () => {
    console.log('Escape pressed');
  },
  { priority: 100 }
);

// Allow in input fields
kbd.register(
  'ctrl+enter',
  () => {
    console.log('Submit form');
  },
  { allowInEditable: true }
);

// One-time shortcut
kbd.register(
  'ctrl+shift+d',
  () => {
    console.log('Debug mode activated');
  },
  { once: true }
);

Framework Integration

React

import { useEffect, useRef } from 'react';
import { KeyboardShortcuts } from 'kbd-shortcuts';

function App() {
  const kbdRef = useRef<KeyboardShortcuts>();

  useEffect(() => {
    kbdRef.current = new KeyboardShortcuts();

    const saveId = kbdRef.current.register('ctrl+s', () => {
      console.log('Save triggered');
    });

    return () => {
      kbdRef.current?.destroy();
    };
  }, []);

  return <div>My App</div>;
}

Notes

  • If you need the real Control key on macOS, set ctrlMapsToCommandOnMac: false
  • Processing stops at first match to avoid ambiguity
  • Bindings in editable elements are blocked by default for better UX
  • All hotkey strings are normalized (case-insensitive, whitespace-trimmed)

License

MIT

About

A tiny, type-safe, cross-platform keyboard shortcuts library with ctrl→Command mapping on macOS.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published