agent_core/tui/themes/
macros.rs

1// Macros for theme definition and registration
2//
3// These macros reduce boilerplate when defining themes by generating
4// the standard theme function from a color palette.
5
6/// Macro to define a theme from a color palette.
7/// Generates the `pub fn theme() -> Theme` function.
8///
9/// # Example
10/// ```ignore
11/// define_theme! {
12///     bg: (0x28, 0x2a, 0x36),
13///     bg_alt: (0x1E, 0x20, 0x29),
14///     fg: (0xf8, 0xf8, 0xf2),
15///     fg_alt: (0x62, 0x72, 0xa4),
16///     base4: (0x56, 0x57, 0x61),
17///     red: (0xff, 0x55, 0x55),
18///     orange: (0xff, 0xb8, 0x6c),
19///     yellow: (0xf1, 0xfa, 0x8c),
20///     green: (0x50, 0xfa, 0x7b),
21///     cyan: (0x8b, 0xe9, 0xfd),
22///     blue: (0x61, 0xbf, 0xff),
23///     magenta: (0xff, 0x79, 0xc6),
24///     violet: (0xbd, 0x93, 0xf9)
25/// }
26/// ```
27#[macro_export]
28macro_rules! define_theme {
29    (
30        bg: ($bg_r:expr, $bg_g:expr, $bg_b:expr),
31        bg_alt: ($bg_alt_r:expr, $bg_alt_g:expr, $bg_alt_b:expr),
32        fg: ($fg_r:expr, $fg_g:expr, $fg_b:expr),
33        fg_alt: ($fg_alt_r:expr, $fg_alt_g:expr, $fg_alt_b:expr),
34        base4: ($base4_r:expr, $base4_g:expr, $base4_b:expr),
35        red: ($red_r:expr, $red_g:expr, $red_b:expr),
36        orange: ($orange_r:expr, $orange_g:expr, $orange_b:expr),
37        yellow: ($yellow_r:expr, $yellow_g:expr, $yellow_b:expr),
38        green: ($green_r:expr, $green_g:expr, $green_b:expr),
39        cyan: ($cyan_r:expr, $cyan_g:expr, $cyan_b:expr),
40        blue: ($blue_r:expr, $blue_g:expr, $blue_b:expr),
41        magenta: ($magenta_r:expr, $magenta_g:expr, $magenta_b:expr),
42        violet: ($violet_r:expr, $violet_g:expr, $violet_b:expr)
43    ) => {
44        pub fn theme() -> Theme {
45            use ratatui::style::{Color, Modifier, Style};
46
47            const BG: Color = Color::Rgb($bg_r, $bg_g, $bg_b);
48            const BG_ALT: Color = Color::Rgb($bg_alt_r, $bg_alt_g, $bg_alt_b);
49            const FG: Color = Color::Rgb($fg_r, $fg_g, $fg_b);
50            const FG_ALT: Color = Color::Rgb($fg_alt_r, $fg_alt_g, $fg_alt_b);
51            const BASE4: Color = Color::Rgb($base4_r, $base4_g, $base4_b);
52            const RED: Color = Color::Rgb($red_r, $red_g, $red_b);
53            const ORANGE: Color = Color::Rgb($orange_r, $orange_g, $orange_b);
54            const YELLOW: Color = Color::Rgb($yellow_r, $yellow_g, $yellow_b);
55            const GREEN: Color = Color::Rgb($green_r, $green_g, $green_b);
56            const CYAN: Color = Color::Rgb($cyan_r, $cyan_g, $cyan_b);
57            const BLUE: Color = Color::Rgb($blue_r, $blue_g, $blue_b);
58            const MAGENTA: Color = Color::Rgb($magenta_r, $magenta_g, $magenta_b);
59            const VIOLET: Color = Color::Rgb($violet_r, $violet_g, $violet_b);
60
61            Theme {
62                background: Style::default().bg(BG),
63                text: Style::default().fg(FG),
64                border: Style::default().fg(BASE4),
65                border_focused: Style::default().fg(CYAN),
66                title_separator: Style::default().fg(BASE4),
67                title_indicator_connected: Style::default().fg(GREEN),
68                title_indicator_disconnected: Style::default().fg(RED),
69                title_text: Style::default().fg(FG),
70                user_prefix: Style::default().fg(BLUE),
71                system_prefix: Style::default().fg(YELLOW),
72                assistant_prefix: Style::default().fg(MAGENTA),
73                timestamp: Style::default().fg(FG_ALT),
74                cursor: Style::default().fg(GREEN),
75                bold: Modifier::BOLD,
76                italic: Modifier::ITALIC,
77                strikethrough: Modifier::CROSSED_OUT,
78                inline_code: Style::default().fg(YELLOW),
79                link_text: Style::default().fg(CYAN),
80                link_url: Style::default().fg(VIOLET),
81                heading_1: Style::default().fg(CYAN).add_modifier(Modifier::BOLD),
82                heading_2: Style::default().fg(GREEN).add_modifier(Modifier::BOLD),
83                heading_3: Style::default().fg(YELLOW).add_modifier(Modifier::BOLD),
84                heading_4: Style::default().fg(FG).add_modifier(Modifier::BOLD),
85                code_block: Style::default().fg(FG).bg(BG_ALT),
86                table_header: Style::default().fg(CYAN).add_modifier(Modifier::BOLD),
87                table_cell: Style::default().fg(FG),
88                table_border: Style::default().fg(BASE4),
89                tool_header: Style::default().fg(ORANGE),
90                tool_executing: Style::default().fg(FG_ALT).add_modifier(Modifier::ITALIC),
91                tool_completed: Style::default().fg(GREEN),
92                tool_failed: Style::default().fg(RED),
93                input_border: Style::default().fg(BASE4),
94                input_text: Style::default().fg(FG),
95                prompt: Style::default().fg(FG),
96                throbber_label: Style::default().fg(FG_ALT),
97                throbber_spinner: Style::default().fg(CYAN),
98                status_help: Style::default().fg(FG_ALT),
99                status_model: Style::default().fg(FG_ALT),
100                popup_border: Style::default().fg(BASE4),
101                popup_header: Style::default().fg(FG_ALT),
102                popup_item: Style::default().fg(FG),
103                popup_item_selected: Style::default().fg(CYAN).add_modifier(Modifier::BOLD),
104                popup_item_desc: Style::default().fg(FG_ALT),
105                popup_item_desc_selected: Style::default().fg(FG),
106                popup_selected_bg: Style::default().bg(BG_ALT),
107                popup_empty: Style::default().fg(FG_ALT).add_modifier(Modifier::ITALIC),
108                help_text: Style::default().fg(FG_ALT),
109                muted_text: Style::default().fg(FG_ALT),
110                focused_text: Style::default().fg(CYAN).add_modifier(Modifier::BOLD),
111                focus_indicator: Style::default().fg(YELLOW),
112                selected: Style::default().fg(GREEN),
113                unselected: Style::default().fg(FG_ALT),
114                button_confirm: Style::default().fg(GREEN),
115                button_confirm_focused: Style::default().fg(GREEN).add_modifier(Modifier::BOLD),
116                button_cancel: Style::default().fg(RED),
117                button_cancel_focused: Style::default().fg(RED).add_modifier(Modifier::BOLD),
118                warning: Style::default().fg(YELLOW),
119                category: Style::default().fg(MAGENTA),
120                resource: Style::default().fg(CYAN),
121            }
122        }
123    };
124}
125
126/// Macro to register all themes with their metadata.
127/// Generates mod declarations, THEMES array, and get_theme function.
128///
129/// # Example
130/// ```ignore
131/// register_themes! {
132///     dracula => { name: "dracula", display: "Dracula", dark: true },
133///     one => { name: "one", display: "One", dark: true },
134/// }
135/// ```
136#[macro_export]
137macro_rules! register_themes {
138    (
139        $(
140            $module:ident => {
141                name: $name:literal,
142                display: $display:literal,
143                dark: $is_dark:expr
144            }
145        ),* $(,)?
146    ) => {
147        // Generate mod declarations
148        $(mod $module;)*
149
150        /// Theme metadata for display in picker
151        pub struct ThemeInfo {
152            pub name: &'static str,
153            pub display_name: &'static str,
154            pub is_dark: bool,
155        }
156
157        /// All available themes
158        pub const THEMES: &[ThemeInfo] = &[
159            $(
160                ThemeInfo {
161                    name: $name,
162                    display_name: $display,
163                    is_dark: $is_dark,
164                },
165            )*
166        ];
167
168        /// Get a theme by name
169        pub fn get_theme(name: &str) -> Option<Theme> {
170            match name {
171                $($name => Some($module::theme()),)*
172                _ => None,
173            }
174        }
175
176        /// List all available theme names
177        pub fn list_themes() -> Vec<&'static str> {
178            THEMES.iter().map(|t| t.name).collect()
179        }
180
181        /// Get default theme name
182        pub fn default_theme_name() -> &'static str {
183            "henna"
184        }
185    };
186}
187
188// Re-export macros at module level for use within the crate
189pub use define_theme;
190pub use register_themes;