Skip to main content

A cell-buffer terminal UI framework

Project description

painted

One library. Print to TUI. One dependency.

from painted import show

show({"cpu": 67, "mem": 82, "disk": 45})

TTY gets a styled bar chart. Pipe gets plain text. --json gets JSON. Same data, same function — the stack figures out the rest.

Enter anywhere

Every entry point uses the same building blocks. Pick the one that fits your problem — you never hand over control, and there's no cliff between them.

Print styled output

Replace print() one call at a time. Auto-detects TTY — no ANSI garbage in pipes.

from painted import Block, Style, print_block

block = Block.text("deploy OK", Style(fg="green", bold=True))
print_block(block)

Compose

Blocks are immutable rectangles. Compose them with functions — no widget tree, no DOM.

from painted import border, join_vertical, pad, ROUNDED

header = Block.text(" api-gateway ", Style(bold=True, reverse=True))
status = join_vertical(
    Block.text("  replicas: 2/3 ready", Style(fg="yellow")),
    Block.text("  /health:  200  12ms", Style(fg="green")),
)
card = border(join_vertical(header, status), chars=ROUNDED)
print_block(card)

CLI harness

One render function, three output modes. Pipe gets static, TTY gets live updates, -i gets full interactive.

from painted import run_cli, CliContext, Block

def render(ctx: CliContext, data: dict) -> Block:
    # your render logic — returns a Block
    ...

def fetch() -> dict:
    return {"status": "ok", "replicas": 3}

run_cli(sys.argv[1:], render=render, fetch=fetch)
myapp              # auto-detect
myapp -q           # quiet (zoom 0)
myapp -v           # verbose (zoom 2)
myapp -i           # interactive TUI
myapp --json       # JSON output
myapp | grep ok    # plain text, no ANSI

Full TUI

Alt screen, keyboard input, async render loop, diff-flush. Subclass Surface, override render() and on_key().

import asyncio
from painted import Block, Style, border
from painted.tui import Surface

class MyApp(Surface):
    def render(self):
        block = Block.text("Hello!", Style(fg="green"))
        border(block, title="Demo").paint(self._buf)

    def on_key(self, key: str):
        if key == "q":
            self.quit()

asyncio.run(MyApp().run())

Install

pip install painted

One dependency: wcwidth (wide character display width).

API

Primitives

Export Purpose
Cell / Style Atomic display unit (char + style, frozen)
Span / Line Styled text with display-width awareness
Block Immutable rectangle of cells for composition

Composition

Export Purpose
join_horizontal / join_vertical Combine Blocks
pad / border / truncate Transform Blocks
BorderChars ROUNDED, HEAVY, DOUBLE, LIGHT, ASCII presets

Display

Export Purpose
show(data) Zero-config display with auto-detection
print_block(block) Print a Block to stdout (TTY-aware)
run_cli(args, render=, fetch=) CLI harness with zoom/mode/format

Views (painted.views)

Export Purpose
shape_lens Auto-dispatch for exploration (numeric → chart, hierarchical → tree)
tree_lens / chart_lens Explicit tree and chart strategies
list_view / table / text_input Stateful interactive components
spinner / progress_bar / sparkline Animation and data viz

TUI (painted.tui)

Export Purpose
Surface Alt screen, keyboard, resize, diff-flush render loop
Layer Modal stack: Stay / Pop / Push / Quit
Buffer / BufferView 2D cell grid with region clipping

Aesthetic

Export Purpose
Palette 5 semantic Style roles (success, warning, error, accent, muted)
IconSet Glyph vocabulary with ASCII fallback

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

painted-0.1.3.tar.gz (7.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

painted-0.1.3-py3-none-any.whl (91.9 kB view details)

Uploaded Python 3

File details

Details for the file painted-0.1.3.tar.gz.

File metadata

  • Download URL: painted-0.1.3.tar.gz
  • Upload date:
  • Size: 7.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for painted-0.1.3.tar.gz
Algorithm Hash digest
SHA256 ea989ee8cb520c2756244d8b95185d178fdcf8d6be5830f3ec11bf51b19c4cd2
MD5 05c3bf91ff575719c4b14a445f4e9360
BLAKE2b-256 681d6a2cb9362d3f85c38bbec2094e13cbab998db96ce8c36ddae1955413a73e

See more details on using hashes here.

File details

Details for the file painted-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: painted-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 91.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for painted-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 33719c10d652616937312e67d2daf3357dcc2ad6c0100b72f3a66f7ac0065ffa
MD5 80da54492be9614bcdbd144e4b09165c
BLAKE2b-256 3b2fbdf54bee447d1cd12feaf17731930139ed7554a9f4b4d8a6da69ab6941c0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page