tmux-popup
Composable tmux popup system with gum UI components.
Features
🎨 Rich Display - Canvas with Markdown and flexible layouts
🔧 Hybrid Approach - Python data handling + full gum passthrough
📦 Zero Dependencies - Pure Python, only needs tmux and gum
🎯 Type-Safe - Full type hints with proper base classes
🔍 Fuzzy Search - Multi-select filtering with dict support
Installation
# Prerequisites
sudo pacman -S tmux gum # Arch
brew install tmux gum # macOS
# Install package
uv add tmux-popup # Recommended
pip install tmux-popup # Alternative
Quick Start
from tmux_popup import Popup, Canvas, Text, Input, Choose
# Display text
popup = Popup(width="60%", height="30%")
canvas = Canvas(border="rounded", padding="1")
canvas.add(Text("Welcome to tmux-popup!"))
popup.add(canvas).show()
# Get input
name = Popup().add(Input(prompt="Name: ")).show()
# Display then choose
popup = Popup()
popup.add(Canvas().add(Text("Continue?")))
result = popup.add(Choose(options=["Yes", "No"])).show()
Core Concepts
Three Patterns
# 1. Display only
Popup().add(Canvas().add(content)).show()
# 2. Input only
Popup().add(interactive_element).show()
# 3. Display + Input
popup = Popup()
popup.add(Canvas().add(content))
result = popup.add(interactive_element).show()
Content & Layout
from tmux_popup import Popup, Canvas, Markdown, Text, Row, Column
# Rich content with Markdown
canvas = Canvas(border="rounded", padding="1")
canvas.add(Markdown("""# Title
**Bold**, *italic*, `code`
\```python
def hello():
print("Hi!")
\```
"""))
# Two-column layout
left = Column(width="50%", border="normal", padding="1")
left.add(Markdown("## Left"))
right = Column(width="50%", border="normal", padding="1")
right.add(Text("Right content"))
canvas.add(Row(left, right))
popup.add(canvas).show()
Interactive Elements
from tmux_popup import Input, Choose, Filter, Confirm, Table
# Text input
email = Popup().add(
Input(prompt="Email: ", placeholder="user@example.com")
).show()
# Single choice (dict shows labels, returns values)
actions = {
"📝 New File": "new",
"📂 Open": "open",
"❌ Quit": "quit"
}
result = Popup().add(Choose(options=actions)).show() # Returns: "new", "open", or "quit"
# Multi-select with fuzzy search
packages = ["numpy", "pandas", "fastapi", "django"]
selected = Popup().add(
Filter(options=packages, no_limit=True, fuzzy=True)
).show() # Returns: ["numpy", "pandas"]
# Confirmation
if Popup().add(Confirm(prompt="Delete all?")).show():
print("Deleting...")
# Table selection
data = [
{"name": "Alice", "role": "Admin"},
{"name": "Bob", "role": "User"}
]
row = Popup().add(Table(data=data)).show() # Returns selected row dict
Advanced Features
Complete Example
from tmux_popup import Popup, Canvas, Row, Column, Markdown, Text, Filter
# Build interface
popup = Popup(width="80%", height="60%")
canvas = Canvas(border="rounded", padding="1")
# Two columns
left = Column(width="50%", padding="1")
left.add(Markdown("## Instructions\n\n• Type to filter\n• Space to select"))
right = Column(width="50%", padding="1")
right.add(Markdown("## Example\n\n packages = ['numpy', 'pandas']"))
canvas.add(Row(left, right))
popup.add(canvas)
# Add interactive filter
packages = {"NumPy": "numpy", "Pandas": "pandas"}
selected = popup.add(
Filter(options=packages, no_limit=True, fuzzy=True)
).show()
Gum Passthrough
All gum flags work via kwargs:
Choose(
options=["A", "B", "C"],
cursor_foreground="212", # gum styling
height=10, # gum display option
select_if_one=True, # gum behavior
header="Select:" # gum text
)
Debug Mode
# See generated shell script
Popup(debug=True).add(Canvas().add("Test")).show()
Components Reference
Core
Popup- Main container (width, height, border, debug)Canvas- Content area (border, padding, margin, align)
Content
Text- Plain textMarkdown- Formatted markdown with code blocks
Layout
Row- Horizontal containerColumn- Vertical container (width, border, padding)
Interactive
Input- Single-line input (prompt, placeholder, header)Write- Multi-line editor (width, height)Confirm- Yes/no dialog (prompt, affirmative, negative)Choose- Single/multi selection (options, limit, header)Filter- Fuzzy search (options, no_limit, fuzzy)Table- Tabular selection (data, border)FilePicker- File browser (path, file, all)Pager- Scrollable viewer (content)Spin- Loading spinner (command, title)Format- Text formatter (content, format_type)
Types
TimeoutResult,CancelledResult- Special return values
Development
git clone https://github.com/angelsen/tap-tools
cd tap-tools/packages/tmux-popup
uv sync
# Run examples
python examples/demo.py
📄 License
MIT - see LICENSE for details.
👤 Author
Fredrik Angelsen
🙏 Acknowledgments
Built on top of:
Metadata
Release files for tmux-popup 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tmux_popup-0.2.2.tar.gz | 19.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tmux_popup-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.4 kB
Release files / tmux_popup-0.2.2.tar.gz
| Download URL | tmux_popup-0.2.2.tar.gz |
|---|---|
| Size | 19.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
04bea0e764fea6c6d866df3a4a22e480a6d6959e93ef6ec13d801febfbfd5618
|
|
BLAKE2b-256 checksum How to use checksums |
cd5e2ffe459dcbd59b136be2c86c46585ab85ddc396a1352eb466c26d671b1d8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.18
|
Release files / tmux_popup-0.2.2-py3-none-any.whl
| Download URL | tmux_popup-0.2.2-py3-none-any.whl |
|---|---|
| Size | 30.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
94f2a21e749c084f2c1413f25f3b2f48f77b1318d6c6334fb90e94b4b7c3600d
|
|
BLAKE2b-256 checksum How to use checksums |
38758e9cc591c4de8a9a8d1db64c42fa4e00bbb6f990c1e380c3b15f10459112
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.18
|