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))]
28#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
29#[serde(rename_all = "snake_case")]
30pub enum LayoutModeOverride {
31 #[default]
33 Auto,
34 Compact,
36 Standard,
38 Wide,
40}
41
42#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
44#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default)]
45#[serde(rename_all = "snake_case")]
46pub enum UiDisplayMode {
47 Full,
49 #[default]
51 Minimal,
52 Focused,
54}
55
56#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
57#[derive(Debug, Clone, Deserialize, Serialize)]
58pub struct UiConfig {
59 #[serde(default = "default_tool_output_mode")]
60 pub tool_output_mode: ToolOutputMode,
61 #[serde(default = "default_tool_output_max_lines")]
62 pub tool_output_max_lines: usize,
63 #[serde(default = "default_tool_output_spool_bytes")]
64 pub tool_output_spool_bytes: usize,
65 #[serde(default)]
66 pub tool_output_spool_dir: Option<String>,
67 #[serde(default = "default_allow_tool_ansi")]
68 pub allow_tool_ansi: bool,
69 #[serde(default = "default_inline_viewport_rows")]
70 pub inline_viewport_rows: u16,
71 #[serde(default = "default_reasoning_display_mode")]
72 pub reasoning_display_mode: ReasoningDisplayMode,
73 #[serde(default = "default_reasoning_visible_default")]
74 pub reasoning_visible_default: bool,
75 #[serde(default)]
76 pub status_line: StatusLineConfig,
77 #[serde(default)]
78 pub keyboard_protocol: KeyboardProtocolConfig,
79
80 #[serde(default)]
82 pub layout_mode: LayoutModeOverride,
83
84 #[serde(default)]
86 pub display_mode: UiDisplayMode,
87
88 #[serde(default = "default_show_sidebar")]
90 pub show_sidebar: bool,
91
92 #[serde(default = "default_show_message_dividers")]
94 pub show_message_dividers: bool,
95
96 #[serde(default = "default_dim_completed_todos")]
98 pub dim_completed_todos: bool,
99
100 #[serde(default = "default_message_block_spacing")]
102 pub message_block_spacing: bool,
103}
104
105fn default_show_sidebar() -> bool {
106 true
107}
108
109fn default_show_message_dividers() -> bool {
110 false }
112
113fn default_dim_completed_todos() -> bool {
114 true
115}
116
117fn default_message_block_spacing() -> bool {
118 true
119}
120
121impl Default for UiConfig {
122 fn default() -> Self {
123 Self {
124 tool_output_mode: default_tool_output_mode(),
125 tool_output_max_lines: default_tool_output_max_lines(),
126 tool_output_spool_bytes: default_tool_output_spool_bytes(),
127 tool_output_spool_dir: None,
128 allow_tool_ansi: default_allow_tool_ansi(),
129 inline_viewport_rows: default_inline_viewport_rows(),
130 reasoning_display_mode: default_reasoning_display_mode(),
131 reasoning_visible_default: default_reasoning_visible_default(),
132 status_line: StatusLineConfig::default(),
133 keyboard_protocol: KeyboardProtocolConfig::default(),
134 layout_mode: LayoutModeOverride::default(),
135 display_mode: UiDisplayMode::default(),
136 show_sidebar: default_show_sidebar(),
137 show_message_dividers: default_show_message_dividers(),
138 dim_completed_todos: default_dim_completed_todos(),
139 message_block_spacing: default_message_block_spacing(),
140 }
141 }
142}
143
144#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
146#[derive(Debug, Clone, Deserialize, Serialize)]
147pub struct PtyConfig {
148 #[serde(default = "default_pty_enabled")]
150 pub enabled: bool,
151
152 #[serde(default = "default_pty_rows")]
154 pub default_rows: u16,
155
156 #[serde(default = "default_pty_cols")]
158 pub default_cols: u16,
159
160 #[serde(default = "default_max_pty_sessions")]
162 pub max_sessions: usize,
163
164 #[serde(default = "default_pty_timeout")]
166 pub command_timeout_seconds: u64,
167
168 #[serde(default = "default_stdout_tail_lines")]
170 pub stdout_tail_lines: usize,
171
172 #[serde(default = "default_scrollback_lines")]
174 pub scrollback_lines: usize,
175
176 #[serde(default = "default_max_scrollback_bytes")]
178 pub max_scrollback_bytes: usize,
179
180 #[serde(default = "default_large_output_threshold_kb")]
182 pub large_output_threshold_kb: usize,
183
184 #[serde(default)]
186 pub preferred_shell: Option<String>,
187}
188
189impl Default for PtyConfig {
190 fn default() -> Self {
191 Self {
192 enabled: default_pty_enabled(),
193 default_rows: default_pty_rows(),
194 default_cols: default_pty_cols(),
195 max_sessions: default_max_pty_sessions(),
196 command_timeout_seconds: default_pty_timeout(),
197 stdout_tail_lines: default_stdout_tail_lines(),
198 scrollback_lines: default_scrollback_lines(),
199 max_scrollback_bytes: default_max_scrollback_bytes(),
200 large_output_threshold_kb: default_large_output_threshold_kb(),
201 preferred_shell: None,
202 }
203 }
204}
205
206fn default_pty_enabled() -> bool {
207 true
208}
209
210fn default_pty_rows() -> u16 {
211 24
212}
213
214fn default_pty_cols() -> u16 {
215 80
216}
217
218fn default_max_pty_sessions() -> usize {
219 10
220}
221
222fn default_pty_timeout() -> u64 {
223 300
224}
225
226fn default_stdout_tail_lines() -> usize {
227 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
228}
229
230fn default_scrollback_lines() -> usize {
231 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
232}
233
234fn default_max_scrollback_bytes() -> usize {
235 25_000_000 }
239
240fn default_large_output_threshold_kb() -> usize {
241 5_000 }
243
244fn default_tool_output_mode() -> ToolOutputMode {
245 ToolOutputMode::Compact
246}
247
248fn default_tool_output_max_lines() -> usize {
249 600
250}
251
252fn default_tool_output_spool_bytes() -> usize {
253 200_000
254}
255
256fn default_allow_tool_ansi() -> bool {
257 false
258}
259
260fn default_inline_viewport_rows() -> u16 {
261 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
262}
263
264fn default_reasoning_display_mode() -> ReasoningDisplayMode {
265 ReasoningDisplayMode::Toggle
266}
267
268fn default_reasoning_visible_default() -> bool {
269 crate::constants::ui::DEFAULT_REASONING_VISIBLE
270}
271
272#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
275#[derive(Debug, Clone, Deserialize, Serialize)]
276pub struct KeyboardProtocolConfig {
277 #[serde(default = "default_keyboard_protocol_enabled")]
279 pub enabled: bool,
280
281 #[serde(default = "default_keyboard_protocol_mode")]
283 pub mode: String,
284
285 #[serde(default = "default_disambiguate_escape_codes")]
288 pub disambiguate_escape_codes: bool,
289
290 #[serde(default = "default_report_event_types")]
292 pub report_event_types: bool,
293
294 #[serde(default = "default_report_alternate_keys")]
296 pub report_alternate_keys: bool,
297
298 #[serde(default = "default_report_all_keys")]
300 pub report_all_keys: bool,
301}
302
303impl Default for KeyboardProtocolConfig {
304 fn default() -> Self {
305 Self {
306 enabled: default_keyboard_protocol_enabled(),
307 mode: default_keyboard_protocol_mode(),
308 disambiguate_escape_codes: default_disambiguate_escape_codes(),
309 report_event_types: default_report_event_types(),
310 report_alternate_keys: default_report_alternate_keys(),
311 report_all_keys: default_report_all_keys(),
312 }
313 }
314}
315
316impl KeyboardProtocolConfig {
317 pub fn validate(&self) -> anyhow::Result<()> {
318 match self.mode.as_str() {
319 "default" | "full" | "minimal" | "custom" => Ok(()),
320 _ => anyhow::bail!(
321 "Invalid keyboard protocol mode '{}'. Must be: default, full, minimal, or custom",
322 self.mode
323 ),
324 }
325 }
326}
327
328fn default_keyboard_protocol_enabled() -> bool {
329 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
330 .ok()
331 .and_then(|v| v.parse().ok())
332 .unwrap_or(true)
333}
334
335fn default_keyboard_protocol_mode() -> String {
336 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE").unwrap_or_else(|_| "default".to_string())
337}
338
339fn default_disambiguate_escape_codes() -> bool {
340 true
341}
342
343fn default_report_event_types() -> bool {
344 true
345}
346
347fn default_report_alternate_keys() -> bool {
348 true
349}
350
351fn default_report_all_keys() -> bool {
352 false
353}