Metadata-Version: 2.4
Name: streamlit-hotkeys
Version: 0.4.0
Summary: Keyboard hotkeys for Streamlit (Ctrl/Cmd/Alt/Shift combos) with edge-triggered events.
Author-email: Viktor Shcherbakov <viktoroo.sch@gmail.com>
Maintainer-email: Viktor Shcherbakov <viktoroo.sch@gmail.com>
License: MIT License
        
        Copyright (c) 2025 Your Name
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
Project-URL: Source, https://github.com/viktor-shcherb/streamlit-hotkeys
Project-URL: Issue Tracker, https://github.com/viktor-shcherb/streamlit-hotkeys/issues
Project-URL: Similar: streamlit-keypress, https://pypi.org/project/streamlit-keypress/
Project-URL: Similar: streamlit-shortcuts, https://pypi.org/project/streamlit-shortcuts/
Project-URL: Similar: streamlit-keyup, https://pypi.org/project/streamlit-keyup/
Project-URL: Similar: keyboard_to_url, https://arnaudmiribel.github.io/streamlit-extras/extras/keyboard_url/
Keywords: streamlit,hotkeys,shortcuts,keyboard,keypress,component
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: streamlit>=1.25.0
Dynamic: license-file

# Streamlit Hotkeys

[![PyPI](https://img.shields.io/pypi/v/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)
[![Python Versions](https://img.shields.io/pypi/pyversions/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)
[![License](https://img.shields.io/pypi/l/streamlit-hotkeys.svg)](LICENSE)
[![Wheel](https://img.shields.io/pypi/wheel/streamlit-hotkeys.svg)](https://pypi.org/project/streamlit-hotkeys/)
![Streamlit Component](https://img.shields.io/badge/streamlit-component-FF4B4B?logo=streamlit\&logoColor=white)
[![Downloads](https://static.pepy.tech/badge/streamlit-hotkeys)](https://pepy.tech/project/streamlit-hotkeys)

Keyboard hotkeys for Streamlit - capture `Ctrl/Cmd/Alt/Shift + key` anywhere in your app and trigger Python once per press (edge-triggered). Uses a single invisible manager component.

**Important:** call `activate(...)` as early as possible in each page. Activation injects CSS that collapses the manager iframe, which avoids layout flicker.

---

## Installation

```bash
pip install streamlit-hotkeys
```

## Quick start

```python
import streamlit as st
import streamlit_hotkeys as hotkeys

hotkeys.activate([
    hotkeys.hk("palette", "k", meta=True, help="Open palette (mac)"),
    hotkeys.hk("palette", "k", ctrl=True, help="Open palette (win/linux)"),
    hotkeys.hk("save", "s", ctrl=True, prevent_default=True, help="Save"),
    hotkeys.hk("show_legend", "?", shift=True, help="Show shortcuts"),
], key="global")

def save():
    st.success("Saved!")
    
if hotkeys.pressed("save"):
    st.info("Thank you for saving")

hotkeys.on_pressed("save", save)

@hotkeys.on_pressed("palette")
def open_palette():
    st.info("Palette opened")
    
@st.dialog("Keyboard Shortcuts")
def _shortcuts_dialog():
    hotkeys.legend()

if hotkeys.pressed("show_legend"):
    _shortcuts_dialog()

st.write("Press Cmd/Ctrl+K, Ctrl+S or Shift+?")
```

## Features

- Single invisible **manager** (one iframe for the whole page)
- Activates early and auto-collapses its iframe to avoid layout flicker
- Edge-triggered events (per-id seq; no sticky booleans)
- Bind single keys or modifier combos (`ctrl`, `alt`, `shift`, `meta`)
- Reuse the **same `id`** across multiple 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 friendly; use `key=` for multiple independent managers
- Optional `debug=True` to log matches in the browser console

## API

### `hk(...)` - define a binding

```python
hk(
  id: str,
  key: str | None = None,           # for example, "k", "Enter", "ArrowDown"
  *,
  code: str | None = None,          # for example, "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,          # text shown in the legend (optional)
) -> dict
```

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

Register bindings and render the single manager. Accepts `hk(...)` dicts, a list of them, or a mapping `id -> spec`.

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

Return `True` exactly once when the binding `id` fires.

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

Render a grouped shortcuts list (merges multiple bindings that share the same `id`, and shows the first non-empty `help` string per id). 

### Shortcuts Legend Example

```python
import streamlit as st
import streamlit_hotkeys as hotkeys

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()  

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

## 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

* [streamlit-keypress] - Original "keypress to Python" component by Sudarsan.
* [streamlit-shortcuts] - Keyboard shortcuts for buttons and widgets; supports multiple bindings and hints.
* [streamlit-keyup] - Text input that emits on every keyup (useful for live filtering).
* [keyboard\_to\_url][keyboard_to_url] - Bind a key to open a URL in a new tab.

[streamlit-keypress]: https://pypi.org/project/streamlit-keypress/
[streamlit-shortcuts]: https://pypi.org/project/streamlit-shortcuts/
[streamlit-keyup]: https://pypi.org/project/streamlit-keyup/
[keyboard_to_url]: https://arnaudmiribel.github.io/streamlit-extras/extras/keyboard_url/

## Credits

Inspired by [streamlit-keypress] by **Sudarsan**. This implementation adds a multi-binding manager, edge-triggered events, modifier handling, `preventDefault`, and `KeyboardEvent.code`.

## Contributing

Issues and PRs are welcome.

## License

MIT. See `LICENSE`.
