Reusable Rich-based braille spinner and transient live panel for CLI output
Project description
rich_transient
Reusable Rich-based braille spinner and transient live panel for CLI output. Use it to show streaming subprocess or task output in a live-updating panel that disappears when the task finishes, with an animated status line.
Requires: Python 3.11+, rich>=13.7.1
Installation
From PyPI:
pip install rich-transient
Standalone (from this repo):
pip install -e ./rich_transient
Quick start: transient live panel
Wrap a long-running task so its output streams into a live panel; when the task ends, the panel is cleared (transient) and only your final message remains.
from rich_transient import transient_live_panel
with transient_live_panel("Installing dependencies") as panel:
def do_work():
# Simulate streaming output
for i in range(20):
print(f"Step {i + 1}/20...")
return "done"
result = panel.run_task(do_work)
print(f"Result: {result}")
The panel shows a scrolling tail of output and an animated braille spinner in the status line; when run_task returns, the panel is removed.
Using the panel API
The context manager yields an object with three methods:
| Method | Description |
|---|---|
panel.append(line: str) |
Add a line to the panel content (e.g. from a subprocess stdout). |
panel.set_status(text: str) |
Update the status line shown next to the spinner (e.g. "Downloading X..."). |
panel.run_task(callable) |
Run a callable in a background thread; the panel refreshes until it completes. Returns the callable's return value; re-raises any exception. |
Streaming subprocess output into the panel:
import subprocess
from rich_transient import transient_live_panel
with transient_live_panel("Running tests") as panel:
def run():
proc = subprocess.Popen(
["pytest", "-v"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)
for line in proc.stdout:
panel.append(line.rstrip())
proc.wait()
return proc.returncode
exit_code = panel.run_task(run)
print(f"Tests exited with code {exit_code}")
Updating the status line:
with transient_live_panel("Building") as panel:
def build():
panel.set_status("Compiling...")
# do compile
panel.set_status("Linking...")
# do link
return 0
panel.run_task(build)
Braille spinner
Use the spinner frames for your own live UI (e.g. a custom table or status line).
With Rich console.status() — use the built-in braille spinner so the whole app shares one style:
from rich.console import Console
from rich_transient import braille_spinner_for_status
console = Console()
with console.status("Loading state...", spinner=braille_spinner_for_status()) as status:
do_work()
status.update("Almost done...")
Custom live UI — use get_braille_frame() so you don't duplicate the frame math:
import time
from rich.console import Console
from rich.live import Live
from rich_transient import SPINNER_BRAILLE, LIVE_REFRESH_PER_SECOND, get_braille_frame
console = Console()
def make_status():
frame = get_braille_frame()
return f"{SPINNER_BRAILLE[frame]} Working..."
with Live(make_status(), refresh_per_second=LIVE_REFRESH_PER_SECOND, console=console) as live:
for _ in range(20):
time.sleep(0.1)
live.update(make_status())
Exports:
SPINNER_BRAILLE— tuple of 10 braille characters for animation frames.LIVE_REFRESH_PER_SECOND— default refresh rate (8.0) for live displays.get_braille_frame()— current animation frame index (use withSPINNER_BRAILLE[i]).braille_spinner_for_status()— Registers the braille spinner with Rich and returns the name"braille"forconsole.status(spinner=...).register_braille_spinner()— Idempotent registration of the braille spinner in Rich's SPINNERS dict (called automatically bybraille_spinner_for_status()).
Presets and options
Presets control buffer size and visible lines:
preset="default"— max_lines=100, display_lines=24 (short steps).preset="streaming"— max_lines=200, display_lines=28 (long streaming output).
Override per call:
with transient_live_panel(
"Long log",
preset="streaming",
max_lines=500,
display_lines=40,
) as panel:
panel.run_task(my_task)
Custom Rich theme: pass a Console so the panel uses your theme:
from rich.console import Console
from rich.theme import Theme
from rich_transient import transient_live_panel
my_theme = Theme({"info": "cyan", "success": "green"})
console = Console(theme=my_theme)
with transient_live_panel("Task", console=console) as panel:
panel.run_task(my_task)
Configuration
For full control, use TransientPanelConfig and TRANSIENT_PANEL_PRESETS:
from rich_transient import (
TransientPanelConfig,
TRANSIENT_PANEL_PRESETS,
transient_live_panel,
)
# Use a built-in preset
cfg = TRANSIENT_PANEL_PRESETS["streaming"]
# Or build your own
custom = TransientPanelConfig(
max_lines=300,
display_lines=30,
default_status="Processing...",
border_style="blue",
)
with transient_live_panel("Custom", config=custom) as panel:
panel.run_task(my_task)
CLI styling helpers (section rules and key-value panels)
For consistent command headers and summary blocks (e.g. run settings, completion messages), use semantic style constants and helpers so all commands share the same look.
Semantic style constants (use with border_style= or style=):
STYLE_SECTION—"blue"(section headers, info panels)STYLE_SUCCESS—"green"(completion, success panels)STYLE_WARNING—"yellow"(warnings)STYLE_DIM—"dim"(separators)
Section header (Rule):
from rich.console import Console
from rich_transient import section_rule, dim_rule, STYLE_SUCCESS
console = Console()
console.print(section_rule("FETCH (THE FEED)"))
# ... content ...
console.print(dim_rule())
console.print(section_rule("Fetch complete", style=STYLE_SUCCESS))
Key-value panel (static summary box with consistent padding and border):
from rich_transient import key_value_panel, STYLE_SECTION, STYLE_SUCCESS
# From (label, value) pairs
console.print(key_value_panel([
("Backend", "sumologic"),
("Hours Back", "24"),
("Limit", "10000"),
], title="[bold blue]Run settings[/]", border_style=STYLE_SECTION))
# Preformatted lines (full markup control)
console.print(key_value_panel([
" [bold]Logs saved to:[/] [cyan]/path/to/logs[/]",
" [bold]Next steps:[/]",
" [dim]# Run analysis:[/]",
" [cyan]ngate analyze /path/to/logs/[/]",
], border_style=STYLE_SUCCESS))
Use these with transient_live_panel so that after the transient panel clears, your final summary uses the same STYLE_SUCCESS and key_value_panel for a consistent “done” block.
Themes
The module provides themes so you can keep the same semantic roles (section, success, warning, dim) but switch palettes. Use a themed console and pass theme style names ("section", "success", "warning", "dim") to section_rule, key_value_panel, and Panel/Rule; the theme maps those names to colors.
Built-in themes:
| Theme name | Description |
|---|---|
default / ngate |
Blue sections, green success, yellow warning (current nGate-style). |
muted |
Softer dim blue/green/yellow; good for low-contrast terminals. |
high_contrast |
Bold bright blue/green/yellow for accessibility. |
mono |
No color: bold for section/success, italic for warning, dim for separator. |
nord |
Nord palette (blue, green, yellow). |
dracula |
Dracula palette (purple, green, yellow). |
Using a theme:
from rich_transient import (
themed_console,
get_theme,
section_rule,
key_value_panel,
THEME_STYLE_SECTION,
THEME_STYLE_SUCCESS,
)
# Option 1: themed_console (one-liner)
console = themed_console("muted")
console.print(section_rule("Fetch complete", style=THEME_STYLE_SECTION))
console.print(key_value_panel([("Output", "/path/to/logs")], border_style=THEME_STYLE_SUCCESS))
# Option 2: get_theme with your own Console
from rich.console import Console
console = Console(theme=get_theme("nord"))
console.print(section_rule("Running", style="section"))
Theme style name constants: THEME_STYLE_SECTION, THEME_STYLE_SUCCESS, THEME_STYLE_WARNING, THEME_STYLE_DIM — use these (or the strings "section", "success", etc.) when printing with a themed console so the theme supplies the actual color.
Without a theme: you can still pass raw colors to the helpers, e.g. section_rule("Title", style=STYLE_SECTION) (or style="blue"). That works with any Console.
API summary
| Export | Type | Description |
|---|---|---|
SPINNER_BRAILLE |
tuple[str, ...] |
Braille spinner frames. |
LIVE_REFRESH_PER_SECOND |
float |
Default refresh rate for live displays. |
STYLE_SECTION |
str |
"blue" for section headers and info panels. |
STYLE_SUCCESS |
str |
"green" for completion/success. |
STYLE_WARNING |
str |
"yellow" for warnings. |
STYLE_DIM |
str |
"dim" for separators. |
THEMES |
dict[str, Theme] |
Built-in themes: default, ngate, muted, high_contrast, mono, nord, dracula. |
THEME_STYLE_SECTION |
str |
Theme key "section" for section headers/panels. |
THEME_STYLE_SUCCESS |
str |
Theme key "success" for completion/success. |
THEME_STYLE_WARNING |
str |
Theme key "warning" for warnings. |
THEME_STYLE_DIM |
str |
Theme key "dim" for separators. |
get_theme(name) |
(str) -> Theme |
Return a built-in theme by name. |
themed_console(theme_name, **kwargs) |
() -> Console |
Console using a built-in theme. |
get_braille_frame() |
() -> int |
Current animation frame index for use with SPINNER_BRAILLE. |
braille_spinner_for_status() |
() -> str |
Registers braille spinner with Rich and returns "braille" for console.status(spinner=...). |
register_braille_spinner() |
() -> None |
Idempotent registration of braille spinner in Rich's SPINNERS. |
section_rule(title, style=...) |
() -> Rule |
Rule with bold section title. |
dim_rule() |
() -> Rule |
Dim Rule separator. |
key_value_panel(lines, ...) |
() -> Panel |
Panel from (label, value) pairs or preformatted lines; optional title, border_style, padding. |
TransientPanelConfig |
dataclass | Panel configuration (max_lines, display_lines, border_style, etc.). |
TRANSIENT_PANEL_PRESETS |
dict[str, TransientPanelConfig] |
"default" and "streaming" presets. |
transient_live_panel(...) |
context manager | Yields an object with append, set_status, run_task. |
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rich_transient-0.1.1.tar.gz.
File metadata
- Download URL: rich_transient-0.1.1.tar.gz
- Upload date:
- Size: 10.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65884d5bdb482ca760ca1704a7b6b4f37f3a1c320489654aa5d809ca92728d9d
|
|
| MD5 |
de2cbb27384e467e4bc97d3840af7253
|
|
| BLAKE2b-256 |
71516bea7b89c5fec7267ff6fa05948f2d24bcd0242b21f7454fcebff1d00469
|
File details
Details for the file rich_transient-0.1.1-py3-none-any.whl.
File metadata
- Download URL: rich_transient-0.1.1-py3-none-any.whl
- Upload date:
- Size: 10.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c5966fba340de6ddf4f8000e4e1394abc2fb195dcaf882516e9e39eb0a86be3
|
|
| MD5 |
a6fab34629e4a1d6664d19357d5e7833
|
|
| BLAKE2b-256 |
41db69bdc869e2e347fb1a4cfbe7db281ca876fa466884f2e0a6cf499d87f6fa
|