1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! # tuile
//!
//! Textual-grade building blocks for [ratatui]: a ported design system (themes to derived
//! colour roles), 108 widget types with keyboard and mouse handling built in, tweened
//! animation, overlays (modals, toasts, palettes, dropdowns), AI/LLM chat components, a
//! 102-spinner catalog and an app runtime that only redraws when something moves.
//!
//! ## Import
//!
//! ```toml
//! [dependencies]
//! tuile = { git = "https://github.com/irvin/tuile" }
//! ```
//!
//! `use tuile::prelude::*;` is the one import: every widget, `Theme`/`theme`, `draw`/`layout`
//! helpers, `anim`, and the ratatui + crossterm types you touch (`Rect`, `Buffer`, `Frame`,
//! `Event`, `KeyCode`…). The crates themselves are re-exported as [`ratatui_core`] and
//! [`crossterm`] so an app needs no version pins of its own.
//!
//! The dependency is [`ratatui-core`][ratatui], not the `ratatui` facade: upstream splits the
//! project so that applications use `ratatui` and widget libraries use `ratatui-core`. The types
//! are identical, so these widgets render into a `Frame` from a `ratatui` app unchanged.
//!
//! ```no_run
//! use tuile::prelude::*;
//! use std::time::Instant;
//!
//! struct Demo { on: SwitchState }
//!
//! impl App for Demo {
//! fn draw(&mut self, frame: &mut Frame, now: Instant) {
//! let area = center(frame.area(), 20, 3);
//! Switch::new().now(now).focused(true).render(area, frame.buffer_mut(), &mut self.on);
//! }
//! fn event(&mut self, ev: Event, now: Instant) -> Flow {
//! if let Event::Key(k) = &ev { if k.code == KeyCode::Char('q') { return Flow::Quit; } }
//! self.on.handle(&ev);
//! Flow::Continue
//! }
//! fn animating(&self, now: Instant) -> bool { self.on.animating(now) }
//! }
//! ```
//!
//! ## Reuse and customize
//!
//! Every widget is a **builder** (per-frame configuration, consumed by `render`) plus a
//! **`<Name>State`** (values, hover, tweens, scroll: plain `Clone + Debug` data with public
//! fields) that implements [`core::Interactive`]: `state.handle(&event)` → [`core::Outcome`].
//!
//! * **Theme.** [`theme::set_by_name`] / [`theme::set`] switch every widget at once;
//! `.theme(&Theme)` overrides one. Build palettes with [`theme::ThemeSpec::new`] and
//! [`theme::Theme::resolve`]; every derived role (`text_muted`, `border_blurred`, `cursor_bg`…)
//! is a public `Rgb` field.
//! * **Time.** Pass the frame `Instant` to `.now(..)`. Looping animations phase from
//! [`anim::EPOCH`]; `.elapsed(secs)` makes them deterministic for tests and screenshots.
//! * **Content.** Builders borrow (`&str`, `&[T]`, `&'static SpinnerDef`); nothing is copied
//! until it is drawn. Custom spinners: `SpinnerDef::new("name", ms, &frames)` or
//! `Spinner::frames(&frames, ms)`. Custom looks: [`draw::Border`] styles, [`theme::Variant`]s.
//! * **Extend.** New widgets use the same public primitives ([`draw`], [`layout`], [`anim`],
//! [`core::HitBox`], [`core::Focus`]); the contract is at <https://osyna.github.io/tuile/reference/widget-contract.html>.
//!
//! [ratatui]: https://ratatui.rs
// A terminal widget library has no reason to reach for unsafe; the crate contains none.
pub use crossterm;
pub use ratatui_core;
/// The `ratatui-crossterm` backend, for apps that build their own [`Terminal`].
///
/// [`Terminal`]: ratatui_core::terminal::Terminal
pub use ratatui_crossterm;
/// Everything an app usually needs.