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
sudo pacman -S tmux gum
brew install tmux gum
uv add tmux-popup
pip install tmux-popup
Quick Start
from tmux_popup import Popup, Canvas, Text, Input, Choose
popup = Popup(width="60%", height="30%")
canvas = Canvas(border="rounded", padding="1")
canvas.add(Text("Welcome to tmux-popup!"))
popup.add(canvas).show()
name = Popup().add(Input(prompt="Name: ")).show()
popup = Popup()
popup.add(Canvas().add(Text("Continue?")))
result = popup.add(Choose(options=["Yes", "No"])).show()
Core Concepts
Three Patterns
Popup().add(Canvas().add(content)).show()
Popup().add(interactive_element).show()
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
canvas = Canvas(border="rounded", padding="1")
canvas.add(Markdown("""# Title
**Bold**, *italic*, `code`
\```python
def hello():
print("Hi!")
\```
"""))
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
email = Popup().add(
Input(prompt="Email: ", placeholder="user@example.com")
).show()
actions = {
"📝 New File": "new",
"📂 Open": "open",
"❌ Quit": "quit"
}
result = Popup().add(Choose(options=actions)).show()
packages = ["numpy", "pandas", "fastapi", "django"]
selected = Popup().add(
Filter(options=packages, no_limit=True, fuzzy=True)
).show()
if Popup().add(Confirm(prompt="Delete all?")).show():
print("Deleting...")
data = [
{"name": "Alice", "role": "Admin"},
{"name": "Bob", "role": "User"}
]
row = Popup().add(Table(data=data)).show()
Advanced Features
Complete Example
from tmux_popup import Popup, Canvas, Row, Column, Markdown, Text, Filter
popup = Popup(width="80%", height="60%")
canvas = Canvas(border="rounded", padding="1")
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)
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",
height=10,
select_if_one=True,
header="Select:"
)
Debug Mode
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 text
Markdown
- Formatted markdown with code blocks
Layout
Row
- Horizontal container
Column
- 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
python examples/demo.py
📄 License
MIT - see LICENSE for details.
👤 Author
Fredrik Angelsen
🙏 Acknowledgments
Built on top of:
- tmux - Terminal multiplexer
- gum - Delightful CLI interactions