Skip to content

UI Components

Tip

Follow the Beautiful UIs guide for examples.!

Spinners

Spin

class Spin(Enum): ...

The spinning animation you'd like to use. The spinners are sourced from the NPM cli-spinners package.

You can see all the spinners in action by running uv run -m examples.spinner. The full list can be found in the code here.

Spinner

A spinner indicating that something is happening behind the scenes. It can be used as a context manager or like a decorator. The context manager usage is like so:

import asyncio
from clypi import Spinner

async def main():
    async with Spinner("Doing something", capture=True) as s:
        await asyncio.sleep(1)
        s.title = "Slept for a bit"
        print("I slept for a bit, will sleep a bit more")
        await asyncio.sleep(1)

asyncio.run(main())
Spinner.__init__()

def __init__(
    self,
    title: str,
    animation: Spin | list[str] = Spin.DOTS,
    prefix: str = " ",
    suffix: str = "…",
    speed: float = 1,
    capture: bool = False,
    output: t.Literal["stdout", "stderr"] = "stderr",
)
Parameters:

  • title: the initial text to display as the spinner spins
  • animation: a provided Spin animation or a list of frames to display
  • prefix: text or padding displayed before the icon
  • suffix: text or padding displayed after the icon
  • speed: a multiplier to speed or slow down the frame rate of the animation
  • capture: if enabled, the Spinner will capture all stdout and stderr and display it nicely
  • output: the pipe to write the spinner animation to
done

async def done(self, msg: str | None = None)
Mark the spinner as done early and optionally display a message.

fail

async def fail(self, msg: str | None = None)
Mark the spinner as failed early and optionally display an error message.

log

async def log(self, msg: str | None = None)
Display extra log messages to the user as the spinner spins and your work progresses.

pipe

async def pipe(
    self,
    pipe: asyncio.StreamReader | None,
    color: ColorType = "blue",
    prefix: str = "",
)
Pipe the output of an async subprocess into the spinner and display the stdout or stderr with a particular color and prefix.

Examples:

import asyncio

async def main():
    async with Spinner("Doing something") as s:
        proc = await asyncio.create_subprocess_shell(
            "for i in $(seq 1 10); do date && sleep 0.4; done;",
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
        )
        await asyncio.gather(
            s.pipe(proc.stdout, color="blue", prefix="(stdout)"),
            s.pipe(proc.stderr, color="red", prefix="(stdout)"),
        )

spinner (decorator)

This is just a utility decorator that let's you wrap functions so that a spinner displays while they run. spinner accepts the same arguments as the context manager Spinner.

import asyncio
from clypi import spinner

@spinner("Doing work", capture=True)
async def do_some_work():
    await asyncio.sleep(2)

asyncio.run(do_some_work())

Boxed

Boxes

class Boxes(Enum): ...

The border style you'd like to use. To see all the box styles in action run uv run -m examples.boxed.

The full list can be found in the code here.

boxed

def boxed(
    lines: T,
    width: t.Literal["auto", "max"] | int = "auto",
    style: Boxes = Boxes.HEAVY,
    alignment: AlignType = "left",
    title: str | None = None,
    color: ColorType = "bright_white",
) -> T:
Wraps text neatly in a box with the selected style, padding, and alignment.

Parameters:

  • lines: the type of lines will determine it's output type. It can be one of str, list[str] or Iterable[str]
  • width: the desired width of the box:
    • If "max", it will be set to the max width of the terminal.
    • If "auto", it will be set to the max width of the content.
    • If width < 0, it will be set to the max width of the terminal - the number.
    • If width > 0, it will be set to that exact width.
  • style: the desired style (see Boxes)
  • alignment: the style of alignment (see align)
  • title: optionally define a title for the box, it's length must be < width
  • color: a color for the box border and title (see colors)

Stack

def stack(*blocks: list[str], width: int | None = None, padding: int = 1) -> str:
def stack(*blocks: list[str], width: int | None = None, padding: int = 1, lines: bool) -> list[str]:

Horizontally aligns blocks of text to display a nice layout where each block is displayed side by side.

Parameters:

  • blocks: a series of blocks of lines of strings to display side by side
  • width: the desired width of the box. If None, it will be set to the max width of the terminal. If negative, it will be set to the max width of the terminal - the number.
  • padding: the space between each block
  • lines: if the output should be returned as lines or as a string

Separator

separator

def separator(
    separator: str = "━",
    width: t.Literal["max"] | int = "max",
    title: str | None = None,
    color: ColorType | None = None,
) -> str:
Prints a line made of the given separator character.

Parameters:

  • separator: the character used to build the separator line
  • width: if max it will use the max size of the terminal. Otherwise you can provide a fixed width.
  • title: optionally provide a title to display in the middle of the separator
  • color: the color for the characters

Indented

indented

def indented(lines: list[str], prefix: str = "  ") -> list[str]
Indents a set of lines with the given prefix

Align

align

def align(s: str, alignment: AlignType, width: int) -> str
Aligns text according to alignment and width. In contrast with the built-in methods rjust, ljust, and center, clypi.align(...) aligns text according to it's true visible width (the built-in methods count color codes as width chars).

Parameters:

  • s: the string being aligned
  • alignment: one of left, right, or center
  • width: the wished final visible width of the string

Examples:

import clypi

 clypi.align("foo", "left", 10) # -> "foo       "
 clypi.align("foo", "right", 10) # -> "          foo"
 clypi.align("foo", "center", 10) # -> "   foo   "