Skip to main content

vtcode_tui/config/
mod.rs

1pub mod constants {
2    pub mod defaults;
3    pub mod ui;
4}
5
6pub mod loader;
7pub mod types;
8
9use anyhow::Result;
10use serde::{Deserialize, Serialize};
11
12pub use types::{
13    ReasoningEffortLevel, SystemPromptMode, ToolDocumentationMode, UiSurfacePreference,
14    VerbosityLevel,
15};
16
17#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
18#[serde(rename_all = "snake_case")]
19pub enum ToolOutputMode {
20    #[default]
21    Compact,
22    Full,
23}
24
25#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
26#[serde(rename_all = "snake_case")]
27pub enum UiDisplayMode {
28    Full,
29    #[default]
30    Minimal,
31    Focused,
32}
33
34#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
35#[serde(rename_all = "snake_case")]
36pub enum NotificationDeliveryMode {
37    Terminal,
38    #[default]
39    Hybrid,
40    Desktop,
41}
42
43#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
44#[serde(rename_all = "snake_case")]
45pub enum AgentClientProtocolZedWorkspaceTrustMode {
46    FullAuto,
47    #[default]
48    ToolsPolicy,
49}
50
51#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
52#[serde(rename_all = "lowercase")]
53pub enum ToolPolicy {
54    Allow,
55    #[default]
56    Prompt,
57    Deny,
58}
59
60#[derive(Debug, Clone, Deserialize, Serialize)]
61pub struct KeyboardProtocolConfig {
62    pub enabled: bool,
63    pub mode: String,
64    pub disambiguate_escape_codes: bool,
65    pub report_event_types: bool,
66    pub report_alternate_keys: bool,
67    pub report_all_keys: bool,
68}
69
70impl Default for KeyboardProtocolConfig {
71    fn default() -> Self {
72        Self {
73            enabled: true,
74            mode: "default".to_string(),
75            disambiguate_escape_codes: true,
76            report_event_types: true,
77            report_alternate_keys: true,
78            report_all_keys: false,
79        }
80    }
81}
82
83impl KeyboardProtocolConfig {
84    pub fn validate(&self) -> Result<()> {
85        match self.mode.as_str() {
86            "default" | "full" | "minimal" | "custom" => Ok(()),
87            _ => anyhow::bail!(
88                "Invalid keyboard protocol mode '{}'. Must be: default, full, minimal, or custom",
89                self.mode
90            ),
91        }
92    }
93}
94
95#[derive(Debug, Clone, Deserialize, Serialize)]
96pub struct UiNotificationsConfig {
97    pub enabled: bool,
98    pub delivery_mode: NotificationDeliveryMode,
99    pub suppress_when_focused: bool,
100    pub tool_failure: bool,
101    pub error: bool,
102    pub completion: bool,
103    pub hitl: bool,
104    pub tool_success: bool,
105}
106
107impl Default for UiNotificationsConfig {
108    fn default() -> Self {
109        Self {
110            enabled: true,
111            delivery_mode: NotificationDeliveryMode::Hybrid,
112            suppress_when_focused: true,
113            tool_failure: true,
114            error: true,
115            completion: true,
116            hitl: true,
117            tool_success: false,
118        }
119    }
120}
121
122#[derive(Debug, Clone, Deserialize, Serialize)]
123pub struct UiConfig {
124    pub tool_output_mode: ToolOutputMode,
125    pub allow_tool_ansi: bool,
126    pub inline_viewport_rows: u16,
127    pub keyboard_protocol: KeyboardProtocolConfig,
128    pub display_mode: UiDisplayMode,
129    pub show_sidebar: bool,
130    pub dim_completed_todos: bool,
131    pub message_block_spacing: bool,
132    pub show_turn_timer: bool,
133    pub notifications: UiNotificationsConfig,
134}
135
136impl Default for UiConfig {
137    fn default() -> Self {
138        Self {
139            tool_output_mode: ToolOutputMode::Compact,
140            allow_tool_ansi: false,
141            inline_viewport_rows: constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS,
142            keyboard_protocol: KeyboardProtocolConfig::default(),
143            display_mode: UiDisplayMode::Minimal,
144            show_sidebar: true,
145            dim_completed_todos: true,
146            message_block_spacing: false,
147            show_turn_timer: false,
148            notifications: UiNotificationsConfig::default(),
149        }
150    }
151}
152
153#[derive(Debug, Clone, Deserialize, Serialize, Default)]
154pub struct AgentCheckpointingConfig {
155    pub enabled: bool,
156}
157
158#[derive(Debug, Clone, Deserialize, Serialize, Default)]
159pub struct AgentSmallModelConfig {
160    pub enabled: bool,
161}
162
163#[derive(Debug, Clone, Deserialize, Serialize, Default)]
164pub struct AgentVibeCodingConfig {
165    pub enabled: bool,
166}
167
168#[derive(Debug, Clone, Deserialize, Serialize)]
169pub struct AgentConfig {
170    pub default_model: String,
171    pub theme: String,
172    pub reasoning_effort: ReasoningEffortLevel,
173    pub system_prompt_mode: SystemPromptMode,
174    pub tool_documentation_mode: ToolDocumentationMode,
175    pub verbosity: VerbosityLevel,
176    pub todo_planning_mode: bool,
177    pub checkpointing: AgentCheckpointingConfig,
178    pub small_model: AgentSmallModelConfig,
179    pub vibe_coding: AgentVibeCodingConfig,
180    pub max_conversation_turns: usize,
181}
182
183impl Default for AgentConfig {
184    fn default() -> Self {
185        Self {
186            default_model: "gpt-5-mini".to_string(),
187            theme: "default".to_string(),
188            reasoning_effort: ReasoningEffortLevel::Medium,
189            system_prompt_mode: SystemPromptMode::Default,
190            tool_documentation_mode: ToolDocumentationMode::Full,
191            verbosity: VerbosityLevel::Medium,
192            todo_planning_mode: false,
193            checkpointing: AgentCheckpointingConfig::default(),
194            small_model: AgentSmallModelConfig::default(),
195            vibe_coding: AgentVibeCodingConfig::default(),
196            max_conversation_turns: 100,
197        }
198    }
199}
200
201#[derive(Debug, Clone, Deserialize, Serialize)]
202pub struct PromptCacheConfig {
203    pub enabled: bool,
204}
205
206impl Default for PromptCacheConfig {
207    fn default() -> Self {
208        Self { enabled: true }
209    }
210}
211
212#[derive(Debug, Clone, Deserialize, Serialize)]
213pub struct McpConfig {
214    pub enabled: bool,
215}
216
217impl Default for McpConfig {
218    fn default() -> Self {
219        Self { enabled: true }
220    }
221}
222
223#[derive(Debug, Clone, Deserialize, Serialize)]
224pub struct AcpZedConfig {
225    pub workspace_trust: AgentClientProtocolZedWorkspaceTrustMode,
226}
227
228impl Default for AcpZedConfig {
229    fn default() -> Self {
230        Self {
231            workspace_trust: AgentClientProtocolZedWorkspaceTrustMode::ToolsPolicy,
232        }
233    }
234}
235
236#[derive(Debug, Clone, Deserialize, Serialize, Default)]
237pub struct AcpConfig {
238    pub zed: AcpZedConfig,
239}
240
241#[derive(Debug, Clone, Deserialize, Serialize, Default)]
242pub struct FullAutoConfig {
243    pub enabled: bool,
244}
245
246#[derive(Debug, Clone, Deserialize, Serialize, Default)]
247pub struct AutomationConfig {
248    pub full_auto: FullAutoConfig,
249}
250
251#[derive(Debug, Clone, Deserialize, Serialize)]
252pub struct ToolsConfig {
253    pub default_policy: ToolPolicy,
254}
255
256impl Default for ToolsConfig {
257    fn default() -> Self {
258        Self {
259            default_policy: ToolPolicy::Prompt,
260        }
261    }
262}
263
264#[derive(Debug, Clone, Deserialize, Serialize)]
265pub struct SecurityConfig {
266    pub human_in_the_loop: bool,
267}
268
269impl Default for SecurityConfig {
270    fn default() -> Self {
271        Self {
272            human_in_the_loop: true,
273        }
274    }
275}
276
277#[derive(Debug, Clone, Deserialize, Serialize)]
278pub struct ContextConfig {
279    pub max_context_tokens: usize,
280    pub trim_to_percent: u8,
281}
282
283impl Default for ContextConfig {
284    fn default() -> Self {
285        Self {
286            max_context_tokens: 128_000,
287            trim_to_percent: 80,
288        }
289    }
290}
291
292#[derive(Debug, Clone, Deserialize, Serialize)]
293pub struct SyntaxHighlightingConfig {
294    pub enabled: bool,
295    pub theme: String,
296    pub cache_themes: bool,
297    pub max_file_size_mb: usize,
298    pub enabled_languages: Vec<String>,
299    pub highlight_timeout_ms: u64,
300}
301
302impl Default for SyntaxHighlightingConfig {
303    fn default() -> Self {
304        Self {
305            enabled: true,
306            theme: "base16-ocean.dark".to_string(),
307            cache_themes: true,
308            max_file_size_mb: 5,
309            enabled_languages: vec![
310                "rust".to_string(),
311                "python".to_string(),
312                "javascript".to_string(),
313                "typescript".to_string(),
314                "go".to_string(),
315                "bash".to_string(),
316                "json".to_string(),
317                "yaml".to_string(),
318                "toml".to_string(),
319                "markdown".to_string(),
320            ],
321            highlight_timeout_ms: 300,
322        }
323    }
324}
325
326#[derive(Debug, Clone, Deserialize, Serialize)]
327pub struct PtyConfig {
328    pub enabled: bool,
329    pub default_rows: u16,
330    pub default_cols: u16,
331    pub command_timeout_seconds: u64,
332}
333
334impl Default for PtyConfig {
335    fn default() -> Self {
336        Self {
337            enabled: true,
338            default_rows: 24,
339            default_cols: 80,
340            command_timeout_seconds: 300,
341        }
342    }
343}
344
345/// Convert KeyboardProtocolConfig to crossterm keyboard enhancement flags.
346pub fn keyboard_protocol_to_flags(
347    config: &KeyboardProtocolConfig,
348) -> ratatui::crossterm::event::KeyboardEnhancementFlags {
349    use ratatui::crossterm::event::KeyboardEnhancementFlags;
350
351    if !config.enabled {
352        return KeyboardEnhancementFlags::empty();
353    }
354
355    match config.mode.as_str() {
356        "default" => {
357            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
358                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
359                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
360        }
361        "full" => {
362            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
363                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
364                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
365                | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
366        }
367        "minimal" => KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES,
368        "custom" => {
369            let mut flags = KeyboardEnhancementFlags::empty();
370            if config.disambiguate_escape_codes {
371                flags |= KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES;
372            }
373            if config.report_event_types {
374                flags |= KeyboardEnhancementFlags::REPORT_EVENT_TYPES;
375            }
376            if config.report_alternate_keys {
377                flags |= KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS;
378            }
379            if config.report_all_keys {
380                flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
381            }
382            flags
383        }
384        _ => {
385            KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
386                | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
387                | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
388        }
389    }
390}