Skip to main content

Keyboard hotkeys for Streamlit (Ctrl/Cmd/Alt/Shift combos) with edge-triggered events.

Project description

Streamlit Hotkeys

PyPI Python Versions License Wheel Streamlit Component Downloads

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.


Installation

pip install streamlit-hotkeys

Quick start

import streamlit as st
import streamlit_hotkeys as hotkeys

# Activate early (top of the script)
hotkeys.activate([
    hotkeys.hk("palette", "k", meta=True),              # Cmd+K (mac)
    hotkeys.hk("palette", "k", ctrl=True),              # Ctrl+K (win/linux)
    hotkeys.hk("save", "s", ctrl=True, prevent_default=True),  # Ctrl+S
])

st.title("Hotkeys quick demo")

if hotkeys.pressed("palette"):
    st.write("Open palette")

if hotkeys.pressed("save"):
    st.write("Saved!")

st.caption("Try Cmd/Ctrl+K and Ctrl+S")

Features

  • Single invisible manager (one iframe per page/fragment)
  • Activate early; CSS auto-collapses the iframe to avoid flicker
  • Edge-triggered across reruns, and non-consuming within a rerun (you can call pressed(id) multiple times and each will see True)
  • Callbacks API: register multiple on_pressed(id, ...) handlers (deduped, run in order)
  • Each shortcut match triggers a rerun — whole page or just the fragment where the manager lives
  • Bind single keys or combos (ctrl, alt, shift, meta)
  • Reuse the same id across bindings (e.g., Cmd+K or Ctrl+K → palette)
  • prevent_default to block browser shortcuts (e.g., Ctrl/Cmd+S)
  • Layout-independent physical keys via code="KeyK" / code="Digit1"
  • ignore_repeat to suppress repeats while a key is held
  • Built-in legend: add help="..." in hk(...) and call hotkeys.legend()
  • Multi-page / multi-manager friendly via key=
  • Optional debug=True to log matches in the browser console

API

hk(...) — define a binding

hk(
  id: str,
  key: str | None = None,           # e.g., "k", "Enter", "ArrowDown"
  *,
  code: str | None = None,          # e.g., "KeyK" (if set, 'key' is ignored)
  alt: bool | None = False,         # True=require, False=forbid, None=ignore
  ctrl: bool | None = False,
  shift: bool | None = False,
  meta: bool | None = False,
  ignore_repeat: bool = True,
  prevent_default: bool = False,
  help: str | None = None,          # optional text shown in legend
) -> dict

Defines 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.

activate(*bindings, key="global", debug=False) -> None

Registers bindings and renders the single invisible manager. Accepted forms:

  • Positional hk(...) dicts
  • A single list/tuple of hk(...) dicts
  • A mapping: id -> spec or id -> [spec, spec, ...]

Notes: 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.

pressed(id, *, key="global") -> bool

Returns 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.

on_pressed(id, callback=None, *, key="global", args=(), kwargs=None)

Registers 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.

legend(*, key="global") -> None

Renders 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.

Examples

Basic: simple hotkeys with if ... pressed(...)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Basic hotkeys")

hotkeys.activate([
    hotkeys.hk("hello", "h"),     # press H
    hotkeys.hk("enter", "Enter"), # press Enter
])

if hotkeys.pressed("hello"):
    st.write("Hello 👋")

if hotkeys.pressed("enter"):
    st.write("You pressed Enter")

Cross-platform binding (same id for Cmd+K / Ctrl+K)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Cross-platform palette (Cmd/Ctrl+K)")

hotkeys.activate([
    hotkeys.hk("palette", "k", meta=True),  # macOS
    hotkeys.hk("palette", "k", ctrl=True),  # Windows/Linux
])

if hotkeys.pressed("palette"):
    st.success("Open command palette")

Block browser default (Ctrl/Cmd+S)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Prevent default on Ctrl/Cmd+S")

hotkeys.activate([
    hotkeys.hk("save", "s", ctrl=True, prevent_default=True),  # Ctrl+S
    hotkeys.hk("save", "s", meta=True,  prevent_default=True), # Cmd+S
])

if hotkeys.pressed("save"):
    st.success("Saved (browser Save dialog was blocked)")

Shortcuts Legend (dialog)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Legend example")

hotkeys.activate({
    "palette": [
        {"key": "k", "meta": True,  "help": "Open command palette"},
        {"key": "k", "ctrl": True},
    ],
    "save": {"key": "s", "ctrl": True, "prevent_default": True, "help": "Save document"},
}, key="global")

@st.dialog("Keyboard Shortcuts")
def _shortcuts():
    hotkeys.legend()  # grouped legend (uses help=...)

if hotkeys.pressed("palette"):
    _shortcuts()

st.caption("Press Cmd/Ctrl+K to open the legend")

Fragment-local reruns (only the fragment updates)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Fragment-local rerun")

st.write("Outside the fragment: no rerun on Ctrl+S")

@st.fragment
def editor_panel():
    hotkeys.activate([hotkeys.hk("save", "s", ctrl=True)], key="editor")
    st.text_area("Document", height=120, key="doc")
    if hotkeys.pressed("save", key="editor"):
        st.success("Saved inside fragment only")

editor_panel()

Hold-to-repeat (ignore_repeat=False)

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Hold ArrowDown to increment")

hotkeys.activate([
    hotkeys.hk("down", "ArrowDown", ignore_repeat=False),
])

st.session_state.setdefault("count", 0)

if hotkeys.pressed("down"):
    st.session_state["count"] += 1

st.metric("Count", st.session_state["count"])
st.caption("Hold ArrowDown to spam events (ignore_repeat=False)")

Multiple callbacks on the same event

import streamlit as st
import streamlit_hotkeys as hotkeys

st.title("Multiple callbacks")

hotkeys.activate([
    hotkeys.hk("save", "s", ctrl=True, prevent_default=True),
])

def save_doc():
    st.write("Saving...")

def toast_saved():
    st.toast("Saved!")

# register both; each runs once per key press
hotkeys.on_pressed("save", save_doc)
hotkeys.on_pressed("save", toast_saved)

# you can still branch with pressed() afterwards
if hotkeys.pressed("save"):
    st.info("Thank you for saving")

Notes and limitations

  • Browsers reserve some shortcuts. Use prevent_default=True to keep the event for your app when allowed.
  • Combos mean modifiers + one key. The platform does not treat two non-modifier keys pressed together (for example, A+S) as a single combo.
  • The page must have focus; events are captured at the document level.

Similar projects

Credits

Inspired by streamlit-keypress by Sudarsan. This implementation adds a multi-binding manager, edge-triggered events, modifier handling, preventDefault, KeyboardEvent.code and many more features.

Contributing

Issues and PRs are welcome.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

streamlit_hotkeys-0.6.0.tar.gz (17.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

streamlit_hotkeys-0.6.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file streamlit_hotkeys-0.6.0.tar.gz.

File metadata

  • Download URL: streamlit_hotkeys-0.6.0.tar.gz
  • Upload date:
  • Size: 17.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.11

File hashes

Hashes for streamlit_hotkeys-0.6.0.tar.gz
Algorithm Hash digest
SHA256 ebc2da4b7125792a9205ec4cb7db763e54e4eb7abe173019bbe7028f47f953be
MD5 bf8b9fdcb89209e53a5d14bcf4351224
BLAKE2b-256 fa8426a443ad5aeaea998350eeb7970dac822a0e91910d751174c1aefb3ee17c

See more details on using hashes here.

File details

Details for the file streamlit_hotkeys-0.6.0-py3-none-any.whl.

File metadata

File hashes

Hashes for streamlit_hotkeys-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 47d1cee07cee9f8f7db20e4489ffe7c6ce7073b18fd093c8810a77bd42c6b50d
MD5 fc69402c7657aa63ee3502d40497e0e2
BLAKE2b-256 2b69cc4d020e2e39ac287c658ee8c00b427e2876ea002f4d9eb14634a1890fce

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page