Expand description
§Cellophane
A terminal animation framework for Rust. Implement one trait, get a complete rendering pipeline with frame diffing, resize handling, and input forwarding.
§Core types
Animation- the trait you implement to define an animationAnimator- drives your animation with a frame-rate-limited render loopFrame- a grid of styledCells representing one frame of outputFrameBuilder- parses ANSI-escaped text into aFrameCell- a single terminal cell with character, colors, and attributesGrapheme- a unicode grapheme cluster backed bySmallVec<[char; 4]>
§Quick start
use std::time::Duration;
use cellophane::{Animation, Animator, Frame, Cell};
use crossterm::style::Color;
struct Rainbow {
tick: usize,
rows: usize,
cols: usize,
}
impl Animation for Rainbow {
fn init(&mut self, initial: Frame) {
let (rows, cols) = initial.dims().unwrap_or((0, 0));
self.rows = rows;
self.cols = cols;
}
fn update(&mut self, _dt: Duration) -> Frame {
let mut frame = Frame::with_capacity(self.cols, self.rows);
for row in 0..self.rows {
for col in 0..self.cols {
let hue = ((col + row + self.tick) % 256) as u8;
if let Some(cell) = frame.get_cell_mut(row, col) {
*cell = Cell::default()
.with_bg(Color::Rgb { r: hue, g: 100, b: 255 - hue });
}
}
}
self.tick += 1;
frame
}
fn is_done(&self) -> bool { false }
fn resize(&mut self, w: usize, h: usize) {
self.cols = w;
self.rows = h;
}
}
fn main() -> std::io::Result<()> {
let anim = Box::new(Rainbow { tick: 0, rows: 0, cols: 0 });
let mut animator = Animator::enter_with(anim)?;
loop {
match animator.tick() {
Ok(true) => continue,
Ok(false) => break,
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => break,
Err(e) => return Err(e),
}
}
Ok(())
}The Animator handles entering the alternate screen, enabling raw mode,
hiding the cursor, frame-rate limiting, frame diffing (only changed cells
are redrawn), terminal resize events, Ctrl+C handling, and terminal
restoration on drop.
§ratatui Integration
Enable with:
cargo add cellophane --features=ratatuiThis feature flag exposes the AnimationWidget struct, which allows Frame to be usable in the ratatui rendering pipeline.
AnimationWidget implements the Widget trait so it can be composed with other ratatui widgets as you would expect.
Here’s an example using the Block widget:
fn main() -> std::io::Result<()> {
let mut anim = SomeAnimation::new();
anim.init(anim.initial_frame());
ratatui::run(|terminal| {
loop {
terminal.draw(|f| {
let chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(f.area());
let block = Block::default().title("Animation").borders(ratatui::widgets::Borders::ALL);
let block_inner = block.inner(chunks[0]);
// resize the animation to match block_inner
anim.resize(block_inner.width as usize, block_inner.height as usize);
let anim_frame = anim.update(); // get the frame
// render the block widget, and the animation frame inside of it
f.render_widget(block, chunks[0]);
f.render_widget(AnimationWidget::new(&anim_frame), block_inner);
})?;
if event::poll(Duration::from_millis(16))? {
if event::read()?.is_key_press() {
break Ok(());
}
}
}
})
}Re-exports§
pub use crossterm;
Structs§
- Animator
- Drives an
Animationwith frame-diffed terminal rendering, resize handling, and input forwarding. - Cell
- A single terminal cell with a character, foreground/background colors, and text attributes.
- Cell
Flags - Bitflags representing text attributes for a Cell. These correspond to the common ANSI SGR attributes.
- Frame
- A rectangular grid of
Cells representing a single frame of terminal output. - Frame
Builder - Parses ANSI-escaped text into a
Frameusing a VTE state machine. - Grapheme
- A unicode grapheme cluster backed by
SmallVec<[char; 4]>.
Traits§
- Animation
- The core trait for defining terminal animations.
Functions§
- to_
graphemes - Split a string into a
VecofGraphemes using Unicode segmentation. Each element represents a single user-perceived character, which may consist of multiple codepoints.