Claude Code for blessed: Terminal UI Toolkit in Python — Claude Skills 360 Blog
Blog / AI / Claude Code for blessed: Terminal UI Toolkit in Python
AI

Claude Code for blessed: Terminal UI Toolkit in Python

Published: March 29, 2028
Read time: 5 min read
By: Claude Skills 360

blessed provides a Pythonic wrapper around terminal capabilities — colors, cursor movement, and keyboard input. pip install blessed. Basic: from blessed import Terminal; term = Terminal(). Clear: print(term.clear). Home: print(term.home). Move: print(term.move(y=5, x=10) + "Hello"). Color text: print(term.bold_red("Error")). term.green("OK"). term.blue_on_white("styled"). Background: term.on_green("green bg"). 256-color: term.color(196)("red"). term.on_color(22)("bg"). Bright: term.bright_cyan("text"). Bold: term.bold("text"). term.underline term.reverse term.blink. Width/Height: term.width; term.height. Center: term.center("text"). Rjust: term.rjust("right"). Terminal resize: term.width re-evaluates dynamically. Fullscreen: with term.fullscreen(): draw(). cbreak: with term.cbreak(): key = term.inkey(timeout=0.1). Key: key.is_sequence, key.name — “KEY_UP”, “KEY_DOWN”, “KEY_LEFT”, “KEY_RIGHT”, “KEY_ENTER”, “KEY_ESCAPE”, “q”. term.raw(). term.hidden_cursor(). term.location(x, y) context manager – moves cursor then restores. term.move_xy(x, y) string. Wrap: term.wrap(text, width=term.width). Standout: term.standout("text"). term.dim. Italic: term.italic. Underline: term.underline. term.normal resets all. Claude Code generates blessed TUIs, dashboard layouts, and interactive keyboard-driven terminal applications.

CLAUDE.md for blessed

## blessed Stack
- Version: blessed >= 1.20 | pip install blessed
- Terminal: term = Terminal() — access term.width, term.height, term.clear, term.home
- Color: term.bold_green("text") | term.red("x") | term.on_blue("bg") | term.color(N)
- Cursor: term.move(y, x) | term.move_xy(x, y) | with term.location(x, y): print(...)
- Keys: with term.cbreak(): key = term.inkey(timeout=0.1) | key.name == "KEY_UP"
- Fullscreen: with term.fullscreen(): draw_loop() — saves/restores terminal
- Reset: term.normal | term.clear | term.home | term.clear_eol

blessed Terminal UI Pipeline

# app/tui.py — blessed terminal UI, keyboard input, and dashboard components
from __future__ import annotations

import signal
import sys
import time
import threading
from contextlib import contextmanager
from typing import Any, Callable, Generator, Iterator

from blessed import Terminal


# ─────────────────────────────────────────────────────────────────────────────
# 1. Terminal helpers
# ─────────────────────────────────────────────────────────────────────────────

term = Terminal()


def clear() -> None:
    """Clear screen and move cursor to home position."""
    print(term.home + term.clear, end="")


def write_at(x: int, y: int, text: str) -> None:
    """Write text at absolute (x, y) position."""
    print(term.move_xy(x, y) + text, end="", flush=True)


def write_centered(text: str, y: int, style: Callable | None = None) -> None:
    """Write text horizontally centered on row y."""
    x = max(0, (term.width - len(text)) // 2)
    styled = style(text) if style else text
    print(term.move_xy(x, y) + styled, end="", flush=True)


def write_line(y: int, text: str, style: Callable | None = None) -> None:
    """Write a full-width line at row y, padded/truncated to terminal width."""
    padded = text.ljust(term.width)[:term.width]
    styled = style(padded) if style else padded
    print(term.move_xy(0, y) + styled, end="", flush=True)


def clear_line(y: int) -> None:
    """Clear a single row."""
    print(term.move_xy(0, y) + term.clear_eol, end="", flush=True)


def draw_box(x: int, y: int, w: int, h: int, title: str = "") -> None:
    """Draw a single-line Unicode box at (x, y) with width w and height h."""
    top    = "┌" + "─" * (w - 2) + "┐"
    bottom = "└" + "─" * (w - 2) + "┘"
    side   = "│" + " " * (w - 2) + "│"

    if title:
        max_title = w - 4
        t = title[:max_title]
        top = "┌─ " + t + " " + "─" * (w - 4 - len(t)) + "┐"

    write_at(x, y, top)
    for row in range(1, h - 1):
        write_at(x, y + row, side)
    write_at(x, y + h - 1, bottom)


def draw_hline(y: int, char: str = "─") -> None:
    """Draw a horizontal line across the full terminal width."""
    print(term.move_xy(0, y) + char * term.width, end="", flush=True)


# ─────────────────────────────────────────────────────────────────────────────
# 2. Text styling helpers
# ─────────────────────────────────────────────────────────────────────────────

def header(text: str) -> str:
    return term.bold_white_on_blue(text.center(term.width))


def error_text(text: str) -> str:
    return term.bold_red(text)


def success_text(text: str) -> str:
    return term.bold_green(text)


def warning_text(text: str) -> str:
    return term.bold_yellow(text)


def info_text(text: str) -> str:
    return term.cyan(text)


def dim_text(text: str) -> str:
    return term.dim(text)


def highlight(text: str) -> str:
    return term.reverse(text)


# ─────────────────────────────────────────────────────────────────────────────
# 3. Progress bar
# ─────────────────────────────────────────────────────────────────────────────

def render_progress_bar(
    value: float,
    total: float,
    width: int = 40,
    filled_char: str = "█",
    empty_char: str = "░",
    color_fn: Callable | None = None,
) -> str:
    """
    Render an ASCII progress bar string.
    Returns colored or plain bar string.
    """
    pct  = min(1.0, max(0.0, value / total)) if total else 0.0
    fill = int(pct * width)
    bar  = filled_char * fill + empty_char * (width - fill)
    text = f"[{bar}] {pct*100:5.1f}% ({int(value)}/{int(total)})"
    return color_fn(text) if color_fn else text


def draw_progress(
    y: int,
    x: int = 2,
    value: float = 0,
    total: float = 100,
    label: str = "",
    width: int = 40,
) -> None:
    """Draw a progress bar at (x, y) on screen."""
    pct = min(1.0, max(0.0, value / total)) if total else 0.0
    color_fn = term.green if pct >= 0.9 else (term.yellow if pct >= 0.5 else term.red)
    bar = render_progress_bar(value, total, width, color_fn=color_fn)
    prefix = f"{label:<20} " if label else ""
    write_at(x, y, prefix + bar)


# ─────────────────────────────────────────────────────────────────────────────
# 4. Keyboard input
# ─────────────────────────────────────────────────────────────────────────────

def read_key(timeout: float = 0.1):
    """
    Read a single keypress (non-blocking with timeout).
    Must be called inside `with term.cbreak():`.
    Returns a blessed Keystroke or None.
    """
    return term.inkey(timeout=timeout) or None


@contextmanager
def interactive_mode() -> Iterator[None]:
    """
    Context manager for interactive input: fullscreen + cbreak + hidden cursor.
    Restores terminal on exit.
    """
    with term.fullscreen(), term.cbreak(), term.hidden_cursor():
        try:
            yield
        except KeyboardInterrupt:
            pass


def wait_for_key(
    valid_keys: set[str] | None = None,
    timeout: float | None = None,
) -> str | None:
    """
    Block until a key is pressed (optionally filtered to valid_keys set).
    Returns key name string (e.g. "KEY_UP") or character, or None on timeout.
    """
    end = time.time() + timeout if timeout else None
    with term.cbreak():
        while True:
            remaining = max(0.01, end - time.time()) if end else 0.1
            key = term.inkey(timeout=remaining)
            if key:
                name = key.name or str(key)
                if valid_keys is None or name in valid_keys or str(key) in valid_keys:
                    return name
            if end and time.time() >= end:
                return None


# ─────────────────────────────────────────────────────────────────────────────
# 5. Simple scrollable list widget
# ─────────────────────────────────────────────────────────────────────────────

class ScrollableList:
    """
    Arrow-key navigable list rendered in a fixed region.

    Usage:
        items = ["alpha", "beta", "gamma", "delta"]
        lst = ScrollableList(items, x=5, y=3, visible_rows=4)
        with interactive_mode():
            lst.draw()
            while True:
                key = read_key()
                if key and key.name == "KEY_UP":    lst.up(); lst.draw()
                if key and key.name == "KEY_DOWN":  lst.down(); lst.draw()
                if key and key.name == "KEY_ENTER": break
        chosen = lst.selected_item
    """

    def __init__(
        self,
        items: list[str],
        x: int = 2,
        y: int = 2,
        visible_rows: int = 10,
    ):
        self.items = items
        self.x = x
        self.y = y
        self.visible_rows = visible_rows
        self._cursor = 0
        self._offset = 0

    @property
    def selected_index(self) -> int:
        return self._offset + self._cursor

    @property
    def selected_item(self) -> str | None:
        idx = self.selected_index
        return self.items[idx] if 0 <= idx < len(self.items) else None

    def up(self) -> None:
        if self._cursor > 0:
            self._cursor -= 1
        elif self._offset > 0:
            self._offset -= 1

    def down(self) -> None:
        total = len(self.items)
        if self._cursor < min(self.visible_rows, total) - 1:
            self._cursor += 1
        elif self._offset + self.visible_rows < total:
            self._offset += 1

    def draw(self) -> None:
        visible = self.items[self._offset: self._offset + self.visible_rows]
        for i, item in enumerate(visible):
            text = f"  {item:<{term.width - self.x - 4}}  "
            if i == self._cursor:
                styled = term.reverse(text)
            else:
                styled = text
            write_at(self.x, self.y + i, styled)
        # clear remaining rows if list is shorter than visible_rows
        for i in range(len(visible), self.visible_rows):
            write_at(self.x, self.y + i, " " * (term.width - self.x))


# ─────────────────────────────────────────────────────────────────────────────
# 6. Simple status dashboard
# ─────────────────────────────────────────────────────────────────────────────

class StatusDashboard:
    """
    Live dashboard with a header, metrics rows, and footer status line.
    Runs in a background thread, refreshing at `interval` seconds.

    Usage:
        dash = StatusDashboard(fetch_fn=collect_metrics, interval=1.0)
        with interactive_mode():
            dash.start()
            while True:
                key = read_key(timeout=0.5)
                if key and str(key) == "q":
                    break
            dash.stop()
    """

    def __init__(
        self,
        fetch_fn: Callable[[], dict[str, Any]],
        interval: float = 1.0,
        title: str = "Dashboard",
    ):
        self._fetch = fetch_fn
        self._interval = interval
        self._title = title
        self._running = False
        self._thread: threading.Thread | None = None
        self._data: dict[str, Any] = {}

    def start(self) -> None:
        self._running = True
        self._thread = threading.Thread(target=self._loop, daemon=True)
        self._thread.start()

    def stop(self) -> None:
        self._running = False
        if self._thread:
            self._thread.join(timeout=2)

    def _loop(self) -> None:
        while self._running:
            self._data = self._fetch()
            self._render()
            time.sleep(self._interval)

    def _render(self) -> None:
        # Header
        write_line(0, self._title, style=lambda t: term.bold_white_on_blue(t))
        draw_hline(1)

        # Metrics
        row = 2
        for key, value in self._data.items():
            label = f"  {key:<20}"
            val   = str(value)
            color = term.green if "ok" in val.lower() else term.yellow
            write_at(0, row, label + color(val))
            row += 1

        # Footer
        draw_hline(term.height - 2)
        write_line(term.height - 1, " [q] Quit  [r] Refresh", style=dim_text)


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    if not sys.stdout.isatty():
        print("Demo requires a TTY — run in a real terminal.")
        sys.exit(0)

    print("=== Text styling ===")
    print(term.bold("Bold text"))
    print(term.green("Green text"))
    print(term.bold_red("Bold red"))
    print(term.on_blue(term.white("White on blue")))
    print(term.underline("Underlined"))
    print(term.reverse("Reversed"))
    print(dim_text("Dim/secondary text"))

    print("\n=== Progress bars ===")
    for i in range(0, 110, 25):
        bar = render_progress_bar(i, 100, width=30)
        print(f"  {i:3d}%  {bar}")

    print("\n=== Box drawing ===")
    with term.location(0, 0):
        pass  # can't draw boxes to stdout in non-fullscreen, show in fullscreen demo

    print("\n=== Fullscreen demo (3 seconds) ===")
    print("  Starting in 1s... (press Ctrl+C to skip)")
    time.sleep(1)

    with interactive_mode():
        clear()
        write_line(0, " blessed TUI Demo", style=lambda t: term.bold_white_on_blue(t))
        draw_hline(1)

        # Progress bars
        write_at(2, 3, term.bold("Progress bars:"))
        metrics = [("Download", 75), ("Upload", 40), ("Install", 95)]
        for idx, (label, pct) in enumerate(metrics):
            draw_progress(y=4+idx, x=2, value=pct, total=100, label=label, width=35)

        # Box
        draw_hline(8)
        draw_box(2, 10, 40, 5, title="System Status")
        write_at(4, 11, term.green("●") + " API        OK")
        write_at(4, 12, term.green("●") + " Database   OK")
        write_at(4, 13, term.yellow("●") + " Cache      DEGRADED")

        # Footer
        draw_hline(term.height - 2)
        write_line(term.height - 1, " Press any key to exit", style=dim_text)

        read_key(timeout=3.0)

For the curses alternative — Python’s stdlib curses is powerful but requires understanding window management, padding, and manual refresh coordination; blessed wraps curses (and Windows ANSI) into a simple API where term.bold_green("text") just works and cursor positioning is a string operation (term.move(y, x) + content), making it far faster to build simple TUIs. For the urwid alternative — urwid is a full widget toolkit with layouts, event loops, and composable widgets (Text, Edit, Button, Columns, Pile); blessed is lower-level — you manage the screen yourself — but this gives you precise pixel-level control for custom dashboards, progress meters, and games where urwid’s layout model would be overkill. The Claude Skills 360 bundle includes blessed skill sets covering Terminal() object, term.move/move_xy/location cursor control, bold/color/on_color text styling, term.clear/home/clear_eol screen management, draw_box() Unicode box drawing, term.cbreak()/inkey() keyboard input, interactive_mode() fullscreen context, render_progress_bar() and draw_progress(), ScrollableList arrow-key widget, StatusDashboard threaded refresh, wait_for_key() with key filtering, and draw_hline() separator. Start with the free tier to try terminal UI code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free