1use vtcode_terminal_detection::is_ghostty_terminal;
8
9pub mod acp;
10pub mod api;
11pub mod api_keys;
12pub mod constants;
13pub mod context;
14pub mod core;
15pub mod defaults;
16pub mod hooks;
17pub mod ide_context;
18pub mod loader;
19pub mod mcp;
20pub mod models;
21pub mod output_styles;
22pub mod telemetry;
23pub mod types;
24pub mod validation;
25pub mod validator;
26
27pub use acp::{
28 AgentClientProtocolConfig, AgentClientProtocolTransport, AgentClientProtocolZedConfig,
29 AgentClientProtocolZedToolsConfig, AgentClientProtocolZedWorkspaceTrustMode,
30 WorkspaceTrustLevel,
31};
32pub use api::{
33 ConfigLayerView, ConfigReadRequest, ConfigReadResponse, ConfigService, ConfigWriteRequest,
34 ConfigWriteResponse, ConfigWriteStrategy, ConfigWriteTarget, OverrideMetadata,
35};
36pub use api_keys::ApiKeySources;
37pub use context::{ContextFeaturesConfig, LedgerConfig};
38pub use core::{
39 AgentConfig, AgentOnboardingConfig, AgentPromptSuggestionsConfig, AutoModeConfig,
40 AutoModeEnvironmentConfig, AutomationConfig, CommandsConfig, EditorToolConfig, FullAutoConfig,
41 GatekeeperConfig, ModelConfig, OpenAIPromptCacheKeyMode, PermissionMode, PermissionsConfig,
42 PersistentMemoryConfig, PromptCachingConfig, ProviderPromptCachingConfig, ScheduledTasksConfig,
43 SecurityConfig, ToolPolicy, ToolsConfig, build_openai_prompt_cache_key,
44 tool_call_delay_for_rate, tool_loop_limit_reached,
45};
46pub use core::{PluginRuntimeConfig, PluginTrustLevel};
47pub use defaults::{
48 ConfigDefaultsProvider, ContextStoreDefaults, PerformanceDefaults, ScenarioDefaults,
49 SyntaxHighlightingDefaults, WorkspacePathsDefaults, current_config_defaults, get_config_dir,
50 get_data_dir, install_config_defaults_provider, reset_to_default_config_defaults,
51 with_config_defaults,
52};
53pub use hooks::{
54 HookCommandConfig, HookCommandKind, HookGroupConfig, HooksConfig, LifecycleHooksConfig,
55};
56pub use ide_context::{
57 IdeContextConfig, IdeContextProviderConfig, IdeContextProviderFamily, IdeContextProviderMode,
58 IdeContextProvidersConfig,
59};
60pub use loader::{ConfigManager, SyntaxHighlightingConfig, VTCodeConfig};
61pub use mcp::{
62 McpAllowListConfig, McpAllowListRules, McpClientConfig, McpHttpServerConfig, McpProviderConfig,
63 McpStdioServerConfig, McpTransportConfig, McpUiConfig, McpUiMode,
64};
65pub use models::{ModelId, OpenRouterMetadata};
66pub use telemetry::TelemetryConfig;
67pub use types::{
68 ReasoningEffortLevel, SystemPromptMode, ToolDocumentationMode, UiSurfacePreference,
69 VerbosityLevel,
70};
71pub use validation::{ValidationResult, validate_config, validate_model_exists};
72pub use validator::ConfigValidator;
73pub use vtcode_config::root::{
74 KeyboardProtocolConfig, LayoutModeOverride, PtyConfig, ReasoningDisplayMode, ToolOutputMode,
75 UiConfig, UiDisplayMode,
76};
77pub use vtcode_config::status_line::{StatusLineConfig, StatusLineMode};
78pub use vtcode_config::terminal_title::{DEFAULT_TERMINAL_TITLE_ITEMS, TerminalTitleConfig};
79pub use vtcode_config::{
80 FileOpener, HistoryConfig, HistoryPersistence, NotificationCondition,
81 TerminalNotificationMethod, TuiAlternateScreen, TuiConfig, TuiNotificationEvent,
82 TuiNotificationsConfig,
83};
84pub use vtcode_config::{TimeoutsConfig, resolve_timeout};
85
86#[cfg(feature = "tui")]
88pub fn keyboard_protocol_to_flags(
89 config: &KeyboardProtocolConfig,
90) -> crossterm::event::KeyboardEnhancementFlags {
91 keyboard_protocol_to_flags_for_terminal(
92 config,
93 cfg!(target_os = "macos"),
94 std::env::var("TERM_PROGRAM").ok().as_deref(),
95 std::env::var("TERM").ok().as_deref(),
96 )
97}
98
99#[cfg(feature = "tui")]
100fn keyboard_protocol_to_flags_for_terminal(
101 config: &KeyboardProtocolConfig,
102 is_macos: bool,
103 term_program: Option<&str>,
104 term: Option<&str>,
105) -> crossterm::event::KeyboardEnhancementFlags {
106 use ratatui::crossterm::event::KeyboardEnhancementFlags;
107
108 if !config.enabled {
109 return KeyboardEnhancementFlags::empty();
110 }
111
112 let mut flags = match config.mode.as_str() {
113 "default" => {
114 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
115 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
116 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
117 }
118 "full" => {
119 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
120 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
121 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
122 | KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES
123 }
124 "minimal" => KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES,
125 "custom" => {
126 let mut flags = KeyboardEnhancementFlags::empty();
127 if config.disambiguate_escape_codes {
128 flags |= KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES;
129 }
130 if config.report_event_types {
131 flags |= KeyboardEnhancementFlags::REPORT_EVENT_TYPES;
132 }
133 if config.report_alternate_keys {
134 flags |= KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS;
135 }
136 if config.report_all_keys {
137 flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
138 }
139 flags
140 }
141 _ => {
142 tracing::warn!(
143 "Invalid keyboard protocol mode '{}', using default",
144 config.mode
145 );
146 KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
147 | KeyboardEnhancementFlags::REPORT_EVENT_TYPES
148 | KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS
149 }
150 };
151
152 if should_force_report_all_keys(config.mode.as_str(), is_macos, term_program, term) {
153 flags |= KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES;
154 }
155
156 flags
157}
158
159#[cfg(feature = "tui")]
160fn should_force_report_all_keys(
161 mode: &str,
162 is_macos: bool,
163 term_program: Option<&str>,
164 term: Option<&str>,
165) -> bool {
166 if !is_macos || !matches!(mode, "default") {
167 return false;
168 }
169
170 is_ghostty_terminal(term_program, term)
173}
174
175#[cfg(all(test, feature = "tui"))]
176mod keyboard_protocol_tests {
177 use super::*;
178 use ratatui::crossterm::event::KeyboardEnhancementFlags;
179
180 fn default_keyboard_protocol_config() -> KeyboardProtocolConfig {
181 KeyboardProtocolConfig {
182 enabled: true,
183 mode: "default".to_string(),
184 disambiguate_escape_codes: true,
185 report_event_types: true,
186 report_alternate_keys: true,
187 report_all_keys: false,
188 }
189 }
190
191 #[test]
192 fn test_keyboard_protocol_default_mode() {
193 let flags = keyboard_protocol_to_flags_for_terminal(
194 &default_keyboard_protocol_config(),
195 false,
196 Some("Ghostty"),
197 Some("xterm-ghostty"),
198 );
199
200 assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
201 assert!(flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
202 assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
203 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
204 }
205
206 #[test]
207 fn test_keyboard_protocol_default_mode_enables_all_keys_for_ghostty_on_macos() {
208 let flags = keyboard_protocol_to_flags_for_terminal(
209 &default_keyboard_protocol_config(),
210 true,
211 Some("Ghostty"),
212 Some("xterm-ghostty"),
213 );
214
215 assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
216 }
217
218 #[test]
219 fn test_keyboard_protocol_minimal_mode() {
220 let config = KeyboardProtocolConfig {
221 enabled: true,
222 mode: "minimal".to_string(),
223 disambiguate_escape_codes: true,
224 report_event_types: true,
225 report_alternate_keys: true,
226 report_all_keys: false,
227 };
228
229 let flags = keyboard_protocol_to_flags_for_terminal(
230 &config,
231 true,
232 Some("Ghostty"),
233 Some("xterm-ghostty"),
234 );
235
236 assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
237 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
238 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
239 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
240 }
241
242 #[test]
243 fn test_keyboard_protocol_disabled() {
244 let config = KeyboardProtocolConfig {
245 enabled: false,
246 mode: "default".to_string(),
247 disambiguate_escape_codes: true,
248 report_event_types: true,
249 report_alternate_keys: true,
250 report_all_keys: false,
251 };
252
253 let flags = keyboard_protocol_to_flags_for_terminal(
254 &config,
255 true,
256 Some("Ghostty"),
257 Some("xterm-ghostty"),
258 );
259 assert!(flags.is_empty());
260 }
261
262 #[test]
263 fn test_keyboard_protocol_custom_mode() {
264 let config = KeyboardProtocolConfig {
265 enabled: true,
266 mode: "custom".to_string(),
267 disambiguate_escape_codes: true,
268 report_event_types: false,
269 report_alternate_keys: true,
270 report_all_keys: false,
271 };
272
273 let flags = keyboard_protocol_to_flags_for_terminal(
274 &config,
275 true,
276 Some("Ghostty"),
277 Some("xterm-ghostty"),
278 );
279
280 assert!(flags.contains(KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES));
281 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_EVENT_TYPES));
282 assert!(flags.contains(KeyboardEnhancementFlags::REPORT_ALTERNATE_KEYS));
283 assert!(!flags.contains(KeyboardEnhancementFlags::REPORT_ALL_KEYS_AS_ESCAPE_CODES));
284 }
285
286 #[test]
287 fn test_keyboard_protocol_validation() {
288 let mut config = KeyboardProtocolConfig {
289 enabled: true,
290 mode: "invalid".to_string(),
291 disambiguate_escape_codes: true,
292 report_event_types: true,
293 report_alternate_keys: true,
294 report_all_keys: false,
295 };
296
297 assert!(config.validate().is_err());
298
299 config.mode = "default".to_string();
300 config.validate().unwrap();
301 }
302}