feat(tui): add role‑based dimmed message border style and color utilities
- Introduce `message_border_style` to render message borders with a dimmed version of the role color. - Add `dim_color` and `color_to_rgb` helpers for color manipulation. - Update role styling to use `theme.mode_command` for system messages. - Adjust card rendering functions to accept role and apply the new border style.
This commit is contained in:
@@ -15,7 +15,7 @@ use owlen_core::{
|
||||
ui::{AppState, AutoScroll, FocusedPanel, InputMode, RoleLabelDisplay},
|
||||
};
|
||||
use pathdiff::diff_paths;
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use textwrap::{Options, WordSeparator, wrap};
|
||||
use tokio::{
|
||||
@@ -1806,11 +1806,24 @@ impl ChatApp {
|
||||
match role {
|
||||
Role::User => Style::default().fg(theme.user_message_role),
|
||||
Role::Assistant => Style::default().fg(theme.assistant_message_role),
|
||||
Role::System => Style::default().fg(theme.unfocused_panel_border),
|
||||
Role::System => Style::default().fg(theme.mode_command),
|
||||
Role::Tool => Style::default().fg(theme.info),
|
||||
}
|
||||
}
|
||||
|
||||
fn message_border_style(theme: &Theme, role: &Role) -> Style {
|
||||
let base_color = match role {
|
||||
Role::User => theme.user_message_role,
|
||||
Role::Assistant => theme.assistant_message_role,
|
||||
Role::System => theme.mode_command,
|
||||
Role::Tool => theme.info,
|
||||
};
|
||||
|
||||
let dimmed = Self::dim_color(base_color);
|
||||
|
||||
Style::default().fg(dimmed).add_modifier(Modifier::DIM)
|
||||
}
|
||||
|
||||
fn content_style(theme: &Theme, role: &Role) -> Style {
|
||||
if matches!(role, Role::Tool) {
|
||||
Style::default().fg(theme.tool_output)
|
||||
@@ -1819,6 +1832,46 @@ impl ChatApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn dim_color(color: Color) -> Color {
|
||||
match color {
|
||||
Color::Reset | Color::Indexed(_) => color,
|
||||
_ => {
|
||||
if let Some((r, g, b)) = Self::color_to_rgb(color) {
|
||||
let dim_component = |component: u8| -> u8 {
|
||||
let value = ((component as u16) * 2) / 5;
|
||||
value as u8
|
||||
};
|
||||
Color::Rgb(dim_component(r), dim_component(g), dim_component(b))
|
||||
} else {
|
||||
color
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn color_to_rgb(color: Color) -> Option<(u8, u8, u8)> {
|
||||
match color {
|
||||
Color::Black => Some((0, 0, 0)),
|
||||
Color::Red => Some((205, 0, 0)),
|
||||
Color::Green => Some((0, 205, 0)),
|
||||
Color::Yellow => Some((205, 205, 0)),
|
||||
Color::Blue => Some((0, 0, 205)),
|
||||
Color::Magenta => Some((205, 0, 205)),
|
||||
Color::Cyan => Some((0, 205, 205)),
|
||||
Color::Gray => Some((170, 170, 170)),
|
||||
Color::DarkGray => Some((85, 85, 85)),
|
||||
Color::LightRed => Some((255, 85, 85)),
|
||||
Color::LightGreen => Some((85, 255, 85)),
|
||||
Color::LightYellow => Some((255, 255, 85)),
|
||||
Color::LightBlue => Some((85, 85, 255)),
|
||||
Color::LightMagenta => Some((255, 85, 255)),
|
||||
Color::LightCyan => Some((85, 255, 255)),
|
||||
Color::White => Some((255, 255, 255)),
|
||||
Color::Rgb(r, g, b) => Some((r, g, b)),
|
||||
Color::Reset | Color::Indexed(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn message_content_hash(role: &Role, content: &str, tool_signature: &str) -> u64 {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
role.to_string().hash(&mut hasher);
|
||||
@@ -2128,10 +2181,10 @@ impl ChatApp {
|
||||
}
|
||||
|
||||
for line in lines {
|
||||
card_lines.push(Self::wrap_card_body_line(line, inner_width, theme));
|
||||
card_lines.push(Self::wrap_card_body_line(line, inner_width, theme, role));
|
||||
}
|
||||
|
||||
card_lines.push(Self::build_card_footer(card_width, theme));
|
||||
card_lines.push(Self::build_card_footer(card_width, theme, role));
|
||||
card_lines
|
||||
}
|
||||
|
||||
@@ -2142,7 +2195,7 @@ impl ChatApp {
|
||||
card_width: usize,
|
||||
theme: &Theme,
|
||||
) -> Line<'static> {
|
||||
let border_style = Style::default().fg(theme.unfocused_panel_border);
|
||||
let border_style = Self::message_border_style(theme, role);
|
||||
let role_style = Self::role_style(theme, role).add_modifier(Modifier::BOLD);
|
||||
let meta_style = Style::default().fg(theme.placeholder);
|
||||
let tool_style = Style::default()
|
||||
@@ -2199,8 +2252,8 @@ impl ChatApp {
|
||||
Line::from(spans)
|
||||
}
|
||||
|
||||
fn build_card_footer(card_width: usize, theme: &Theme) -> Line<'static> {
|
||||
let border_style = Style::default().fg(theme.unfocused_panel_border);
|
||||
fn build_card_footer(card_width: usize, theme: &Theme, role: &Role) -> Line<'static> {
|
||||
let border_style = Self::message_border_style(theme, role);
|
||||
let mut spans = Vec::new();
|
||||
spans.push(Span::styled("└", border_style));
|
||||
let horizontal = card_width.saturating_sub(2);
|
||||
@@ -2215,8 +2268,9 @@ impl ChatApp {
|
||||
line: Line<'static>,
|
||||
inner_width: usize,
|
||||
theme: &Theme,
|
||||
role: &Role,
|
||||
) -> Line<'static> {
|
||||
let border_style = Style::default().fg(theme.unfocused_panel_border);
|
||||
let border_style = Self::message_border_style(theme, role);
|
||||
let mut spans = Vec::new();
|
||||
spans.push(Span::styled("│ ", border_style));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user