aisling 0.1.0

Embeddable terminal text effects for Rust TUI applications.
Documentation
# Aisling

Embeddable terminal text effects for Rust TUI applications.

Aisling renders animated text effects as plain Rust data. It does not own your terminal, start an event loop, sleep, or print unless you run the included examples. Your app drives the iterator and maps each styled `Frame` into Ratatui, Crossterm, ANSI output, tests, logs, or any custom renderer.

The effect catalog is ported from the Python `terminaltexteffects` project shape and includes every built-in reference effect exposed by the current snapshot.

## Install

After publishing:

```bash
cargo add aisling
```

Or in `Cargo.toml`:

```toml
[dependencies]
aisling = "0.1"
```

For local development from this repository:

```toml
[dependencies]
aisling = { path = "../aisling" }
```

## Quick Start

```rust
use aisling::{Effect, EffectConfig, EffectKind};

let config = EffectConfig::default()
    .with_duration(80)
    .with_seed(42);

let effect = Effect::with_config(EffectKind::Matrix, "Aisling", config);

for frame in effect.iter() {
    // Map frame.cells() into your TUI buffer.
    let plain = frame.to_plain_string();
    let ansi = frame.to_ansi_string();
}
```

## TUI Integration

`Effect::iter()` yields `Frame` values. A `Frame` is a fixed-size grid with row-major `Cell` data.

Important types:

| Type | Purpose |
| --- | --- |
| `Effect` | Owns the effect kind, input text, and config. |
| `EffectKind` | Selects one of the built-in effects. |
| `EffectConfig` | Controls duration, seed, gradient, canvas size, color handling, and more. |
| `Frame` | Rendered 2D grid for a single animation tick. |
| `Cell` | Character plus foreground/background colors and simple styles. |
| `Color`, `Gradient`, `Easing` | Color and timing helpers. |

Minimal renderer loop shape:

```rust
use aisling::{Effect, EffectKind};

let effect = Effect::new(EffectKind::Wipe, "ship it");

for frame in effect.iter() {
    for y in 0..frame.height() {
        for x in 0..frame.width() {
            let cell = frame.cell(x, y).unwrap();
            // Write cell.ch and cell.colors into your TUI buffer here.
        }
    }
}
```

## Effects

All built-in effects are available through `EffectKind` and `EffectKind::all()`.

| Command | Enum |
| --- | --- |
| `beams` | `EffectKind::Beams` |
| `binarypath` | `EffectKind::BinaryPath` |
| `blackhole` | `EffectKind::Blackhole` |
| `bouncyballs` | `EffectKind::BouncyBalls` |
| `bubbles` | `EffectKind::Bubbles` |
| `burn` | `EffectKind::Burn` |
| `colorshift` | `EffectKind::ColorShift` |
| `crumble` | `EffectKind::Crumble` |
| `decrypt` | `EffectKind::Decrypt` |
| `errorcorrect` | `EffectKind::ErrorCorrect` |
| `expand` | `EffectKind::Expand` |
| `fireworks` | `EffectKind::Fireworks` |
| `highlight` | `EffectKind::Highlight` |
| `laseretch` | `EffectKind::LaserEtch` |
| `matrix` | `EffectKind::Matrix` |
| `middleout` | `EffectKind::MiddleOut` |
| `orbittingvolley` | `EffectKind::OrbittingVolley` |
| `overflow` | `EffectKind::Overflow` |
| `pour` | `EffectKind::Pour` |
| `print` | `EffectKind::Print` |
| `rain` | `EffectKind::Rain` |
| `randomsequence` | `EffectKind::RandomSequence` |
| `rings` | `EffectKind::Rings` |
| `scattered` | `EffectKind::Scattered` |
| `slice` | `EffectKind::Slice` |
| `slide` | `EffectKind::Slide` |
| `smoke` | `EffectKind::Smoke` |
| `spotlights` | `EffectKind::Spotlights` |
| `spray` | `EffectKind::Spray` |
| `swarm` | `EffectKind::Swarm` |
| `sweep` | `EffectKind::Sweep` |
| `synthgrid` | `EffectKind::SynthGrid` |
| `thunderstorm` | `EffectKind::Thunderstorm` |
| `unstable` | `EffectKind::Unstable` |
| `vhstape` | `EffectKind::VhsTape` |
| `waves` | `EffectKind::Waves` |
| `wipe` | `EffectKind::Wipe` |

String parsing is supported:

```rust
use std::str::FromStr;
use aisling::EffectKind;

let kind = EffectKind::from_str("laseretch").unwrap();
assert_eq!(kind, EffectKind::LaserEtch);
```

## Configuration

```rust
use aisling::{Color, EffectConfig, Easing, Gradient, GradientDirection};

let config = EffectConfig::default()
    .with_duration(120)
    .with_seed(9001)
    .with_canvas_size(48, 8)
    .with_gradient(
        Gradient::new(
            vec![Color::rgb(255, 170, 60), Color::rgb(80, 110, 95)],
            24,
        ),
        GradientDirection::Horizontal,
    );
```

Notable `EffectConfig` fields:

| Field | Meaning |
| --- | --- |
| `duration` | Number of animation frames. |
| `hold_final_frames` | Extra frames that hold the final text. |
| `seed` | Deterministic random seed for repeatable playback. |
| `gradient` | Final/effect color gradient. |
| `gradient_direction` | Horizontal, vertical, diagonal, or radial mapping. |
| `easing` | Global easing used by many effect renderers. |
| `existing_color_handling` | Preserve or ignore parsed ANSI input colors. |
| `no_color` | Disable color output. |
| `canvas_width`, `canvas_height` | Optional fixed frame dimensions. |
| `tab_width` | Spaces per tab when parsing input text. |

## ANSI Input

Aisling parses common ANSI SGR sequences in input text, including foreground/background 3-bit, bright 3-bit, 8-bit xterm, and 24-bit RGB colors. Parsed styling is kept in each final `Cell` when the color-handling mode allows it.

```rust
use aisling::{Effect, EffectKind};

let effect = Effect::new(EffectKind::Print, "\x1b[38;2;255;80;80mred text\x1b[0m");
let final_frame = effect.final_frame();
assert_eq!(final_frame.to_plain_string(), "red text");
```

## Examples

Run a single effect in the terminal:

```bash
cargo run --example demo -- wipe "Hello from Aisling"
cargo run --example demo -- matrix "opencode style"
```

Use the helper script while developing locally:

```bash
./run.sh wipe "Hello from Aisling"
./run.sh matrix "opencode style"
./run.sh list
./run.sh all "Aisling"
```

Run the multi-pane all-effects showcase:

```bash
./run.sh showcase
./run.sh showcase --theme industrial
./run.sh showcase --seconds 60
./run.sh showcase --forever
```

`test.sh` launches the showcase with a longer embedded text sample:

```bash
./test.sh
./test.sh 90
./test.sh --industrial
./test.sh --industrial 90
```

## Package Verification

Before publishing:

```bash
cargo fmt
cargo test
cargo package --allow-dirty
```

Publish when ready:

```bash
cargo publish
```

## License

MIT