{"id":30539034,"url":"https://github.com/viktor-shcherb/streamlit-hotkeys","last_synced_at":"2025-08-27T21:24:36.406Z","repository":{"id":309096215,"uuid":"1035152774","full_name":"viktor-shcherb/streamlit-hotkeys","owner":"viktor-shcherb","description":"Keyboard hotkeys for Streamlit — capture Ctrl/Cmd/Alt/Shift + key combos and trigger Python once per press (edge-triggered, optional preventDefault).","archived":false,"fork":false,"pushed_at":"2025-08-18T11:33:44.000Z","size":69,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-18T12:03:25.470Z","etag":null,"topics":["cmdk","command-palette","hotkeys","keybindings","keyboard","python","shortcuts","streamlit","streamlit-component","ui"],"latest_commit_sha":null,"homepage":"","language":"Python","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/viktor-shcherb.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2025-08-09T19:07:02.000Z","updated_at":"2025-08-18T11:33:48.000Z","dependencies_parsed_at":"2025-08-09T21:08:54.353Z","dependency_job_id":"736d0ce0-e5e1-400c-9cae-c9d16c779684","html_url":"https://github.com/viktor-shcherb/streamlit-hotkeys","commit_stats":null,"previous_names":["viktor-shcherb/streamlit-hotkeys"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/viktor-shcherb/streamlit-hotkeys","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viktor-shcherb%2Fstreamlit-hotkeys","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viktor-shcherb%2Fstreamlit-hotkeys/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viktor-shcherb%2Fstreamlit-hotkeys/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viktor-shcherb%2Fstreamlit-hotkeys/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/viktor-shcherb","download_url":"https://codeload.github.com/viktor-shcherb/streamlit-hotkeys/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/viktor-shcherb%2Fstreamlit-hotkeys/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272382600,"owners_count":24924972,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-27T02:00:09.397Z","response_time":76,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["cmdk","command-palette","hotkeys","keybindings","keyboard","python","shortcuts","streamlit","streamlit-component","ui"],"created_at":"2025-08-27T21:24:35.922Z","updated_at":"2025-08-27T21:24:36.395Z","avatar_url":"https://github.com/viktor-shcherb.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Streamlit Hotkeys\n\n[![PyPI](https://img.shields.io/pypi/v/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)\n[![Python Versions](https://img.shields.io/pypi/pyversions/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)\n[![License](https://img.shields.io/pypi/l/streamlit-hotkeys.svg)](LICENSE)\n[![Wheel](https://img.shields.io/pypi/wheel/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)\n![Streamlit Component](https://img.shields.io/badge/streamlit-component-FF4B4B?logo=streamlit\\\u0026logoColor=white)\n[![Downloads](https://static.pepy.tech/badge/streamlit-hotkeys)](https://pepy.tech/project/streamlit-hotkeys)\n\n**Streamlit Hotkeys** lets you wire up fast, app-wide keyboard shortcuts in seconds. Bind `Ctrl/Cmd/Alt/Shift + key` to actions and get **edge-triggered** events with a clean, Pythonic API (`if hotkeys.pressed(\"save\"):` or multiple `on_pressed(...)` callbacks). It runs through a **single invisible manager**—no widget clutter, no flicker—and can scope reruns to the **whole page or just a fragment**. Reuse the same `id` across combos (e.g., Cmd+K **or** Ctrl+K → `palette`), block browser defaults like `Ctrl/Cmd+S`, and use physical key codes for layout-independent bindings.\n\n---\n\n## Installation\n\n```bash\npip install streamlit-hotkeys\n```\n\n## Quick start\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\n# Activate early (top of the script)\nhotkeys.activate([\n    hotkeys.hk(\"palette\", \"k\", meta=True),              # Cmd+K (mac)\n    hotkeys.hk(\"palette\", \"k\", ctrl=True),              # Ctrl+K (win/linux)\n    hotkeys.hk(\"save\", \"s\", ctrl=True, prevent_default=True),  # Ctrl+S\n])\n\nst.title(\"Hotkeys quick demo\")\n\nif hotkeys.pressed(\"palette\"):\n    st.write(\"Open palette\")\n\nif hotkeys.pressed(\"save\"):\n    st.write(\"Saved!\")\n\nst.caption(\"Try Cmd/Ctrl+K and Ctrl+S\")\n```\n\n## Features\n\n* Single invisible **manager** (one iframe per page/fragment)\n* Activate early; CSS auto-collapses the iframe to avoid flicker\n* **Edge-triggered across reruns**, and **non-consuming within a rerun**\n  (you can call `pressed(id)` multiple times and each will see `True`)\n* **Callbacks API:** register multiple `on_pressed(id, ...)` handlers (deduped, run in order)\n* Each shortcut match **triggers a rerun** — whole page or just the fragment where the manager lives\n* Bind single keys or combos (`ctrl`, `alt`, `shift`, `meta`)\n* Reuse the **same `id`** across bindings (e.g., Cmd+K **or** Ctrl+K → `palette`)\n* `prevent_default` to block browser shortcuts (e.g., Ctrl/Cmd+S)\n* Layout-independent physical keys via `code=\"KeyK\"` / `code=\"Digit1\"`\n* `ignore_repeat` to suppress repeats while a key is held\n* Built-in legend: add `help=\"...\"` in `hk(...)` and call `hotkeys.legend()`\n* Multi-page / multi-manager friendly via `key=`\n* Optional `debug=True` to log matches in the browser console\n\n## API\n\n### `hk(...)` — define a binding\n\n```python\nhk(\n  id: str,\n  key: str | None = None,           # e.g., \"k\", \"Enter\", \"ArrowDown\"\n  *,\n  code: str | None = None,          # e.g., \"KeyK\" (if set, 'key' is ignored)\n  alt: bool | None = False,         # True=require, False=forbid, None=ignore\n  ctrl: bool | None = False,\n  shift: bool | None = False,\n  meta: bool | None = False,\n  ignore_repeat: bool = True,\n  prevent_default: bool = False,\n  help: str | None = None,          # optional text shown in legend\n) -\u003e dict\n```\n\nDefines one shortcut. You may reuse the **same `id`** across multiple bindings (e.g., Cmd+K **or** Ctrl+K → `palette`). Use `code=\"KeyK\"` for layout-independent physical keys.\n\n### `activate(*bindings, key=\"global\", debug=False) -\u003e None`\n\nRegisters bindings and renders the single invisible manager. Accepted forms:\n\n* Positional `hk(...)` dicts\n* A single list/tuple of `hk(...)` dicts\n* A mapping: `id -\u003e spec` **or** `id -\u003e [spec, spec, ...]`\n\nNotes: call **as early as possible** on each page/fragment. The `key` scopes events; if the manager lives inside a fragment, only that fragment reruns on a match. `debug=True` logs matches in the browser console.\n\n### `pressed(id, *, key=\"global\") -\u003e bool`\n\nReturns `True` **once per new key press** (edge-triggered across reruns). Within the **same rerun**, you can call `pressed(id)` multiple times in different places and each will see `True`.\n\n### `on_pressed(id, callback=None, *, key=\"global\", args=(), kwargs=None)`\n\nRegisters a callback to run **once per key press**. Multiple callbacks can be registered for the same `id` (deduped across reruns). You can use decorator or direct form. Callbacks run **before** any subsequent `pressed(...)` checks in the same rerun.\n\n### `legend(*, key=\"global\") -\u003e None`\n\nRenders a grouped list of shortcuts for the given manager key. Bindings that share the same `id` are merged; the first non-empty `help` string per `id` is shown.\n\n## Examples\n\n### Basic: simple hotkeys with `if ... pressed(...)`\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Basic hotkeys\")\n\nhotkeys.activate([\n    hotkeys.hk(\"hello\", \"h\"),     # press H\n    hotkeys.hk(\"enter\", \"Enter\"), # press Enter\n])\n\nif hotkeys.pressed(\"hello\"):\n    st.write(\"Hello 👋\")\n\nif hotkeys.pressed(\"enter\"):\n    st.write(\"You pressed Enter\")\n```\n\n### Cross-platform binding (same `id` for Cmd+K / Ctrl+K)\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Cross-platform palette (Cmd/Ctrl+K)\")\n\nhotkeys.activate([\n    hotkeys.hk(\"palette\", \"k\", meta=True),  # macOS\n    hotkeys.hk(\"palette\", \"k\", ctrl=True),  # Windows/Linux\n])\n\nif hotkeys.pressed(\"palette\"):\n    st.success(\"Open command palette\")\n```\n\n### Block browser default (Ctrl/Cmd+S)\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Prevent default on Ctrl/Cmd+S\")\n\nhotkeys.activate([\n    hotkeys.hk(\"save\", \"s\", ctrl=True, prevent_default=True),  # Ctrl+S\n    hotkeys.hk(\"save\", \"s\", meta=True,  prevent_default=True), # Cmd+S\n])\n\nif hotkeys.pressed(\"save\"):\n    st.success(\"Saved (browser Save dialog was blocked)\")\n```\n\n### Shortcuts Legend (dialog)\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Legend example\")\n\nhotkeys.activate({\n    \"palette\": [\n        {\"key\": \"k\", \"meta\": True,  \"help\": \"Open command palette\"},\n        {\"key\": \"k\", \"ctrl\": True},\n    ],\n    \"save\": {\"key\": \"s\", \"ctrl\": True, \"prevent_default\": True, \"help\": \"Save document\"},\n}, key=\"global\")\n\n@st.dialog(\"Keyboard Shortcuts\")\ndef _shortcuts():\n    hotkeys.legend()  # grouped legend (uses help=...)\n\nif hotkeys.pressed(\"palette\"):\n    _shortcuts()\n\nst.caption(\"Press Cmd/Ctrl+K to open the legend\")\n```\n\n### Fragment-local reruns (only the fragment updates)\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Fragment-local rerun\")\n\nst.write(\"Outside the fragment: no rerun on Ctrl+S\")\n\n@st.fragment\ndef editor_panel():\n    hotkeys.activate([hotkeys.hk(\"save\", \"s\", ctrl=True)], key=\"editor\")\n    st.text_area(\"Document\", height=120, key=\"doc\")\n    if hotkeys.pressed(\"save\", key=\"editor\"):\n        st.success(\"Saved inside fragment only\")\n\neditor_panel()\n```\n\n### Hold-to-repeat (`ignore_repeat=False`)\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Hold ArrowDown to increment\")\n\nhotkeys.activate([\n    hotkeys.hk(\"down\", \"ArrowDown\", ignore_repeat=False),\n])\n\nst.session_state.setdefault(\"count\", 0)\n\nif hotkeys.pressed(\"down\"):\n    st.session_state[\"count\"] += 1\n\nst.metric(\"Count\", st.session_state[\"count\"])\nst.caption(\"Hold ArrowDown to spam events (ignore_repeat=False)\")\n```\n\n### Multiple callbacks on the same event\n\n```python\nimport streamlit as st\nimport streamlit_hotkeys as hotkeys\n\nst.title(\"Multiple callbacks\")\n\nhotkeys.activate([\n    hotkeys.hk(\"save\", \"s\", ctrl=True, prevent_default=True),\n])\n\ndef save_doc():\n    st.write(\"Saving...\")\n\ndef toast_saved():\n    st.toast(\"Saved!\")\n\n# register both; each runs once per key press\nhotkeys.on_pressed(\"save\", save_doc)\nhotkeys.on_pressed(\"save\", toast_saved)\n\n# you can still branch with pressed() afterwards\nif hotkeys.pressed(\"save\"):\n    st.info(\"Thank you for saving\")\n```\n\n## Notes and limitations\n\n* Browsers reserve some shortcuts. Use `prevent_default=True` to keep the event for your app when allowed.\n* Combos mean modifiers + one key. The platform does not treat two non-modifier keys pressed together (for example, `A+S`) as a single combo.\n* The page must have focus; events are captured at the document level.\n\n## Similar projects\n\n* [streamlit-keypress] - Original \"keypress to Python\" component by Sudarsan.\n* [streamlit-shortcuts] - Keyboard shortcuts for buttons and widgets; supports multiple bindings and hints.\n* [streamlit-keyup] - Text input that emits on every keyup (useful for live filtering).\n* [keyboard\\_to\\_url][keyboard_to_url] - Bind a key to open a URL in a new tab.\n\n[streamlit-keypress]: https://pypi.org/project/streamlit-keypress/\n[streamlit-shortcuts]: https://pypi.org/project/streamlit-shortcuts/\n[streamlit-keyup]: https://pypi.org/project/streamlit-keyup/\n[keyboard_to_url]: https://arnaudmiribel.github.io/streamlit-extras/extras/keyboard_url/\n\n## Credits\n\nInspired by [streamlit-keypress] by **Sudarsan**. This implementation adds a multi-binding manager, edge-triggered events, modifier handling, `preventDefault`, `KeyboardEvent.code` and many more features.\n\n## Contributing\n\nIssues and PRs are welcome.\n\n## License\n\nMIT. See `LICENSE`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviktor-shcherb%2Fstreamlit-hotkeys","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fviktor-shcherb%2Fstreamlit-hotkeys","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fviktor-shcherb%2Fstreamlit-hotkeys/lists"}