feat(ui): add show_cursor_outside_insert setting and Unicode‑aware wrapping; introduce grayscale‑high‑contrast theme

- Added `show_cursor_outside_insert` (default false) to `UiSettings` and synced it from config.
- Cursor rendering now follows `cursor_should_be_visible`, allowing visibility outside insert mode based on the new setting.
- Replaced `textwrap::wrap` with `wrap_unicode`, which uses Unicode break properties for proper CJK and emoji handling.
- Added `grayscale-high-contrast.toml` theme, registered it in theme loading, and updated README and tests.
This commit is contained in:
2025-10-12 15:47:22 +02:00
parent 0bd560b408
commit ae9c3af096
6 changed files with 182 additions and 40 deletions

View File

@@ -347,6 +347,10 @@ pub fn built_in_themes() -> HashMap<String, Theme> {
"ansi_basic",
include_str!("../../../themes/ansi-basic.toml"),
),
(
"grayscale-high-contrast",
include_str!("../../../themes/grayscale-high-contrast.toml"),
),
("gruvbox", include_str!("../../../themes/gruvbox.toml")),
("dracula", include_str!("../../../themes/dracula.toml")),
("solarized", include_str!("../../../themes/solarized.toml")),
@@ -397,6 +401,7 @@ fn get_fallback_theme(name: &str) -> Option<Theme> {
"monokai" => Some(monokai()),
"material-dark" => Some(material_dark()),
"material-light" => Some(material_light()),
"grayscale-high-contrast" => Some(grayscale_high_contrast()),
_ => None,
}
}
@@ -831,6 +836,49 @@ fn material_light() -> Theme {
}
}
/// Grayscale high-contrast theme
fn grayscale_high_contrast() -> Theme {
Theme {
name: "grayscale_high_contrast".to_string(),
text: Color::Rgb(247, 247, 247),
background: Color::Black,
focused_panel_border: Color::White,
unfocused_panel_border: Color::Rgb(76, 76, 76),
user_message_role: Color::Rgb(240, 240, 240),
assistant_message_role: Color::Rgb(214, 214, 214),
tool_output: Color::Rgb(189, 189, 189),
thinking_panel_title: Color::Rgb(224, 224, 224),
command_bar_background: Color::Black,
status_background: Color::Rgb(15, 15, 15),
mode_normal: Color::White,
mode_editing: Color::Rgb(230, 230, 230),
mode_model_selection: Color::Rgb(204, 204, 204),
mode_provider_selection: Color::Rgb(179, 179, 179),
mode_help: Color::Rgb(153, 153, 153),
mode_visual: Color::Rgb(242, 242, 242),
mode_command: Color::Rgb(208, 208, 208),
selection_bg: Color::Rgb(240, 240, 240),
selection_fg: Color::Black,
cursor: Color::White,
placeholder: Color::Rgb(122, 122, 122),
error: Color::White,
info: Color::Rgb(200, 200, 200),
agent_thought: Color::Rgb(230, 230, 230),
agent_action: Color::Rgb(204, 204, 204),
agent_action_input: Color::Rgb(176, 176, 176),
agent_observation: Color::Rgb(153, 153, 153),
agent_final_answer: Color::White,
agent_badge_running_fg: Color::Black,
agent_badge_running_bg: Color::Rgb(247, 247, 247),
agent_badge_idle_fg: Color::Black,
agent_badge_idle_bg: Color::Rgb(189, 189, 189),
operating_chat_fg: Color::Black,
operating_chat_bg: Color::Rgb(242, 242, 242),
operating_code_fg: Color::Black,
operating_code_bg: Color::Rgb(191, 191, 191),
}
}
// Helper functions for color serialization/deserialization
fn deserialize_color<'de, D>(deserializer: D) -> Result<Color, D::Error>
@@ -924,5 +972,6 @@ mod tests {
assert!(themes.contains_key("default_dark"));
assert!(themes.contains_key("gruvbox"));
assert!(themes.contains_key("dracula"));
assert!(themes.contains_key("grayscale-high-contrast"));
}
}