vtcode_commons/ui_protocol/
style.rs1use std::sync::Arc;
4
5use anstyle::{Color as AnsiColorEnum, Effects, Style as AnsiStyle};
6
7use crate::ui_protocol::types::EditingMode;
8
9#[derive(Clone, Debug, Default, PartialEq)]
11pub struct InlineTextStyle {
12 pub color: Option<AnsiColorEnum>,
13 pub bg_color: Option<AnsiColorEnum>,
14 pub effects: Effects,
15}
16
17impl InlineTextStyle {
18 #[must_use]
19 pub fn with_color(mut self, color: Option<AnsiColorEnum>) -> Self {
20 self.color = color;
21 self
22 }
23
24 #[must_use]
25 pub fn with_bg_color(mut self, color: Option<AnsiColorEnum>) -> Self {
26 self.bg_color = color;
27 self
28 }
29
30 #[must_use]
31 pub fn merge_color(mut self, fallback: Option<AnsiColorEnum>) -> Self {
32 if self.color.is_none() {
33 self.color = fallback;
34 }
35 self
36 }
37
38 #[must_use]
39 pub fn merge_bg_color(mut self, fallback: Option<AnsiColorEnum>) -> Self {
40 if self.bg_color.is_none() {
41 self.bg_color = fallback;
42 }
43 self
44 }
45
46 #[must_use]
47 pub fn bold(mut self) -> Self {
48 self.effects |= Effects::BOLD;
49 self
50 }
51
52 #[must_use]
53 pub fn italic(mut self) -> Self {
54 self.effects |= Effects::ITALIC;
55 self
56 }
57
58 #[must_use]
59 pub fn underline(mut self) -> Self {
60 self.effects |= Effects::UNDERLINE;
61 self
62 }
63
64 #[must_use]
65 pub fn dim(mut self) -> Self {
66 self.effects |= Effects::DIMMED;
67 self
68 }
69
70 #[must_use]
71 pub fn to_ansi_style(&self, fallback: Option<AnsiColorEnum>) -> AnsiStyle {
72 let mut style = AnsiStyle::new();
73 if let Some(color) = self.color.or(fallback) {
74 style = style.fg_color(Some(color));
75 }
76 if let Some(bg) = self.bg_color {
77 style = style.bg_color(Some(bg));
78 }
79 if self.effects.contains(Effects::BOLD) {
80 style = style.bold();
81 }
82 if self.effects.contains(Effects::ITALIC) {
83 style = style.italic();
84 }
85 if self.effects.contains(Effects::UNDERLINE) {
86 style = style.underline();
87 }
88 if self.effects.contains(Effects::DIMMED) {
89 style = style.dimmed();
90 }
91 style
92 }
93}
94
95#[derive(Clone, Debug, Default)]
97pub struct InlineSegment {
98 pub text: String,
99 pub style: Arc<InlineTextStyle>,
100}
101
102#[derive(Clone, Debug, PartialEq, Eq)]
104pub enum InlineLinkTarget {
105 Url(String),
106}
107
108#[derive(Clone, Debug, PartialEq, Eq)]
110pub struct InlineLinkRange {
111 pub start: usize,
112 pub end: usize,
113 pub target: InlineLinkTarget,
114}
115
116#[derive(Clone, Debug, Default)]
118pub struct InlineTheme {
119 pub foreground: Option<AnsiColorEnum>,
120 pub background: Option<AnsiColorEnum>,
121 pub primary: Option<AnsiColorEnum>,
122 pub secondary: Option<AnsiColorEnum>,
123 pub tool_accent: Option<AnsiColorEnum>,
124 pub tool_body: Option<AnsiColorEnum>,
125 pub pty_body: Option<AnsiColorEnum>,
126}
127
128#[derive(Clone, Debug, Default, PartialEq, Eq)]
134pub enum InlineHeaderStatusTone {
135 #[default]
136 Ready,
137 Warning,
138 Error,
139}
140
141#[derive(Clone, Debug, Default, PartialEq, Eq)]
143pub struct InlineHeaderStatusBadge {
144 pub text: String,
145 pub tone: InlineHeaderStatusTone,
146}
147
148#[derive(Clone, Debug, Default, PartialEq)]
150pub struct InlineHeaderBadge {
151 pub text: String,
152 pub style: InlineTextStyle,
153 pub full_background: bool,
154}
155
156#[derive(Clone, Debug, Default, PartialEq, Eq)]
158pub struct InlineHeaderHighlight {
159 pub title: String,
160 pub lines: Vec<String>,
161}
162
163#[derive(Clone, Debug)]
165pub struct InlineHeaderContext {
166 pub app_name: String,
167 pub provider: String,
168 pub model: String,
169 pub context_window_size: Option<usize>,
170 pub version: String,
171 pub search_tools: Option<InlineHeaderStatusBadge>,
172 pub persistent_memory: Option<InlineHeaderStatusBadge>,
173 pub pr_review: Option<InlineHeaderStatusBadge>,
174 pub editor_context: Option<String>,
175 pub git: String,
176 pub mode: String,
177 pub reasoning: String,
178 pub reasoning_stage: Option<String>,
179 pub workspace_trust: String,
180 pub tools: String,
181 pub mcp: String,
182 pub highlights: Vec<InlineHeaderHighlight>,
183 pub subagent_badges: Vec<InlineHeaderBadge>,
184 pub editing_mode: EditingMode,
186 pub autonomous_mode: bool,
188}
189
190impl Default for InlineHeaderContext {
191 fn default() -> Self {
192 let version = env!("CARGO_PKG_VERSION").to_string();
193 Self {
194 app_name: "App".to_string(),
195 provider: "Provider: unavailable".to_string(),
196 model: "Model: unavailable".to_string(),
197 context_window_size: None,
198 version,
199 search_tools: None,
200 persistent_memory: None,
201 pr_review: None,
202 editor_context: None,
203 git: "git: unavailable".to_string(),
204 mode: "Inline session".to_string(),
205 reasoning: "Reasoning effort: unavailable".to_string(),
206 reasoning_stage: None,
207 workspace_trust: "Trust: unavailable".to_string(),
208 tools: "Tools: unavailable".to_string(),
209 mcp: "MCP: unavailable".to_string(),
210 highlights: Vec::new(),
211 subagent_badges: Vec::new(),
212 editing_mode: EditingMode::default(),
213 autonomous_mode: false,
214 }
215 }
216}
217
218fn convert_ansi_color(color: AnsiColorEnum) -> Option<AnsiColorEnum> {
223 Some(match color {
224 AnsiColorEnum::Ansi(ansi) => AnsiColorEnum::Ansi(ansi),
225 AnsiColorEnum::Ansi256(value) => AnsiColorEnum::Ansi256(value),
226 AnsiColorEnum::Rgb(rgb) => AnsiColorEnum::Rgb(rgb),
227 })
228}
229
230fn convert_style_color(style: &AnsiStyle) -> Option<AnsiColorEnum> {
231 style.get_fg_color().and_then(convert_ansi_color)
232}
233
234fn convert_style_bg_color(style: &AnsiStyle) -> Option<AnsiColorEnum> {
235 style.get_bg_color().and_then(convert_ansi_color)
236}
237
238pub fn convert_style(style: AnsiStyle) -> InlineTextStyle {
240 InlineTextStyle {
241 color: convert_style_color(&style),
242 bg_color: convert_style_bg_color(&style),
243 effects: style.get_effects(),
244 }
245}
246
247pub fn theme_from_color_fields(
249 foreground: AnsiColorEnum,
250 background: AnsiColorEnum,
251 primary: AnsiStyle,
252 secondary: AnsiStyle,
253 tool: AnsiStyle,
254 tool_detail: AnsiStyle,
255 pty_output: AnsiStyle,
256) -> InlineTheme {
257 InlineTheme {
258 foreground: convert_ansi_color(foreground),
259 background: convert_ansi_color(background),
260 primary: convert_style_color(&primary),
261 secondary: convert_style_color(&secondary),
262 tool_accent: convert_style_color(&tool),
263 tool_body: convert_style_color(&tool_detail),
264 pty_body: convert_style_color(&pty_output),
265 }
266}