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, Deserialize, Serialize)]
17pub struct UiConfig {
18 #[serde(default = "default_tool_output_mode")]
19 pub tool_output_mode: ToolOutputMode,
20 #[serde(default = "default_tool_output_max_lines")]
21 pub tool_output_max_lines: usize,
22 #[serde(default = "default_tool_output_spool_bytes")]
23 pub tool_output_spool_bytes: usize,
24 #[serde(default)]
25 pub tool_output_spool_dir: Option<String>,
26 #[serde(default = "default_allow_tool_ansi")]
27 pub allow_tool_ansi: bool,
28 #[serde(default = "default_inline_viewport_rows")]
29 pub inline_viewport_rows: u16,
30 #[serde(default)]
31 pub status_line: StatusLineConfig,
32 #[serde(default)]
33 pub keyboard_protocol: KeyboardProtocolConfig,
34}
35
36impl Default for UiConfig {
37 fn default() -> Self {
38 Self {
39 tool_output_mode: default_tool_output_mode(),
40 tool_output_max_lines: default_tool_output_max_lines(),
41 tool_output_spool_bytes: default_tool_output_spool_bytes(),
42 tool_output_spool_dir: None,
43 allow_tool_ansi: default_allow_tool_ansi(),
44 inline_viewport_rows: default_inline_viewport_rows(),
45 status_line: StatusLineConfig::default(),
46 keyboard_protocol: KeyboardProtocolConfig::default(),
47 }
48 }
49}
50
51#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
53#[derive(Debug, Clone, Deserialize, Serialize)]
54pub struct PtyConfig {
55 #[serde(default = "default_pty_enabled")]
57 pub enabled: bool,
58
59 #[serde(default = "default_pty_rows")]
61 pub default_rows: u16,
62
63 #[serde(default = "default_pty_cols")]
65 pub default_cols: u16,
66
67 #[serde(default = "default_max_pty_sessions")]
69 pub max_sessions: usize,
70
71 #[serde(default = "default_pty_timeout")]
73 pub command_timeout_seconds: u64,
74
75 #[serde(default = "default_stdout_tail_lines")]
77 pub stdout_tail_lines: usize,
78
79 #[serde(default = "default_scrollback_lines")]
81 pub scrollback_lines: usize,
82
83 #[serde(default = "default_max_scrollback_bytes")]
85 pub max_scrollback_bytes: usize,
86
87 #[serde(default = "default_large_output_threshold_kb")]
89 pub large_output_threshold_kb: usize,
90
91 #[serde(default)]
93 pub preferred_shell: Option<String>,
94}
95
96impl Default for PtyConfig {
97 fn default() -> Self {
98 Self {
99 enabled: default_pty_enabled(),
100 default_rows: default_pty_rows(),
101 default_cols: default_pty_cols(),
102 max_sessions: default_max_pty_sessions(),
103 command_timeout_seconds: default_pty_timeout(),
104 stdout_tail_lines: default_stdout_tail_lines(),
105 scrollback_lines: default_scrollback_lines(),
106 max_scrollback_bytes: default_max_scrollback_bytes(),
107 large_output_threshold_kb: default_large_output_threshold_kb(),
108 preferred_shell: None,
109 }
110 }
111}
112
113fn default_pty_enabled() -> bool {
114 true
115}
116
117fn default_pty_rows() -> u16 {
118 24
119}
120
121fn default_pty_cols() -> u16 {
122 80
123}
124
125fn default_max_pty_sessions() -> usize {
126 10
127}
128
129fn default_pty_timeout() -> u64 {
130 300
131}
132
133fn default_stdout_tail_lines() -> usize {
134 crate::constants::defaults::DEFAULT_PTY_STDOUT_TAIL_LINES
135}
136
137fn default_scrollback_lines() -> usize {
138 crate::constants::defaults::DEFAULT_PTY_SCROLLBACK_LINES
139}
140
141fn default_max_scrollback_bytes() -> usize {
142 25_000_000 }
146
147fn default_large_output_threshold_kb() -> usize {
148 5_000 }
150
151fn default_tool_output_mode() -> ToolOutputMode {
152 ToolOutputMode::Compact
153}
154
155fn default_tool_output_max_lines() -> usize {
156 600
157}
158
159fn default_tool_output_spool_bytes() -> usize {
160 200_000
161}
162
163fn default_allow_tool_ansi() -> bool {
164 false
165}
166
167fn default_inline_viewport_rows() -> u16 {
168 crate::constants::ui::DEFAULT_INLINE_VIEWPORT_ROWS
169}
170
171#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
174#[derive(Debug, Clone, Deserialize, Serialize)]
175pub struct KeyboardProtocolConfig {
176 #[serde(default = "default_keyboard_protocol_enabled")]
178 pub enabled: bool,
179
180 #[serde(default = "default_keyboard_protocol_mode")]
182 pub mode: String,
183
184 #[serde(default = "default_disambiguate_escape_codes")]
187 pub disambiguate_escape_codes: bool,
188
189 #[serde(default = "default_report_event_types")]
191 pub report_event_types: bool,
192
193 #[serde(default = "default_report_alternate_keys")]
195 pub report_alternate_keys: bool,
196
197 #[serde(default = "default_report_all_keys")]
199 pub report_all_keys: bool,
200}
201
202impl Default for KeyboardProtocolConfig {
203 fn default() -> Self {
204 Self {
205 enabled: default_keyboard_protocol_enabled(),
206 mode: default_keyboard_protocol_mode(),
207 disambiguate_escape_codes: default_disambiguate_escape_codes(),
208 report_event_types: default_report_event_types(),
209 report_alternate_keys: default_report_alternate_keys(),
210 report_all_keys: default_report_all_keys(),
211 }
212 }
213}
214
215impl KeyboardProtocolConfig {
216 pub fn validate(&self) -> anyhow::Result<()> {
217 match self.mode.as_str() {
218 "default" | "full" | "minimal" | "custom" => Ok(()),
219 _ => anyhow::bail!(
220 "Invalid keyboard protocol mode '{}'. Must be: default, full, minimal, or custom",
221 self.mode
222 ),
223 }
224 }
225}
226
227fn default_keyboard_protocol_enabled() -> bool {
228 std::env::var("VTCODE_KEYBOARD_PROTOCOL_ENABLED")
229 .ok()
230 .and_then(|v| v.parse().ok())
231 .unwrap_or(true)
232}
233
234fn default_keyboard_protocol_mode() -> String {
235 std::env::var("VTCODE_KEYBOARD_PROTOCOL_MODE").unwrap_or_else(|_| "default".to_string())
236}
237
238fn default_disambiguate_escape_codes() -> bool {
239 true
240}
241
242fn default_report_event_types() -> bool {
243 true
244}
245
246fn default_report_alternate_keys() -> bool {
247 true
248}
249
250fn default_report_all_keys() -> bool {
251 false
252}