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
110
111
112
113
114
115
//! `tuika` — a small composable terminal UI toolkit over
//! [`ratatui`](https://docs.rs/ratatui).
//!
//! `tuika` adds the pieces ratatui leaves to you — a flexbox-style layout
//! solver, anchored overlays, focus/input-ownership, an alternate-screen host,
//! and a set of components (text, boxes, scroll, select, spinner, progress) —
//! while letting ratatui keep ownership of the cell buffer and its diff against
//! the terminal. It builds against `ratatui-core` (and `ratatui-crossterm` for
//! the backend) directly rather than the `ratatui` umbrella — it renders none of
//! ratatui's own widgets — so `ratatui-widgets` and `ratatui-macros` stay out of
//! its dependency tree. It otherwise depends only on `crossterm`, `textwrap`,
//! `unicode-segmentation`, and `unicode-width`.
//!
//! It was extracted from the [yolop](https://github.com/everruns/yolop) coding
//! agent, whose full-screen renderer is built on it, but it knows nothing about
//! any host application.
//!
//! # Model
//!
//! - **Views** ([`view::View`]) are ephemeral, rebuilt from application state
//! each frame; ratatui diffs the resulting cell buffer, so this is cheap.
//! - **State** that must persist across frames ([`components::ScrollState`],
//! [`components::SelectState`], [`focus::FocusRegistry`]) lives in the host,
//! in the `StatefulWidget` idiom.
//! - **Layout** is a flexbox subset ([`layout`]); **overlays** ([`overlay`])
//! anchor over the base tree; the **host** ([`host`]) owns the alternate
//! screen, translates crossterm input, and composites the frame.
//!
//! # Finding things
//!
//! The crate root re-exports the **framework**: the view model, layout,
//! events, styling, and the host seam — the types you compose *with*. The
//! widgets themselves live in [`components`], and everything that talks to the
//! terminal outside the cell grid (clipboard, hyperlinks, images, native
//! progress, capability detection) lives in [`term`].
//!
//! For application code that wants the common surface in one line, glob-import
//! [`prelude`]:
//!
//! ```
//! use tuika::prelude::*;
//!
//! let screen = element(Flex::column().fixed(1, element(Text::raw("hello"))));
//! # let _ = screen;
//! ```
//!
//! # Extending
//!
//! Add a component by implementing [`view::View`] in a new module under
//! [`components`]. No registration step; containers accept any boxed `View`.
//!
//! Existing ratatui widgets should normally be wrapped in
//! [`RatatuiView`](interop::RatatuiView), which preserves Tuika clipping without
//! exposing the frame buffer. [`TerminalSession`] and [`runner::Runner`] are
//! optional host-side lifecycle helpers; with `feature = "async"`,
//! [`runner::AsyncRunner`] is the same loop for hosts that already
//! run on Tokio.
// On docs.rs (nightly, `--cfg docsrs`) annotate feature-gated items with the
// feature that enables them. A no-op on stable builds. `doc_auto_cfg` was
// merged into `doc_cfg` in 1.92 and removed as a name, so gating on the old
// one is a hard rustdoc error on current nightly — which is what silently
// left 0.4.0 undocumented on docs.rs.
// The framework spine: the types a host composes with on essentially every
// frame. Widgets are not here on purpose — they live in `components`, and
// `prelude` is the one-line import that brings both. Anything reachable only
// through its module (`themes::by_name`, `probe::RectProbe`, `term::clipboard`)
// is deliberately not flattened: a shallow path is worth something only if the
// name earns it.
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use Surface;
pub use ;