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_dim_completed_todos")]
94 pub dim_completed_todos: bool,
95
96 #[serde(default = "default_message_block_spacing")]
98 pub message_block_spacing: bool,
99}
100
101fn default_show_sidebar() -> bool {
102 true
103}
104
105fn default_dim_completed_todos() -> bool {
106 true
107}
108
109fn default_message_block_spacing() -> bool {
110 true
111}
112
113impl Default for UiConfig {
114 fn default() -> Self {
115 Self {
116 tool_output_mode: default_tool_output_mode(),
117 tool_output_max_lines: default_tool_output_max_lines(),
118 tool_output_spool_bytes: default_tool_output_spool_bytes(),
119 tool_output_spool_dir: None,
120 allow_tool_ansi: default_allow_tool_ansi(),
121 inline_viewport_rows: default_inline_viewport_rows(),
122 reasoning_display_mode: default_reasoning_display_mode(),
123 reasoning_visible_default: default_reasoning_visible_default(),
124 status_line: StatusLineConfig::default(),
125 keyboard_protocol: KeyboardProtocolConfig::default(),
126 layout_mode: LayoutModeOverride::default(),
127 display_mode: UiDisplayMode::default(),
128 show_sidebar: default_show_sidebar(),
129 dim_completed_todos: default_dim_completed_todos(),
130 message_block_spacing: default_message_block_spacing(),
131 }
132 }
133}
134
135#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
137#[derive(Debug, Clone, Deserialize, Serialize)]
138pub struct PtyConfig {
139 #[serde(default = "default_pty_enabled")]
141 pub enabled: bool,
142
143 #[serde(default = "default_pty_rows")]
145 pub default_rows: u16,
146
147 #[serde(default = "default_pty_cols")]
149 pub default_cols: u16,
150
151 #[serde(default = "default_max_pty_sessions")]
153 pub max_sessions: usize,
154
155 #[serde(default = "default_pty_timeout")]
157 pub command_timeout_seconds: u64,
158
159 #[serde(default = "default_stdout_tail_lines")]
161 pub stdout_tail_lines: usize,
162
163 #[serde(default = "default_scrollback_lines")]
165 pub scrollback_lines: usize,
166
167 #[serde(default = "default_max_scrollback_bytes")]
169 pub max_scrollback_bytes: usize,
170
171 #[serde(default = "default_large_output_threshold_kb")]
173 pub large_output_threshold_kb: usize,
174
175 #[serde(default)]
177 pub preferred_shell: Option<String>,
178}
179
180impl Default for PtyConfig {
181 fn default() -> Self {
182 Self {
183 enabled: default_pty_enabled(),
184 default_rows: default_pty_rows(),
185 default_cols: default_pty_cols(),
186 max_sessions: default_max_pty_sessions(),
187 command_timeout_seconds: default_pty_timeout(),
188 stdout_tail_lines: default_stdout_tail_lines(),
189 scrollback_lines: default_scrollback_lines(),
190 max_scrollback_bytes: default_max_scrollback_bytes(),
191 large_output_threshold_kb: default_large_output_threshold_kb(),
192 preferred_shell: None,
193 }
194 }
195}
196
197fn default_pty_enabled() -> bool {
198 true
199}
200
201fn default_pty_rows() -> u16 {
202 24
203}
204
205fn default_pty_cols() -> u16 {
206 80
207}
208
209fn default_max_pty_sessions() -> usize {
210 10
211}
212
213fn default_pty_timeout() -> u64 {
214 300
215}
216
217fn default_stdout_tail_lines() -> usize {
218 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
219}
220
221fn default_scrollback_lines() -> usize {
222 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
223}
224
225fn default_max_scrollback_bytes() -> usize {
226 25_000_000 }
230
231fn default_large_output_threshold_kb() -> usize {
232 5_000 }
234
235fn default_tool_output_mode() -> ToolOutputMode {
236 ToolOutputMode::Compact
237}
238
239fn default_tool_output_max_lines() -> usize {
240 600
241}
242
243fn default_tool_output_spool_bytes() -> usize {
244 200_000
245}
246
247fn default_allow_tool_ansi() -> bool {
248 false
249}
250
251fn default_inline_viewport_rows() -> u16 {
252 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
253}
254
255fn default_reasoning_display_mode() -> ReasoningDisplayMode {
256 ReasoningDisplayMode::Toggle
257}
258
259fn default_reasoning_visible_default() -> bool {
260 crate::constants::ui::DEFAULT_REASONING_VISIBLE
261}
262
263#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
266#[derive(Debug, Clone, Deserialize, Serialize)]
267pub struct KeyboardProtocolConfig {
268 #[serde(default = "default_keyboard_protocol_enabled")]
270 pub enabled: bool,
271
272 #[serde(default = "default_keyboard_protocol_mode")]
274 pub mode: String,
275
276 #[serde(default = "default_disambiguate_escape_codes")]
279 pub disambiguate_escape_codes: bool,
280
281 #[serde(default = "default_report_event_types")]
283 pub report_event_types: bool,
284
285 #[serde(default = "default_report_alternate_keys")]
287 pub report_alternate_keys: bool,
288
289 #[serde(default = "default_report_all_keys")]
291 pub report_all_keys: bool,
292}
293
294impl Default for KeyboardProtocolConfig {
295 fn default() -> Self {
296 Self {
297 enabled: default_keyboard_protocol_enabled(),
298 mode: default_keyboard_protocol_mode(),
299 disambiguate_escape_codes: default_disambiguate_escape_codes(),
300 report_event_types: default_report_event_types(),
301 report_alternate_keys: default_report_alternate_keys(),
302 report_all_keys: default_report_all_keys(),
303 }
304 }
305}
306
307impl KeyboardProtocolConfig {
308 pub fn validate(&self) -> anyhow::Result<()> {
309 match self.mode.as_str() {
310 "default" | "full" | "minimal" | "custom" => Ok(()),
311 _ => anyhow::bail!(
312 "Invalid keyboard protocol mode '{}'. Must be: default, full, minimal, or custom",
313 self.mode
314 ),
315 }
316 }
317}
318
319fn default_keyboard_protocol_enabled() -> bool {
320 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
321 .ok()
322 .and_then(|v| v.parse().ok())
323 .unwrap_or(true)
324}
325
326fn default_keyboard_protocol_mode() -> String {
327 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE").unwrap_or_else(|_| "default".to_string())
328}
329
330fn default_disambiguate_escape_codes() -> bool {
331 true
332}
333
334fn default_report_event_types() -> bool {
335 true
336}
337
338fn default_report_alternate_keys() -> bool {
339 true
340}
341
342fn default_report_all_keys() -> bool {
343 false
344}