Metadata-Version: 2.4
Name: py-sparcli
Version: 0.4.0
Summary: Styled CLI output and interactive input widgets, dependency-free
Project-URL: Homepage, https://github.com/cgroening/py-sparcli
Project-URL: Repository, https://github.com/cgroening/py-sparcli
Project-URL: Documentation, https://github.com/cgroening/py-sparcli#readme
Project-URL: Changelog, https://github.com/cgroening/py-sparcli/blob/main/CHANGELOG.md
Project-URL: Issues, https://github.com/cgroening/py-sparcli/issues
Author: Corvin Gröning
License-Expression: MIT
License-File: LICENSE
Keywords: ansi,cli,color,prompt,terminal
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Terminals
Classifier: Typing :: Typed
Requires-Python: >=3.12
Description-Content-Type: text/markdown

# sparcli

[![PyPI version](https://img.shields.io/pypi/v/py-sparcli.svg)](https://pypi.org/project/py-sparcli/) [![Python versions](https://img.shields.io/pypi/pyversions/py-sparcli.svg)](https://pypi.org/project/py-sparcli/) [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![Typed](https://img.shields.io/badge/typed-yes-blue.svg)](https://peps.python.org/pep-0561/)

Styled CLI output and interactive input widgets for Python, built directly on ANSI escape codes with no third-party dependencies.

sparcli is a native Python port of the Rust library of the same name. It renders styled text, tables, panels, trees and progress bars, and drives single-input prompts (text, password, number, confirm, select, fuzzy, date and more) – all from the standard library alone. It is meant for small, lightweight CLI tools: a single accent color, muted defaults, rounded borders, and graceful behavior under `NO_COLOR` or when output is piped. Heavy, full-screen retained TUIs are out of scope.


![sparcli output widgets](https://raw.githubusercontent.com/cgroening/py-sparcli/main/images/screenshot-1.png)

![sparcli input widgets](https://raw.githubusercontent.com/cgroening/py-sparcli/main/images/screenshot-2.png)


## Highlights

- Output: styled text, inline markup, tables (colspan, rowspan, striping, wrapping, titles), panels, cards (a filled surface whose whole palette is derived from one accent color), alerts, rules, lists, trees, key-value lists, badges, progress bars, spinners, multi-progress, diffs, columns, live in-place display, pager, and the composition helpers `align`, `pad` and `vstack`.
- Input: confirm, text (validation, character filters, history, ghost autocomplete, dropdown), password, number (with a calculator), textarea, single and multi select, an inline fuzzy select, and a calendar date picker.
- A unified `Theme` for input and output, set once and overridable per call.
- Robust by design: prompts never raise on input, a RAII terminal guard restores the terminal, and results come back as values rather than exceptions.
- Zero dependencies: pure Python, standard library only, typed under strict basedpyright.

## Installation

```bash
pip install py-sparcli
```

The distribution is named `py-sparcli`, but the import package is `sparcli`:

```python
import sparcli
from sparcli import Panel, Table, TextInput
```

sparcli requires Python 3.12 or newer.

## Feature overview

| Category | Components |
| --- | --- |
| Text and style | `Style`, `Color`, `Attribute`, `Span`, `Line`, `Text`, `markup` |
| Framing and layout | `Panel`, `Card`, `Rule`, `Columns`, `align`, `pad`, `vstack`, `BorderType`, `Align`, `Edges`, `Title` |
| Data widgets | `Table` (`Column`, `Cell`), `List` (`Marker`), `Tree` (`TreeNode`), `KeyValue`, `Diff`, `Badge`, `Alert` (`AlertKind`) |
| Progress and live | `Spinner` (`SpinnerStyle`), `ProgressBar` (`ProgressStyle`, `Thresholds`), `MultiProgress`, `Live`, `Pager` |
| Input prompts | `TextInput`, `PasswordInput`, `NumberInput`, `Confirm`, `Select`, `FuzzySelect`, `DatePicker`, `Textarea` |
| Prompt support | `Outcome`, `History`, `Shortcut`, `validate`, `event` |
| Theming and terminal | `Theme`, `theme`, `set_theme`, `color_support`, `is_input_tty`, `is_output_tty`, `term_width` |

## Output example

Every output widget exposes `print()` to write to stdout, `print_to(writer)` to capture the result, and `render(max_width)` to lay it out as a composable block. When stdout is not a terminal (a pipe, a file, or with `NO_COLOR` set), no escape codes are emitted.

```python
from sparcli import Alert, Table

Alert.success("Build finished.").print()

Table().columns(["Name", "Status"]).row(["web-1", "online"]).row(
    ["db-1", "online"]
).striped(True).print()
```

```text
╭───────────────────╮
│ ✔ Build finished. │
╰───────────────────╯
╭───────┬────────╮
│ Name  │ Status │
├───────┼────────┤
│ web-1 │ online │
│ db-1  │ online │
╰───────┴────────╯
```

A `Panel` frames content with a rounded border and an optional title. A left-aligned title reads as part of the frame: one connecting border glyph sits before it, never a flush corner – unless the title is too wide for the frame, in which case it is truncated into the border rather than widening the panel.

```python
from sparcli import Panel

Panel("All systems nominal.").title("Status").print()
```

```text
╭─ Status ─────────────╮
│ All systems nominal. │
╰──────────────────────╯
```

Where a `Panel` draws a frame, a `Card` fills a surface. It takes a single accent color and derives everything else from it through HSL: the title stays saturated, the body text and both backgrounds become desaturated, darker shades of the same hue. Title and footer sit on rows of their own, each with its own background, and each region takes its own padding and alignment.

```python
from sparcli import BorderType, Card, Color

Card("Deployed to production.").title("Release 1.4.0").footer(
    "3 minutes ago"
).accent(Color.from_hex("#a6e3a1") or Color.GREEN).border(
    BorderType.TALL
).width(40).print()
```

```text
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Release 1.4.0                        ┃
┃                                      ┃
┃ Deployed to production.              ┃
┃                                      ┃
┃ 3 minutes ago                        ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
```

That block is what the card degrades to, because the preview above is captured without color. With truecolor, `BorderType.TALL` draws a thin block frame instead: the side bars ink a quarter of their cell's width and the top and bottom lines an eighth of their cell's height, which comes out equally thick since a terminal cell is about twice as tall as it is wide, and the horizontal lines run across the corner cells so the corners close. Only `Card` renders it that way – the bars need a filled surface to read against, so every other framed widget receives the heavy line glyphs shown here. The same applies to a card without truecolor support, and `BorderType.ASCII` takes over when the theme disables Unicode.

## Input example

Prompts return an `Outcome` – a submitted value, a cancellation, or a fired shortcut – and never raise on input. They require an interactive terminal; without one, `run()` raises `NoTerminalError`. Each prompt also has a `frame()` method that renders its static opening frame without a TTY, which is how the previews below are produced.

```python
from sparcli import Select

outcome = Select(
    "Environment", options=["staging", "production", "local"]
).run()
if outcome.is_submitted:
    print(f"selected option #{outcome.value}")
```

Its opening frame, with the cursor on the second row:

```text
Environment
  staging
‣ production
  local
```

Text prompts chain validators and filters fluently:

```python
from sparcli import Confirm, TextInput
from sparcli import validate

name = TextInput("Your name?").validate(validate.non_empty()).run()
if name.is_submitted:
    if Confirm("Continue?").run().submitted_or(False):
        print(f"Hello, {name.value}!")
```

## Theming

A single process-wide theme drives both output widgets and input prompts. Set it once; per-call widget options still override individual values.

```python
from sparcli import Color, Theme, set_theme, theme

set_theme(
    Theme(
        accent=Color.rgb(180, 142, 173),
        unicode=True,  # set False for ASCII-only glyphs
    )
)

# Read the active theme anywhere:
active = theme()
```

## Output components

The `output_readme.py` example composes a hero panel, a three-column dashboard and a progress bar:

![sparcli output widgets: a hero panel, a status table, a numbered list, a file tree, a key-value block, colored status badges and a progress bar](https://raw.githubusercontent.com/cgroening/py-sparcli/main/images/screenshot-1.png)

Captured with `NO_COLOR=1` the same collage renders as plain text:

```text
╭─────────────────────────────────  sparcli  ──────────────────────────────────╮
│              A dependency-free Python library for styled output              │
│              and input - panels, tables, trees, lists and more.              │
╰──────────────────────────────────────────────────────────────────────────────╯

          Overview            │ 1. Compose widgets side by side │ host    localhost
╭─────────┬────────┬────────╮ │ 2. Capture, pad and align them  │ port    8080
│ Service │ Status │ Uptime │ │ 3. Render to any UTF-8 terminal │ scheme  https
├─────────┼────────┼────────┤ │                                 │
│ api     │   OK   │ 99.98% │ │ project/                        │ [ DONE ] [ INFO ]
├─────────┼────────┼────────┤ │ ├── api/                        │
│ auth    │   OK   │ 99.91% │ │ │   ├── routes.py               │ [ WARN ] [ FAIL ]
├─────────┼────────┼────────┤ │ │   └── auth.py                 │
│ billing │  WARN  │  97.4% │ │ └── worker.py                   │
╰─────────┴────────┴────────╯ │                                 │

Building [███████████████████████████░░]  92% (92/100)
```

## Input widgets

The `prompt_readme.py` example stacks the static opening frame of every prompt into a single dashboard, produced entirely through `frame()` with no TTY:

![sparcli input widgets: confirm, text, password, number, textarea, single and multi select, fuzzy select and a calendar date picker](https://raw.githubusercontent.com/cgroening/py-sparcli/main/images/screenshot-2.png)

The same dashboard as plain text:

```text
╭─────────────────────  sparcli - input widgets  ─────────────────────╮
│       Interactive prompts - confirm, select, text, password,        │
│                  number, textarea, fuzzy and date.                  │
╰─────────────────────────────────────────────────────────────────────╯

Deploy to production? [Yes]  No  │ Environment  │ Release date
                                 │   staging    │ May 2026
Service api-gateway              │ ‣ production │ Mo Tu We Th Fr Sa Su
Password *******                 │   local      │              1  2  3
Replicas 3                       │              │  4  5  6  7  8  9 10
Email  you@example.com           │ Targets      │ 11 12 13 14 15 16 17
                                 │ ‣ ◉ web      │ 18 19 20 21 22 23 24
Notes                            │   ◯ api      │ 25 26 27 28 29 30 31
first line                       │   ◉ worker   │
second line                      │   ◯ db       │ Language ru
                                 │              │ ‣ Rust
                                 │              │   Ruby
```

## Running the examples

The `examples/` directory holds five runnable programs. The static ones are deterministic and pipe-friendly:

```bash
python examples/output_gallery.py   # every static output widget
python examples/output_dynamic.py   # spinner, progress, multi-progress, live, pager
python examples/prompts.py          # every interactive prompt (needs a real TTY)
python examples/output_readme.py    # the output showcase collage
python examples/prompt_readme.py    # the input showcase, via frame()
```

The `prompts.py` example needs an interactive terminal; run without one it prints a notice and exits. The `output_dynamic.py` animations collapse to a single final frame off a terminal, so it stays clean when piped or redirected.

## NO_COLOR and non-terminal behavior

sparcli honors the common terminal environment variables:

- `NO_COLOR` disables all color: styled spans are written as plain text with no escape codes.
- `CLICOLOR_FORCE` forces color even when output is not a terminal.
- `SPARCLI_NO_TTY` forces "no terminal" behavior, used for deterministic captures and tests.

When stdout is not a TTY (a pipe or a file), color is disabled automatically unless `CLICOLOR_FORCE` is set, and the in-place engines print only their final frame. Every text showcase in this README is real captured output, produced with `NO_COLOR=1 SPARCLI_NO_TTY=1`; the two screenshots are the same collages captured in color.

## Documentation

- `docs/DEVELOPMENT.md` covers building, testing, linting and contributing.
- `CHANGELOG.md` records release notes.
- Every public class and function carries a docstring; browse the source under `sparcli/` for the complete reference.

## License

MIT – see `LICENSE`.
