Skip to content

About

Terminal widgets with no dependencies

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

10 Commits

Folders and files

Repository files navigation

cli-widgets

Prompts, pickers and progress bars for the terminal. No dependencies, just the standard library.

Widgets draw in place and redraw over the lines they own. Once a question is answered it collapses to a single line and stays there, so a long run of prompts reads back like a transcript rather than a scrolling mess.

Installing

It is not on PyPI. Depend on it by git reference:

dependencies = [
    "cli-widgets @ git+https://github.com/KontrolQ/cli-widgets@v0.1.0",
]

Requires Python 3.11 or newer. Works on Windows, macOS and Linux.

Asking for text

from cli_widgets.widgets import entry

name = entry.ask("Name for this machine", default="win98se")

Pass limit to cap the length, placeholder for greyed-out hint text, and validate for a function that returns a complaint string when the value is no good, or None when it is fine:

def not_empty(text):
    if not text.strip():
        return "That cannot be blank."

owner = entry.ask("Owner name", validate=not_empty)

Left, right, home, end, backspace and delete all work while typing.

Picking one thing

from cli_widgets.widgets import choice

size = choice.ask("System disk", ["2 GB", "4 GB", "8 GB"], chosen=1)

chosen is where the pointer starts. The list scrolls if it does not fit and shows a counter when it scrolls.

Options do not have to be plain strings. A tuple or a dict gives you a second column of dim text beside the label:

choice.ask("System disk", [
    ("4 GB", ""),
    ("160 GB", "needs a large disk patch"),
])

choice.ask("Guest", [
    {"label": "Windows 98 SE", "note": "windows-9x"},
])

Picking several

from cli_widgets.widgets import checklist

updates = checklist.ask("Updates", ["IE6 SP1", "DirectX 9.0c", "KernelEx"], ticked=[0])

Space ticks and unticks, enter accepts. ticked takes the positions that start ticked. You get back the options themselves, not their positions.

Yes or no

from cli_widgets.widgets import confirm

if confirm.ask("Build it"):
    build()

Pass affirmed_by_default=False to start on No.

Picking a file or folder

from cli_widgets.widgets import browser

folder = browser.ask_folder("Where should it be created", ".")
disc = browser.ask_file("Installation disc", ".", (".iso", ".img"))

Up and down move, enter opens a folder or chooses a file, left goes up a level. On Windows the drive letters appear once you reach the top. Files show their size. Leave suffixes off to accept anything. Both return None if nothing was chosen.

Showing progress

from cli_widgets.widgets import progress

bar = progress.new("Copying", len(files))

for file in files:
    copy(file)
    bar = progress.step(bar, 1, caption=file.name)

progress.finish(bar)

step advances the bar and redraws it. The caption is optional and only changes when you pass one.

Showing a summary

from cli_widgets.widgets import summary

summary.show("Ready to build", [
    ("Machine", "win98se"),
    ("Memory", "256 MB"),
    ("System disk", "4 GB"),
])

Labels line up in a column with dotted leaders.

Keeping a header on screen

For longer flows you can hold the alternate screen and pin a header above the prompts:

from cli_widgets.widgets import prompting

prompting.take_screen()
prompting.begin(["Windows 98 Second Edition"])

name = entry.ask("Name for this machine")
prompting.repin([f"Windows 98 Second Edition    {name}"])

...

prompting.end()

begin sets the pinned lines and clears the answers below them, repin replaces the header without touching the answers, and end gives the screen back. Without take_screen the widgets just print normally, which is what you want when the output is being piped somewhere.

Ctrl+C raises KeyboardInterrupt from any widget.

Characters

Box drawing, arrows and ticks are used when the terminal encoding can represent them, and plain ASCII when it cannot. Nothing needs configuring.

Building it

uv sync --group dev
uv run pytest
uv run ruff check .
uv run mypy cli_widgets
uv run pyright

Tests live in tests/ and are named *.test.py. Coverage has to stay at 100 percent.

About

Terminal widgets with no dependencies

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages