返回 CodeWhale
lib.rs
1 //! Codewhale's terminal component library for ratatui.
2 //!
3 //! Reusable components from Codewhale's current terminal UI. Native palettes
4 //! and layout are available through [`Theme::tui`], [`NativeComposer`],
5 //! [`Workbar`] and [`TerminalShell`]. Desktop token themes and additional
6 //! compositions remain available.
7 //!
8 //! ```no_run
9 //! use codewhale_ratatui::{Paint, Theme, KeyHint, KeyHints};
10 //! # fn draw(frame: &mut ratatui::Frame) {
11 //! let theme = Theme::detect().tui();
12 //! let hints = KeyHints::new(vec![
13 //! KeyHint::new("↑↓", "move"),
14 //! KeyHint::new("Enter", "select"),
15 //! KeyHint::new("Esc", "cancel"),
16 //! ]);
17 //! frame.render_widget(hints.themed(&theme), frame.area());
18 //! # }
19 //! ```
20
21 pub mod color;
22 pub mod detect;
23 pub mod glyphs;
24 pub mod keys;
25 pub mod ocean;
26 pub mod tui_palettes;
27 pub use tui_palettes::{TuiGround, TuiInk, TuiPalette};
28 pub mod ombre;
29 pub mod osc11;
30 #[rustfmt::skip]
31 mod roles;
32 pub mod text;
33 pub mod theme;
34 /// The vendored Codewhale design tokens (`vendor/codewhale-design/tokens.rs`):
35 /// spacing, type and motion constants alongside the colors.
36 #[path = "../vendor/codewhale-design/tokens.rs"]
37 pub mod tokens;
38
39 mod components;
40 pub mod gallery;
41 pub mod testing;
42 pub mod whale;
43 pub mod whale_motion;
44
45 // Every component module is re-exported whole (see `components/mod.rs`), so
46 // a package that makes an item `pub` in its own file exports it from here
47 // without touching this file.
48 pub use components::*;
49 pub use ocean::{OceanColumn, OceanPhase, OceanRamp};
50 pub use ombre::{Ombre, OmbreDirection, WaterPalette};
51 pub use theme::{Caps, Ground, Role, Theme};
52 pub use whale::{Whale, WhaleState};
53
54 use ratatui::{buffer::Buffer, layout::Rect, widgets::Widget};
55
56 /// A component that paints with a [`Theme`].
57 ///
58 /// Components hold only what they show. The theme arrives when they paint,
59 /// so nothing caches a color and a theme change reaches every component on
60 /// the next frame.
61 pub trait Paint {
62 fn paint(&self, area: Rect, buf: &mut Buffer, theme: &Theme);
63
64 /// Rows this component wants at `width`.
65 fn height(&self, _width: u16, _theme: &Theme) -> u16 {
66 1
67 }
68
69 /// Wrap as a ratatui [`Widget`] for `frame.render_widget`.
70 ///
71 /// Render the returned wrapper by reference to reuse it across frames.
72 /// [`Themed::new`] also accepts a `dyn Paint` for heterogeneous collections.
73 fn themed<'a>(&'a self, theme: &'a Theme) -> Themed<'a, Self>
74 where
75 Self: Sized,
76 {
77 Themed::new(self, theme)
78 }
79 }
80
81 /// A component paired with the theme it paints with.
82 ///
83 /// The wrapper borrows both values. Rendering it by reference leaves the
84 /// component and wrapper available for another frame, without cloning either.
85 ///
86 /// ```no_run
87 /// use codewhale_ratatui::{NativeComposer, Paint, Theme};
88 /// # fn draw(frame: &mut ratatui::Frame<'_>) {
89 /// let theme = Theme::detect().tui();
90 /// let composer = NativeComposer::new("Review the changes");
91 /// let widget = composer.themed(&theme);
92 /// frame.render_widget(&widget, frame.area());
93 /// # }
94 /// ```
95 ///
96 /// Use [`Themed::new`] when the component's concrete type is erased:
97 ///
98 /// ```no_run
99 /// use codewhale_ratatui::{KeyHint, KeyHints, NativeComposer, Paint, Themed, Theme};
100 /// # fn draw(frame: &mut ratatui::Frame<'_>) {
101 /// let theme = Theme::detect().tui();
102 /// let composer = NativeComposer::new("Review the changes");
103 /// let hints = KeyHints::new(vec![KeyHint::new("Enter", "send")]);
104 /// let parts: [&dyn Paint; 2] = [&composer, &hints];
105 /// # let areas = [frame.area(), frame.area()];
106 /// for (part, area) in parts.into_iter().zip(areas) {
107 /// frame.render_widget(Themed::new(part, &theme), area);
108 /// }
109 /// # }
110 /// ```
111 #[must_use]
112 pub struct Themed<'a, P: Paint + ?Sized> {
113 component: &'a P,
114 theme: &'a Theme,
115 }
116
117 impl<'a, P: Paint + ?Sized> Themed<'a, P> {
118 /// Borrow a component and its theme, including a `dyn Paint` component.
119 pub const fn new(component: &'a P, theme: &'a Theme) -> Self {
120 Self { component, theme }
121 }
122 }
123
124 impl<P: Paint + ?Sized> Widget for Themed<'_, P> {
125 fn render(self, area: Rect, buf: &mut Buffer) {
126 (&self).render(area, buf);
127 }
128 }
129
130 impl<P: Paint + ?Sized> Widget for &Themed<'_, P> {
131 fn render(self, area: Rect, buf: &mut Buffer) {
132 self.component.paint(area, buf, self.theme);
133 }
134 }
135
135 lines RUST