feat(ui): add glass modals and theme preview
AC:\n- Theme, help, command, and model modals share the glass chrome.\n- Theme selector shows a live preview for the highlighted palette.\n- Updated docs and screenshots explain the refreshed cockpit.\n\nTests:\n- cargo test -p owlen-tui
This commit is contained in:
@@ -23,7 +23,7 @@ This project is currently in **alpha** and under active development. Core featur
|
||||
|
||||

|
||||
|
||||
The OWLEN interface features a clean, multi-panel layout with vim-inspired navigation. See more screenshots in the [`images/`](images/) directory.
|
||||
The refreshed chrome introduces a cockpit-style header with live gradient gauges for context and cloud usage, plus glassy panels that keep vim-inspired navigation easy to follow. See more screenshots in the [`images/`](images/) directory.
|
||||
|
||||
## Features
|
||||
|
||||
@@ -32,6 +32,7 @@ The OWLEN interface features a clean, multi-panel layout with vim-inspired navig
|
||||
- **Advanced Text Editing**: Multi-line input, history, and clipboard support.
|
||||
- **Session Management**: Save, load, and manage conversations.
|
||||
- **Code Side Panel**: Switch to code mode (`:mode code`) and open files inline with `:open <path>` for LLM-assisted coding.
|
||||
- **Cockpit Header**: Gradient context and cloud usage bars with live quota bands and provider fallbacks.
|
||||
- **Theming System**: 10 built-in themes and support for custom themes.
|
||||
- **Modular Architecture**: Extensible provider system orchestrated by the new `ProviderManager`, ready for additional MCP-backed providers.
|
||||
- **Dual-Source Model Picker**: Merge local and cloud catalogues with real-time availability badges powered by the background health worker.
|
||||
|
||||
@@ -115,6 +115,7 @@ pub struct ContextUsage {
|
||||
pub(crate) struct LayoutSnapshot {
|
||||
pub(crate) frame: Rect,
|
||||
pub(crate) content: Rect,
|
||||
pub(crate) header_panel: Option<Rect>,
|
||||
pub(crate) file_panel: Option<Rect>,
|
||||
pub(crate) chat_panel: Option<Rect>,
|
||||
pub(crate) thinking_panel: Option<Rect>,
|
||||
@@ -131,6 +132,7 @@ impl LayoutSnapshot {
|
||||
Self {
|
||||
frame,
|
||||
content,
|
||||
header_panel: None,
|
||||
file_panel: None,
|
||||
chat_panel: None,
|
||||
thinking_panel: None,
|
||||
@@ -155,6 +157,11 @@ impl LayoutSnapshot {
|
||||
return Some(UiRegion::ModelInfo);
|
||||
}
|
||||
}
|
||||
if let Some(rect) = self.header_panel {
|
||||
if Self::contains(rect, column, row) {
|
||||
return Some(UiRegion::Header);
|
||||
}
|
||||
}
|
||||
if let Some(rect) = self.code_panel {
|
||||
if Self::contains(rect, column, row) {
|
||||
return Some(UiRegion::Code);
|
||||
@@ -213,6 +220,7 @@ impl Default for LayoutSnapshot {
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum UiRegion {
|
||||
Header,
|
||||
Frame,
|
||||
Content,
|
||||
FileTree,
|
||||
@@ -7992,7 +8000,8 @@ impl ChatApp {
|
||||
| UiRegion::Status
|
||||
| UiRegion::Chat
|
||||
| UiRegion::Content
|
||||
| UiRegion::Frame => {
|
||||
| UiRegion::Frame
|
||||
| UiRegion::Header => {
|
||||
if self.focus_panel(FocusedPanel::Chat) {
|
||||
self.auto_scroll
|
||||
.on_user_scroll(amount, self.viewport_height);
|
||||
@@ -8036,7 +8045,8 @@ impl ChatApp {
|
||||
| UiRegion::Status
|
||||
| UiRegion::Chat
|
||||
| UiRegion::Content
|
||||
| UiRegion::Frame => {
|
||||
| UiRegion::Frame
|
||||
| UiRegion::Header => {
|
||||
self.focus_panel(FocusedPanel::Chat);
|
||||
self.set_input_mode(InputMode::Normal);
|
||||
}
|
||||
|
||||
144
crates/owlen-tui/src/glass.rs
Normal file
144
crates/owlen-tui/src/glass.rs
Normal file
@@ -0,0 +1,144 @@
|
||||
use owlen_core::theme::Theme;
|
||||
use ratatui::style::Color;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct GlassPalette {
|
||||
pub active: Color,
|
||||
pub inactive: Color,
|
||||
pub highlight: Color,
|
||||
pub track: Color,
|
||||
pub label: Color,
|
||||
pub shadow: Color,
|
||||
pub context_stops: [Color; 3],
|
||||
pub usage_stops: [Color; 3],
|
||||
}
|
||||
|
||||
impl GlassPalette {
|
||||
pub fn for_theme(theme: &Theme) -> Self {
|
||||
let luminance = color_luminance(theme.background);
|
||||
if luminance < 0.5 {
|
||||
Self {
|
||||
active: Color::Rgb(26, 28, 40),
|
||||
inactive: Color::Rgb(18, 20, 30),
|
||||
highlight: Color::Rgb(32, 35, 48),
|
||||
track: Color::Rgb(35, 38, 50),
|
||||
label: Color::Rgb(241, 245, 249),
|
||||
shadow: Color::Rgb(8, 9, 16),
|
||||
context_stops: [
|
||||
Color::Rgb(56, 189, 248),
|
||||
Color::Rgb(250, 204, 21),
|
||||
Color::Rgb(248, 113, 113),
|
||||
],
|
||||
usage_stops: [
|
||||
Color::Rgb(34, 211, 238),
|
||||
Color::Rgb(250, 204, 21),
|
||||
Color::Rgb(248, 113, 113),
|
||||
],
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
active: Color::Rgb(242, 247, 255),
|
||||
inactive: Color::Rgb(229, 235, 250),
|
||||
highlight: Color::Rgb(224, 230, 248),
|
||||
track: Color::Rgb(203, 210, 230),
|
||||
label: Color::Rgb(31, 41, 55),
|
||||
shadow: Color::Rgb(200, 205, 220),
|
||||
context_stops: [
|
||||
Color::Rgb(59, 130, 246),
|
||||
Color::Rgb(234, 179, 8),
|
||||
Color::Rgb(239, 68, 68),
|
||||
],
|
||||
usage_stops: [
|
||||
Color::Rgb(20, 184, 166),
|
||||
Color::Rgb(245, 158, 11),
|
||||
Color::Rgb(239, 68, 68),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn gradient_color(stops: &[Color; 3], t: f64) -> Color {
|
||||
let clamped = t.clamp(0.0, 1.0);
|
||||
let segments = stops.len() - 1;
|
||||
let scaled = clamped * segments as f64;
|
||||
let index = scaled.floor() as usize;
|
||||
let frac = scaled - index as f64;
|
||||
let start = stops[index.min(stops.len() - 1)];
|
||||
let end = stops[(index + 1).min(stops.len() - 1)];
|
||||
let (sr, sg, sb) = color_to_rgb(start);
|
||||
let (er, eg, eb) = color_to_rgb(end);
|
||||
let mix = |a: u8, b: u8| -> u8 { (a as f64 + (b as f64 - a as f64) * frac).round() as u8 };
|
||||
Color::Rgb(mix(sr, er), mix(sg, eg), mix(sb, eb))
|
||||
}
|
||||
|
||||
fn color_luminance(color: Color) -> f64 {
|
||||
let (r, g, b) = color_to_rgb(color);
|
||||
let r = r as f64 / 255.0;
|
||||
let g = g as f64 / 255.0;
|
||||
let b = b as f64 / 255.0;
|
||||
0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||
}
|
||||
|
||||
fn color_to_rgb(color: Color) -> (u8, u8, u8) {
|
||||
match color {
|
||||
Color::Reset => (0, 0, 0),
|
||||
Color::Black => (0, 0, 0),
|
||||
Color::Red => (205, 49, 49),
|
||||
Color::Green => (49, 205, 49),
|
||||
Color::Yellow => (205, 198, 49),
|
||||
Color::Blue => (49, 49, 205),
|
||||
Color::Magenta => (205, 49, 205),
|
||||
Color::Cyan => (49, 205, 205),
|
||||
Color::Gray => (170, 170, 170),
|
||||
Color::DarkGray => (100, 100, 100),
|
||||
Color::LightRed => (255, 128, 128),
|
||||
Color::LightGreen => (144, 238, 144),
|
||||
Color::LightYellow => (255, 255, 170),
|
||||
Color::LightBlue => (173, 216, 230),
|
||||
Color::LightMagenta => (255, 182, 255),
|
||||
Color::LightCyan => (175, 238, 238),
|
||||
Color::White => (255, 255, 255),
|
||||
Color::Rgb(r, g, b) => (r, g, b),
|
||||
Color::Indexed(idx) => indexed_to_rgb(idx),
|
||||
}
|
||||
}
|
||||
|
||||
fn indexed_to_rgb(idx: u8) -> (u8, u8, u8) {
|
||||
match idx {
|
||||
0 => (0, 0, 0),
|
||||
1 => (128, 0, 0),
|
||||
2 => (0, 128, 0),
|
||||
3 => (128, 128, 0),
|
||||
4 => (0, 0, 128),
|
||||
5 => (128, 0, 128),
|
||||
6 => (0, 128, 128),
|
||||
7 => (192, 192, 192),
|
||||
8 => (128, 128, 128),
|
||||
9 => (255, 0, 0),
|
||||
10 => (0, 255, 0),
|
||||
11 => (255, 255, 0),
|
||||
12 => (92, 92, 255),
|
||||
13 => (255, 0, 255),
|
||||
14 => (0, 255, 255),
|
||||
15 => (255, 255, 255),
|
||||
16..=231 => {
|
||||
let idx = idx - 16;
|
||||
let r = idx / 36;
|
||||
let g = (idx % 36) / 6;
|
||||
let b = idx % 6;
|
||||
let convert = |component: u8| {
|
||||
if component == 0 {
|
||||
0
|
||||
} else {
|
||||
component.saturating_mul(40).saturating_add(55)
|
||||
}
|
||||
};
|
||||
(convert(r), convert(g), convert(b))
|
||||
}
|
||||
232..=255 => {
|
||||
let shade = 8 + (idx - 232) * 10;
|
||||
(shade, shade, shade)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ pub mod code_app;
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
pub mod events;
|
||||
pub(crate) mod glass;
|
||||
pub mod highlight;
|
||||
pub mod model_info_panel;
|
||||
pub mod slash;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ use ratatui::{
|
||||
layout::{Constraint, Direction, Layout, Rect},
|
||||
style::{Color, Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph},
|
||||
widgets::{Block, Borders, Clear, List, ListItem, ListState, Paragraph, block::Padding},
|
||||
};
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
@@ -16,6 +16,7 @@ use crate::chat_app::{
|
||||
ChatApp, HighlightMask, ModelAvailabilityState, ModelScope, ModelSearchInfo,
|
||||
ModelSelectorItemKind,
|
||||
};
|
||||
use crate::glass::GlassPalette;
|
||||
|
||||
/// Filtering modes for the model picker popup.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -29,6 +30,7 @@ pub enum FilterMode {
|
||||
|
||||
pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
let theme = app.theme();
|
||||
let palette = GlassPalette::for_theme(theme);
|
||||
let area = frame.area();
|
||||
if area.width == 0 || area.height == 0 {
|
||||
return;
|
||||
@@ -62,17 +64,33 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
}
|
||||
|
||||
let popup_area = Rect::new(x, y, width, height);
|
||||
if popup_area.width > 2 && popup_area.height > 2 {
|
||||
let shadow_area = Rect::new(
|
||||
popup_area.x.saturating_add(1),
|
||||
popup_area.y.saturating_add(1),
|
||||
popup_area.width.saturating_sub(1),
|
||||
popup_area.height.saturating_sub(1),
|
||||
);
|
||||
if shadow_area.width > 0 && shadow_area.height > 0 {
|
||||
frame.render_widget(
|
||||
Block::default().style(Style::default().bg(palette.shadow)),
|
||||
shadow_area,
|
||||
);
|
||||
}
|
||||
}
|
||||
frame.render_widget(Clear, popup_area);
|
||||
|
||||
let mut title_spans = vec![
|
||||
Span::styled(
|
||||
" Model Selector ",
|
||||
Style::default().fg(theme.info).add_modifier(Modifier::BOLD),
|
||||
Style::default()
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled(
|
||||
format!("· Provider: {}", app.selected_provider),
|
||||
Style::default()
|
||||
.fg(theme.placeholder)
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM),
|
||||
),
|
||||
];
|
||||
@@ -83,9 +101,10 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
|
||||
let block = Block::default()
|
||||
.title(Line::from(title_spans))
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(theme.info))
|
||||
.style(Style::default().bg(theme.background).fg(theme.text));
|
||||
.title_style(Style::default().fg(palette.label))
|
||||
.borders(Borders::NONE)
|
||||
.padding(Padding::new(2, 2, 1, 1))
|
||||
.style(Style::default().bg(palette.active).fg(palette.label));
|
||||
|
||||
let inner = block.inner(popup_area);
|
||||
frame.render_widget(block, popup_area);
|
||||
@@ -104,10 +123,10 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
|
||||
let matches = app.visible_model_count();
|
||||
let search_prefix = Style::default()
|
||||
.fg(theme.placeholder)
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM);
|
||||
let bracket_style = Style::default()
|
||||
.fg(theme.placeholder)
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM);
|
||||
let caret_style = if search_active {
|
||||
Style::default()
|
||||
@@ -115,7 +134,7 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
.add_modifier(Modifier::BOLD)
|
||||
} else {
|
||||
Style::default()
|
||||
.fg(theme.placeholder)
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM)
|
||||
};
|
||||
|
||||
@@ -135,8 +154,8 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
search_spans.push(Span::styled(
|
||||
"Type to search…",
|
||||
Style::default()
|
||||
.fg(theme.placeholder)
|
||||
.add_modifier(Modifier::DIM),
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM | Modifier::ITALIC),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -153,35 +172,37 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
suffix_label,
|
||||
if matches == 1 { "" } else { "s" }
|
||||
),
|
||||
Style::default().fg(theme.placeholder),
|
||||
Style::default()
|
||||
.fg(palette.label)
|
||||
.add_modifier(Modifier::DIM),
|
||||
));
|
||||
|
||||
let search_line = Line::from(search_spans);
|
||||
|
||||
let instruction_line = if search_active {
|
||||
Line::from(vec![
|
||||
Span::styled("Backspace", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Backspace", Style::default().fg(palette.label)),
|
||||
Span::raw(": delete "),
|
||||
Span::styled("Ctrl+U", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Ctrl+U", Style::default().fg(palette.label)),
|
||||
Span::raw(": clear "),
|
||||
Span::styled("Enter", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Enter", Style::default().fg(palette.label)),
|
||||
Span::raw(": select "),
|
||||
Span::styled("Esc", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Esc", Style::default().fg(palette.label)),
|
||||
Span::raw(": close"),
|
||||
])
|
||||
} else {
|
||||
Line::from(vec![
|
||||
Span::styled("Enter", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Enter", Style::default().fg(palette.label)),
|
||||
Span::raw(": select "),
|
||||
Span::styled("Space", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Space", Style::default().fg(palette.label)),
|
||||
Span::raw(": toggle provider "),
|
||||
Span::styled("Esc", Style::default().fg(theme.placeholder)),
|
||||
Span::styled("Esc", Style::default().fg(palette.label)),
|
||||
Span::raw(": close"),
|
||||
])
|
||||
};
|
||||
|
||||
let search_paragraph = Paragraph::new(vec![search_line, instruction_line])
|
||||
.style(Style::default().bg(theme.background).fg(theme.text));
|
||||
.style(Style::default().bg(palette.highlight).fg(palette.label));
|
||||
frame.render_widget(search_paragraph, layout[0]);
|
||||
|
||||
let highlight_style = Style::default()
|
||||
@@ -236,7 +257,7 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
));
|
||||
|
||||
let line = clip_line_to_width(Line::from(spans), max_line_width);
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(theme.background)));
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(palette.active)));
|
||||
}
|
||||
ModelSelectorItemKind::Scope { label, status, .. } => {
|
||||
let (style, icon) = scope_status_style(*status, theme);
|
||||
@@ -248,7 +269,7 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
]),
|
||||
max_line_width,
|
||||
);
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(theme.background)));
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(palette.active)));
|
||||
}
|
||||
ModelSelectorItemKind::Model { model_index, .. } => {
|
||||
let mut lines: Vec<Line<'static>> = Vec::new();
|
||||
@@ -286,7 +307,7 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
max_line_width,
|
||||
));
|
||||
}
|
||||
items.push(ListItem::new(lines).style(Style::default().bg(theme.background)));
|
||||
items.push(ListItem::new(lines).style(Style::default().bg(palette.active)));
|
||||
}
|
||||
ModelSelectorItemKind::Empty {
|
||||
message, status, ..
|
||||
@@ -299,7 +320,7 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
let mut spans = vec![Span::styled(icon, style), Span::raw(" ")];
|
||||
spans.push(Span::styled(format!(" {}", msg), style));
|
||||
let line = clip_line_to_width(Line::from(spans), max_line_width);
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(theme.background)));
|
||||
items.push(ListItem::new(vec![line]).style(Style::default().bg(palette.active)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +332,8 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
.fg(theme.selection_fg)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.highlight_symbol(" ");
|
||||
.highlight_symbol(" ")
|
||||
.style(Style::default().bg(palette.active).fg(palette.label));
|
||||
|
||||
let mut state = ListState::default();
|
||||
state.select(app.selected_model_item());
|
||||
@@ -325,10 +347,10 @@ pub fn render_model_picker(frame: &mut Frame<'_>, app: &ChatApp) {
|
||||
|
||||
let footer = Paragraph::new(Line::from(Span::styled(
|
||||
footer_text,
|
||||
Style::default().fg(theme.placeholder),
|
||||
Style::default().fg(palette.label),
|
||||
)))
|
||||
.alignment(ratatui::layout::Alignment::Center)
|
||||
.style(Style::default().bg(theme.background).fg(theme.placeholder));
|
||||
.style(Style::default().bg(palette.highlight).fg(palette.label));
|
||||
frame.render_widget(footer, layout[2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -121,5 +121,6 @@ If you are experiencing performance issues, you can try the following:
|
||||
|
||||
- **Reduce context size:** A smaller context size will result in faster responses from the LLM.
|
||||
- **Use a less resource-intensive model:** Some models are faster but less capable than others.
|
||||
- **Watch the header gauges:** The cockpit header now shows live context usage and cloud quota bands—if either bar turns amber or red, trim the prompt or switch providers before retrying.
|
||||
|
||||
If you are still having trouble, please [open an issue](https://github.com/Owlibou/owlen/issues) on our GitHub repository.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 64 KiB |
@@ -1,30 +1,30 @@
|
||||
name = "default_dark"
|
||||
text = "white"
|
||||
background = "black"
|
||||
focused_panel_border = "lightmagenta"
|
||||
unfocused_panel_border = "#5f1487"
|
||||
user_message_role = "lightblue"
|
||||
assistant_message_role = "yellow"
|
||||
tool_output = "gray"
|
||||
thinking_panel_title = "lightmagenta"
|
||||
command_bar_background = "black"
|
||||
status_background = "black"
|
||||
mode_normal = "lightblue"
|
||||
mode_editing = "lightgreen"
|
||||
mode_model_selection = "lightyellow"
|
||||
mode_provider_selection = "lightcyan"
|
||||
mode_help = "lightmagenta"
|
||||
mode_visual = "magenta"
|
||||
mode_command = "yellow"
|
||||
selection_bg = "lightblue"
|
||||
selection_fg = "black"
|
||||
cursor = "magenta"
|
||||
code_block_background = "#191919"
|
||||
code_block_border = "lightmagenta"
|
||||
code_block_text = "white"
|
||||
code_block_keyword = "yellow"
|
||||
code_block_string = "lightgreen"
|
||||
code_block_comment = "gray"
|
||||
placeholder = "darkgray"
|
||||
error = "red"
|
||||
info = "lightgreen"
|
||||
text = "#e2e8f0"
|
||||
background = "#020617"
|
||||
focused_panel_border = "#7dd3fc"
|
||||
unfocused_panel_border = "#1e293b"
|
||||
user_message_role = "#38bdf8"
|
||||
assistant_message_role = "#fbbf24"
|
||||
tool_output = "#94a3b8"
|
||||
thinking_panel_title = "#a855f7"
|
||||
command_bar_background = "#0f172a"
|
||||
status_background = "#111827"
|
||||
mode_normal = "#38bdf8"
|
||||
mode_editing = "#34d399"
|
||||
mode_model_selection = "#fbbf24"
|
||||
mode_provider_selection = "#22d3ee"
|
||||
mode_help = "#a855f7"
|
||||
mode_visual = "#f472b6"
|
||||
mode_command = "#facc15"
|
||||
selection_bg = "#1d4ed8"
|
||||
selection_fg = "#f8fafc"
|
||||
cursor = "#f472b6"
|
||||
code_block_background = "#111827"
|
||||
code_block_border = "#2563eb"
|
||||
code_block_text = "#e2e8f0"
|
||||
code_block_keyword = "#fbbf24"
|
||||
code_block_string = "#34d399"
|
||||
code_block_comment = "#64748b"
|
||||
placeholder = "#64748b"
|
||||
error = "#f87171"
|
||||
info = "#38bdf8"
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
name = "default_light"
|
||||
text = "black"
|
||||
background = "white"
|
||||
focused_panel_border = "#4a90e2"
|
||||
unfocused_panel_border = "#dddddd"
|
||||
user_message_role = "#0055a4"
|
||||
assistant_message_role = "#8e44ad"
|
||||
tool_output = "gray"
|
||||
thinking_panel_title = "#8e44ad"
|
||||
command_bar_background = "white"
|
||||
status_background = "white"
|
||||
mode_normal = "#0055a4"
|
||||
mode_editing = "#2e8b57"
|
||||
mode_model_selection = "#b58900"
|
||||
mode_provider_selection = "#008b8b"
|
||||
mode_help = "#8e44ad"
|
||||
mode_visual = "#8e44ad"
|
||||
mode_command = "#b58900"
|
||||
selection_bg = "#a4c8f0"
|
||||
selection_fg = "black"
|
||||
cursor = "#d95f02"
|
||||
code_block_background = "#f5f5f5"
|
||||
code_block_border = "#009688"
|
||||
code_block_text = "black"
|
||||
code_block_keyword = "#b58900"
|
||||
code_block_string = "#388e3c"
|
||||
code_block_comment = "#90a4ae"
|
||||
placeholder = "gray"
|
||||
error = "#c0392b"
|
||||
info = "green"
|
||||
text = "#0f172a"
|
||||
background = "#f8fafc"
|
||||
focused_panel_border = "#2563eb"
|
||||
unfocused_panel_border = "#c7d2fe"
|
||||
user_message_role = "#2563eb"
|
||||
assistant_message_role = "#9333ea"
|
||||
tool_output = "#64748b"
|
||||
thinking_panel_title = "#7c3aed"
|
||||
command_bar_background = "#e2e8f0"
|
||||
status_background = "#e0e7ff"
|
||||
mode_normal = "#2563eb"
|
||||
mode_editing = "#0ea5e9"
|
||||
mode_model_selection = "#facc15"
|
||||
mode_provider_selection = "#0ea5e9"
|
||||
mode_help = "#7c3aed"
|
||||
mode_visual = "#7c3aed"
|
||||
mode_command = "#f97316"
|
||||
selection_bg = "#bfdbfe"
|
||||
selection_fg = "#0f172a"
|
||||
cursor = "#f97316"
|
||||
code_block_background = "#e2e8f0"
|
||||
code_block_border = "#2563eb"
|
||||
code_block_text = "#0f172a"
|
||||
code_block_keyword = "#b45309"
|
||||
code_block_string = "#15803d"
|
||||
code_block_comment = "#94a3b8"
|
||||
placeholder = "#64748b"
|
||||
error = "#dc2626"
|
||||
info = "#2563eb"
|
||||
|
||||
Reference in New Issue
Block a user