Metadata-Version: 2.4
Name: hascii
Version: 0.1.2
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Scientific/Engineering :: Visualization
License-File: LICENSE
Summary: Convert Graphviz DOT files to ASCII box-drawing art
Keywords: graphviz,ascii,dot,diagram,unicode
License: Apache-2.0
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Repository, https://github.com/atemate/hascii

<p align="center">
  <img src="img/logo.png" alt="hascii" width="230">
</p>

<h1 align="center">hascii</h1>

<p align="center">
  <strong>Convert Graphviz DOT graphs to Unicode box-drawing art</strong>
</p>

<p align="center">
  <a href="https://github.com/atemate/hascii/actions/workflows/ci.yml"><img src="https://github.com/atemate/hascii/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
  <a href="https://crates.io/crates/hascii"><img src="https://img.shields.io/crates/v/hascii.svg" alt="crates.io"></a>
  <a href="https://pypi.org/project/hascii/"><img src="https://img.shields.io/pypi/v/hascii.svg" alt="PyPI"></a>
  <a href="https://github.com/atemate/hascii/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue.svg" alt="License"></a>
</p>

<p align="center">
  Embed readable flow diagrams in docstrings, source code, and plain-text docs.<br>
  No image viewer needed — just Unicode text.
</p>

---

## What it does

Takes a Graphviz DOT graph and renders it as ASCII art with box-drawing characters:

```
  ┌───────┐
  │ Start │◀─┐
  └───────┘  │
      │      │retry
      │      │
      ▼      │
  ┌───────┐  │
  │ Check │──┘
  └───────┘
      │ok
      │
      ▼
  ┌──────┐
  │ Done │
  └──────┘
```

### Back-edges, conditions, and edge labels

```
    ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
 ┌─▶┆ router_while_simple_flow_while_i_lt_max_iterations ┆
 │  └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
 │                             │p['i'] < p['max_iterations']
 │                             └─────────────────────────────────┐
 │                             │                                 │else
 │                             ▼                                 ▼
 │          ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐     ┌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┐
 │          ┆ router_while_simple_flow_seq_set_i ┆     ╎ handler_finalize ╎
 │          ┆            p['i'] += 1             ┆     └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
 │          └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
 │           ┌─────────────────┘
 │           ▼
 │  ┏┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┓
 └──┇ handler_process ┇
    ┗┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┅┛
```

<details>
<summary>Full graph and source files</summary>

[flow.txt](img/sample-asya-flows/03_while_loop/flow.txt) |
[flow.dot](img/sample-asya-flows/03_while_loop/flow.dot) |
[flow.png](img/sample-asya-flows/03_while_loop/flow.png)
</details>

### Subgraph grouping

Cluster subgraphs render as dashed group boxes:

```
       ┌────────────┐
       │ preprocess │
       └────────────┘
              │
   ┌ validation_stage ╌╌╌┐
   ╎          ▼          ╎
   ╎    ┌──────────┐     ╎
   ╎    │ validate │     ╎
   ╎    └──────────┘     ╎
   ╎          │          ╎
   ╎          ▼          ╎
   ╎     ┌────────┐      ╎
   ╎     │ enrich │      ╎
   ╎     └────────┘      ╎
   └╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┘
              │
              ▼
          ┌───────┐
          │ store │
          └───────┘
```

### Color modes

Node fill colors are conveyed through border styles (B/W) or ANSI colors (terminal):

| Mode | Borders | When |
|------|---------|------|
| **ANSI colors** | `┌──┐` + colored | Terminal (default), Python `color=True` |
| **Hue borders** | `┌┄┄┐ ┏┅┅┓ ┌╌╌┐` | CLI `--no-color`, piped output |
| **Plain** | `┌──┐` uniform | Python `color=False` |

<details>
<summary>Hue border reference</summary>

| Border | Color family | Example fills |
|--------|-------------|---------------|
| `┌──┐` solid | Green | palegreen |
| `┌┄┄┐` light triple-dash | Orange/Yellow | wheat, gold |
| `┌╌╌┐` light double-dash | Red | lightsalmon |
| `┏┅┅┓` heavy triple-dash | Blue | lightblue |
| `┏╍╍┓` heavy double-dash | Purple/Pink | plum |
| `┏━━┓` heavy solid | Dark | black |
</details>

## Install

**Python** (requires [Graphviz](https://graphviz.org/download/) system package):

```sh
pip install hascii
```

**Rust CLI**:

```sh
cargo install hascii
```

**Graphviz** is a required dependency (hascii uses `dot` for layout computation):

```sh
sudo apt-get install graphviz   # Debian/Ubuntu
brew install graphviz            # macOS
```

## Python API

```python
import hascii

dot = '''
digraph {
    A [label="Start"]; B [label="End"];
    A -> B;
}
'''

# ANSI colors (default) — for terminal display
print(hascii.render(dot))

# Plain solid borders — for docstrings and source code
print(hascii.render(dot, color=False))

# All options
hascii.render(
    dot_source,
    max_width=72,       # constrain output width
    trim="smart",       # label trimming: end, start, middle, smart
    max_label=30,       # max label length
    color=False,        # True: ANSI colors, False: plain borders
)

# From file
hascii.render_file("flow.dot", color=False)
```

## CLI

```
hascii [OPTIONS] <FILE>

Arguments:
  <FILE>  Path to a .dot file, or "-" to read from stdin

Options:
      --width <N>          Max output width in characters
      --trim <STRAT>       Label trimming strategy [default: smart]
      --max-label <N>      Max label width in characters
      --no-color           Use hue-based border styles instead of ANSI colors
  -v, --verbose...         Increase verbosity
  -h, --help               Print help
```

```sh
# Render a DOT file (colors on terminal)
hascii flow.dot

# Pipe from stdin
echo 'digraph { A -> B }' | hascii -

# Save to file (hue-based borders, no ANSI)
hascii --no-color flow.dot > flow.txt

# Constrain for docstrings
hascii --width 72 --max-label 25 flow.dot
```

## Rust API

```rust
use hascii::{render, RenderOptions};

let dot = r#"digraph { A -> B -> C }"#;
let options = RenderOptions::default();
let ascii = render(dot, &options).expect("render failed");
println!("{ascii}");
```

## Label trimming

Long labels are trimmed with `--trim` / `trim=`:

| Strategy | Result (budget 25) |
|----------|--------------------|
| `smart` (default) | `router_text...threshold` |
| `end` | `router_text_impro...` |
| `start` | `...score_ge_threshold` |
| `middle` | `router_te...threshold` |

## License

Apache-2.0

