A fast, extensible XML-to-ASCII rendering engine that converts XML/HTML-like markup into beautifully formatted terminal output. Built in Rust for performance and memory efficiency.
This engine allows you to describe terminal illustrations using familiar XML syntax, offloading the complex layout and rendering logic to the engine. It's designed to help AI agents and developers render formatted text diagrams, documents, and UI blockouts in their responses.
For complete element and attribute reference, see REFERENCE.md.
XML Input → Parser → DOM Tree → Layout Engine → Rasterizer → ASCII Output
- DOM Module (
src/dom/): AST structures for elements and text nodes - Parser (
src/parser/): XML-like syntax parser with attribute support - Layout Engine (
src/layout/): Calculates positions and dimensions using box model - Rasterizer (
src/render/): Converts layout tree to 2D character buffer
axie '<div width="40"><h1>Hello World</h1><div width="30"><span>Status: </span><span>ONLINE</span></div></div>'Output:
┌────────────────────────────────────────┐
│HELLO WORLD │
│────────────────────────────────────────│
│┌────────────────────────────┐ │
││Status:ONLINE │ │
│└────────────────────────────┘ │
└────────────────────────────────────────┘
The renderer supports three ways to provide XML input:
1. Direct XML String (for quick testing)
axie '<div width="40"><h1>Hello</h1></div>'2. File Input (for complex documents)
# Using -f or --file flag
axie -f dashboard.xml
axie --file report.xml 100 --backend=truecolor3. Stdin Pipe (for integration with other tools)
# Pipe from echo
echo '<div width="30">Hello World</div>' | axie
# Pipe from cat
cat template.xml | axie 80 --backend=plain
# Use with other tools
python generate_dashboard.py | axie | lessaxie can be used as a Rust library for programmatic rendering:
use axie::{render, RenderOptions, BackendType};
fn main() {
let xml = r#"<div width="40"><h1>Hello World</h1></div>"#;
let options = RenderOptions {
width: Some(80),
backend: BackendType::Plain,
collect_warnings: true,
track_clickable_areas: false,
};
match render(xml, &options) {
Ok(output) => {
println!("{}", output.rendered);
println!("Dimensions: {}x{}",
output.dimensions.width,
output.dimensions.height
);
}
Err(e) => eprintln!("Error: {}", e),
}
}width: Target width in characters (None = auto-detect terminal width)backend: Color backend -Auto,TrueColor,Color256, orPlaincollect_warnings: Enable warning collection during renderingtrack_clickable_areas: Track anchor element positions for interactive use
The render function returns a RenderOutput struct containing:
rendered: The formatted terminal output as a stringdimensions: Width and height of the rendered outputwarnings: Any warnings collected during rendering (if enabled)clickable_areas: Positions and URLs of anchor elements (if tracking enabled)
See examples/basic.rs for comprehensive usage examples.
The --json flag enables machine-readable JSON output for CI/CD integration, programmatic processing, and automation workflows. This mode returns structured data including the rendered output, dimensions, warnings, and clickable areas.
axie '<div>Hello</div>' --jsonExample output:
{
"success": true,
"data": {
"rendered": "┌───────┐\n│Hello │\n└───────┘",
"dimensions": {
"width": 9,
"height": 3
},
"warnings": [
{
"severity": "warning",
"message": "Content area has zero width due to excessive padding"
}
],
"clickable_areas": [
{
"x": 1,
"y": 1,
"width": 5,
"height": 1,
"href": "https://example.com",
"text": "Hello"
}
]
}
}Success response:
success(boolean):truewhen rendering succeedsdata.rendered(string): The formatted terminal outputdata.dimensions.width(number): Output width in charactersdata.dimensions.height(number): Output height in charactersdata.warnings(array): Warnings collected during rendering, each with:severity:"error","warning", or"info"message: Human-readable description
data.clickable_areas(array): Anchor element positions, each with:x,y: Position coordinateswidth,height: Area dimensionshref: Target URLtext: Link text content
Error response:
success(boolean):falsewhen rendering failserror.type(string): Error category (e.g.,"parse","render")error.message(string): Human-readable error descriptionerror.fatal(boolean):trueif error prevents rendering
--jsonautomatically enables warning collection and clickable area tracking- Output is always valid JSON, suitable for piping to tools like
jq - Use
--jsonfor headless automation where parsing text output is difficult
Parse output with jq for shell scripting:
# Extract just the rendered output
axie '<div>Hello</div>' --json | jq -r '.data.rendered'
# Check for warnings in CI pipeline
axie '<div>Test</div>' --json | jq '.data.warnings | length'
# Extract clickable link URLs
axie '<div><a href="https://example.com">Link</a></div>' --json | jq '.data.clickable_areas[].href'- Renders with borders on all sides
- Stacks children vertically
- Supports inline flow for text/span children
- Use
widthattribute for explicit width (in character units)
<div width="50">Content here</div>- Flows horizontally within parent
- No borders or padding
- Useful for styling text segments
<span>Inline text</span>Six levels of headings with progressive visual weight:
┌─────────┬───────────┬───────────┬───────────┐
│ Element │ Case │ Underline │ Usage │
├─────────┼───────────┼───────────┼───────────┤
│h1 │UPPERCASE │══ │Main title │
│ │ │double │ │
├─────────┼───────────┼───────────┼───────────┤
│h2 │UPPERCASE │── │Major │
│ │ │single │section │
├─────────┼───────────┼───────────┼───────────┤
│h3 │normal │── │Subsection │
│ │ │single │ │
├─────────┼───────────┼───────────┼───────────┤
│h4 │UPPERCASE │none │Minor │
│ │ │ │section │
├─────────┼───────────┼───────────┼───────────┤
│h5 │normal │none │Subheading │
├─────────┼───────────┼───────────┼───────────┤
│h6 │normal │none │Small │
└─────────┴───────────┴───────────┴───────────┘
Features:
- Block-level elements
- Automatic text transformation (uppercase for h1, h2, h4)
- Automatic underline (h1 double, h2/h3 single)
- Use
widthattribute for explicit width
<h1>Main Title</h1>
<h2>Section Header</h2>
<h3>Subsection</h3>
<h4>Minor Section</h4>
<h5>Subheading</h5>
<h6>Small Heading</h6>HTML-like tables with rows (<tr>), data cells (<td>), and header cells (<th>):
<table>
<tr><th>Name</th><th>Status</th></tr>
<tr><td>Server A</td><td>Online</td></tr>
<tr><td>Server B</td><td>Offline</td></tr>
</table>Features:
- Automatic column width calculation
- Header cells (
<th>) centered by default - Border-collapse support via CSS
border-collapse: collapse - Support for nested content (lists, text) within cells
CSS Properties:
border-collapse: collapse/separate- Controls whether cell borders are shared
Create bulleted and numbered lists:
<!-- Unordered list -->
<ul>
<li>First item</li>
<li>Second item with nested list
<ul>
<li>Nested item</li>
</ul>
</li>
</ul>
<!-- Ordered list -->
<ol>
<li>Step one</li>
<li>Step two</li>
</ol>Features:
- Automatic indentation based on nesting level
- Multiple list styles (disc, circle, square, decimal, alpha, roman)
- Ordered lists with custom start numbers (
startattribute) - Deep nesting supported (3+ levels)
- Tables can be nested inside list items
CSS Properties:
list-style-type: disc/circle/square/decimal/lower-alpha/upper-alpha/lower-roman/upper-roman/none
- Preserves all whitespace exactly as written (spaces, tabs, newlines)
- No word wrapping - content displayed exactly as-is
- Perfect for code blocks and ASCII art
- Supports borders, padding, and background colors
<pre>
function example() {
return "preserved whitespace";
}
</pre>- Block-level text container with no borders by default
- Automatic top and bottom margins (1 row each) for visual separation
- Supports inline content (text, spans, anchors)
- Text wraps at word boundaries
<p>This is a paragraph with some text content.</p>
<p>Another paragraph with <span>styled text</span> inside.</p>Features:
- Default margins (top=1, bottom=1) create space between paragraphs
- Margins can be overridden with explicit
margin-topormargin-bottomstyles - No borders by default (unlike
<div>) - Full support for colors, padding, and text alignment
- Automatically collapsed (multiple spaces become single space)
- Wraps at word boundaries
- Long words break if necessary
The style attribute supports CSS-like syntax for padding, margins, and width:
Padding:
<!-- Uniform padding on all sides -->
<div style="padding: 2">Content</div>
<!-- Vertical and horizontal -->
<div style="padding: 1 2">Content</div>
<!-- Individual sides -->
<div style="padding-top: 1; padding-right: 2">Content</div>Margins:
<!-- Space between elements -->
<div>First</div>
<div style="margin-top: 2">Second (with 2 row gap)</div>Border-Box Model:
The total width includes content + padding + borders. The specified width is never exceeded:
<!-- Content area will be: 30 - 4 padding - 2 borders = 24 chars -->
<div width="30" style="padding: 2">Content</div>Control element dimensions with minimum and maximum constraints:
<!-- Minimum width - element will be at least this wide -->
<div style="min-width: 40">Content</div>
<!-- Maximum width - element will not exceed this width -->
<div style="max-width: 80">Long content that needs constraining</div>
<!-- Combined constraints -->
<div style="min-width: 40; max-width: 100">Responsive content</div>
<!-- Height constraints work similarly -->
<div style="min-height: 5; max-height: 20">Content with height limits</div>Use percentages for fluid, responsive layouts. Percentages are calculated relative to the parent container's available width:
<!-- Half of parent width -->
<div style="width: 50%">Half width</div>
<!-- Quarter of parent width -->
<div style="width: 25%">Quarter width</div>
<!-- Full width of container -->
<div style="width: 100%">Full width</div>For the root element, percentages are calculated against the terminal width (if available) or otherwise a default of 80 characters.
Control how content behaves when it exceeds container boundaries:
<!-- Wrap (default) - content wraps to next line -->
<div style="width: 20; overflow: wrap">Long text wraps automatically</div>
<!-- Clip - content is truncated at boundary -->
<div style="width: 20; overflow: clip">Long text is clipped</div>
<!-- Scroll - content is clipped but marked for scrolling (future) -->
<div style="width: 20; overflow: scroll">Content marked for scroll</div>Overflow modes affect:
- Text content: How text is wrapped or clipped
- Child elements: How nested content is handled
Color Support: Full ANSI color support with CSS-like syntax. Colors can be specified as named colors, hex values, or RGB:
<!-- Text color -->
<span style="color: red">Red text</span>
<!-- Background color -->
<div style="background-color: blue">Blue background</div>
<!-- Border color -->
<div style="border-color: green">Green borders</div>
<!-- Hex colors -->
<span style="color: #ff5733">Custom hex color</span>
<!-- RGB colors -->
<span style="color: rgb(255, 128, 0)">RGB orange</span>
<!-- Combined styling -->
<div style="color: white; background-color: navy; border-color: cyan"> Fully styled box
</div>Colors inherit from parent elements to children, allowing for nested color schemes.
Border Styles:
Control the appearance of borders with CSS-like border-style property:
<!-- Standard styles -->
<div style="border-style: single">Single line (default)</div>
<div style="border-style: double">Double line</div>
<div style="border-style: thick">Thick/heavy lines</div>
<div style="border-style: ascii">ASCII-only (+--+|)</div>
<div style="border-style: dotted">Dotted (· · ·)</div>
<div style="border-style: dashed">Dashed (┄ ┄ ┄)</div>
<!-- Custom bunting with repeating pattern (no quotes needed) -->
<div style="border-style: bunting(~)">Wavy border</div>
<div style="border-style: bunting(=*)">Pattern: =*=*=*</div>
<div style="border-style: bunting(xo)">Pattern: xoxoxo</div>
<!-- Combined with colors -->
<div style="border-style: double; border-color: cyan; color: white; background-color: navy">
Styled box
</div>Text Alignment:
Control text alignment within containers using CSS-like text-align property:
<!-- Left alignment (default) -->
<div style="text-align: left">Left aligned text</div>
<!-- Center alignment -->
<div style="text-align: center">Center aligned text</div>
<!-- Right alignment -->
<div style="text-align: right">Right aligned text</div>
<!-- Combined with other styles -->
<div style="text-align: center; color: white; background-color: navy">
Centered styled text
</div>Text alignment inherits from parent elements. Nested containers automatically align their text to match their parent unless explicitly overridden.
Error Handling: Comprehensive fail-forward error handling system that produces the best possible output even when encountering errors:
<!-- Parser continues despite malformed XML -->
<div width="40"><span>Missing closing tag<div>Still renders</div></span></div>
<!-- Unknown CSS properties generate warnings but don't stop rendering -->
<div style="unknown-property: value; color: red">Valid styles applied</div>The system distinguishes between:
- Fatal errors: Only unclosed root tags at EOF (cannot determine document boundaries)
- Recoverable errors: Malformed markup, unknown styles, invalid values (rendering continues)
CLI flags control verbosity:
# Silent mode - no warnings, zero overhead
axie '<malformed>' --quiet
# Show warnings (default)
axie '<invalid>' --warnings
# Verbose mode - detailed diagnostics
axie '<input>' --verboseBackend Selection: The renderer automatically detects terminal color capabilities, but you can override this:
# Auto-detect (default)
axie '<div style="color: red">Text</div>'
# Force truecolor (24-bit RGB)
axie '<div style="color: #ff5733">Text</div>' --backend=truecolor
# Force 256-color mode
axie '<div style="color: red">Text</div>' --backend=256
# Disable colors (plain ASCII)
axie '<div style="color: red">Text</div>' --backend=plainaxie '<div width="60"><h1>Server Dashboard</h1><div width="50"><span>CPU: </span><span>45% | </span><span>RAM: </span><span>78% | </span><span>Disk: </span><span>23%</span></div><div width="50">System status and metrics display.</div></div>'Output:
┌──────────────────────────────────────────────────────────┐
│SERVER DASHBOARD │
│──────────────────────────────────────────────────────────│
│┌────────────────────────────────────────────────┐ │
││CPU:45% |RAM:78% |Disk:23% │ │
│└────────────────────────────────────────────────┘ │
│┌────────────────────────────────────────────────┐ │
││System status and metrics display. │ │
│└────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
axie '<div width="40"><table width="40" style="border-collapse: collapse"><tr><th>Name</th><th>Value</th></tr><tr><td>Item A</td><td>100</td></tr><tr><td>Item B</td><td>200</td></tr></table></div>'Output:
┌──────────────────────────────────────┐
│┌──────────────────┬─────────────────┐│
││ Name │ Value ││
│├──────────────────┼─────────────────┤│
││Item A │100 ││
│├──────────────────┼─────────────────┤│
││Item B │200 ││
│└──────────────────┴─────────────────┘│
└──────────────────────────────────────┘
axie '<div width="40"><ul><li>First item</li><li>Second item</li><li>Third item</li></ul></div>'Output:
┌──────────────────────────────────────┐
│ • First item │
│ • Second item │
│ • Third item │
└──────────────────────────────────────┘
axie '<div width="60" style="border-color: cyan"><h1>Server Dashboard</h1><div width="50" style="background-color: navy; color: white"><span style="color: green">● ONLINE</span> | CPU: 45% | RAM: 78%</div></div>' --backend=truecolor# Development build
cargo build
# Release build (optimized)
cargo build --release
# Run tests
cargo test
# Build library only
cargo build --lib
# Run example
cargo run --example basic- Performance: Zero-cost abstractions, single-pass layout, buffer reuse
- Extensibility: Trait-based element system for easy customization
- Simplicity: Minimal API, explicit over implicit
- WYTIWYG: What You Type Is What You Get - clear visibility into layout
MIT