| 1 |
# alloy_tui |
| 2 |
|
| 3 |
Alloy's design system for [ratatui](https://ratatui.rs): themed widgets, a |
| 4 |
reserved keymap, and a focus ring, driven by |
| 5 |
[makeover](https://crates.io/crates/makeover) themes. |
| 6 |
|
| 7 |
Extracted from the Alloy console so other terminal UIs can share one look |
| 8 |
rather than each re-deriving colors from a palette. |
| 9 |
|
| 10 |
## What it gives you |
| 11 |
|
| 12 |
- **`Theme`** — a makeover `.toml` theme resolved into ratatui `Color`/`Style`. |
| 13 |
Two Alloy-specific tokens (`border-subtle`, `border-strong`) are derived |
| 14 |
locally so theme files stay minimal and cross-app compatible. |
| 15 |
- **Widgets** — `AlloyBlock`, `AlloyList`, `AlloyTabs`, `AlloyStatusBar`, |
| 16 |
`AlloyModal`, `AlloyLog`, each taking a `&Theme`. |
| 17 |
- **Forms** — `AlloyForm` over `AlloyField` rows, `AlloyPicker` for the |
| 18 |
filterable overlay a closed vocabulary opens, plus a display-only |
| 19 |
`AlloyTable`. One field widget carrying a `FieldKind` value cell rather than |
| 20 |
one widget per value type: the widgets own no value and do no validation, so |
| 21 |
they differ only in how the cell paints. `TextField` is the caret buffer a |
| 22 |
field shows in edit mode. |
| 23 |
- **`keys`** — the reserved keymap every Alloy TUI agrees on, so the same keys |
| 24 |
mean the same thing across tools. |
| 25 |
- **`focus`** — a focus ring, the piece ratatui deliberately leaves to the app. |
| 26 |
|
| 27 |
## Color is information |
| 28 |
|
| 29 |
Chrome is tinted greyscale. Accent color lives on text, carried by `Severity`, |
| 30 |
so a colored thing on screen means something rather than decorating. Widgets |
| 31 |
enforce this; you get it by using them. |
| 32 |
|
| 33 |
## Usage |
| 34 |
|
| 35 |
```rust |
| 36 |
use alloy_tui::{AlloyStatusBar, Theme, hint}; |
| 37 |
|
| 38 |
// Resolve a makeover theme into ratatui colors. |
| 39 |
let dirs = vec![(makeover::bundled_themes_dir().unwrap(), false)]; |
| 40 |
let colors = makeover::load_theme(&dirs, "akari-night").unwrap(); |
| 41 |
let theme = Theme::from_theme(&colors).unwrap(); |
| 42 |
|
| 43 |
// Widgets take &Theme. |
| 44 |
let footer = AlloyStatusBar::new(&theme, [hint("Tab", "focus"), hint("q", "quit")]); |
| 45 |
``` |
| 46 |
|
| 47 |
There is deliberately no built-in fallback palette. A missing or malformed |
| 48 |
theme is an error you surface, not something papered over by rendering in |
| 49 |
colors that exist nowhere in the theme files. |
| 50 |
|
| 51 |
## License |
| 52 |
|
| 53 |
MIT. |
| 54 |
|
| 55 |
Split out of the [Alloy](https://git.sr.ht/~maxmj/alloy) repository, which is |
| 56 |
GPLv3-or-later. This crate is permissive on purpose: it is the reusable design |
| 57 |
system, not the application, and other terminal UIs should be able to adopt the |
| 58 |
look without inheriting copyleft. |
| 59 |
|