beautiful-python-cli 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -2
- package/SKILL.md +178 -0
- package/examples/concurrent_progress.py +100 -0
- package/examples/interactive_prompts.py +79 -0
- package/examples/live_dashboard.py +85 -0
- package/examples/showcase.py +113 -0
- package/examples/tui_layout.py +112 -0
- package/install.sh +158 -0
- package/package.json +41 -4
- package/references/activation-tests.md +34 -0
- package/references/concurrent-progress.md +69 -0
- package/references/interaction-patterns.md +109 -0
- package/references/layouts-and-tui.md +57 -0
- package/references/prompts-and-inputs.md +91 -0
- package/references/rich-components.md +118 -0
- package/references/testing-and-accessibility.md +81 -0
- package/references/tracebacks-and-debugging.md +88 -0
- package/requirements.txt +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 syrex1013
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,107 @@
|
|
|
1
|
-
#
|
|
1
|
+
# beautiful-python-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A comprehensive agent skill for crafting polished, accessible terminal user interfaces (TUIs) and CLI applications in Python using [Rich](https://github.com/Textualize/rich).
|
|
4
|
+
|
|
5
|
+
Designed for modern AI coding assistants including Claude Code, Cursor, Gemini / Antigravity, Codex, Windsurf, and Pi.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Capabilities
|
|
10
|
+
|
|
11
|
+
- **Status & Spinners**: Indeterminate operation indicators with clean context manager lifecycle.
|
|
12
|
+
- **Progress Bars**: Measurable multi-task, byte-transfer, and ETA progress bars.
|
|
13
|
+
- **Live Dashboards & TUIs**: Real-time updating multi-pane layouts with responsive screen partitioning.
|
|
14
|
+
- **Interactive Prompts**: Input validation, defaults, yes/no confirmations, and masked secret entries.
|
|
15
|
+
- **Structured Output**: Stylized tables, summary panels, and tree structures with terminal width adaptability.
|
|
16
|
+
- **Diagnostics & Tracebacks**: Production-safe stack trace formatting and syntax highlighting.
|
|
17
|
+
- **Contract Adherence**: Full compliance with `NO_COLOR`, non-TTY environments, and pure machine-readable `--json` streams.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
### Via `npx skills` (Recommended)
|
|
24
|
+
|
|
25
|
+
Install directly into your AI coding assistant:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# Add to current project
|
|
29
|
+
npx skills add syrex1013/beautiful-python-cli
|
|
30
|
+
|
|
31
|
+
# Install globally across all agents
|
|
32
|
+
npx skills add syrex1013/beautiful-python-cli -g
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Via Shell Installer
|
|
36
|
+
|
|
37
|
+
Clone and run the universal installer:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/syrex1013/beautiful-python-cli.git
|
|
41
|
+
cd beautiful-python-cli
|
|
42
|
+
|
|
43
|
+
# Install for all supported agents
|
|
44
|
+
./install.sh --all
|
|
45
|
+
|
|
46
|
+
# Or target specific agents
|
|
47
|
+
./install.sh --claude
|
|
48
|
+
./install.sh --cursor
|
|
49
|
+
./install.sh --antigravity
|
|
50
|
+
./install.sh --codex
|
|
51
|
+
./install.sh --windsurf
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Directory Structure
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
beautiful-python-cli/
|
|
60
|
+
├── SKILL.md # Core agent skill instructions and routing
|
|
61
|
+
├── package.json # npm / skills.sh package metadata
|
|
62
|
+
├── LICENSE # MIT License
|
|
63
|
+
├── requirements.txt # Python dependencies (rich)
|
|
64
|
+
├── install.sh # Multi-agent installer script
|
|
65
|
+
├── references/ # In-depth architectural guides
|
|
66
|
+
│ ├── rich-components.md # Component selection and theme definitions
|
|
67
|
+
│ ├── interaction-patterns.md # Spinners, progress, and live updates
|
|
68
|
+
│ ├── prompts-and-inputs.md # Interactive prompt validation & secrets
|
|
69
|
+
│ ├── tracebacks-and-debugging.md # Error reporting and syntax highlighting
|
|
70
|
+
│ ├── layouts-and-tui.md # Screen partitioning & responsive grids
|
|
71
|
+
│ ├── concurrent-progress.md # Multi-threaded and async workers
|
|
72
|
+
│ └── testing-and-accessibility.md # Non-TTY handling, JSON purity, test suite
|
|
73
|
+
├── examples/ # Runnable implementation patterns
|
|
74
|
+
│ ├── showcase.py # Tables, panels, status, and progress
|
|
75
|
+
│ ├── live_dashboard.py # Dynamic Live dashboard
|
|
76
|
+
│ ├── interactive_prompts.py # Prompts, choices, and confirmation
|
|
77
|
+
│ ├── concurrent_progress.py # ThreadPoolExecutor multi-bar progress
|
|
78
|
+
│ └── tui_layout.py # Multi-pane terminal dashboard
|
|
79
|
+
└── tests/
|
|
80
|
+
└── test_skill.py # Automated unit tests
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Running the Examples
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python3 -m pip install -r requirements.txt
|
|
89
|
+
|
|
90
|
+
# Run the comprehensive showcase
|
|
91
|
+
python3 examples/showcase.py
|
|
92
|
+
|
|
93
|
+
# Run interactive wizard demonstration
|
|
94
|
+
python3 examples/interactive_prompts.py --non-interactive
|
|
95
|
+
|
|
96
|
+
# Run multi-threaded progress demo
|
|
97
|
+
python3 examples/concurrent_progress.py --workers 4
|
|
98
|
+
|
|
99
|
+
# Run real-time monitoring TUI
|
|
100
|
+
python3 examples/tui_layout.py --seconds 3
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## License
|
|
106
|
+
|
|
107
|
+
MIT © syrex1013
|
package/SKILL.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: beautiful-python-cli
|
|
3
|
+
description: Design, implement, or refactor Python command-line output into polished, accessible terminal UIs using Rich. Use when adding spinners, loading states, progress bars, concurrent tasks, interactive prompts, live dashboards, TUI layouts, real-time logs, colored status, tables, panels, themes, or terminal UX. Do not use for GUI/web interfaces or trivial one-off scripts that only need plain output.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Beautiful Python CLI
|
|
7
|
+
|
|
8
|
+
Build terminal interfaces that are useful first and visually polished second. Prefer clear information hierarchy, truthful status, restrained color, and reusable rendering code over decoration.
|
|
9
|
+
|
|
10
|
+
## Instruction priority
|
|
11
|
+
|
|
12
|
+
Follow the user's explicit requirements and the repository's established conventions first. This skill supplies defaults where the user or project has not specified a choice. Do not migrate CLI frameworks, redesign unrelated code, or add dependencies without a concrete reason.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
1. **Inspect before changing**
|
|
17
|
+
- Read the relevant entry point, CLI framework, `pyproject.toml`/requirements, supported Python version, and nearby tests.
|
|
18
|
+
- Identify whether stdout is intended for humans, scripts, or both.
|
|
19
|
+
- Preserve existing commands, flags, exit codes, output contracts, and business logic.
|
|
20
|
+
2. **Choose the smallest useful design**
|
|
21
|
+
- Use `rich` for rendering. Keep the project's existing `argparse`, Typer, Click, or other CLI framework; Typer is optional, not a requirement.
|
|
22
|
+
- Avoid adding `questionary`, `loguru`, or other dependencies when Rich or the existing stack is sufficient.
|
|
23
|
+
- If Rich is missing, explain the dependency and add it through the repository's existing dependency mechanism. Do not silently run a global `pip install`.
|
|
24
|
+
3. **Centralize presentation**
|
|
25
|
+
- Create one shared `Console` (or a small UI module) and a reusable semantic theme.
|
|
26
|
+
- Keep UI/rendering separate from domain logic. Prefer functions such as `render_summary`, `render_findings_table`, `show_status`, and `make_progress`.
|
|
27
|
+
- Use stable style names (`success`, `warning`, `error`, `info`, `muted`, `accent`) instead of scattering raw ANSI escapes or color literals.
|
|
28
|
+
4. **Match the indicator to the work**
|
|
29
|
+
- Use a spinner/status for work whose completion percentage is unknown.
|
|
30
|
+
- Use a progress bar only when real progress can be measured (items, bytes, stages, or completed tasks).
|
|
31
|
+
- Use a live dashboard only when continuously changing information materially helps the operator.
|
|
32
|
+
- Never invent percentages, ETAs, throughput, completion counts, or scan results.
|
|
33
|
+
5. **Handle every output environment**
|
|
34
|
+
- Detect TTY/terminal capability; do not force ANSI color or animation by default.
|
|
35
|
+
- Support `NO_COLOR`, redirected output, CI logs, SSH terminals, and narrow terminal widths.
|
|
36
|
+
- Keep machine-readable output (especially JSON) clean. Send human diagnostics/progress to stderr when stdout is a data stream.
|
|
37
|
+
- Provide a plain or no-color mode when appropriate.
|
|
38
|
+
6. **Verify the result**
|
|
39
|
+
- Run focused tests and the project's formatter/linter/type checker where available.
|
|
40
|
+
- Test a normal terminal, redirected/non-TTY output, narrow width, errors, and empty results.
|
|
41
|
+
- Confirm that progress and live displays stop cleanly on success, failure, and cancellation.
|
|
42
|
+
|
|
43
|
+
## Visual system
|
|
44
|
+
|
|
45
|
+
Use a restrained, consistent palette with semantic meaning:
|
|
46
|
+
|
|
47
|
+
| Role | Suggested style | Meaning |
|
|
48
|
+
|---|---|---|
|
|
49
|
+
| Success | `green` / `bold green` | Completed, valid, healthy |
|
|
50
|
+
| Warning | `yellow` / `bold yellow` | Degraded, partial, needs attention |
|
|
51
|
+
| Error | `red` / `bold red` | Failed, invalid, blocked |
|
|
52
|
+
| Info | `cyan` | Neutral operational information |
|
|
53
|
+
| Accent | `bright_magenta` or `bright_blue` | Product identity or emphasis |
|
|
54
|
+
| Muted | `dim` | Secondary metadata |
|
|
55
|
+
|
|
56
|
+
Color must never be the only signal. Pair it with words (`OK`, `WARN`, `FAIL`), symbols, or explicit columns. Avoid rainbow output, excessive bolding, and large banners on every command. Respect user-specified brand colors and existing themes.
|
|
57
|
+
|
|
58
|
+
## Required UI patterns
|
|
59
|
+
|
|
60
|
+
### Startup and command header
|
|
61
|
+
- Show a compact application name/version only when it adds context.
|
|
62
|
+
- Keep banners short; avoid delaying useful output with decorative animations.
|
|
63
|
+
- Display target, scope, configuration, or execution mode only when relevant and safe to disclose.
|
|
64
|
+
- Never print credentials, tokens, cookies, private keys, or other secrets.
|
|
65
|
+
|
|
66
|
+
### Spinners and loading states
|
|
67
|
+
- Use `console.status("Connecting…", spinner="dots")` for a short, indeterminate operation.
|
|
68
|
+
- Choose a spinner that renders reliably in common terminals; allow Rich to fall back when Unicode is unavailable.
|
|
69
|
+
- Use `console.status` as a context manager so it stops on exceptions.
|
|
70
|
+
- Do not show a spinner for instant operations or when output is non-interactive.
|
|
71
|
+
- For multiple independent tasks, prefer a multi-task `Progress` display over nested spinners.
|
|
72
|
+
|
|
73
|
+
### Progress bars
|
|
74
|
+
- Use `rich.progress.Progress` with columns that answer useful questions:
|
|
75
|
+
- `SpinnerColumn` or `TextColumn` for task identity
|
|
76
|
+
- `BarColumn` for visual completion
|
|
77
|
+
- `TaskProgressColumn` or `MofNCompleteColumn` for actual progress
|
|
78
|
+
- `TimeElapsedColumn` and `TimeRemainingColumn` only when meaningful
|
|
79
|
+
- `DownloadColumn` and `TransferSpeedColumn` for byte transfers
|
|
80
|
+
- Track real units: files, hosts, requests, records, bytes, or completed stages.
|
|
81
|
+
- For unknown totals, use an indeterminate task; do not display a fabricated total.
|
|
82
|
+
- For concurrent work, use separate named tasks and synchronized updates. Avoid interleaved `print()` calls from workers.
|
|
83
|
+
- Use `transient=True` only when the progress display should disappear after completion; keep important final results visible.
|
|
84
|
+
- Disable animated progress when `not console.is_terminal`.
|
|
85
|
+
|
|
86
|
+
### Tables
|
|
87
|
+
- Use `rich.table.Table` for structured results. Give columns concise names and meaningful alignment.
|
|
88
|
+
- Right-align numbers; left-align names and descriptions; use `no_wrap` selectively.
|
|
89
|
+
- Use sensible overflow (`fold`, `ellipsis`, or `crop`) and let Rich adapt to terminal width.
|
|
90
|
+
- Include a useful empty-state message instead of rendering a blank table.
|
|
91
|
+
- Sort or group results when that improves scanning; do not hide important fields just to make the table look tidy.
|
|
92
|
+
- For security findings, distinguish severity, confidence, status, and evidence; do not imply severity from color alone.
|
|
93
|
+
|
|
94
|
+
### Panels, summaries, and hierarchy
|
|
95
|
+
- Use `Panel` for a cohesive summary, alert, or grouped section—not around every line.
|
|
96
|
+
- Use `Rule` for major section boundaries and `Columns`/`Group` for compact related content.
|
|
97
|
+
- Put the most actionable information first. Keep secondary metadata dim and concise.
|
|
98
|
+
- Prefer one strong summary panel plus detailed tables over many nested boxes.
|
|
99
|
+
|
|
100
|
+
### Live output and dashboards
|
|
101
|
+
- Use `Live` with `Layout`, `Table`, `Group`, or a custom renderable for dashboards that update repeatedly.
|
|
102
|
+
- Keep refresh rates modest (typically 4–10 updates/second); slower is usually sufficient for CLI monitoring.
|
|
103
|
+
- Use `screen=True` only for intentional full-screen terminal applications. Prefer `screen=False` for ordinary CLI progress.
|
|
104
|
+
- Use `console.log()` for timestamped messages while a live display is active; do not call built-in `print()` and corrupt the live region.
|
|
105
|
+
- Keep the dashboard readable at narrow widths and provide a final static summary when it exits.
|
|
106
|
+
- Ensure `Live` is stopped via a context manager, including on exceptions and Ctrl-C.
|
|
107
|
+
|
|
108
|
+
### Interactive prompts and user input
|
|
109
|
+
- Use `Prompt.ask` for text input with defaults and validation.
|
|
110
|
+
- Use `Prompt.ask(..., choices=[...])` for constrained choices instead of freeform parsing.
|
|
111
|
+
- Use `Confirm.ask` for yes/no decisions.
|
|
112
|
+
- Use `Prompt.ask(..., password=True)` for secrets and tokens; never echo secrets back to stdout.
|
|
113
|
+
- Check `sys.stdin.isatty()` and provide safe non-interactive defaults for CI/CD or automated pipelines.
|
|
114
|
+
|
|
115
|
+
### Tracebacks and error diagnostics
|
|
116
|
+
- Use `rich.traceback.install(show_locals=False, max_frames=5)` at the CLI boundary for readable error reports.
|
|
117
|
+
- Suppress framework frames (`click`, `typer`, `asyncio`) to focus on user code.
|
|
118
|
+
- Only enable `show_locals=True` when explicitly requested via `--debug` or `--verbose`.
|
|
119
|
+
- Prefer actionable summary panels for anticipated errors over raw stack traces.
|
|
120
|
+
|
|
121
|
+
### Layouts and multi-pane TUIs
|
|
122
|
+
- Use `rich.layout.Layout` to partition terminal windows into headers, sidebars, main bodies, and footers.
|
|
123
|
+
- Combine with `rich.live.Live` for real-time monitoring and surveillance TUIs.
|
|
124
|
+
- Default to `screen=False` so command history remains accessible in scrollback.
|
|
125
|
+
- Design responsive layouts that remain clean down to 80x24 terminal dimensions.
|
|
126
|
+
|
|
127
|
+
### Logs and errors
|
|
128
|
+
- Use `RichHandler` when integrating Python `logging`; configure it once at the application boundary.
|
|
129
|
+
- Keep logs concise and structured. Use timestamps/levels when they help diagnosis, not as decoration.
|
|
130
|
+
- Present expected operational errors as short actionable messages; expose tracebacks only in debug mode.
|
|
131
|
+
- Preserve non-zero exit codes and send diagnostics to stderr.
|
|
132
|
+
- Never swallow exceptions merely to keep the interface attractive.
|
|
133
|
+
|
|
134
|
+
### Themes and output modes
|
|
135
|
+
- Define a `Theme` once and pass it to the shared `Console`.
|
|
136
|
+
- Use `Console(no_color=True)` or an explicit `--no-color` option when the project supports it; otherwise rely on terminal detection and `NO_COLOR`.
|
|
137
|
+
- When supporting `--json`, `--quiet`, or `--plain`, make these modes real contracts:
|
|
138
|
+
- `--json`: valid JSON only on stdout; no banner, spinner, table, or progress text on stdout.
|
|
139
|
+
- `--quiet`: suppress decorative and routine output while preserving errors and requested machine output.
|
|
140
|
+
- `--plain`: readable text without box drawing or ANSI styling.
|
|
141
|
+
- Do not hardcode terminal width. Use Rich's layout/wrapping behavior and test narrow widths.
|
|
142
|
+
|
|
143
|
+
## Code quality rules
|
|
144
|
+
|
|
145
|
+
- Prefer Rich renderables over hand-built ANSI escape sequences.
|
|
146
|
+
- Use `console.print`, `console.log`, `console.status`, `Progress`, `Live`, `Table`, `Panel`, `Columns`, `Tree`, `Syntax`, and `JSON` where they fit.
|
|
147
|
+
- Avoid global monkey-patching, shelling out to `clear`, and repeated terminal erasure.
|
|
148
|
+
- Do not couple core operations to Rich objects; return data from core logic and render it at the CLI boundary.
|
|
149
|
+
- Do not add decorative output that makes copy/paste, piping, or log parsing harder.
|
|
150
|
+
- Use context managers for progress, status, live displays, and other resources.
|
|
151
|
+
- Make cancellation and failure paths as polished and reliable as the success path.
|
|
152
|
+
|
|
153
|
+
## Definition of done
|
|
154
|
+
|
|
155
|
+
- The output communicates state, progress, results, and errors accurately.
|
|
156
|
+
- Spinners are used only for indeterminate work; progress bars reflect real measurements.
|
|
157
|
+
- Tables and panels remain readable at typical and narrow terminal widths.
|
|
158
|
+
- Live updates do not corrupt logs or final output.
|
|
159
|
+
- Redirected, CI, `NO_COLOR`, and JSON output remain usable.
|
|
160
|
+
- Existing CLI behavior and exit semantics are preserved.
|
|
161
|
+
- Tests or a reproducible manual verification cover the relevant output paths.
|
|
162
|
+
|
|
163
|
+
## Supporting references
|
|
164
|
+
|
|
165
|
+
Read only the reference needed for the task:
|
|
166
|
+
- `references/rich-components.md` — Rich APIs, columns, tables, themes, and renderables.
|
|
167
|
+
- `references/interaction-patterns.md` — spinners, progress, live dashboards, async/concurrency patterns.
|
|
168
|
+
- `references/prompts-and-inputs.md` — interactive prompts, validation, secrets, and non-TTY guards.
|
|
169
|
+
- `references/tracebacks-and-debugging.md` — exception formatting, syntax highlighting, and introspection.
|
|
170
|
+
- `references/layouts-and-tui.md` — multi-pane screen partitioning and dynamic TUIs.
|
|
171
|
+
- `references/concurrent-progress.md` — thread-safe and async multi-task progress bars.
|
|
172
|
+
- `references/testing-and-accessibility.md` — TTY behavior, JSON/plain output, accessibility, and tests.
|
|
173
|
+
- `references/activation-tests.md` — positive and negative prompts for checking skill routing.
|
|
174
|
+
- `examples/showcase.py` — comprehensive panels, tables, and progress showcase.
|
|
175
|
+
- `examples/live_dashboard.py` — dynamic Live dashboard updating in real-time.
|
|
176
|
+
- `examples/interactive_prompts.py` — prompts, input validation, confirmation, and secret masking.
|
|
177
|
+
- `examples/concurrent_progress.py` — multi-threaded background workers with simultaneous progress bars.
|
|
178
|
+
- `examples/tui_layout.py` — multi-pane terminal layout for system monitoring.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Demonstration of multi-threaded concurrent workers with Rich progress bars."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import concurrent.futures
|
|
8
|
+
import random
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.progress import (
|
|
14
|
+
BarColumn,
|
|
15
|
+
Progress,
|
|
16
|
+
SpinnerColumn,
|
|
17
|
+
TaskID,
|
|
18
|
+
TaskProgressColumn,
|
|
19
|
+
TextColumn,
|
|
20
|
+
TimeRemainingColumn,
|
|
21
|
+
)
|
|
22
|
+
from rich.table import Table
|
|
23
|
+
from rich.theme import Theme
|
|
24
|
+
|
|
25
|
+
THEME = Theme(
|
|
26
|
+
{
|
|
27
|
+
"success": "bold green",
|
|
28
|
+
"warning": "bold yellow",
|
|
29
|
+
"error": "bold red",
|
|
30
|
+
"info": "cyan",
|
|
31
|
+
"muted": "dim",
|
|
32
|
+
"accent": "bold cyan",
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
console = Console(theme=THEME)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def simulate_worker(progress: Progress, task_id: TaskID, name: str, total_chunks: int) -> tuple[str, bool, int]:
|
|
40
|
+
chunks_done = 0
|
|
41
|
+
for chunk in range(total_chunks):
|
|
42
|
+
time.sleep(random.uniform(0.02, 0.06))
|
|
43
|
+
chunks_done += 1
|
|
44
|
+
progress.advance(task_id, advance=1)
|
|
45
|
+
if chunk == total_chunks // 2:
|
|
46
|
+
progress.update(task_id, description=f"[info]{name}[/info] [muted](halfway)[/muted]")
|
|
47
|
+
progress.update(task_id, description=f"[success]{name}[/success] [muted](done)[/muted]")
|
|
48
|
+
return name, True, chunks_done
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main() -> None:
|
|
52
|
+
parser = argparse.ArgumentParser(description="Demonstrate concurrent multi-task progress.")
|
|
53
|
+
parser.add_argument("--workers", type=int, default=4, help="Number of concurrent workers")
|
|
54
|
+
args = parser.parse_args()
|
|
55
|
+
|
|
56
|
+
console.print(
|
|
57
|
+
Panel(
|
|
58
|
+
f"[accent]Concurrent Processing Engine[/accent]\n"
|
|
59
|
+
f"[muted]Dispatching {args.workers} background worker threads...[/muted]",
|
|
60
|
+
border_style="cyan",
|
|
61
|
+
)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
results: list[tuple[str, bool, int]] = []
|
|
65
|
+
|
|
66
|
+
with Progress(
|
|
67
|
+
SpinnerColumn(),
|
|
68
|
+
TextColumn("[progress.description]{task.description}"),
|
|
69
|
+
BarColumn(bar_width=None),
|
|
70
|
+
TaskProgressColumn(),
|
|
71
|
+
TimeRemainingColumn(),
|
|
72
|
+
console=console,
|
|
73
|
+
) as progress:
|
|
74
|
+
with concurrent.futures.ThreadPoolExecutor(max_workers=args.workers) as executor:
|
|
75
|
+
task_map = {}
|
|
76
|
+
for i in range(args.workers):
|
|
77
|
+
worker_name = f"Worker #{i + 1}"
|
|
78
|
+
steps = random.randint(15, 25)
|
|
79
|
+
task_id = progress.add_task(f"[accent]{worker_name}[/accent]", total=steps)
|
|
80
|
+
future = executor.submit(simulate_worker, progress, task_id, worker_name, steps)
|
|
81
|
+
task_map[future] = worker_name
|
|
82
|
+
|
|
83
|
+
for future in concurrent.futures.as_completed(task_map):
|
|
84
|
+
results.append(future.result())
|
|
85
|
+
|
|
86
|
+
# Final summary table
|
|
87
|
+
table = Table(title="Worker Execution Summary", expand=True)
|
|
88
|
+
table.add_column("Worker", style="bold")
|
|
89
|
+
table.add_column("Status", justify="center")
|
|
90
|
+
table.add_column("Processed Chunks", justify="right")
|
|
91
|
+
|
|
92
|
+
for name, success, chunks in sorted(results):
|
|
93
|
+
status_text = "[success]COMPLETED[/success]" if success else "[error]FAILED[/error]"
|
|
94
|
+
table.add_row(name, status_text, str(chunks))
|
|
95
|
+
|
|
96
|
+
console.print(table)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
main()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Demonstration of interactive prompts, validation, and confirmation dialogs."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.prompt import Confirm, IntPrompt, Prompt
|
|
12
|
+
from rich.theme import Theme
|
|
13
|
+
|
|
14
|
+
THEME = Theme(
|
|
15
|
+
{
|
|
16
|
+
"success": "bold green",
|
|
17
|
+
"warning": "bold yellow",
|
|
18
|
+
"error": "bold red",
|
|
19
|
+
"info": "cyan",
|
|
20
|
+
"muted": "dim",
|
|
21
|
+
"accent": "bold bright_magenta",
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
console = Console(theme=THEME)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def main() -> None:
|
|
29
|
+
parser = argparse.ArgumentParser(description="Demonstrate Rich interactive prompts.")
|
|
30
|
+
parser.add_argument("--non-interactive", action="store_true", help="Run with simulated answers")
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
|
|
33
|
+
console.print(
|
|
34
|
+
Panel(
|
|
35
|
+
"[accent]CLI Configuration Wizard[/accent]\n"
|
|
36
|
+
"[muted]Demonstrates Prompt, Confirm, IntPrompt, and secret masking.[/muted]",
|
|
37
|
+
border_style="magenta",
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
if args.non_interactive or not sys.stdin.isatty():
|
|
42
|
+
console.print("[info]Running in non-interactive mode. Using defaults.[/info]")
|
|
43
|
+
project = "my-awesome-app"
|
|
44
|
+
env = "staging"
|
|
45
|
+
port = 8080
|
|
46
|
+
proceed = True
|
|
47
|
+
else:
|
|
48
|
+
project = Prompt.ask("[bold]Project Name[/bold]", default="my-awesome-app", console=console)
|
|
49
|
+
env = Prompt.ask(
|
|
50
|
+
"[bold]Environment[/bold]",
|
|
51
|
+
choices=["development", "staging", "production"],
|
|
52
|
+
default="staging",
|
|
53
|
+
console=console,
|
|
54
|
+
)
|
|
55
|
+
port = IntPrompt.ask("[bold]Service Port[/bold]", default=8080, console=console)
|
|
56
|
+
_token = Prompt.ask("[bold]API Token (masked)[/bold]", password=True, console=console)
|
|
57
|
+
proceed = Confirm.ask("\nSave configuration and initialize?", default=True, console=console)
|
|
58
|
+
|
|
59
|
+
if proceed:
|
|
60
|
+
console.print(
|
|
61
|
+
Panel(
|
|
62
|
+
f"[success]Configuration saved![/success]\n\n"
|
|
63
|
+
f"• Project: [accent]{project}[/accent]\n"
|
|
64
|
+
f"• Environment: [info]{env}[/info]\n"
|
|
65
|
+
f"• Port: [muted]{port}[/muted]",
|
|
66
|
+
title="[success]Summary[/success]",
|
|
67
|
+
border_style="green",
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
else:
|
|
71
|
+
console.print("[warning]Setup aborted by user.[/warning]")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__":
|
|
75
|
+
try:
|
|
76
|
+
main()
|
|
77
|
+
except KeyboardInterrupt:
|
|
78
|
+
console.print("\n[error]Operation cancelled.[/error]")
|
|
79
|
+
sys.exit(130)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""A small Live dashboard example with a durable final summary."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import random
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.live import Live
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
from rich.theme import Theme
|
|
15
|
+
|
|
16
|
+
THEME = Theme(
|
|
17
|
+
{
|
|
18
|
+
"success": "bold green",
|
|
19
|
+
"warning": "bold yellow",
|
|
20
|
+
"error": "bold red",
|
|
21
|
+
"muted": "dim",
|
|
22
|
+
"accent": "bold cyan",
|
|
23
|
+
}
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class Worker:
|
|
29
|
+
name: str
|
|
30
|
+
completed: int = 0
|
|
31
|
+
total: int = 12
|
|
32
|
+
state: str = "RUNNING"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def render(workers: list[Worker]) -> Table:
|
|
36
|
+
table = Table(title="Live worker dashboard", expand=True)
|
|
37
|
+
table.add_column("Worker", style="accent")
|
|
38
|
+
table.add_column("State")
|
|
39
|
+
table.add_column("Completed", justify="right")
|
|
40
|
+
table.add_column("Progress", ratio=1)
|
|
41
|
+
|
|
42
|
+
for worker in workers:
|
|
43
|
+
if worker.state == "DONE":
|
|
44
|
+
state = "[success]DONE[/success]"
|
|
45
|
+
else:
|
|
46
|
+
state = "[warning]RUNNING[/warning]"
|
|
47
|
+
table.add_row(
|
|
48
|
+
worker.name,
|
|
49
|
+
state,
|
|
50
|
+
f"{worker.completed}/{worker.total}",
|
|
51
|
+
f"{worker.completed / worker.total:.0%}",
|
|
52
|
+
)
|
|
53
|
+
return table
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def main() -> int:
|
|
57
|
+
parser = argparse.ArgumentParser(description="Demonstrate a Rich live dashboard.")
|
|
58
|
+
parser.add_argument("--seconds", type=float, default=4.0)
|
|
59
|
+
args = parser.parse_args()
|
|
60
|
+
|
|
61
|
+
console = Console(theme=THEME)
|
|
62
|
+
workers = [Worker("worker-01"), Worker("worker-02"), Worker("worker-03")]
|
|
63
|
+
|
|
64
|
+
if not console.is_terminal:
|
|
65
|
+
console.print("Live dashboard disabled: stdout is not an interactive terminal.")
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
deadline = time.monotonic() + max(0.2, args.seconds)
|
|
69
|
+
with Live(render(workers), console=console, refresh_per_second=4, screen=False) as live:
|
|
70
|
+
while time.monotonic() < deadline and any(w.completed < w.total for w in workers):
|
|
71
|
+
for worker in workers:
|
|
72
|
+
if worker.completed < worker.total and random.random() > 0.35:
|
|
73
|
+
worker.completed += 1
|
|
74
|
+
if worker.completed == worker.total:
|
|
75
|
+
worker.state = "DONE"
|
|
76
|
+
live.update(render(workers), refresh=True)
|
|
77
|
+
time.sleep(0.25)
|
|
78
|
+
|
|
79
|
+
console.print(render(workers))
|
|
80
|
+
console.print("[success]Dashboard stopped; final state retained.[/success]")
|
|
81
|
+
return 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
if __name__ == "__main__":
|
|
85
|
+
raise SystemExit(main())
|