Skip to main content

Terminal rendering library with full character-level control. Rich on steroids.

Project description

libtodraw

Terminal rendering library with full character-level control.

Pure Python. Zero dependencies. Install with pip install libtodraw.

Author: silencet.me/hatedfame

pip install libtodraw
from libtodraw import Console

c = Console()
c.print("[bold green]Hello![/bold green] [red]World[/red]")

Table of Contents


Quick Start

from libtodraw import Console, Table, Panel, Tree, Syntax, Markdown

c = Console()

# Styled text
c.print("[bold red]Error:[/bold red] file not found")

# Table
t = Table()
t.add_column("Name", style="cyan")
t.add_column("Score", style="green")
t.add_row("Alice", "95")
c.print(t)

# Panel
c.print(Panel("[yellow]Important message[/yellow]", title="Alert"))

# Syntax highlighting
c.print(Syntax("print('hello')", "python", theme="monokai"))

# Markdown
c.print(Markdown("# Title\n\n- item 1\n- item 2"))

Console

Console is the core object. It handles printing, markup, styles, and output.

from libtodraw import Console

c = Console()

# Basic print
c.print("Hello")

# With markup
c.print("[bold green]Success![/bold green]")

# With style
c.print("Error", style="bold red")

# Separator
c.rule("Section")

# Logging (with caller info)
c.log("something happened")

# Input
name = c.input("[cyan]Name:[/cyan] ")

# Clear screen
c.clear()

Console Options

c = Console(
    file=sys.stdout,          # Output file (default: stderr)
    width=80,                 # Fixed width
    height=24,                # Fixed height
    color_system="truecolor", # "truecolor", "256", "16", "8", "no-color"
    force_terminal=True,      # Force terminal mode
    highlight=True,           # Auto-highlight code
    markup=True,              # Enable [tag] markup
)

Markup

Rich-style markup: [style]text[/style].

Colors

c.print("[red]Red text[/red]")
c.print("[#ff6600]Orange hex color[/]")
c.print("[green]Green[/green] [blue]Blue[/blue]")

Styles

c.print("[bold]Bold[/bold]")
c.print("[italic]Italic[/italic]")
c.print("[underline]Underline[/underline]")
c.print("[strike]Strikethrough[/strike]")
c.print("[dim]Dimmed[/dim]")
c.print("[blink]Blinking (terminal dependent)[/blink]")
c.print("[reverse]Inverted[/reverse]")

Combining

c.print("[bold red on blue]Bold red on blue background[/]")
c.print("[bold italic green]Bold italic green[/]")

Background

c.print("[bg=#ff0000]White on red background[/]")
c.print("[bg=blue]Text on blue[/]")

Gradient

c.print("[gradient=red,blue]Rainbow gradient text[/gradient]")
c.print("[gradient=#00ff00,#ff00ff]Custom hex gradient[/gradient]")

Gradient Engine (v1.0.2)

Full control over gradients: angle, multi-color, percentage, direction presets.

from libtodraw import Gradient, Canvas, Color

# Basic 2-color gradient
g = Gradient("red", "blue")

# Multi-color gradient
g = Gradient("red", "yellow", "green", "blue")

# With direction preset
g = Gradient("red", "blue", angle="top-left")
g = Gradient("red", "blue", angle="bottom-right")
g = Gradient("red", "blue", angle="center")  # radial

# Custom angle (degrees)
g = Gradient("red", "blue", angle=45)
g = Gradient("red", "blue", angle=90)
g = Gradient("red", "blue", angle=135)

# Percentage control
g = Gradient("red", "blue", color1_pct=0.2, color2_pct=0.8)

# Available directions:
# left, right, top, bottom
# top-left, top-right, bottom-left, bottom-right
# center (radial), diagonal, reverse-diagonal

# Apply to canvas
canvas = Canvas(80, 24)
canvas.apply_gradient(0, 0, 80, 24, g)
canvas.gradient_fill(0, 0, 80, 5, Color(255,0,0), Color(0,0,255), angle="diagonal")
canvas.gradient_border(0, 0, 80, 10, Color(255,200,0), Color(0,100,255), angle="center")

Links

c.print("[link=https://example.com]Click here[/link]")

Escaping

c.print("\[not a tag\]")  # Literal brackets

Canvas — Character Control

Full control over every single cell. This is what makes libtodraw different.

from libtodraw import Canvas, Style, Color

c = Canvas(80, 24)  # width, height

# Set single character
c.set(5, 3, "X", Style(color=Color(255, 0, 0)))

# Place text
c.put(10, 5, "Hello World", Style(bold=True))

# Fill region
c.fill(0, 0, 80, 24, " ", Style(bgcolor=Color(30, 30, 50)))

# Draw shapes
c.draw_box(5, 5, 30, 10, box_type="rounded")
c.draw_circle(40, 12, 5, "●", Style(color=Color(255, 100, 100)))
c.draw_line(10, 10, 70, 20, "·", Style(color=Color(100, 255, 100)))

# Gradient fill
c.gradient_fill(0, 0, 80, 1, Color(255, 0, 0), Color(0, 0, 255))

# Gradient border
c.gradient_border(10, 5, 40, 15, Color(255, 100, 0), Color(0, 100, 255))

# Render to terminal
c.print()

Canvas API

Single Cell Control

c.set(x, y, char, style)        # Set cell
c.get(x, y)                     # Get cell → Cell(char, style)
c.set_char(x, y, char)          # Set char only
c.set_style(x, y, style)        # Set style only
c.clear_cell(x, y)              # Reset to blank
c[x, y]                         # Get cell (bracket syntax)
c[x, y] = ("X", style)          # Set cell (bracket syntax)

Regions

c.fill(x, y, w, h, char, style)    # Fill rectangle
c.clear(x, y, w, h)                # Clear region
c.clear_all()                       # Clear entire canvas

Drawing Primitives

c.draw_box(x, y, w, h, style=..., box_type="simple")
# box_type: "simple", "rounded", "heavy", "double", "dashed", "dotted"

c.draw_rect(x, y, w, h, border=style, fill=style, chars={...})
c.draw_hline(x, y, length, char="─", style=...)
c.draw_vline(x, y, length, char="│", style=...)
c.draw_circle(cx, cy, radius, char="●", style=..., filled=True)
c.draw_line(x0, y0, x1, y1, char="·", style=...)

Custom border characters:

c.draw_rect(5, 5, 20, 10, chars={
    "tl": "╔", "tr": "╗", "bl": "╚", "br": "╝",
    "hor": "═", "ver": "║", "fill": " "
})

Text Placement

c.put(x, y, "text", style=...)           # Place text
c.put_centered(y, "text", style=...)     # Center horizontally
c.put_right(y, "text", style=...)        # Right-align
c.put_at(x, y, "text", style=..., align="center", max_width=40)
c.write_line(y, x, "text", justify="left", width=80)
# justify: "left", "center", "right", "full"

Gradients

c.gradient_fill(x, y, w, h, c1, c2, char="█", horizontal=True, angle=None)
c.gradient_border(x, y, w, h, c1, c2, box_type="simple", angle=None)
c.apply_gradient(x, y, w, h, gradient_object, char="█")

Compositing

c.overlay(other_canvas, x=0, y=0, overwrite=True)
c.copy()                        # Deep copy
c.resize(width, height)         # Resize (preserves content)

Inspection

c.get_row(y)                    # All cells in row → [Cell]
c.get_col(x)                    # All cells in column → [Cell]
c.get_region(x, y, w, h)       # Rectangular region → [[Cell]]
c.find(char)                    # Find all positions → [(x,y)]
c.to_text()                     # Plain text (no ANSI)
c.width                         # Canvas width
c.height                        # Canvas height

Sub-Regions

region = c.region(x, y, w, h)
region.set(0, 0, "X", style)   # Coordinates relative to region
region.put(1, 1, "Hello")
region.fill(char, style)
region.clear()
region.draw_box(style, box_type="heavy")

Colors

Named Colors

from libtodraw import Color

c = Color(255, 0, 0)           # RGB
c = Color.from_hex("#ff6600")  # Hex
c = Color.from_name("red")     # Named

Available names: black, red, green, blue, yellow, cyan, magenta, white, gray, grey, orange, pink, purple, brown, coral, crimson, gold, indigo, lime, maroon, navy, olive, salmon, teal, tomato, turquoise, violet, beige, tan, dark_red, dark_green, dark_blue, dark_yellow, dark_cyan, dark_magenta, dark_gray, light_red, light_green, light_blue, light_yellow, light_cyan, light_magenta, light_gray, steel_blue, sienna, plum, ivory, khaki, lavender, wheat, firebrick.

Color Operations

# Interpolation
blended = color1.lerp(color2, 0.5)  # 50% blend

# ANSI output
color.to_ansi_fg()  # "\033[38;2;r;g;b;m"
color.to_ansi_bg()  # "\033[48;2;r;g;b;m"

# Parse any format
Color.parse("red")
Color.parse("#ff6600")
Color.parse(color_obj)  # passthrough

Styles

from libtodraw import Style

s = Style(
    color="red",
    bgcolor="blue",
    bold=True,
    dim=False,
    italic=True,
    underline=False,
    blink=False,
    reverse=False,
    strikethrough=False,
    link="https://example.com",
)

# Parse from string
s = Style.parse("bold italic red on blue")

# Merge styles
combined = style1 + style2  # style2 overrides style1

# Generate ANSI
s.to_ansi()     # Full escape sequence
s.render("text")  # "\033[...mtext\033[0m"

Text

Text is a styled text object with spans.

from libtodraw import Text, Style

t = Text()
t.append("Hello ", Style(bold=True))
t.append("World", Style(color="red"))

# Or chain
t = Text().append("A", Style(bold=True)).append("B")

# Operations
len(t)              # Length
t.plain             # Plain text
t + other           # Concatenate
t[2:5]             # Slice
t.truncate(10)      # Truncate with ellipsis
t.center(40)        # Center in width
t.left(40)          # Left-align
t.right(40)         # Right-align
t.wrap(40)          # Word wrap → [Text]
t.highlight(r"\berror\b", Style(color="red"))  # Regex highlight

Table

from libtodraw import Table

t = Table(
    show_header=True,
    show_lines=True,
    expand=False,
    border_style="cyan",
    header_style="bold white on blue",
    padding=(0, 1),
)

t.add_column("Name", style="cyan", no_wrap=True)
t.add_column("Score", style="green")
t.add_column("Status")

t.add_row("Alice", "95", "[green]Active[/green]")
t.add_row("Bob", "87", "[red]Inactive[/red]")

c.print(t)

Panel

from libtodraw import Panel

# Simple panel
c.print(Panel("Hello World"))

# With title and style
c.print(Panel(
    "[bold yellow]Important![/bold yellow]",
    title="Warning",
    subtitle="v1.0",
    border_style="red",
    box="rounded",  # simple, rounded, heavy, double
    padding=(1, 2),
    expand=True,
    width=60,
))

# Panel with content
c.print(Panel(
    "Line 1\nLine 2\nLine 3",
    title="Log",
    border_style="green",
))

Tree

from libtodraw import Tree, TreeNode

root = TreeNode("[bold]project/[/bold]")

src = TreeNode("[cyan]src/[/cyan]")
src.add(TreeNode("main.py"))
src.add(TreeNode("utils.py"))
root.add(src)

root.add(TreeNode("[yellow]Makefile[/yellow]"))
root.add(TreeNode("[dim]README.md[/dim]"))

c.print(Tree(root))

Syntax Highlighting

from libtodraw import Syntax

code = '''
def hello():
    print("Hello, World!")
    return 42
'''

c.print(Syntax(
    code,
    lexer="python",          # python, javascript, bash, json, rust, go, c, cpp, java, html, css, sql
    theme="monokai",         # monokai, dracula, solarized, github-dark, one-dark
    line_numbers=True,
    highlight_lines=[2],     # Highlight specific lines
    tab_size=4,
    start_line=1,
))

Markdown

from libtodraw import Markdown

md = """
# Title

## Subtitle

**Bold** and *italic* text.

- Item 1
- Item 2

```python
x = 1

Blockquote """

c.print(Markdown(md))


---

## Layout & Columns

### Columns

```python
from libtodraw import Columns, Panel

left = Panel("[green]Left[/green]", title="A")
center = Panel("[blue]Center[/blue]", title="B")
right = Panel("[red]Right[/red]", title="C")

c.print(Columns([left, center, right], padding=(0, 1)))

Layout

from libtodraw import Layout

layout = Layout()
layout.split_column(
    Layout(name="header", size=3),
    Layout(name="body"),
    Layout(name="footer", size=3),
)

Progress Bars

from libtodraw import Progress

with Progress() as progress:
    task = progress.add_task("Downloading...", total=100)

    for i in range(100):
        time.sleep(0.01)
        progress.update(task, advance=1)

Custom Columns

from libtodraw import Progress, SpinnerColumn, TextColumn, BarColumn, TimeRemainingColumn

with Progress(
    SpinnerColumn(),
    TextColumn("[bold blue]{task.description}"),
    BarColumn(),
    TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
    TimeRemainingColumn(),
) as progress:
    task = progress.add_task("Processing...", total=100)
    for i in range(100):
        time.sleep(0.02)
        progress.update(task, advance=1)

Spinners & Status

Spinner

from libtodraw import Spinner

s = Spinner("dots", "Loading...", style="green")
s.start()
time.sleep(2)
s.stop("Done!")

Available spinners: ascii, dots, dots2, line, bar, arc, bounce, arrow, aesthetic, toggle, earth, weather, bouncingBar, christmas.

Status (context manager)

with c.status("Working...", spinner="dots", spinner_style="green"):
    time.sleep(2)

Live Display

Real-time updating display.

from libtodraw import Live, Table

with Live(Table()) as live:
    for i in range(100):
        time.sleep(0.1)
        table = Table()
        table.add_row(f"Item {i}")
        live.update(table)

Rules / Separators

c.rule()                              # ─────────────────────
c.rule("Section")                     # ──── Section ────────
c.rule("[bold]Title[/bold]")          # Styled title
c.rule(characters="═")               # ═══════════════════════
c.rule(characters="─", style="blue")  # Colored rule

Export

Text (plain, no ANSI)

from libtodraw import export_text

text = export_text([table, panel, tree])

HTML

from libtodraw import export_html

html = export_html([table, panel])
# Returns HTML with inline CSS styles

Console HTML Export

c.begin_capture()
c.print("[red]Hello[/red]")
c.end_capture()
html = c.get_export()

Inspector & Debug

Object Inspector

from libtodraw import pretty_inspect

pretty_inspect(my_object)
# Shows: type, docstring, methods, properties, attributes

Pretty Traceback

from libtodraw import install_traceback

install_traceback()  # Beautiful error output with syntax highlighting

Terminal Detection

from libtodraw import get_size, detect_truecolor, detect_unicode

width, height = get_size()      # Terminal dimensions
truecolor = detect_truecolor()  # TrueColor support
unicode = detect_unicode()      # Unicode support

API Reference

Console

Method Description
print(*objects, end, style, ...) Print with markup
log(*objects) Log with caller info
input(prompt) Styled input
out(*objects) Low-level output
rule(title, style, characters) Print separator
status(text, spinner) Status spinner context manager
capture() Capture output context manager
clear() Clear screen
size() Terminal size (w, h)
options(width, max_width, ...) Get console options
get_style(name) Get named style from theme
push_style(style) / pop_style() Style stack
render(renderable, options) Render to lines
measure(renderable, max_width) Measure width
export_html() Export as HTML
begin_capture() / end_capture() Capture for export

Canvas

Method Description
set(x, y, char, style) Set cell
get(x, y) Get cell
set_char(x, y, char) Set char only
set_style(x, y, style) Set style only
clear_cell(x, y) Reset cell
fill(x, y, w, h, char, style) Fill region
clear(x, y, w, h) Clear region
clear_all() Clear canvas
draw_box(x, y, w, h, style, box_type) Draw box
draw_rect(x, y, w, h, border, fill, chars) Draw rectangle
draw_hline(x, y, len, char, style) Horizontal line
draw_vline(x, y, len, char, style) Vertical line
draw_circle(cx, cy, r, char, style, filled) Circle
draw_line(x0, y0, x1, y1, char, style) Line
put(x, y, text, style) Place text
put_centered(y, text, style) Center text
put_right(y, text, style) Right-align text
put_at(x, y, text, style, align, max_width) Text with alignment
write_line(y, x, text, justify, width) Full-line output
gradient_fill(x, y, w, h, c1, c2, char, horizontal, angle) Gradient fill with optional angle
gradient_border(x, y, w, h, c1, c2, box_type, angle) Gradient border with optional angle
apply_gradient(x, y, w, h, gradient, char) Apply Gradient object
overlay(other, x, y, overwrite) Composite canvas
copy() Deep copy
resize(w, h) Resize
region(x, y, w, h) Sub-region
get_row(y) / get_col(x) Row/column access
get_region(x, y, w, h) Region access
find(char) Find character positions
to_text() Plain text export
render() ANSI string
print(file) Print to terminal

Color

Method Description
Color(r, g, b) RGB constructor
Color.from_hex("#rrggbb") From hex string
Color.from_name("red") From named color
Color.parse(value) Parse any format
color.lerp(other, t) Interpolate
color.to_ansi_fg() Foreground ANSI
color.to_ansi_bg() Background ANSI

Style

Method Description
Style(color, bgcolor, bold, ...) Constructor
Style.parse("bold red on blue") Parse string
style1 + style2 Merge styles
style.to_ansi() Generate ANSI
style.render(text) Wrap text with ANSI

Text

Method Description
Text(text, style) Constructor
text.append(text, style) Append span
text.stylize(style, start, end) Apply style to range
text.plain Plain text property
text.truncate(max_width) Truncate
text.center(width) / left / right Align
text.wrap(width) Word wrap
text.highlight(pattern, style) Regex highlight

Table

Method Description
Table(**kwargs) Constructor
table.add_column(header, **kwargs) Add column
table.add_row(*cells) Add row

Panel

Method Description
Panel(renderable, title, ...) Constructor

Tree

Method Description
Tree(root) Constructor
TreeNode(label) Create node
node.add(child) Add child

Syntax

Method Description
Syntax(code, lexer, theme, ...) Constructor

Progress

Method Description
Progress(**columns) Constructor
progress.add_task(desc, total) Add task
progress.update(task, advance) Update task

Gradient

Method Description
Gradient(*colors, angle, color1_pct, color2_pct) Constructor
gradient.color_at(t) Color at position 0..1
gradient.get_color(x, y, w, h) Color at pixel
gradient.generate(w, h) Generate color grid
gradient.to_stops(n) Get color stops
gradient.add_color(color, position) Add color
gradient.remove_color(index) Remove color
gradient.angle Angle property (get/set)
gradient.colors List of colors
LinearGradient(...) Linear gradient
RadialGradient(...) Radial gradient
ConicGradient(..., start_angle) Conic gradient
gradient(*colors, angle=0) Factory function

Spinner

Method Description
Spinner(name, text, speed, style) Constructor
spinner.start() / stop() Start/stop
spinner.update(text, color) Update

Installation

pip install libtodraw

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

libtodraw-1.0.2.tar.gz (133.5 kB view details)

Uploaded Source

Built Distribution

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

libtodraw-1.0.2-py3-none-any.whl (55.4 kB view details)

Uploaded Python 3

File details

Details for the file libtodraw-1.0.2.tar.gz.

File metadata

  • Download URL: libtodraw-1.0.2.tar.gz
  • Upload date:
  • Size: 133.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for libtodraw-1.0.2.tar.gz
Algorithm Hash digest
SHA256 b6007bf14303712638c2b6580e8ea6f02ffaf0fe0d422cc6c076074e1faab1dc
MD5 3386727060db77a71da79ba999898349
BLAKE2b-256 385b2d09639f2581e881641196398fb8df0d7326faa524ea31a0d7d59ecffb7a

See more details on using hashes here.

File details

Details for the file libtodraw-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: libtodraw-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 55.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for libtodraw-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1bfb76375f5d5ecfa1df2bf337bd8d0e272e1ca9eef9025c38f94c18930becd1
MD5 3125d6f9005cbd2f9d9a848ef9d3009a
BLAKE2b-256 ba6555c2d511dd37a297b887a2f7169f0dc31113b9c22d6730a9a68595dfac59

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