Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

khalifmv/modkey

Open more actions menu

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Modkey

Modkey 🙈

A framework-agnostic global keyboard shortcut manager for the web.

Installation

npm install modkey

Quick Start

import { createShortcutManager } from 'modkey';

// Create a new shortcut manager
const modkey = createShortcutManager();

// Register shortcuts
modkey.registerShortcut({
  id: 'save',
  name: 'Save',
  description: 'Save the current document',
  keys: 'mod+s'
});

modkey.registerShortcut({
  id: 'undo',
  name: 'Undo',
  description: 'Undo last action',
  keys: 'mod+z'
});

// Subscribe to shortcuts
modkey.subscribe('save', (event) => {
  console.log('Save triggered!', event);
  // Your save logic here
});

// Initialize the manager (starts listening for keyboard events)
modkey.init();

// Clean up when done
// modkey.destroy();

Features

  • Framework-agnostic - Works with React, Vue, Svelte, or vanilla JS
  • Cross-platform - mod key maps to Cmd on Mac, Ctrl on Windows/Linux
  • Scoped shortcuts - Activate shortcuts only in specific contexts
  • Conflict detection - Warns about duplicate key bindings
  • Zero dependencies - Lightweight and self-contained
  • TypeScript first - Full type definitions included

API Reference

Creating a Manager

import { createShortcutManager, ShortcutManager } from 'modkey';

// Using the factory function
const modkey = createShortcutManager({
  shortcuts: [], // Initial shortcuts
  scope: 'global', // Initial scope
  debug: false // Enable debug logging
});

// Or using the class directly
const modkey = new ShortcutManager({ debug: true });

Registering Shortcuts

// Register a single shortcut
modkey.registerShortcut({
  id: 'copy',
  name: 'Copy',
  description: 'Copy selected text',
  keys: 'mod+c',
  preventDefault: true, // Prevent default browser behavior (default: true)
  enabled: true, // Whether shortcut is active (default: true)
  scope: 'editor' // Optional scope filter
});

// Register multiple shortcuts
modkey.registerShortcuts([
  { id: 'cut', name: 'Cut', description: 'Cut selected text', keys: 'mod+x' },
  { id: 'paste', name: 'Paste', description: 'Paste from clipboard', keys: 'mod+v' }
]);

Subscribing to Shortcuts

// Subscribe to a specific shortcut
const subscription = modkey.subscribe('save', (event) => {
  console.log('Save triggered at:', event.timestamp);
  console.log('Original event:', event.originalEvent);
});

// Unsubscribe later
subscription.unsubscribe();

// Subscribe to ALL shortcuts
const unsubscribe = modkey.onTrigger((event) => {
  console.log('Shortcut triggered:', event.id);
});

Managing Shortcuts

// Enable/disable shortcuts
modkey.enableShortcut('save');
modkey.disableShortcut('save');

// Unregister a shortcut
modkey.unregisterShortcut('save');

// Get a shortcut by ID
const shortcut = modkey.getShortcut('save');

// Get all shortcuts
const all = modkey.getAllShortcuts();

// Get only enabled shortcuts
const active = modkey.getActiveShortcuts();

Scopes

Scopes allow you to activate shortcuts only in specific contexts. This is useful when your application has different modes or views where certain shortcuts should only work.

Example: Imagine a photo editor application with different panels:

// Define shortcuts with different scopes
modkey.registerShortcuts([
  // Global shortcuts - always active
  { id: 'save', name: 'Save', keys: 'mod+s', scope: 'global' },
  { id: 'open', name: 'Open', keys: 'mod+o', scope: 'global' },
  
  // Editor-only shortcuts - only when editing
  { id: 'crop', name: 'Crop', keys: 'c', scope: 'editor' },
  { id: 'rotate', name: 'Rotate', keys: 'r', scope: 'editor' },
  { id: 'brush', name: 'Brush Tool', keys: 'b', scope: 'editor' },
  
  // Modal shortcuts - only in dialogs/modals
  { id: 'confirm', name: 'Confirm', keys: 'enter', scope: 'modal' },
  { id: 'cancel', name: 'Cancel', keys: 'escape', scope: 'modal' }
]);

// Switch scope based on UI state
function openEditor() {
  modkey.setScope('editor');
  // Now 'c', 'r', 'b' shortcuts are active
}

function openModal() {
  modkey.setScope('modal');
  // Now only 'enter' and 'escape' work (plus global shortcuts)
}

function closeModal() {
  modkey.setScope('editor');
  // Back to editor shortcuts
}

// Get current scope
const currentScope = modkey.getScope(); // 'editor' | 'modal' | 'global'

Note: Shortcuts with scope: 'global' are always active regardless of the current scope.

Using Stores

Modkey includes reactive stores for building UIs:

// Subscribe to shortcuts store
modkey.shortcuts.subscribe((shortcuts) => {
  // Map of id -> ShortcutConfig
  console.log('Shortcuts updated:', shortcuts);
});

// Subscribe to last triggered shortcut
modkey.lastTriggeredShortcut.subscribe((event) => {
  if (event) {
    console.log('Last shortcut:', event.id);
  }
});

// Subscribe to pressed keys (for UI display)
modkey.pressedKeys.subscribe((keys) => {
  console.log('Currently pressed:', Array.from(keys));
});

Utility Functions

import {
  formatKeyCombo,
  getPlatform,
  isMac,
  matchesKeyCombination,
  detectConflicts
} from 'modkey';

// Format a key combo for display
formatKeyCombo('mod+s'); // '⌘S' on Mac, 'Ctrl+S' on Windows

// Detect platform
getPlatform(); // 'mac' | 'windows' | 'linux'
isMac(); // true | false

// Check if an event matches a combo
matchesKeyCombination(event, 'mod+s'); // true | false

// Detect conflicts in a list of shortcuts
const conflicts = detectConflicts(shortcuts);

Key Format

Use mod for cross-platform compatibility:

Key Format Mac Windows/Linux
mod+s ⌘S Ctrl+S
mod+shift+z ⌘⇧Z Ctrl+Shift+Z
ctrl+alt+delete ⌃⌥Delete Ctrl+Alt+Delete

Other examples:

  • shift+f5
  • escape
  • f11
  • arrowup, arrowdown, arrowleft, arrowright

Framework Examples

React

Using Modkey as a global singleton across your React application:

1. Create the global store (lib/modkey.ts)

// lib/modkey.ts
import { createShortcutManager } from 'modkey';

// Create a singleton instance
export const modkey = createShortcutManager({ debug: true });

// Register all your application shortcuts here
modkey.registerShortcuts([
  { id: 'save', name: 'Save', description: 'Save document', keys: 'mod+s', scope: 'global' },
  { id: 'undo', name: 'Undo', description: 'Undo action', keys: 'mod+z', scope: 'editor' },
  { id: 'redo', name: 'Redo', description: 'Redo action', keys: 'mod+shift+z', scope: 'editor' },
]);

2. Initialize in your root component (App.tsx)

// App.tsx
import { useEffect } from 'react';
import { modkey } from './lib/modkey';
import { Editor } from './components/Editor';

export function App() {
  useEffect(() => {
    // Initialize once at app root
    modkey.init();
    
    return () => {
      modkey.destroy();
    };
  }, []);

  return (
    <div>
      <Editor />
    </div>
  );
}

3. Subscribe in any component (components/Editor.tsx)

// components/Editor.tsx
import { useEffect } from 'react';
import { modkey } from '../lib/modkey';

export function Editor() {
  useEffect(() => {
    // Set scope when this component mounts
    modkey.setScope('editor');

    // Subscribe to shortcuts
    const saveSub = modkey.subscribe('save', () => {
      console.log('Saving...');
    });

    const undoSub = modkey.subscribe('undo', () => {
      console.log('Undo!');
    });

    return () => {
      saveSub.unsubscribe();
      undoSub.unsubscribe();
    };
  }, []);

  return <div>Editor Component - Press Cmd/Ctrl+S to save</div>;
}

Vue

<script setup>
import { onMounted, onUnmounted } from 'vue';
import { createShortcutManager } from 'modkey';

const modkey = createShortcutManager();

onMounted(() => {
  modkey.registerShortcut({
    id: 'save',
    name: 'Save',
    description: 'Save document',
    keys: 'mod+s'
  });

  modkey.subscribe('save', () => {
    console.log('Save!');
  });

  modkey.init();
});

onUnmounted(() => {
  modkey.destroy();
});
</script>

<template>
  <div>Press Cmd/Ctrl+S to save</div>
</template>

Svelte

<script>
  import { onMount, onDestroy } from 'svelte';
  import { createShortcutManager } from 'modkey';

  const modkey = createShortcutManager();

  onMount(() => {
    modkey.registerShortcut({
      id: 'save',
      name: 'Save',
      description: 'Save document',
      keys: 'mod+s'
    });

    modkey.subscribe('save', () => {
      console.log('Save!');
    });

    modkey.init();
  });

  onDestroy(() => {
    modkey.destroy();
  });
</script>

<div>Press Cmd/Ctrl+S to save</div>

License

MIT

About

A framework-agnostic global keyboard shortcut manager

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Morty Proxy This is a proxified and sanitized view of the page, visit original site.