vtcode_config/
root.rs

1use serde::{Deserialize, Serialize};
2
3use crate::status_line::StatusLineConfig;
4
5#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
6#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
7#[serde(rename_all = "snake_case")]
8#[derive(Default)]
9pub enum ToolOutputMode {
10    #[default]
11    Compact,
12    Full,
13}
14
15#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
17#[serde(rename_all = "snake_case")]
18#[derive(Default)]
19pub enum ReasoningDisplayMode {
20    Always,
21    #[default]
22    Toggle,
23    Hidden,
24}
25
26#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
27#[derive(Debug, Clone, Deserialize, Serialize)]
28pub struct UiConfig {
29    #[serde(default = "default_tool_output_mode")]
30    pub tool_output_mode: ToolOutputMode,
31    #[serde(default = "default_tool_output_max_lines")]
32    pub tool_output_max_lines: usize,
33    #[serde(default = "default_tool_output_spool_bytes")]
34    pub tool_output_spool_bytes: usize,
35    #[serde(default)]
36    pub tool_output_spool_dir: Option<String>,
37    #[serde(default = "default_allow_tool_ansi")]
38    pub allow_tool_ansi: bool,
39    #[serde(default = "default_inline_viewport_rows")]
40    pub inline_viewport_rows: u16,
41    #[serde(default = "default_reasoning_display_mode")]
42    pub reasoning_display_mode: ReasoningDisplayMode,
43    #[serde(default = "default_reasoning_visible_default")]
44    pub reasoning_visible_default: bool,
45    #[serde(default)]
46    pub status_line: StatusLineConfig,
47    #[serde(default)]
48    pub keyboard_protocol: KeyboardProtocolConfig,
49}
50
51impl Default for UiConfig {
52    fn default() -> Self {
53        Self {
54            tool_output_mode: default_tool_output_mode(),
55            tool_output_max_lines: default_tool_output_max_lines(),
56            tool_output_spool_bytes: default_tool_output_spool_bytes(),
57            tool_output_spool_dir: None,
58            allow_tool_ansi: default_allow_tool_ansi(),
59            inline_viewport_rows: default_inline_viewport_rows(),
60            reasoning_display_mode: default_reasoning_display_mode(),
61            reasoning_visible_default: default_reasoning_visible_default(),
62            status_line: StatusLineConfig::default(),
63            keyboard_protocol: KeyboardProtocolConfig::default(),
64        }
65    }
66}
67
68/// PTY configuration
69#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
70#[derive(Debug, Clone, Deserialize, Serialize)]
71pub struct PtyConfig {
72    /// Enable PTY functionality
73    #[serde(default = "default_pty_enabled")]
74    pub enabled: bool,
75
76    /// Default terminal rows
77    #[serde(default = "default_pty_rows")]
78    pub default_rows: u16,
79
80    /// Default terminal columns
81    #[serde(default = "default_pty_cols")]
82    pub default_cols: u16,
83
84    /// Maximum number of concurrent PTY sessions
85    #[serde(default = "default_max_pty_sessions")]
86    pub max_sessions: usize,
87
88    /// Command timeout in seconds
89    #[serde(default = "default_pty_timeout")]
90    pub command_timeout_seconds: u64,
91
92    /// Number of PTY stdout lines to display in chat output
93    #[serde(default = "default_stdout_tail_lines")]
94    pub stdout_tail_lines: usize,
95
96    /// Maximum number of scrollback lines retained per PTY session
97    #[serde(default = "default_scrollback_lines")]
98    pub scrollback_lines: usize,
99
100    /// Maximum bytes of output to retain per PTY session (prevents memory explosion)
101    #[serde(default = "default_max_scrollback_bytes")]
102    pub max_scrollback_bytes: usize,
103
104    /// Threshold (KB) at which to auto-spool large outputs to disk
105    #[serde(default = "default_large_output_threshold_kb")]
106    pub large_output_threshold_kb: usize,
107
108    /// Preferred shell program for PTY sessions (falls back to environment when unset)
109    #[serde(default)]
110    pub preferred_shell: Option<String>,
111}
112
113impl Default for PtyConfig {
114    fn default() -> Self {
115        Self {
116            enabled: default_pty_enabled(),
117            default_rows: default_pty_rows(),
118            default_cols: default_pty_cols(),
119            max_sessions: default_max_pty_sessions(),
120            command_timeout_seconds: default_pty_timeout(),
121            stdout_tail_lines: default_stdout_tail_lines(),
122            scrollback_lines: default_scrollback_lines(),
123            max_scrollback_bytes: default_max_scrollback_bytes(),
124            large_output_threshold_kb: default_large_output_threshold_kb(),
125            preferred_shell: None,
126        }
127    }
128}
129
130fn default_pty_enabled() -> bool {
131    true
132}
133
134fn default_pty_rows() -> u16 {
135    24
136}
137
138fn default_pty_cols() -> u16 {
139    80
140}
141
142fn default_max_pty_sessions() -> usize {
143    10
144}
145
146fn default_pty_timeout() -> u64 {
147    300
148}
149
150fn default_stdout_tail_lines() -> usize {
151    crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
152}
153
154fn default_scrollback_lines() -> usize {
155    crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
156}
157
158fn default_max_scrollback_bytes() -> usize {
159    // Reduced from 50MB to 25MB for memory-constrained development environments
160    // Can be overridden in vtcode.toml with: pty.max_scrollback_bytes = 52428800
161    25_000_000 // 25MB max to prevent memory explosion
162}
163
164fn default_large_output_threshold_kb() -> usize {
165    5_000 // 5MB threshold for auto-spooling
166}
167
168fn default_tool_output_mode() -> ToolOutputMode {
169    ToolOutputMode::Compact
170}
171
172fn default_tool_output_max_lines() -> usize {
173    600
174}
175
176fn default_tool_output_spool_bytes() -> usize {
177    200_000
178}
179
180fn default_allow_tool_ansi() -> bool {
181    false
182}
183
184fn default_inline_viewport_rows() -> u16 {
185    crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
186}
187
188fn default_reasoning_display_mode() -> ReasoningDisplayMode {
189    ReasoningDisplayMode::Toggle
190}
191
192fn default_reasoning_visible_default() -> bool {
193    crate::constants::ui::DEFAULT_REASONING_VISIBLE
194}
195
196/// Kitty keyboard protocol configuration
197/// Reference: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
198#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
199#[derive(Debug, Clone, Deserialize, Serialize)]
200pub struct KeyboardProtocolConfig {
201    /// Enable keyboard protocol enhancements (master toggle)
202    #[serde(default = "default_keyboard_protocol_enabled")]
203    pub enabled: bool,
204
205    /// Preset mode: "default", "full", "minimal", "custom"
206    #[serde(default = "default_keyboard_protocol_mode")]
207    pub mode: String,
208
209    /// Individual flag controls (used when mode = "custom")
210    /// Resolve Esc key ambiguity (recommended)
211    #[serde(default = "default_disambiguate_escape_codes")]
212    pub disambiguate_escape_codes: bool,
213
214    /// Report press/release/repeat events
215    #[serde(default = "default_report_event_types")]
216    pub report_event_types: bool,
217
218    /// Report alternate key layouts
219    #[serde(default = "default_report_alternate_keys")]
220    pub report_alternate_keys: bool,
221
222    /// Report modifier-only keys (Shift, Ctrl, Alt alone)
223    #[serde(default = "default_report_all_keys")]
224    pub report_all_keys: bool,
225}
226
227impl Default for KeyboardProtocolConfig {
228    fn default() -> Self {
229        Self {
230            enabled: default_keyboard_protocol_enabled(),
231            mode: default_keyboard_protocol_mode(),
232            disambiguate_escape_codes: default_disambiguate_escape_codes(),
233            report_event_types: default_report_event_types(),
234            report_alternate_keys: default_report_alternate_keys(),
235            report_all_keys: default_report_all_keys(),
236        }
237    }
238}
239
240impl KeyboardProtocolConfig {
241    pub fn validate(&self) -> anyhow::Result<()> {
242        match self.mode.as_str() {
243            "default" | "full" | "minimal" | "custom" => Ok(()),
244            _ => anyhow::bail!(
245                "Invalid keyboard protocol mode '{}'. Must be: default, full, minimal, or custom",
246                self.mode
247            ),
248        }
249    }
250}
251
252fn default_keyboard_protocol_enabled() -> bool {
253    std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
254        .ok()
255        .and_then(|v| v.parse().ok())
256        .unwrap_or(true)
257}
258
259fn default_keyboard_protocol_mode() -> String {
260    std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE").unwrap_or_else(|_| "default".to_string())
261}
262
263fn default_disambiguate_escape_codes() -> bool {
264    true
265}
266
267fn default_report_event_types() -> bool {
268    true
269}
270
271fn default_report_alternate_keys() -> bool {
272    true
273}
274
275fn default_report_all_keys() -> bool {
276    false
277}