chore(deps/ui): upgrade ratatui 0.29 and refresh gradients

Acceptance Criteria:
- Workspace builds against ratatui 0.29, crossterm 0.28.1, and tui-textarea 0.7 with palette support enabled
- Chat header context and usage gauges render with refreshed tailwind gradients
- Header layout uses the Flex API to balance top-row metadata across window widths

Test Notes:
- cargo test -p owlen-tui
This commit is contained in:
2025-10-25 00:26:01 +02:00
parent 6e12bb3acb
commit 40e42c8918
6 changed files with 61 additions and 76 deletions

View File

@@ -34,9 +34,9 @@ futures = "0.3"
futures-util = "0.3" futures-util = "0.3"
# TUI framework # TUI framework
ratatui = "0.28" ratatui = { version = "0.29", features = ["palette"] }
crossterm = "0.28" crossterm = "0.28.1"
tui-textarea = "0.6" tui-textarea = "0.7"
# HTTP client and JSON handling # HTTP client and JSON handling
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] } reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }

View File

@@ -17,7 +17,7 @@ serde_json = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
tokio = { workspace = true } tokio = { workspace = true }
unicode-segmentation = "1.11" unicode-segmentation = "1.11"
unicode-width = "0.1" unicode-width = "0.2"
uuid = { workspace = true } uuid = { workspace = true }
textwrap = { workspace = true } textwrap = { workspace = true }
futures = { workspace = true } futures = { workspace = true }

View File

@@ -7,4 +7,4 @@ description = "Lightweight markdown to ratatui::Text renderer for OWLEN"
[dependencies] [dependencies]
ratatui = { workspace = true } ratatui = { workspace = true }
unicode-width = "0.1" unicode-width = "0.2"

View File

@@ -17,7 +17,7 @@ ratatui = { workspace = true }
crossterm = { workspace = true } crossterm = { workspace = true }
tui-textarea = { workspace = true } tui-textarea = { workspace = true }
textwrap = { workspace = true } textwrap = { workspace = true }
unicode-width = "0.1" unicode-width = "0.2"
unicode-segmentation = "1.11" unicode-segmentation = "1.11"
async-trait = "0.1" async-trait = "0.1"
globset = "0.4" globset = "0.4"

View File

@@ -1,5 +1,5 @@
use owlen_core::theme::Theme; use owlen_core::theme::Theme;
use ratatui::style::Color; use ratatui::style::{Color, palette::tailwind};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct GlassPalette { pub struct GlassPalette {
@@ -18,40 +18,40 @@ impl GlassPalette {
let luminance = color_luminance(theme.background); let luminance = color_luminance(theme.background);
if luminance < 0.5 { if luminance < 0.5 {
Self { Self {
active: Color::Rgb(26, 28, 40), active: tailwind::SLATE.c900,
inactive: Color::Rgb(18, 20, 30), inactive: tailwind::SLATE.c800,
highlight: Color::Rgb(32, 35, 48), highlight: tailwind::SLATE.c800,
track: Color::Rgb(35, 38, 50), track: tailwind::SLATE.c700,
label: Color::Rgb(241, 245, 249), label: tailwind::SLATE.c100,
shadow: Color::Rgb(8, 9, 16), shadow: tailwind::SLATE.c950,
context_stops: [ context_stops: [
Color::Rgb(56, 189, 248), tailwind::SKY.c400,
Color::Rgb(250, 204, 21), tailwind::AMBER.c300,
Color::Rgb(248, 113, 113), tailwind::ROSE.c400,
], ],
usage_stops: [ usage_stops: [
Color::Rgb(34, 211, 238), tailwind::CYAN.c400,
Color::Rgb(250, 204, 21), tailwind::AMBER.c300,
Color::Rgb(248, 113, 113), tailwind::ROSE.c400,
], ],
} }
} else { } else {
Self { Self {
active: Color::Rgb(242, 247, 255), active: tailwind::ZINC.c100,
inactive: Color::Rgb(229, 235, 250), inactive: tailwind::ZINC.c200,
highlight: Color::Rgb(224, 230, 248), highlight: tailwind::ZINC.c200,
track: Color::Rgb(203, 210, 230), track: tailwind::ZINC.c300,
label: Color::Rgb(31, 41, 55), label: tailwind::SLATE.c700,
shadow: Color::Rgb(200, 205, 220), shadow: tailwind::ZINC.c300,
context_stops: [ context_stops: [
Color::Rgb(59, 130, 246), tailwind::BLUE.c500,
Color::Rgb(234, 179, 8), tailwind::AMBER.c400,
Color::Rgb(239, 68, 68), tailwind::ROSE.c500,
], ],
usage_stops: [ usage_stops: [
Color::Rgb(20, 184, 166), tailwind::TEAL.c400,
Color::Rgb(245, 158, 11), tailwind::AMBER.c400,
Color::Rgb(239, 68, 68), tailwind::ROSE.c500,
], ],
} }
} }
@@ -60,7 +60,7 @@ impl GlassPalette {
pub fn gradient_color(stops: &[Color; 3], t: f64) -> Color { pub fn gradient_color(stops: &[Color; 3], t: f64) -> Color {
let clamped = t.clamp(0.0, 1.0); let clamped = t.clamp(0.0, 1.0);
let segments = stops.len() - 1; let segments = stops.len().saturating_sub(1).max(1);
let scaled = clamped * segments as f64; let scaled = clamped * segments as f64;
let index = scaled.floor() as usize; let index = scaled.floor() as usize;
let frac = scaled - index as f64; let frac = scaled - index as f64;

View File

@@ -1,7 +1,7 @@
use log::Level; use log::Level;
use pathdiff::diff_paths; use pathdiff::diff_paths;
use ratatui::Frame; use ratatui::Frame;
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use ratatui::layout::{Alignment, Constraint, Direction, Flex, Layout, Rect};
use ratatui::style::{Color, Modifier, Style}; use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span}; use ratatui::text::{Line, Span};
use ratatui::widgets::block::Padding; use ratatui::widgets::block::Padding;
@@ -421,9 +421,8 @@ fn render_chat_header(
constraints.push(Constraint::Min(2)); constraints.push(Constraint::Min(2));
} }
let rows = Layout::default() let rows = Layout::vertical(constraints)
.direction(Direction::Vertical) .flex(Flex::Start)
.constraints(constraints)
.split(highlight_area); .split(highlight_area);
render_header_top(frame, rows[0], app, palette, theme); render_header_top(frame, rows[0], app, palette, theme);
@@ -444,9 +443,8 @@ fn render_header_top(
return; return;
} }
let columns = Layout::default() let columns = Layout::horizontal([Constraint::Percentage(60), Constraint::Percentage(40)])
.direction(Direction::Horizontal) .flex(Flex::SpaceBetween)
.constraints([Constraint::Percentage(60), Constraint::Percentage(40)])
.split(area); .split(area);
let mut left_spans = Vec::new(); let mut left_spans = Vec::new();
@@ -558,9 +556,8 @@ fn render_header_bars(
return; return;
} }
let columns = Layout::default() let columns = Layout::horizontal([Constraint::Percentage(45), Constraint::Percentage(55)])
.direction(Direction::Horizontal) .flex(Flex::SpaceBetween)
.constraints([Constraint::Percentage(45), Constraint::Percentage(55)])
.split(area); .split(area);
render_context_column(frame, columns[0], app, palette, theme); render_context_column(frame, columns[0], app, palette, theme);
@@ -823,14 +820,12 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) {
let header_height = header_height.max(3).min(frame_area.height); let header_height = header_height.max(3).min(frame_area.height);
let segments = if frame_area.height <= header_height { let segments = if frame_area.height <= header_height {
Layout::default() Layout::vertical([Constraint::Length(frame_area.height)])
.direction(Direction::Vertical) .flex(Flex::Start)
.constraints([Constraint::Length(frame_area.height)])
.split(frame_area) .split(frame_area)
} else { } else {
Layout::default() Layout::vertical([Constraint::Length(header_height), Constraint::Min(1)])
.direction(Direction::Vertical) .flex(Flex::Start)
.constraints([Constraint::Length(header_height), Constraint::Min(1)])
.split(frame_area) .split(frame_area)
}; };
@@ -874,17 +869,15 @@ pub fn render_chat(frame: &mut Frame<'_>, app: &mut ChatApp) {
} else { } else {
let max_sidebar = content_area.width.saturating_sub(30).max(10); let max_sidebar = content_area.width.saturating_sub(30).max(10);
let sidebar_width = app.file_panel_width().min(max_sidebar).max(10); let sidebar_width = app.file_panel_width().min(max_sidebar).max(10);
let segments = Layout::default() let segments = Layout::horizontal([Constraint::Length(sidebar_width), Constraint::Min(30)])
.direction(Direction::Horizontal) .flex(Flex::Start)
.constraints([Constraint::Length(sidebar_width), Constraint::Min(30)])
.split(content_area); .split(content_area);
(Some(segments[0]), segments[1]) (Some(segments[0]), segments[1])
}; };
let (chat_area, code_area) = if app.should_show_code_view() { let (chat_area, code_area) = if app.should_show_code_view() {
let segments = Layout::default() let segments = Layout::horizontal([Constraint::Percentage(65), Constraint::Percentage(35)])
.direction(Direction::Horizontal) .flex(Flex::Start)
.constraints([Constraint::Percentage(65), Constraint::Percentage(35)])
.split(main_area); .split(main_area);
(segments[0], Some(segments[1])) (segments[0], Some(segments[1]))
} else { } else {
@@ -5653,28 +5646,20 @@ fn render_symbol_search(frame: &mut Frame<'_>, app: &mut ChatApp) {
} }
fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect { fn centered_rect(percent_x: u16, percent_y: u16, area: Rect) -> Rect {
let vertical = Layout::default() let vertical = Layout::vertical([
.direction(Direction::Vertical)
.constraints(
[
Constraint::Percentage((100 - percent_y) / 2), Constraint::Percentage((100 - percent_y) / 2),
Constraint::Percentage(percent_y), Constraint::Percentage(percent_y),
Constraint::Percentage((100 - percent_y) / 2), Constraint::Percentage((100 - percent_y) / 2),
] ])
.as_ref(), .flex(Flex::Center)
)
.split(area); .split(area);
Layout::default() Layout::horizontal([
.direction(Direction::Horizontal)
.constraints(
[
Constraint::Percentage((100 - percent_x) / 2), Constraint::Percentage((100 - percent_x) / 2),
Constraint::Percentage(percent_x), Constraint::Percentage(percent_x),
Constraint::Percentage((100 - percent_x) / 2), Constraint::Percentage((100 - percent_x) / 2),
] ])
.as_ref(), .flex(Flex::Center)
)
.split(vertical[1])[1] .split(vertical[1])[1]
} }