{"id":24976881,"url":"https://github.com/rezigned/keymap-rs","last_synced_at":"2026-04-25T09:08:54.892Z","repository":{"id":184784497,"uuid":"672422490","full_name":"rezigned/keymap-rs","owner":"rezigned","description":"A key mapping library with compile-time validation and declarative configuration for multiple backends (crossterm, wasm, etc.).","archived":false,"fork":false,"pushed_at":"2026-04-18T05:22:22.000Z","size":203,"stargazers_count":15,"open_issues_count":2,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-18T07:25:10.239Z","etag":null,"topics":["cli","crossterm","keymap","rust","termion","tui"],"latest_commit_sha":null,"homepage":"https://rezigned.com/keymap-rs/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rezigned.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-07-30T03:09:24.000Z","updated_at":"2026-03-03T14:58:58.000Z","dependencies_parsed_at":"2025-06-01T16:25:29.220Z","dependency_job_id":"5036ca04-a128-4344-834c-20169ae1080d","html_url":"https://github.com/rezigned/keymap-rs","commit_stats":null,"previous_names":["rezigned/keymap-rs"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/rezigned/keymap-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rezigned%2Fkeymap-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rezigned%2Fkeymap-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rezigned%2Fkeymap-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rezigned%2Fkeymap-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rezigned","download_url":"https://codeload.github.com/rezigned/keymap-rs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rezigned%2Fkeymap-rs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32256274,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-25T04:23:17.126Z","status":"ssl_error","status_checked_at":"2026-04-25T04:21:53.360Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","crossterm","keymap","rust","termion","tui"],"created_at":"2025-02-03T22:59:20.222Z","updated_at":"2026-04-25T09:08:54.887Z","avatar_url":"https://github.com/rezigned.png","language":"Rust","readme":"# keymap-rs\n\n[![Crates.io](https://img.shields.io/crates/v/keymap.svg)](https://crates.io/crates/keymap)\n[![Docs.rs](https://docs.rs/keymap/badge.svg)](https://docs.rs/keymap)\n[![CI](https://github.com/rezigned/keymap-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/rezigned/keymap-rs/actions/workflows/ci.yml)\n[![License](https://img.shields.io/crates/l/keymap.svg)](https://github.com/rezigned/keymap-rs/blob/main/LICENSE)\n\n**keymap-rs** is a lightweight and extensible key mapping library for Rust that simplifies input processing for terminal user interfaces (TUIs), WebAssembly (WASM) applications, and more. It parses keymaps from derive macros or configuration files and maps them to actions from various input backends, including [`crossterm`](https://crates.io/crates/crossterm), [`termion`](https://docs.rs/termion/latest/termion/), and [`wasm`](https://webassembly.org/).\n\n## 📋 Table of Contents\n\n- [Installation](#-installation)\n- [Usage](#-usage)\n  - [1. Deriving `KeyMap`](#1-deriving-keymap)\n  - [2. Using External Configuration](#2-using-external-configuration)\n  - [3. Compile-Time Validation](#3-compile-time-validation)\n  - [4. Direct Key Parsing](#4-direct-key-parsing)\n- [Key Syntax Reference](#-key-syntax-reference)\n- [Examples](#-examples)\n- [License](#-license)\n- [Contributions](#-contributions)\n\n---\n\n## 🔧 Features\n\n* ✅ **Declarative Key Mappings**: Define keymaps via simple configuration files (e.g., TOML, YAML) or directly in your code using derive macros.\n* ⌨️ **Key Patterns**: Supports single keys (`a`), combinations (`ctrl-b`), and multi-key sequences (`ctrl-b n`).\n* 🧠 **Key Groups**: Use built-in pattern matching for common key groups (`@upper`, `@lower`, `@alpha`, `@alnum`, and `@any`).\n* 📸 **Key Group Capturing**: Capture specific keypress data (like the actual `char` from `@any` or `@digit`) directly into your action enum variants at runtime.\n* 🧬 **Compile-Time Safety**: The `keymap_derive` macro validates key syntax at compile time, preventing runtime errors.\n* 🌐 **Backend-Agnostic**: Works with multiple backends, including `crossterm`, `termion`, and `wasm`.\n* 🪶 **Lightweight \u0026 Extensible**: Designed to be minimal and easy to extend with new backends or features.\n\n---\n\n## 🕹️ Demo\n\nSee `keymap-rs` in action with the [WASM example](https://rezigned.com/keymap-rs/):\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth width=\"500px\"\u003eNyan Jump!\u003c/th\u003e\n      \u003cth width=\"500px\"\u003eTur - Turing Machine Language\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd\u003e\n        \u003ca href=\"https://rezigned.com/keymap-rs\"\u003e\u003cimg src=\"./examples/wasm/public/preview.png\" alt=\"keymap-rs WASM Demo\"/\u003e\u003c/a\u003e\n      \u003c/td\u003e\n      \u003ctd\u003e\n        \u003ca href=\"https://rezigned.com/tur\"\u003e\u003cimg src=\"https://rezigned.com/tur/tur-web.png\" /\u003e\u003c/a\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n---\n\n## 📦 Installation\n\nAdd `keymap` to your `Cargo.toml`, enabling the feature for your chosen backend:\n\n```sh\ncargo add keymap --feature {crossterm | termion | wasm}\n```\n\n---\n\n## 🚀 Usage\n\n### 1. Deriving `KeyMap`\n\nThe easiest way to get started is with the `keymap::KeyMap` derive macro.\n\n**Define your actions:**\n\n```rust\nuse keymap::KeyMap;\n\n/// Application actions.\n#[derive(KeyMap, Debug, PartialEq, Eq)]\npub enum Action {\n    /// Quit the application.\n    #[key(\"q\", \"esc\")]\n    Quit,\n\n    /// Move left.\n    #[key(\"left\", \"h\")]\n    Left,\n\n    /// Move right.\n    #[key(\"right\", \"l\")]\n    Right,\n\n    /// Jump.\n    #[key(\"space\")]\n    Jump,\n\n    /// Key Group Capturing action (e.g. tracking which character was pressed).\n    /// `char` will be captured from any matched key group macro (like `@any` or `@digit`) at runtime.\n    #[key(\"@any\")]\n    Shoot(char),\n}\n```\n\n**Use the generated keymap:**\n\nThe `KeyMap` derive macro generates an associated `keymap_config()` method, which returns a `Config\u003cAction\u003e`.\n\n```rust\n// Retrieve the config\nlet config = Action::keymap_config();\n\n// `key` is a key code from the input backend, e.g., `crossterm::event::KeyCode`\n// You can lookup the default pre-instantiated action reference:\nmatch config.get(\u0026key) {\n    Some(action) =\u003e match action {\n        Action::Quit =\u003e break,\n        Action::Jump =\u003e println!(\"Jump!\"),\n        _ =\u003e println!(\"Action: {action:?} - {}\", action.keymap_item().description),\n    }\n    _ =\u003e {}\n}\n\n// Or use Key Group Capturing to extract the actual `char` from `@any` or `@digit`!\nif let Some(Action::Shoot(c)) = config.get_bound(\u0026key) {\n    println!(\"Captured key: {c}\");\n}\n```\n\n\u003e **Note**: `keymap_derive` automatically generates custom `Serialize` and `Deserialize` implementations for the derived `enum`, making your variants with captured data serialize as simple tags (e.g. `\"Shoot\"`) out of the box so that Map deserialization continues to work flawlessly.\n\n### 2. Using External Configuration\n\n`keymap-rs` also supports loading keymaps from external files (e.g., `config.toml`). This is useful for user-configurable keybindings.\n\n**Example `config.toml`:**\n\n```toml\n# Override or add new keybindings\nJump = { keys = [\"j\", \"up\"], description = \"Jump with 'j' or up arrow!\" }\nQuit = { keys = [\"@any\"], description = \"Quit on any key press.\" }\n```\n\nThis configuration can be loaded in two ways:\n\n#### `Config\u003cT\u003e`: Load from File Only\n\nThis deserializes **only** the keybindings from the configuration file, ignoring any `#[key(\"...\")]` attributes on your enum.\n\n```rust\n// This config will only contain 'Jump' and 'Quit' from the TOML file.\nlet config: Config\u003cAction\u003e = toml::from_str(\u0026data)?;\n```\n\n**Resulting keybindings:**\n\n| Key           | Action |\n| ------------- | ------ |\n| `\"j\"`, `\"up\"` | Jump   |\n| `@any`        | Quit   |\n\n#### `DerivedConfig\u003cT\u003e`: Merge Derived and File Configs\n\nThis **merges** keybindings from the `#[key(\"...\")]` attributes with those from the configuration file. Keys from the external file will override any conflicting keys defined in the enum.\n\n```rust\n// This config contains keys from both the derive macro and the TOML file.\nlet config: DerivedConfig\u003cAction\u003e = toml::from_str(\u0026data)?;\n```\n\n**Resulting keybindings:**\n\n| Key                      | Action | Source |\n| ------------------------ | ------ | ------ |\n| `\"j\"`, `\"up\"`            | Jump   | Config file (overrides `\"space\"`) |\n| `\"h\"`, `\"left\"`          | Left   | Derive macro |\n| `\"l\"`, `\"right\"`         | Right  | Derive macro |\n| `@any`                   | Quit   | Config file (overrides `\"q\"`, `\"esc\"`) |\n\n\u003e **Note**: When using `DerivedConfig\u003cT\u003e`, keys from the config file take precedence over derive macro keys for the same action.\n\n### 3. Compile-Time Validation\n\nThe `keymap_derive` macro validates all key strings at **compile time**, providing immediate feedback on invalid syntax.\n\n**Invalid Key Example:**\n\n```rust\n#[derive(keymap::KeyMap)]\nenum Action {\n    // \"enter2\" is not a valid key.\n    #[key(\"enter2\", \"ctrl-b n\")]\n    Invalid,\n}\n```\n\n**Compiler Error:**\n\nThis code will fail to compile with a clear error message:\n\n```\nerror: Invalid key \"enter2\": Parse error at position 5: expect end of input, found: 2\n --\u003e keymap_derive/tests/derive.rs:7:11\n  |\n7 |     #[key(\"enter2\", \"ctrl-b n\")]\n  |           ^^^^^^^^\n```\n\n### 4. Direct Key Parsing\n\nKey strings can also be parsed directly into a `KeyMap` or a backend-specific key event.\n\n```rust\nuse crossterm::event::{KeyCode, KeyEvent, KeyModifiers};\nuse keymap::{backend::crossterm::parse, Key, KeyMap, Modifier};\n\n// Parse into a generic KeyMap\nassert_eq!(\n    \"ctrl-l\".parse::\u003cKeyMap\u003e(),\n    Ok(KeyMap::new(Some(Modifier::Ctrl), Key::Char('l')))\n);\n\n// Or use the backend-specific parser\nassert_eq!(\n    parse(\"ctrl-l\").unwrap(),\n    KeyEvent::new(KeyCode::Char('l'), KeyModifiers::CONTROL)\n);\n```\n\n---\n\n## 📝 Key Syntax Reference\n\n| Type | Description | Example |\n|---|---|---|\n| **Single Keys** | Individual characters, special keys, arrow keys, and function keys. | `a`, `enter`, `up`, `f1` |\n| **Key Combinations** | Keys pressed simultaneously with modifiers (Ctrl, Alt, Shift). | `ctrl-c`, `alt-f4`, `ctrl-alt-shift-f1` |\n| **Key Sequences** | Multiple keys pressed in order. | `g g` (press `g` twice), `ctrl-b n` (Ctrl+B, then N), `ctrl-b c` (tmux-style new window) |\n| **Key Groups** | Predefined patterns matching sets of keys. | `@upper` (A-Z), `@alpha` (A-Z, a-z), `@any` (any key) |\n\n**Examples in Configuration:**\n```toml\n# Single keys\nQuit = { keys = [\"q\", \"esc\"] }\n\n# Key combinations\nSave = { keys = [\"ctrl-s\"] }\nForceQuit = { keys = [\"ctrl-alt-f4\"] }\n\n# Key sequences\nShowGitStatus = { keys = [\"g s\"] }\nNewTmuxWindow = { keys = [\"ctrl-b c\"] }\n\n# Key groups\nAnyLetter = { keys = [\"@alpha\"] }\nAnyKey = { keys = [\"@any\"] }\n```\n\n---\n\n## 📖 Examples\n\nFor complete, runnable examples, check out the [`/examples`](https://github.com/rezigned/keymap-rs/tree/main/examples) directory.\n\n---\n\n## 📜 License\n\nThis project is licensed under the [MIT License](https://github.com/rezigned/keymap-rs/blob/main/LICENSE).\n\n---\n\n## 🙌 Contributions\n\nContributions, issues, and feature requests are welcome! Feel free to open an issue or submit a pull request.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frezigned%2Fkeymap-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frezigned%2Fkeymap-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frezigned%2Fkeymap-rs/lists"}