A highly configurable progress indicator library for Python, built on Rich. ThothSpinner provides beautiful, composable terminal UI components including spinners, progress bars, timers, and animated messages with shimmer effects.
- π¨ Modular Components: Mix and match spinner, progress, timer, message, and hint components
- π State Management: Built-in success/error states with automatic transitions
- β¨ Shimmer Effects: Eye-catching animation effects for messages
- π― Thread-Safe: Proper locking for concurrent operations
- π Performance Optimized: Efficient rendering with minimal CPU usage
- π Rich Integration: Seamless integration with Rich Console and Live displays
- πΊ Textual Support: Native Textual widgets with reactive state management
- Rich API Reference - Complete API documentation for Rich components
- Textual API Reference - Textual widget documentation
- Examples Gallery - Runnable Rich examples
- Textual Examples - Textual application examples
- Rich to Textual Guide - Migration guide
- Troubleshooting Guide - Solutions to common issues
- Release Guide - Build, publish, and CI/CD architecture
- First-Time Publish Guide - Step-by-step v1.0.0 PyPI publish walkthrough
# Install from PyPI
pip install thothspinner
# or with uv (recommended)
uv add thothspinner# Preview all 48 spinner styles in the terminal
thothspinner preview
# Preview a specific style
thothspinner preview npm_dots
# Open interactive TUI style browser
thothspinner browsefrom thothspinner import ThothSpinner
from rich.console import Console
from rich.live import Live
import time
console = Console()
# Simple usage with all components
with Live(ThothSpinner(), console=console) as live:
spinner = live.renderable
spinner.start()
# Simulate work with progress
for i in range(100):
spinner.update_progress(current=i, total=100)
time.sleep(0.05)
spinner.success("Task completed!")from textual.app import App, ComposeResult
from thothspinner.textual import TextualThothSpinner
import asyncio
class MyApp(App):
def compose(self) -> ComposeResult:
yield TextualThothSpinner(
spinner_style="npm_dots",
message_text="Processing data",
message_shimmer=True,
)
async def on_mount(self) -> None:
spinner = self.query_one(TextualThothSpinner)
spinner.start()
await asyncio.sleep(2)
spinner.success("Done!")
MyApp().run()# Run all examples (Rich + Textual)
just examples
# Or run by category
just examples-thothspinner # Rich ThothSpinner examples
just examples-textual # Textual widget examples
# Or run individual examples
just example-thothspinner-basic
just example-thothspinner-full
just example-progress
just example-timer
just example-message
just example-message-shimmer# Clone the repository
git clone https://github.com/smorin/thothspinner.git
cd thothspinner
# Install with uv (recommended)
uv sync
# Or with pip
pip install -e .pip install thothspinner
# Or with uv (recommended)
uv add thothspinner- SpinnerComponent: Animated spinners with multiple styles (npm_dots, claude_stars, etc.)
- ProgressComponent: Progress counters with various formats (percentage, fraction, etc.)
- TimerComponent: Elapsed time display with flexible formatting
- MessageComponent: Rotating action words with shimmer effects
- HintComponent: Static hint text for instructions
- ThothSpinner: Orchestrator that combines all components
from thothspinner import ThothSpinner
from thothspinner.rich.components import SpinnerComponent, ProgressComponent
# Individual components
spinner = SpinnerComponent(style="claude_stars", color="#FFA500")
progress = ProgressComponent(format={"style": "percentage"}, color="#00FF00")
# Or use the orchestrator for everything
spinner = ThothSpinner(
spinner_style="npm_dots",
message_text="Processing data", # initial rotating message text
message_shimmer=True,
progress_format="percentage",
timer_format="auto",
hint_text="(esc to cancel)"
)set_message() updates the current rotating message text. Use
set_message_pinned() only when you explicitly want a non-rotating message.
ThothSpinner supports both kwargs and dictionary configuration:
# Using kwargs
spinner = ThothSpinner(
spinner_style="claude_stars",
message_shimmer=True,
success_duration=2.0 # Auto-clear after 2 seconds
)
# Using configuration dictionary
config = {
"defaults": {"color": "#D97706"},
"elements": {
"spinner": {"style": "npm_dots"},
"message": {"shimmer": {"enabled": True, "width": 3}},
"progress": {"format": {"style": "percentage"}}
},
"states": {
"success": {
"spinner": {"icon": "β", "color": "#00FF00"},
"message": {"text": "Complete!"}
}
}
}
spinner = ThothSpinner.from_dict(config)thothspinner/
βββ src/thothspinner/ # Source code
β βββ rich/ # Rich-based components
β β βββ components/ # Individual components
β β βββ thothspinner.py # Main orchestrator
β βββ textual/ # Textual widgets
βββ tests/ # Test suite (97%+ coverage)
βββ docs/ # Documentation
β βββ thothspinner_rich.md # Rich API reference
β βββ thothspinner_textual.md # Textual API reference
β βββ examples/ # Example scripts
β βββ troubleshooting.md # Troubleshooting guide
βββ examples/ # Demo scripts
βββ justfile # Task automation
βββ pyproject.toml # Project configuration
# Format code
just format
# Lint code
just lint
# Type check
just typecheck
# Run tests with coverage
just test-cov
# Security scan (bandit)
just security
# Generate changelog (git-cliff)
just changelog
# Version management
just current-version
just bump-patch
just bump-minor
just bump-major
# Regenerate visual regression snapshots
just update-snapshots
# Run all checks (format, lint, typecheck, security, test)
just all
# Clean build artifacts
just clean# Preview all 48 spinner styles
uv run thothspinner preview
# Preview a specific style
uv run thothspinner preview npm_dots
# Open interactive TUI style browser
uv run thothspinner browse# Install GIF generation tools (macOS only, one-time)
just install-readme-animation
# Generate demo.gif
just demo-gif
# Verify it looks right
open docs/images/demo.gif
# Commit and push
git add docs/images/demo.gif
git commit -m "docs: add demo.gif for README"
git push origin mainThe project maintains 97%+ test coverage, including visual regression tests via pytest-textual-snapshot:
# Run tests
just test
# Run tests with coverage report
just test-cov
# Coverage report generated at htmlcov/index.html
# Run specific test file
just test tests/rich/test_spinner.py
# Regenerate visual regression snapshots (Textual widgets)
just update-snapshotsfrom thothspinner import ThothSpinner
from rich.live import Live
with Live(ThothSpinner()) as live:
spinner = live.renderable
spinner.start()
for i in range(100):
spinner.update_progress(current=i, total=100)
time.sleep(0.05)
spinner.success()from pathlib import Path
files = list(Path(".").glob("*.py"))
spinner = ThothSpinner(progress_format="fraction")
with Live(spinner) as live:
spinner.start()
for i, file in enumerate(files):
spinner.set_message(text=f"Processing {file.name}") # rotating message update
spinner.update_progress(current=i, total=len(files))
process_file(file)
spinner.success(f"Processed {len(files)} files")with Live(ThothSpinner()) as live:
spinner = live.renderable
spinner.start()
try:
risky_operation()
spinner.success("Operation successful")
except Exception as e:
spinner.error(f"Operation failed: {e}")More examples in the Examples Gallery.
β M01βM05: Core Rich Components (v0.1.0βv0.5.0)
- Hint, Spinner, Progress, Timer, and Message components
- ThothSpinner orchestrator with state management
- 97%+ test coverage, thread-safe operations with proper locking
β M06: Rich Documentation (v0.6.0)
- Comprehensive API reference, examples gallery with 20+ examples, troubleshooting guide
β M07βM13: Textual Components & Documentation (v0.7.0βv0.13.0)
- Full Textual widget set with reactive state management
- Feature parity with all Rich components
- Textual examples, integration guides, and API reference
β M15: Progress Bar Format & Animation Smoothing (v1.1.0)
- Bar format style for Textual ProgressWidget with configurable fill characters
- Smooth animated transitions when progress values change
β M14: Publishing to PyPI (v1.0.0)
- PyPI package publication with OIDC trusted publishing
- GitHub Actions CI/CD pipeline (test matrix, CodeQL, publish)
- Release automation with git-cliff changelog generation
First time? Follow the First-Time Publish Guide for step-by-step OIDC setup and the v1.0.0 publish walkthrough.
For subsequent releases, see the Release Guide. Quick reference:
# 1. Bump version (pick one), then run checks and commit:
just bump-patch # or bump-minor / bump-major
just all # format, lint, typecheck, security, test β must all pass
git add pyproject.toml
git commit -m "chore: release v1.2.3"
git push origin main
# 2. Tag and publish (auto-generates changelog, builds, tags, pushes β triggers CI β PyPI)
just release 1.2.3Releases are published automatically via OIDC trusted publishing β no API tokens required. See RELEASE.md for OIDC setup, CI/CD pipeline details, and troubleshooting.
Contributions are welcome! See CONTRIBUTING.md for full details. Quick summary:
- Check the MILESTONES.md for current tasks
- Follow the established code patterns
- Maintain test coverage above 90%
- Use the development toolchain (just commands)
- Write tests for new features
- Update documentation as needed
For milestone-specific work, reference tasks in the milestone documents (M01.md, M02.md, etc.).
A .devcontainer config is included β open in Codespaces or VS Code Dev Containers for an instant, pre-configured development environment.
MIT License - See LICENSE file for details.
- Built on the excellent Rich and Textual libraries by Will McGugan
- Inspired by various terminal UI libraries including ora, cli-spinners, and progress
- Name inspired by Thoth, ancient Egyptian deity of wisdom and writing
- Development patterns influenced by Rich's battle-tested implementation
For issues, questions, or suggestions:
- π Open an issue on GitHub
- π Check the Documentation
- π Review the Troubleshooting Guide
- π§ Contact the maintainers
Current Version: 1.0.0 | Python: 3.11+ | Coverage: 97%+ | Rich Docs | Textual Docs

