# teasr-core
Orchestration and capture library for teasr. Handles config loading, server lifecycle, web/terminal/screen capture, and GIF conversion.
This crate is the engine behind the `teasr` CLI. Use it directly to embed teasr capture into your own Rust programs.
## Usage
```toml
[dependencies]
teasr-core = "0.11"
tokio = { version = "1", features = ["full"] }
```
```rust
use teasr_core::{config, orchestrator};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Discover teasr.toml walking up from cwd
let cwd = std::env::current_dir()?;
let config_path = config::discover_config(&cwd)
.expect("no teasr.toml found");
let config = config::load_config(&config_path)?;
let results = orchestrator::run(&config).await?;
for result in &results {
println!("{}: {:?}", result.scene_name, result.files);
}
Ok(())
}
```
## Config Loading
```rust
// Auto-discover: walks up from a directory to filesystem root
let path: Option<PathBuf> = config::discover_config(&start_dir);
// Load and resolve (applies defaults, validates scenes non-empty)
let config: ResolvedConfig = config::load_config(&path)?;
```
`ResolvedConfig` is the fully-defaulted version of the TOML config with all `Option` fields resolved.
## Types
### `ResolvedConfig`
```rust
pub struct ResolvedConfig {
pub scenes: Vec<SceneConfig>,
pub server: Option<ServerConfig>,
pub viewport: ViewportConfig, // default: 1280x720
pub output: OutputConfig, // default dir: ./teasr-output, formats: [Png]
pub frame_duration_ms: u64, // derived from fps (default: 24fps → 41ms)
pub seconds: f64, // target output duration (default: 2.5s)
pub scene_timeout: f64, // per-scene wall-clock timeout (default: 60s)
}
```
### `SceneConfig`
A tagged enum with variants `Web`, `Screen`, and `Terminal`. Mirrors the `[[scenes]]` TOML entries. The `Web` variant uses a single `uri` field that auto-detects remote URLs, local files, and Markdown documents.
### `ServerConfig`
```rust
pub struct ServerConfig {
pub command: String,
pub url: String,
pub timeout: u64, // ms, default: 10000
}
```
### `OutputConfig`
```rust
pub struct OutputConfig {
pub dir: String, // default: "./teasr-output"
pub formats: Vec<OutputFormat>, // default: [Png]
}
```
### `OutputFormat`
```rust
pub enum OutputFormat {
Png(PngConfig),
Gif(GifConfig), // quality: u8, fast: bool, repeat: Option<u16>
Mp4(Mp4Config), // fps: u32
}
```
### `CaptureResult`
```rust
pub struct CaptureResult {
pub scene_name: String,
pub files: Vec<String>, // absolute or relative paths of written files
}
```
## Orchestrator
```rust
pub async fn orchestrator::run(config: &ResolvedConfig) -> Result<Vec<CaptureResult>>
```
Runs all scenes in declaration order:
1. Creates the output directory.
2. Starts the server (if `server` is set) and health-polls it until ready.
3. Iterates scenes, dispatching to the appropriate capture backend.
4. On drop, kills the server process group (Unix: `SIGTERM` then `SIGKILL`; Windows: `TerminateProcess`).
## Capture Backends
### Web (`capture::web`)
Uses chromiumoxide (Chrome DevTools Protocol) to navigate, execute actions (click, scroll, hover, wait), and take screenshots. A single `uri` is classified by the orchestrator: `http(s)://` URLs go direct, `.md`/`.markdown` files are rendered via `render::markdown::render_to_html` into a temp file, and anything else is loaded via `file://` (with PDF page selection applied to the URL fragment). Requires Chrome or Chromium on `PATH` or at a standard install location.
### Terminal (`capture::terminal`)
Runs the command in a PTY via portable-pty, collects ANSI output, and delegates to the `term_render` module to produce a styled PNG.
### Screen (`capture::screen`)
Captures a display or region using xcap. Supports display index selection and pixel-precise region cropping.
## GIF Conversion (`convert::gif`)
```rust
pub fn frames_to_gif(frames: &[CapturedFrame], gif_path: &Path, config: &GifConfig) -> Result<()>
```
Assembles multiple captured frames into an animated GIF using gifski (pure Rust, no FFmpeg).
## Server Lifecycle
`ManagedServer` is an RAII guard that starts a shell command in its own process group and tears it down on drop.
```rust
let server = ManagedServer::start(&server_config).await?;
// server is health-polled until config.url returns 2xx/3xx
// ...captures run...
// server killed when `server` is dropped
```
On Unix the entire process group receives `SIGTERM`, then `SIGKILL` 200 ms later, preventing orphaned child processes.
## License
Apache-2.0