Skip to main content

Crate eye_declare

Crate eye_declare 

Source
Expand description

Declarative inline TUI rendering for Rust, built on Ratatui.

eye_declare provides a React-like component model for terminal UIs that render inline — content grows downward into the terminal’s native scrollback rather than taking over the full screen. This makes it ideal for CLI tools, AI assistants, build systems, and interactive prompts where earlier output should remain visible.

§Quick start

use eye_declare::{element, Application, Elements, Spinner};

struct State { messages: Vec<String>, loading: bool }

fn view(state: &State) -> Elements {
    element! {
        #(for (i, msg) in state.messages.iter().enumerate() {
            Text(key: format!("msg-{i}")) { #(msg.clone()) }
        })
        #(if state.loading {
            Spinner(key: "loading", label: "Thinking...")
        })
    }
}

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let (mut app, handle) = Application::builder()
        .state(State { messages: vec![], loading: true })
        .view(view)
        .build()?;

    tokio::spawn(async move {
        handle.update(|s| s.messages.push("Hello!".into()));
        handle.update(|s| s.loading = false);
    });

    app.run().await
}

§Core concepts

  • #[component] + #[props] — Define components as functions. Props are a struct with #[props]; the function receives props, optional state, hooks, and children, returning an Elements tree. Hooks declare behavioral capabilities (events, focus, intervals).

  • Elements — A list of component descriptions returned by view functions. The framework reconciles the new list against the existing tree, preserving state for reused nodes.

  • element! — A JSX-like proc macro for building Elements declaratively. Supports props, children, keys, conditionals (#(if ...)), loops (#(for ...)), and splicing pre-built Elements (#(expr)).

  • Application — Owns your state and manages the render loop. Handle lets you send state updates from any thread or async task.

  • InlineRenderer — The lower-level rendering engine for when you need direct control over the render loop (sync code, embedding, custom event loops).

§Built-in components

ComponentDescription
TextStyled text with word wrapping (data children: Span)
SpinnerAnimated spinner with auto-tick via lifecycle hooks
MarkdownHeadings, bold, italic, inline code, code blocks, lists
ViewUnified layout container with optional borders, padding, and background
CanvasRaw buffer rendering via a user-provided closure
VStackVertical container — children stack top-to-bottom
HStackHorizontal container with WidthConstraint-based layout

§Layout

Vertical stacking is the default. HStack provides horizontal layout where children declare their width via WidthConstraint::Fixed or WidthConstraint::Fill. Components can declare Insets for border/padding chrome — children render inside the inset area while the component draws its chrome in the full area.

§Lifecycle hooks

Components declare effects using the Hooks API:

§Context

The context system lets ancestor components provide values to their descendants without prop-drilling. Register root-level contexts via ApplicationBuilder::with_context, or have components provide them via Hooks::provide_context in their component function. Descendants read context values with Hooks::use_context.

This is commonly used with Application::run_loop to give components access to an app-domain event channel:

let (tx, mut rx) = tokio::sync::mpsc::channel(32);
let (mut app, handle) = Application::builder()
    .state(MyState::default())
    .view(my_view)
    .with_context(tx)
    .build()?;

let h = handle.clone();
tokio::spawn(async move {
    while let Some(event) = rx.recv().await {
        match event {
            AppEvent::Submit(val) => h.update(|s| s.result = val),
            AppEvent::Quit => { h.exit(); break; }
        }
    }
});

app.run_loop().await?;

§Feature flags

FlagDefaultDescription
macrosyesEnables the element! proc macro via eye_declare_macros

Re-exports§

pub use app::Application;
pub use app::ApplicationBuilder;
pub use app::CommittedElement;
pub use app::ControlFlow;
pub use app::CtrlCBehavior;
pub use app::Handle;
pub use app::KeyboardProtocol;
pub use cells::Cells;
pub use children::AddTo;
pub use children::ChildCollector;
pub use children::ComponentWithSlot;
pub use children::DataChildren;
pub use children::DataHandle;
pub use children::SpliceInto;
pub use component::Column;
pub use component::Component;
pub use component::EventResult;
pub use component::HStack;
pub use component::Tracked;
pub use component::VStack;
pub use components::canvas::Canvas;
pub use components::markdown::Markdown;
pub use components::markdown::MarkdownState;
pub use components::spinner::Spinner;
pub use components::spinner::SpinnerState;
pub use components::text::Span;
pub use components::text::Text;
pub use components::text::TextChild;
pub use components::view::Direction;
pub use components::view::View;
pub use element::ElementHandle;
pub use element::Elements;
pub use hooks::Hooks;
pub use inline::InlineRenderer;
pub use insets::Insets;

Modules§

app
Application wrapper, builder, handle, and control flow types.
cells
Cell measurement type for component props. See Cells. Cell measurement type for component props.
children
Traits and types for the element! macro’s child collection system.
component
The Component trait (framework-internal) and built-in containers (VStack, HStack, Column).
components
Built-in components: Text, Spinner, Markdown, and View. Built-in components shipped with eye_declare.
element
The Elements list and ElementHandle for building component trees.
hooks
Lifecycle hooks for declaring component effects.
inline
The InlineRenderer — low-level inline rendering engine.
insets
The Insets type for declaring content padding and border chrome.

Macros§

element
impl_slot_children
Implement ChildCollector for a component so it accepts slot children in the element! macro.

Structs§

NodeId
Opaque handle identifying a node in the component tree.

Enums§

BorderType
Re-exported from ratatui_widgets for use with View::border. The type of border of a Block.
Layout
Layout direction for a container’s children.
WidthConstraint
How a child claims horizontal space inside an HStack.

Attribute Macros§

component
Declarative element tree macro.
props
Declarative element tree macro — the primary way to build UIs in eye_declare.

Derive Macros§

TypedBuilder