mural 0.1.0

Conversational terminal rendering for command-line applications.
Documentation

Conversational terminal rendering for command-line applications.

Mural manages a small live conversation surface in the terminal's normal buffer. Applications add transcript-like live blocks first and optional pinned blocks after them for status lines, progress, or ephemeral UI. Nothing is drawn until [Terminal::render] is called, so callers can batch several mutations before one terminal update. Calling [Terminal::finish] removes pinned UI, leaves the rendered live transcript behind, restores the cursor, and flushes the backend.

Mural intentionally does not own an alternate screen, stdin, raw mode, signal handling, or the event loop. Resize handling is caller-driven through [Terminal::resize]. If another component writes to the terminal, use the backend escape hatch and then call [Terminal::force_full_redraw] before the next render so Mural can recover its cached screen snapshot with a full rewrite.

The public API is organized around:

  • [terminal] for the renderer lifecycle and live/pinned block management.
  • [text] for validated plain, raw, ANSI, styled, and wrapped text.
  • [backend] for stdout integration, fake backends, and custom terminal I/O.
  • [blocks] for pre-built renderable blocks such as [Hr], [ListItem], [Spinner], [Textarea], and [Padding].
  • [error] for typed lifecycle and identified-block errors.

See examples/conversation.rs for a runnable end-to-end conversation model.

# use mural::{FakeBackend, Size, Terminal, Text};
# fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut terminal = Terminal::new(FakeBackend::new(Size::new(80, 24)))?;

terminal.push_live(Text::from_plain("user: hello")?)?;
terminal.insert_live("assistant", Text::from_plain("assistant: …")?)?;
terminal.insert_pinned("status", Text::from_plain("status: streaming")?)?;
terminal.render()?;

*terminal.live_block_mut::<Text>("assistant")? =
    Text::from_plain("assistant: hello back")?;
terminal.resize(Size::new(60, 20))?;
terminal.force_full_redraw()?;
terminal.render()?;

terminal.finish()?;
# Ok(())
# }