Metadata-Version: 2.4
Name: libtodraw
Version: 1.0.0
Summary: Terminal rendering library with full character-level control. Rich on steroids.
Project-URL: Homepage, https://github.com/libtodraw/libtodraw
Project-URL: Documentation, https://github.com/libtodraw/libtodraw#readme
Project-URL: Repository, https://github.com/libtodraw/libtodraw
Project-URL: Issues, https://github.com/libtodraw/libtodraw/issues
Author: libtodraw contributors
License-Expression: MIT
License-File: LICENSE
Keywords: ansi,color,console,markup,rich,terminal,tui
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Terminals
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# libtodraw

**Terminal rendering library with full character-level control.**

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

```
pip install libtodraw
```

```python
from libtodraw import Console

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

---

## Table of Contents

- [Quick Start](#quick-start)
- [Console](#console)
- [Markup](#markup)
- [Canvas — Character Control](#canvas--character-control)
- [Colors](#colors)
- [Styles](#styles)
- [Text](#text)
- [Table](#table)
- [Panel](#panel)
- [Tree](#tree)
- [Syntax Highlighting](#syntax-highlighting)
- [Markdown](#markdown)
- [Layout & Columns](#layout--columns)
- [Progress Bars](#progress-bars)
- [Spinners & Status](#spinners--status)
- [Live Display](#live-display)
- [Rules / Separators](#rules--separators)
- [Export](#export)
- [Inspector & Debug](#inspector--debug)
- [Terminal Detection](#terminal-detection)
- [API Reference](#api-reference)

---

## Quick Start

```python
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.

```python
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

```python
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

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

### Styles

```python
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

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

### Background

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

### Gradient

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

### Links

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

### Escaping

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

---

## Canvas — Character Control

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

```python
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

```python
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

```python
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

```python
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:
```python
c.draw_rect(5, 5, 20, 10, chars={
    "tl": "╔", "tr": "╗", "bl": "╚", "br": "╝",
    "hor": "═", "ver": "║", "fill": " "
})
```

#### Text Placement

```python
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

```python
c.gradient_fill(x, y, w, h, color_start, color_end, char="█", horizontal=True)
c.gradient_border(x, y, w, h, color_start, color_end, box_type="simple")
```

#### Compositing

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

#### Inspection

```python
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

```python
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

```python
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

```python
# 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

```python
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.

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
from libtodraw import Layout

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

---

## Progress Bars

```python
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

```python
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

```python
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)

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

---

## Live Display

Real-time updating display.

```python
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

```python
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)

```python
from libtodraw import export_text

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

### HTML

```python
from libtodraw import export_html

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

### Console HTML Export

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

---

## Inspector & Debug

### Object Inspector

```python
from libtodraw import pretty_inspect

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

### Pretty Traceback

```python
from libtodraw import install_traceback

install_traceback()  # Beautiful error output with syntax highlighting
```

---

## Terminal Detection

```python
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)` | Gradient fill |
| `gradient_border(x, y, w, h, c1, c2, box_type)` | Gradient border |
| `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 |

### Spinner

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

---

## Installation

```bash
pip install libtodraw
```

## License

MIT
