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#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
70#[derive(Debug, Clone, Deserialize, Serialize)]
71pub struct PtyConfig {
72 #[serde(default = "default_pty_enabled")]
74 pub enabled: bool,
75
76 #[serde(default = "default_pty_rows")]
78 pub default_rows: u16,
79
80 #[serde(default = "default_pty_cols")]
82 pub default_cols: u16,
83
84 #[serde(default = "default_max_pty_sessions")]
86 pub max_sessions: usize,
87
88 #[serde(default = "default_pty_timeout")]
90 pub command_timeout_seconds: u64,
91
92 #[serde(default = "default_stdout_tail_lines")]
94 pub stdout_tail_lines: usize,
95
96 #[serde(default = "default_scrollback_lines")]
98 pub scrollback_lines: usize,
99
100 #[serde(default = "default_max_scrollback_bytes")]
102 pub max_scrollback_bytes: usize,
103
104 #[serde(default = "default_large_output_threshold_kb")]
106 pub large_output_threshold_kb: usize,
107
108 #[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 25_000_000 }
163
164fn default_large_output_threshold_kb() -> usize {
165 5_000 }
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#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
199#[derive(Debug, Clone, Deserialize, Serialize)]
200pub struct KeyboardProtocolConfig {
201 #[serde(default = "default_keyboard_protocol_enabled")]
203 pub enabled: bool,
204
205 #[serde(default = "default_keyboard_protocol_mode")]
207 pub mode: String,
208
209 #[serde(default = "default_disambiguate_escape_codes")]
212 pub disambiguate_escape_codes: bool,
213
214 #[serde(default = "default_report_event_types")]
216 pub report_event_types: bool,
217
218 #[serde(default = "default_report_alternate_keys")]
220 pub report_alternate_keys: bool,
221
222 #[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}