1pub mod prettifier;
7
8use crate::snippets::{CustomActionConfig, SnippetConfig};
9use crate::themes::Theme;
10use crate::types::{
11 AlertEvent, AlertSoundConfig, BackgroundImageMode, BackgroundMode, CursorShaderConfig,
12 CursorStyle, DividerStyle, DownloadSaveLocation, DroppedFileQuoteStyle, FontRange,
13 ImageScalingMode, InstallPromptState, IntegrationVersions, KeyBinding, LogLevel,
14 ModifierRemapping, OptionKeyMode, PaneBackgroundConfig, PaneTitlePosition, PowerPreference,
15 ProgressBarPosition, ProgressBarStyle, SemanticHistoryEditorMode, SessionLogFormat,
16 ShaderConfig, ShaderInstallPrompt, ShellExitAction, SmartSelectionRule, StartupDirectoryMode,
17 StatusBarPosition, TabBarMode, TabBarPosition, TabStyle, TabTitleMode, ThinStrokesMode,
18 UnfocusedCursorStyle, UpdateCheckFrequency, VsyncMode, WindowType,
19 default_smart_selection_rules,
20};
21
22use anyhow::Result;
23use regex::Regex;
24use serde::{Deserialize, Serialize};
25use std::collections::HashMap;
26use std::fs;
27use std::path::PathBuf;
28
29pub fn substitute_variables(input: &str) -> String {
39 let escaped_placeholder = "\x00ESC_DOLLAR\x00";
41 let working = input.replace("$${", escaped_placeholder);
42
43 let re = Regex::new(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-((?:[^}\\]|\\.)*))?}")
45 .expect("invalid regex");
46
47 let result = re.replace_all(&working, |caps: ®ex::Captures| {
48 let var_name = &caps[1];
49 match std::env::var(var_name) {
50 Ok(val) => val,
51 Err(_) => {
52 caps.get(2)
54 .map(|m| m.as_str().replace("\\}", "}"))
55 .unwrap_or_else(|| caps[0].to_string())
56 }
57 }
58 });
59
60 result.replace(escaped_placeholder, "${")
62}
63
64fn deserialize_shell_exit_action<'de, D>(deserializer: D) -> Result<ShellExitAction, D::Error>
70where
71 D: serde::Deserializer<'de>,
72{
73 #[derive(Deserialize)]
74 #[serde(untagged)]
75 enum BoolOrAction {
76 Bool(bool),
77 Action(ShellExitAction),
78 }
79
80 match BoolOrAction::deserialize(deserializer)? {
81 BoolOrAction::Bool(true) => Ok(ShellExitAction::Close),
82 BoolOrAction::Bool(false) => Ok(ShellExitAction::Keep),
83 BoolOrAction::Action(action) => Ok(action),
84 }
85}
86
87fn default_acp_protocol() -> String {
88 "acp".to_string()
89}
90
91fn default_acp_type() -> String {
92 "coding".to_string()
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
97pub struct CustomAcpAgentActionConfig {
98 #[serde(default)]
99 pub command: Option<String>,
100 #[serde(default)]
101 pub description: Option<String>,
102}
103
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
106pub struct CustomAcpAgentConfig {
107 pub identity: String,
108 pub name: String,
109 pub short_name: String,
110 #[serde(default = "default_acp_protocol")]
111 pub protocol: String,
112 #[serde(default = "default_acp_type")]
113 pub r#type: String,
114 #[serde(default)]
115 pub active: Option<bool>,
116 pub run_command: HashMap<String, String>,
117 #[serde(default)]
118 pub env: HashMap<String, String>,
119 #[serde(default)]
120 pub ollama_context_length: Option<u32>,
121 #[serde(default)]
122 pub install_command: Option<String>,
123 #[serde(default)]
124 pub actions: HashMap<String, HashMap<String, CustomAcpAgentActionConfig>>,
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct Config {
131 #[serde(default = "crate::defaults::cols")]
136 pub cols: usize,
137
138 #[serde(default = "crate::defaults::rows")]
140 pub rows: usize,
141
142 #[serde(default = "crate::defaults::font_size")]
144 pub font_size: f32,
145
146 #[serde(default = "crate::defaults::font_family")]
148 pub font_family: String,
149
150 #[serde(default)]
152 pub font_family_bold: Option<String>,
153
154 #[serde(default)]
156 pub font_family_italic: Option<String>,
157
158 #[serde(default)]
160 pub font_family_bold_italic: Option<String>,
161
162 #[serde(default)]
166 pub font_ranges: Vec<FontRange>,
167
168 #[serde(default = "crate::defaults::line_spacing")]
170 pub line_spacing: f32,
171
172 #[serde(default = "crate::defaults::char_spacing")]
174 pub char_spacing: f32,
175
176 #[serde(default = "crate::defaults::text_shaping")]
179 pub enable_text_shaping: bool,
180
181 #[serde(default = "crate::defaults::bool_true")]
183 pub enable_ligatures: bool,
184
185 #[serde(default = "crate::defaults::bool_true")]
187 pub enable_kerning: bool,
188
189 #[serde(default = "crate::defaults::bool_true")]
192 pub font_antialias: bool,
193
194 #[serde(default = "crate::defaults::bool_true")]
198 pub font_hinting: bool,
199
200 #[serde(default)]
208 pub font_thin_strokes: ThinStrokesMode,
209
210 #[serde(default = "crate::defaults::minimum_contrast")]
218 pub minimum_contrast: f32,
219
220 #[serde(default = "crate::defaults::window_title")]
222 pub window_title: String,
223
224 #[serde(default = "crate::defaults::bool_true")]
227 pub allow_title_change: bool,
228
229 #[serde(default = "crate::defaults::max_fps", alias = "refresh_rate")]
235 pub max_fps: u32,
236
237 #[serde(default)]
244 pub vsync_mode: VsyncMode,
245
246 #[serde(default)]
253 pub power_preference: PowerPreference,
254
255 #[serde(default = "crate::defaults::reduce_flicker")]
258 pub reduce_flicker: bool,
259
260 #[serde(default = "crate::defaults::reduce_flicker_delay_ms")]
264 pub reduce_flicker_delay_ms: u32,
265
266 #[serde(default = "crate::defaults::maximize_throughput")]
270 pub maximize_throughput: bool,
271
272 #[serde(default = "crate::defaults::throughput_render_interval_ms")]
275 pub throughput_render_interval_ms: u32,
276
277 #[serde(default = "crate::defaults::window_padding")]
279 pub window_padding: f32,
280
281 #[serde(default = "crate::defaults::bool_true")]
284 pub hide_window_padding_on_split: bool,
285
286 #[serde(default = "crate::defaults::window_opacity")]
288 pub window_opacity: f32,
289
290 #[serde(default = "crate::defaults::bool_false")]
292 pub window_always_on_top: bool,
293
294 #[serde(default = "crate::defaults::bool_true")]
296 pub window_decorations: bool,
297
298 #[serde(default)]
303 pub window_type: WindowType,
304
305 #[serde(default)]
308 pub target_monitor: Option<usize>,
309
310 #[serde(default)]
314 pub target_space: Option<u32>,
315
316 #[serde(default = "crate::defaults::bool_false")]
319 pub lock_window_size: bool,
320
321 #[serde(default = "crate::defaults::bool_false")]
324 pub show_window_number: bool,
325
326 #[serde(default = "crate::defaults::bool_true")]
330 pub transparency_affects_only_default_background: bool,
331
332 #[serde(default = "crate::defaults::bool_true")]
335 pub keep_text_opaque: bool,
336
337 #[serde(default = "crate::defaults::bool_false")]
340 pub blur_enabled: bool,
341
342 #[serde(default = "crate::defaults::blur_radius")]
345 pub blur_radius: u32,
346
347 #[serde(default)]
349 pub background_image: Option<String>,
350
351 #[serde(default = "crate::defaults::bool_true")]
353 pub background_image_enabled: bool,
354
355 #[serde(default)]
362 pub background_image_mode: BackgroundImageMode,
363
364 #[serde(default = "crate::defaults::background_image_opacity")]
366 pub background_image_opacity: f32,
367
368 #[serde(default)]
373 pub image_scaling_mode: ImageScalingMode,
374
375 #[serde(default = "crate::defaults::bool_true")]
377 pub image_preserve_aspect_ratio: bool,
378
379 #[serde(default)]
381 pub background_mode: BackgroundMode,
382
383 #[serde(default)]
385 pub pane_backgrounds: Vec<crate::config::PaneBackgroundConfig>,
386
387 #[serde(default = "crate::defaults::background_color")]
391 pub background_color: [u8; 3],
392
393 #[serde(default)]
398 pub download_save_location: DownloadSaveLocation,
399
400 #[serde(default, skip_serializing_if = "Option::is_none")]
402 pub last_download_directory: Option<String>,
403
404 #[serde(default)]
408 pub custom_shader: Option<String>,
409
410 #[serde(default = "crate::defaults::bool_true")]
412 pub custom_shader_enabled: bool,
413
414 #[serde(default = "crate::defaults::bool_true")]
417 pub custom_shader_animation: bool,
418
419 #[serde(default = "crate::defaults::custom_shader_speed")]
421 pub custom_shader_animation_speed: f32,
422
423 #[serde(default = "crate::defaults::text_opacity")]
426 pub custom_shader_text_opacity: f32,
427
428 #[serde(default = "crate::defaults::bool_false")]
432 pub custom_shader_full_content: bool,
433
434 #[serde(default = "crate::defaults::custom_shader_brightness")]
437 pub custom_shader_brightness: f32,
438
439 #[serde(default)]
442 pub custom_shader_channel0: Option<String>,
443
444 #[serde(default)]
446 pub custom_shader_channel1: Option<String>,
447
448 #[serde(default)]
450 pub custom_shader_channel2: Option<String>,
451
452 #[serde(default)]
454 pub custom_shader_channel3: Option<String>,
455
456 #[serde(default)]
461 pub custom_shader_cubemap: Option<String>,
462
463 #[serde(default = "crate::defaults::cubemap_enabled")]
466 pub custom_shader_cubemap_enabled: bool,
467
468 #[serde(default = "crate::defaults::use_background_as_channel0")]
473 pub custom_shader_use_background_as_channel0: bool,
474
475 #[serde(default)]
481 pub cursor_shader: Option<String>,
482
483 #[serde(default = "crate::defaults::bool_false")]
485 pub cursor_shader_enabled: bool,
486
487 #[serde(default = "crate::defaults::bool_true")]
489 pub cursor_shader_animation: bool,
490
491 #[serde(default = "crate::defaults::custom_shader_speed")]
493 pub cursor_shader_animation_speed: f32,
494
495 #[serde(default = "crate::defaults::cursor_shader_color")]
498 pub cursor_shader_color: [u8; 3],
499
500 #[serde(default = "crate::defaults::cursor_trail_duration")]
503 pub cursor_shader_trail_duration: f32,
504
505 #[serde(default = "crate::defaults::cursor_glow_radius")]
508 pub cursor_shader_glow_radius: f32,
509
510 #[serde(default = "crate::defaults::cursor_glow_intensity")]
513 pub cursor_shader_glow_intensity: f32,
514
515 #[serde(default = "crate::defaults::bool_false")]
519 pub cursor_shader_hides_cursor: bool,
520
521 #[serde(default = "crate::defaults::cursor_shader_disable_in_alt_screen")]
524 pub cursor_shader_disable_in_alt_screen: bool,
525
526 #[serde(default)]
534 pub left_option_key_mode: OptionKeyMode,
535
536 #[serde(default)]
542 pub right_option_key_mode: OptionKeyMode,
543
544 #[serde(default)]
547 pub modifier_remapping: ModifierRemapping,
548
549 #[serde(default = "crate::defaults::bool_false")]
554 pub use_physical_keys: bool,
555
556 #[serde(default = "crate::defaults::bool_true")]
561 pub auto_copy_selection: bool,
562
563 #[serde(
566 default = "crate::defaults::bool_false",
567 alias = "strip_trailing_newline_on_copy"
568 )]
569 pub copy_trailing_newline: bool,
570
571 #[serde(default = "crate::defaults::bool_true")]
573 pub middle_click_paste: bool,
574
575 #[serde(default = "crate::defaults::paste_delay_ms")]
578 pub paste_delay_ms: u64,
579
580 #[serde(default)]
586 pub dropped_file_quote_style: DroppedFileQuoteStyle,
587
588 #[serde(default = "crate::defaults::scroll_speed")]
593 pub mouse_scroll_speed: f32,
594
595 #[serde(default = "crate::defaults::double_click_threshold")]
597 pub mouse_double_click_threshold: u64,
598
599 #[serde(default = "crate::defaults::triple_click_threshold")]
601 pub mouse_triple_click_threshold: u64,
602
603 #[serde(default = "crate::defaults::bool_true")]
607 pub option_click_moves_cursor: bool,
608
609 #[serde(default = "crate::defaults::bool_false")]
612 pub focus_follows_mouse: bool,
613
614 #[serde(default = "crate::defaults::bool_true")]
617 pub report_horizontal_scroll: bool,
618
619 #[serde(default = "crate::defaults::word_characters")]
626 pub word_characters: String,
627
628 #[serde(default = "crate::defaults::smart_selection_enabled")]
632 pub smart_selection_enabled: bool,
633
634 #[serde(default = "crate::types::default_smart_selection_rules")]
638 pub smart_selection_rules: Vec<SmartSelectionRule>,
639
640 #[serde(default = "crate::defaults::bool_true")]
647 pub copy_mode_enabled: bool,
648
649 #[serde(default = "crate::defaults::bool_true")]
653 pub copy_mode_auto_exit_on_yank: bool,
654
655 #[serde(default = "crate::defaults::bool_true")]
659 pub copy_mode_show_status: bool,
660
661 #[serde(default = "crate::defaults::scrollback", alias = "scrollback_size")]
666 pub scrollback_lines: usize,
667
668 #[serde(default = "crate::defaults::unicode_version")]
675 pub unicode_version: par_term_emu_core_rust::UnicodeVersion,
676
677 #[serde(default = "crate::defaults::ambiguous_width")]
681 pub ambiguous_width: par_term_emu_core_rust::AmbiguousWidth,
682
683 #[serde(default = "crate::defaults::normalization_form")]
691 pub normalization_form: par_term_emu_core_rust::NormalizationForm,
692
693 #[serde(default = "crate::defaults::bool_false")]
695 pub cursor_blink: bool,
696
697 #[serde(default = "crate::defaults::cursor_blink_interval")]
699 pub cursor_blink_interval: u64,
700
701 #[serde(default)]
703 pub cursor_style: CursorStyle,
704
705 #[serde(default = "crate::defaults::cursor_color")]
707 pub cursor_color: [u8; 3],
708
709 #[serde(default)]
713 pub cursor_text_color: Option<[u8; 3]>,
714
715 #[serde(default = "crate::defaults::bool_false")]
718 pub lock_cursor_visibility: bool,
719
720 #[serde(default = "crate::defaults::bool_false")]
723 pub lock_cursor_style: bool,
724
725 #[serde(default = "crate::defaults::bool_false")]
728 pub lock_cursor_blink: bool,
729
730 #[serde(default = "crate::defaults::bool_false")]
735 pub cursor_guide_enabled: bool,
736
737 #[serde(default = "crate::defaults::cursor_guide_color")]
739 pub cursor_guide_color: [u8; 4],
740
741 #[serde(default = "crate::defaults::bool_false")]
743 pub cursor_shadow_enabled: bool,
744
745 #[serde(default = "crate::defaults::cursor_shadow_color")]
747 pub cursor_shadow_color: [u8; 4],
748
749 #[serde(default = "crate::defaults::cursor_shadow_offset")]
751 pub cursor_shadow_offset: [f32; 2],
752
753 #[serde(default = "crate::defaults::cursor_shadow_blur")]
755 pub cursor_shadow_blur: f32,
756
757 #[serde(default = "crate::defaults::cursor_boost")]
760 pub cursor_boost: f32,
761
762 #[serde(default = "crate::defaults::cursor_boost_color")]
764 pub cursor_boost_color: [u8; 3],
765
766 #[serde(default)]
771 pub unfocused_cursor_style: UnfocusedCursorStyle,
772
773 #[serde(default = "crate::defaults::scrollbar_autohide_delay")]
778 pub scrollbar_autohide_delay: u64,
779
780 #[serde(default = "crate::defaults::theme")]
785 pub theme: String,
786
787 #[serde(default)]
789 pub auto_dark_mode: bool,
790
791 #[serde(default = "crate::defaults::light_theme")]
793 pub light_theme: String,
794
795 #[serde(default = "crate::defaults::dark_theme")]
797 pub dark_theme: String,
798
799 #[serde(default = "crate::defaults::screenshot_format")]
804 pub screenshot_format: String,
805
806 #[serde(
813 default,
814 deserialize_with = "deserialize_shell_exit_action",
815 alias = "exit_on_shell_exit",
816 alias = "close_on_shell_exit"
817 )]
818 pub shell_exit_action: ShellExitAction,
819
820 #[serde(default)]
822 pub custom_shell: Option<String>,
823
824 #[serde(default)]
826 pub shell_args: Option<Vec<String>>,
827
828 #[serde(default)]
831 pub working_directory: Option<String>,
832
833 #[serde(default)]
838 pub startup_directory_mode: StartupDirectoryMode,
839
840 #[serde(default)]
843 pub startup_directory: Option<String>,
844
845 #[serde(default)]
848 pub last_working_directory: Option<String>,
849
850 #[serde(default)]
852 pub shell_env: Option<std::collections::HashMap<String, String>>,
853
854 #[serde(default = "crate::defaults::login_shell")]
858 pub login_shell: bool,
859
860 #[serde(default = "crate::defaults::initial_text")]
863 pub initial_text: String,
864
865 #[serde(default = "crate::defaults::initial_text_delay_ms")]
867 pub initial_text_delay_ms: u64,
868
869 #[serde(default = "crate::defaults::initial_text_send_newline")]
871 pub initial_text_send_newline: bool,
872
873 #[serde(default = "crate::defaults::answerback_string")]
879 pub answerback_string: String,
880
881 #[serde(default = "crate::defaults::bool_false")]
886 pub prompt_on_quit: bool,
887
888 #[serde(default = "crate::defaults::bool_false")]
892 pub confirm_close_running_jobs: bool,
893
894 #[serde(default = "crate::defaults::jobs_to_ignore")]
899 pub jobs_to_ignore: Vec<String>,
900
901 #[serde(default = "crate::defaults::bool_true")]
907 pub semantic_history_enabled: bool,
908
909 #[serde(default)]
915 pub semantic_history_editor_mode: SemanticHistoryEditorMode,
916
917 #[serde(default = "crate::defaults::semantic_history_editor")]
927 pub semantic_history_editor: String,
928
929 #[serde(default = "crate::defaults::link_highlight_color")]
931 pub link_highlight_color: [u8; 3],
932
933 #[serde(default = "crate::defaults::bool_true")]
935 pub link_highlight_underline: bool,
936
937 #[serde(default)]
939 pub link_underline_style: crate::types::LinkUnderlineStyle,
940
941 #[serde(default)]
952 pub link_handler_command: String,
953
954 #[serde(default = "crate::defaults::scrollbar_position")]
959 pub scrollbar_position: String,
960
961 #[serde(default = "crate::defaults::scrollbar_width")]
963 pub scrollbar_width: f32,
964
965 #[serde(default = "crate::defaults::scrollbar_thumb_color")]
967 pub scrollbar_thumb_color: [f32; 4],
968
969 #[serde(default = "crate::defaults::scrollbar_track_color")]
971 pub scrollbar_track_color: [f32; 4],
972
973 #[serde(default = "crate::defaults::bool_true")]
975 pub scrollbar_command_marks: bool,
976
977 #[serde(default = "crate::defaults::bool_false")]
979 pub scrollbar_mark_tooltips: bool,
980
981 #[serde(default = "crate::defaults::bool_false")]
986 pub command_separator_enabled: bool,
987
988 #[serde(default = "crate::defaults::command_separator_thickness")]
990 pub command_separator_thickness: f32,
991
992 #[serde(default = "crate::defaults::command_separator_opacity")]
994 pub command_separator_opacity: f32,
995
996 #[serde(default = "crate::defaults::bool_true")]
998 pub command_separator_exit_color: bool,
999
1000 #[serde(default = "crate::defaults::command_separator_color")]
1002 pub command_separator_color: [u8; 3],
1003
1004 #[serde(
1009 default = "crate::defaults::clipboard_max_sync_events",
1010 alias = "max_clipboard_sync_events"
1011 )]
1012 pub clipboard_max_sync_events: usize,
1013
1014 #[serde(
1016 default = "crate::defaults::clipboard_max_event_bytes",
1017 alias = "max_clipboard_event_bytes"
1018 )]
1019 pub clipboard_max_event_bytes: usize,
1020
1021 #[serde(default = "crate::defaults::command_history_max_entries")]
1026 pub command_history_max_entries: usize,
1027
1028 #[serde(default = "crate::defaults::bool_false", alias = "bell_desktop")]
1033 pub notification_bell_desktop: bool,
1034
1035 #[serde(default = "crate::defaults::bell_sound", alias = "bell_sound")]
1037 pub notification_bell_sound: u8,
1038
1039 #[serde(default = "crate::defaults::bool_true", alias = "bell_visual")]
1041 pub notification_bell_visual: bool,
1042
1043 #[serde(
1045 default = "crate::defaults::bool_false",
1046 alias = "activity_notifications"
1047 )]
1048 pub notification_activity_enabled: bool,
1049
1050 #[serde(
1052 default = "crate::defaults::activity_threshold",
1053 alias = "activity_threshold"
1054 )]
1055 pub notification_activity_threshold: u64,
1056
1057 #[serde(default = "crate::defaults::bool_false")]
1059 pub anti_idle_enabled: bool,
1060
1061 #[serde(default = "crate::defaults::anti_idle_seconds")]
1063 pub anti_idle_seconds: u64,
1064
1065 #[serde(default = "crate::defaults::anti_idle_code")]
1067 pub anti_idle_code: u8,
1068
1069 #[serde(
1071 default = "crate::defaults::bool_false",
1072 alias = "silence_notifications"
1073 )]
1074 pub notification_silence_enabled: bool,
1075
1076 #[serde(
1078 default = "crate::defaults::silence_threshold",
1079 alias = "silence_threshold"
1080 )]
1081 pub notification_silence_threshold: u64,
1082
1083 #[serde(default = "crate::defaults::bool_false", alias = "session_ended")]
1085 pub notification_session_ended: bool,
1086
1087 #[serde(default = "crate::defaults::bool_true")]
1089 pub suppress_notifications_when_focused: bool,
1090
1091 #[serde(
1093 default = "crate::defaults::notification_max_buffer",
1094 alias = "max_notifications"
1095 )]
1096 pub notification_max_buffer: usize,
1097
1098 #[serde(default)]
1101 pub alert_sounds: HashMap<AlertEvent, AlertSoundConfig>,
1102
1103 #[serde(default = "crate::defaults::bool_false")]
1108 pub enable_mdns_discovery: bool,
1109
1110 #[serde(default = "crate::defaults::mdns_timeout")]
1112 pub mdns_scan_timeout_secs: u32,
1113
1114 #[serde(default = "crate::defaults::bool_true")]
1116 pub ssh_auto_profile_switch: bool,
1117
1118 #[serde(default = "crate::defaults::bool_true")]
1120 pub ssh_revert_profile_on_disconnect: bool,
1121
1122 #[serde(default)]
1128 pub tab_style: TabStyle,
1129
1130 #[serde(default = "crate::defaults::light_tab_style")]
1132 pub light_tab_style: TabStyle,
1133
1134 #[serde(default = "crate::defaults::dark_tab_style")]
1136 pub dark_tab_style: TabStyle,
1137
1138 #[serde(default)]
1140 pub tab_bar_mode: TabBarMode,
1141
1142 #[serde(default)]
1144 pub tab_title_mode: TabTitleMode,
1145
1146 #[serde(default = "crate::defaults::tab_bar_height")]
1148 pub tab_bar_height: f32,
1149
1150 #[serde(default)]
1152 pub tab_bar_position: TabBarPosition,
1153
1154 #[serde(default = "crate::defaults::tab_bar_width")]
1156 pub tab_bar_width: f32,
1157
1158 #[serde(default = "crate::defaults::bool_true")]
1160 pub tab_show_close_button: bool,
1161
1162 #[serde(default = "crate::defaults::bool_false")]
1164 pub tab_show_index: bool,
1165
1166 #[serde(default = "crate::defaults::bool_true")]
1168 pub tab_inherit_cwd: bool,
1169
1170 #[serde(default = "crate::defaults::zero")]
1172 pub max_tabs: usize,
1173
1174 #[serde(default = "crate::defaults::bool_false")]
1177 pub show_profile_drawer_button: bool,
1178
1179 #[serde(default = "crate::defaults::bool_false")]
1182 pub new_tab_shortcut_shows_profiles: bool,
1183
1184 #[serde(default = "crate::defaults::tab_bar_background")]
1189 pub tab_bar_background: [u8; 3],
1190
1191 #[serde(default = "crate::defaults::tab_active_background")]
1193 pub tab_active_background: [u8; 3],
1194
1195 #[serde(default = "crate::defaults::tab_inactive_background")]
1197 pub tab_inactive_background: [u8; 3],
1198
1199 #[serde(default = "crate::defaults::tab_hover_background")]
1201 pub tab_hover_background: [u8; 3],
1202
1203 #[serde(default = "crate::defaults::tab_active_text")]
1205 pub tab_active_text: [u8; 3],
1206
1207 #[serde(default = "crate::defaults::tab_inactive_text")]
1209 pub tab_inactive_text: [u8; 3],
1210
1211 #[serde(default = "crate::defaults::tab_active_indicator")]
1213 pub tab_active_indicator: [u8; 3],
1214
1215 #[serde(default = "crate::defaults::tab_activity_indicator")]
1217 pub tab_activity_indicator: [u8; 3],
1218
1219 #[serde(default = "crate::defaults::tab_bell_indicator")]
1221 pub tab_bell_indicator: [u8; 3],
1222
1223 #[serde(default = "crate::defaults::tab_close_button")]
1225 pub tab_close_button: [u8; 3],
1226
1227 #[serde(default = "crate::defaults::tab_close_button_hover")]
1229 pub tab_close_button_hover: [u8; 3],
1230
1231 #[serde(default = "crate::defaults::bool_true")]
1234 pub dim_inactive_tabs: bool,
1235
1236 #[serde(default = "crate::defaults::inactive_tab_opacity")]
1240 pub inactive_tab_opacity: f32,
1241
1242 #[serde(default = "crate::defaults::tab_min_width")]
1245 pub tab_min_width: f32,
1246
1247 #[serde(default = "crate::defaults::tab_stretch_to_fill")]
1250 pub tab_stretch_to_fill: bool,
1251
1252 #[serde(default = "crate::defaults::tab_html_titles")]
1255 pub tab_html_titles: bool,
1256
1257 #[serde(default = "crate::defaults::tab_border_color")]
1260 pub tab_border_color: [u8; 3],
1261
1262 #[serde(default = "crate::defaults::tab_border_width")]
1264 pub tab_border_width: f32,
1265
1266 #[serde(default = "crate::defaults::bool_false")]
1270 pub tab_inactive_outline_only: bool,
1271
1272 #[serde(default = "crate::defaults::pane_divider_width")]
1277 pub pane_divider_width: Option<f32>,
1278
1279 #[serde(default = "crate::defaults::pane_divider_hit_width")]
1282 pub pane_divider_hit_width: f32,
1283
1284 #[serde(default = "crate::defaults::pane_padding")]
1286 pub pane_padding: f32,
1287
1288 #[serde(default = "crate::defaults::pane_min_size")]
1291 pub pane_min_size: usize,
1292
1293 #[serde(default = "crate::defaults::pane_background_opacity")]
1296 pub pane_background_opacity: f32,
1297
1298 #[serde(default = "crate::defaults::pane_divider_color")]
1300 pub pane_divider_color: [u8; 3],
1301
1302 #[serde(default = "crate::defaults::pane_divider_hover_color")]
1304 pub pane_divider_hover_color: [u8; 3],
1305
1306 #[serde(default = "crate::defaults::bool_false")]
1308 pub dim_inactive_panes: bool,
1309
1310 #[serde(default = "crate::defaults::inactive_pane_opacity")]
1312 pub inactive_pane_opacity: f32,
1313
1314 #[serde(default = "crate::defaults::bool_false")]
1316 pub show_pane_titles: bool,
1317
1318 #[serde(default = "crate::defaults::pane_title_height")]
1320 pub pane_title_height: f32,
1321
1322 #[serde(default)]
1324 pub pane_title_position: PaneTitlePosition,
1325
1326 #[serde(default = "crate::defaults::pane_title_color")]
1328 pub pane_title_color: [u8; 3],
1329
1330 #[serde(default = "crate::defaults::pane_title_bg_color")]
1332 pub pane_title_bg_color: [u8; 3],
1333
1334 #[serde(default)]
1336 pub pane_title_font: String,
1337
1338 #[serde(default)]
1340 pub pane_divider_style: DividerStyle,
1341
1342 #[serde(default = "crate::defaults::max_panes")]
1344 pub max_panes: usize,
1345
1346 #[serde(default = "crate::defaults::bool_true")]
1348 pub pane_focus_indicator: bool,
1349
1350 #[serde(default = "crate::defaults::pane_focus_color")]
1352 pub pane_focus_color: [u8; 3],
1353
1354 #[serde(default = "crate::defaults::pane_focus_width")]
1356 pub pane_focus_width: f32,
1357
1358 #[serde(default = "crate::defaults::bool_false")]
1363 pub tmux_enabled: bool,
1364
1365 #[serde(default = "crate::defaults::tmux_path")]
1367 pub tmux_path: String,
1368
1369 #[serde(default = "crate::defaults::tmux_default_session")]
1371 pub tmux_default_session: Option<String>,
1372
1373 #[serde(default = "crate::defaults::bool_false")]
1375 pub tmux_auto_attach: bool,
1376
1377 #[serde(default = "crate::defaults::tmux_auto_attach_session")]
1379 pub tmux_auto_attach_session: Option<String>,
1380
1381 #[serde(default = "crate::defaults::bool_true")]
1384 pub tmux_clipboard_sync: bool,
1385
1386 #[serde(default)]
1390 pub tmux_profile: Option<String>,
1391
1392 #[serde(default = "crate::defaults::bool_false")]
1395 pub tmux_show_status_bar: bool,
1396
1397 #[serde(default = "crate::defaults::tmux_status_bar_refresh_ms")]
1402 pub tmux_status_bar_refresh_ms: u64,
1403
1404 #[serde(default = "crate::defaults::tmux_prefix_key")]
1409 pub tmux_prefix_key: String,
1410
1411 #[serde(default = "crate::defaults::bool_false")]
1416 pub tmux_status_bar_use_native_format: bool,
1417
1418 #[serde(default = "crate::defaults::tmux_status_bar_left")]
1430 pub tmux_status_bar_left: String,
1431
1432 #[serde(default = "crate::defaults::tmux_status_bar_right")]
1438 pub tmux_status_bar_right: String,
1439
1440 #[serde(default = "crate::defaults::bool_true")]
1446 pub pause_shaders_on_blur: bool,
1447
1448 #[serde(default = "crate::defaults::bool_false")]
1451 pub pause_refresh_on_blur: bool,
1452
1453 #[serde(default = "crate::defaults::unfocused_fps")]
1456 pub unfocused_fps: u32,
1457
1458 #[serde(default = "crate::defaults::inactive_tab_fps")]
1463 pub inactive_tab_fps: u32,
1464
1465 #[serde(default = "crate::defaults::bool_false")]
1471 pub shader_hot_reload: bool,
1472
1473 #[serde(default = "crate::defaults::shader_hot_reload_delay")]
1476 pub shader_hot_reload_delay: u64,
1477
1478 #[serde(default)]
1484 pub shader_configs: HashMap<String, ShaderConfig>,
1485
1486 #[serde(default)]
1488 pub cursor_shader_configs: HashMap<String, CursorShaderConfig>,
1489
1490 #[serde(default = "crate::defaults::keybindings")]
1496 pub keybindings: Vec<KeyBinding>,
1497
1498 #[serde(default)]
1506 pub shader_install_prompt: ShaderInstallPrompt,
1507
1508 #[serde(default)]
1510 pub shell_integration_state: InstallPromptState,
1511
1512 #[serde(default)]
1514 pub integration_versions: IntegrationVersions,
1515
1516 #[serde(default = "crate::defaults::update_check_frequency")]
1525 pub update_check_frequency: UpdateCheckFrequency,
1526
1527 #[serde(default)]
1529 pub last_update_check: Option<String>,
1530
1531 #[serde(default)]
1533 pub skipped_version: Option<String>,
1534
1535 #[serde(default)]
1537 pub last_notified_version: Option<String>,
1538
1539 #[serde(default, skip_serializing_if = "Option::is_none")]
1544 pub auto_restore_arrangement: Option<String>,
1545
1546 #[serde(default = "crate::defaults::bool_false")]
1548 pub restore_session: bool,
1549
1550 #[serde(default = "crate::defaults::session_undo_timeout_secs")]
1552 pub session_undo_timeout_secs: u32,
1553
1554 #[serde(default = "crate::defaults::session_undo_max_entries")]
1556 pub session_undo_max_entries: usize,
1557
1558 #[serde(default = "crate::defaults::session_undo_preserve_shell")]
1561 pub session_undo_preserve_shell: bool,
1562
1563 #[serde(default = "crate::defaults::search_highlight_color")]
1568 pub search_highlight_color: [u8; 4],
1569
1570 #[serde(default = "crate::defaults::search_current_highlight_color")]
1572 pub search_current_highlight_color: [u8; 4],
1573
1574 #[serde(default = "crate::defaults::bool_false")]
1576 pub search_case_sensitive: bool,
1577
1578 #[serde(default = "crate::defaults::bool_false")]
1580 pub search_regex: bool,
1581
1582 #[serde(default = "crate::defaults::bool_true")]
1584 pub search_wrap_around: bool,
1585
1586 #[serde(default = "crate::defaults::bool_false")]
1592 pub auto_log_sessions: bool,
1593
1594 #[serde(default)]
1599 pub session_log_format: SessionLogFormat,
1600
1601 #[serde(default = "crate::defaults::session_log_directory")]
1604 pub session_log_directory: String,
1605
1606 #[serde(default = "crate::defaults::bool_true")]
1609 pub archive_on_close: bool,
1610
1611 #[serde(default)]
1618 pub log_level: LogLevel,
1619
1620 #[serde(default = "crate::defaults::bool_false")]
1625 pub badge_enabled: bool,
1626
1627 #[serde(default = "crate::defaults::badge_format")]
1630 pub badge_format: String,
1631
1632 #[serde(default = "crate::defaults::badge_color")]
1634 pub badge_color: [u8; 3],
1635
1636 #[serde(default = "crate::defaults::badge_color_alpha")]
1638 pub badge_color_alpha: f32,
1639
1640 #[serde(default = "crate::defaults::badge_font")]
1642 pub badge_font: String,
1643
1644 #[serde(default = "crate::defaults::bool_true")]
1646 pub badge_font_bold: bool,
1647
1648 #[serde(default = "crate::defaults::badge_top_margin")]
1650 pub badge_top_margin: f32,
1651
1652 #[serde(default = "crate::defaults::badge_right_margin")]
1654 pub badge_right_margin: f32,
1655
1656 #[serde(default = "crate::defaults::badge_max_width")]
1658 pub badge_max_width: f32,
1659
1660 #[serde(default = "crate::defaults::badge_max_height")]
1662 pub badge_max_height: f32,
1663
1664 #[serde(default = "crate::defaults::bool_false")]
1669 pub status_bar_enabled: bool,
1670
1671 #[serde(default)]
1673 pub status_bar_position: StatusBarPosition,
1674
1675 #[serde(default = "crate::defaults::status_bar_height")]
1677 pub status_bar_height: f32,
1678
1679 #[serde(default = "crate::defaults::status_bar_bg_color")]
1681 pub status_bar_bg_color: [u8; 3],
1682
1683 #[serde(default = "crate::defaults::status_bar_bg_alpha")]
1685 pub status_bar_bg_alpha: f32,
1686
1687 #[serde(default = "crate::defaults::status_bar_fg_color")]
1689 pub status_bar_fg_color: [u8; 3],
1690
1691 #[serde(default)]
1693 pub status_bar_font: String,
1694
1695 #[serde(default = "crate::defaults::status_bar_font_size")]
1697 pub status_bar_font_size: f32,
1698
1699 #[serde(default = "crate::defaults::status_bar_separator")]
1701 pub status_bar_separator: String,
1702
1703 #[serde(default = "crate::defaults::bool_true")]
1705 pub status_bar_auto_hide_fullscreen: bool,
1706
1707 #[serde(default = "crate::defaults::bool_false")]
1709 pub status_bar_auto_hide_mouse_inactive: bool,
1710
1711 #[serde(default = "crate::defaults::status_bar_mouse_inactive_timeout")]
1713 pub status_bar_mouse_inactive_timeout: f32,
1714
1715 #[serde(default = "crate::defaults::status_bar_system_poll_interval")]
1717 pub status_bar_system_poll_interval: f32,
1718
1719 #[serde(default = "crate::defaults::status_bar_git_poll_interval")]
1721 pub status_bar_git_poll_interval: f32,
1722
1723 #[serde(default = "crate::defaults::status_bar_time_format")]
1725 pub status_bar_time_format: String,
1726
1727 #[serde(default = "crate::defaults::bool_true")]
1729 pub status_bar_git_show_status: bool,
1730
1731 #[serde(default = "crate::status_bar::default_widgets")]
1733 pub status_bar_widgets: Vec<crate::status_bar::StatusBarWidgetConfig>,
1734
1735 #[serde(default = "crate::defaults::bool_true")]
1741 pub progress_bar_enabled: bool,
1742
1743 #[serde(default)]
1747 pub progress_bar_style: ProgressBarStyle,
1748
1749 #[serde(default)]
1753 pub progress_bar_position: ProgressBarPosition,
1754
1755 #[serde(default = "crate::defaults::progress_bar_height")]
1757 pub progress_bar_height: f32,
1758
1759 #[serde(default = "crate::defaults::progress_bar_opacity")]
1761 pub progress_bar_opacity: f32,
1762
1763 #[serde(default = "crate::defaults::progress_bar_normal_color")]
1765 pub progress_bar_normal_color: [u8; 3],
1766
1767 #[serde(default = "crate::defaults::progress_bar_warning_color")]
1769 pub progress_bar_warning_color: [u8; 3],
1770
1771 #[serde(default = "crate::defaults::progress_bar_error_color")]
1773 pub progress_bar_error_color: [u8; 3],
1774
1775 #[serde(default = "crate::defaults::progress_bar_indeterminate_color")]
1777 pub progress_bar_indeterminate_color: [u8; 3],
1778
1779 #[serde(default)]
1784 pub triggers: Vec<crate::automation::TriggerConfig>,
1785
1786 #[serde(default)]
1788 pub coprocesses: Vec<crate::automation::CoprocessDefConfig>,
1789
1790 #[serde(default)]
1792 pub scripts: Vec<crate::scripting::ScriptConfig>,
1793
1794 #[serde(default)]
1799 pub snippets: Vec<SnippetConfig>,
1800
1801 #[serde(default)]
1803 pub actions: Vec<CustomActionConfig>,
1804
1805 #[serde(default = "crate::defaults::bool_true")]
1811 pub enable_prettifier: bool,
1812
1813 #[serde(default)]
1815 pub content_prettifier: prettifier::PrettifierYamlConfig,
1816
1817 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1823 pub collapsed_settings_sections: Vec<String>,
1824
1825 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1830 pub dynamic_profile_sources: Vec<crate::profile::DynamicProfileSource>,
1831
1832 #[serde(default = "crate::defaults::ai_inspector_enabled")]
1837 pub ai_inspector_enabled: bool,
1838
1839 #[serde(default = "crate::defaults::ai_inspector_open_on_startup")]
1841 pub ai_inspector_open_on_startup: bool,
1842
1843 #[serde(default = "crate::defaults::ai_inspector_width")]
1845 pub ai_inspector_width: f32,
1846
1847 #[serde(default = "crate::defaults::ai_inspector_default_scope")]
1849 pub ai_inspector_default_scope: String,
1850
1851 #[serde(default = "crate::defaults::ai_inspector_view_mode")]
1853 pub ai_inspector_view_mode: String,
1854
1855 #[serde(default = "crate::defaults::ai_inspector_live_update")]
1857 pub ai_inspector_live_update: bool,
1858
1859 #[serde(default = "crate::defaults::ai_inspector_show_zones")]
1861 pub ai_inspector_show_zones: bool,
1862
1863 #[serde(default = "crate::defaults::ai_inspector_agent")]
1865 pub ai_inspector_agent: String,
1866
1867 #[serde(default = "crate::defaults::ai_inspector_auto_launch")]
1869 pub ai_inspector_auto_launch: bool,
1870
1871 #[serde(default = "crate::defaults::ai_inspector_auto_context")]
1873 pub ai_inspector_auto_context: bool,
1874
1875 #[serde(default = "crate::defaults::ai_inspector_context_max_lines")]
1877 pub ai_inspector_context_max_lines: usize,
1878
1879 #[serde(default = "crate::defaults::ai_inspector_auto_approve")]
1881 pub ai_inspector_auto_approve: bool,
1882
1883 #[serde(default = "crate::defaults::ai_inspector_agent_terminal_access")]
1885 pub ai_inspector_agent_terminal_access: bool,
1886
1887 #[serde(default = "crate::defaults::ai_inspector_agent_screenshot_access")]
1889 pub ai_inspector_agent_screenshot_access: bool,
1890
1891 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1896 pub ai_inspector_custom_agents: Vec<CustomAcpAgentConfig>,
1897}
1898
1899impl Default for Config {
1900 fn default() -> Self {
1901 Self {
1902 cols: crate::defaults::cols(),
1903 rows: crate::defaults::rows(),
1904 font_size: crate::defaults::font_size(),
1905 font_family: crate::defaults::font_family(),
1906 font_family_bold: None,
1907 font_family_italic: None,
1908 font_family_bold_italic: None,
1909 font_ranges: Vec::new(),
1910 line_spacing: crate::defaults::line_spacing(),
1911 char_spacing: crate::defaults::char_spacing(),
1912 enable_text_shaping: crate::defaults::text_shaping(),
1913 enable_ligatures: crate::defaults::bool_true(),
1914 enable_kerning: crate::defaults::bool_true(),
1915 font_antialias: crate::defaults::bool_true(),
1916 font_hinting: true,
1917 font_thin_strokes: ThinStrokesMode::default(),
1918 minimum_contrast: crate::defaults::minimum_contrast(),
1919 copy_mode_enabled: crate::defaults::bool_true(),
1920 copy_mode_auto_exit_on_yank: crate::defaults::bool_true(),
1921 copy_mode_show_status: crate::defaults::bool_true(),
1922 scrollback_lines: crate::defaults::scrollback(),
1923 unicode_version: crate::defaults::unicode_version(),
1924 ambiguous_width: crate::defaults::ambiguous_width(),
1925 normalization_form: crate::defaults::normalization_form(),
1926 cursor_blink: crate::defaults::bool_false(),
1927 cursor_blink_interval: crate::defaults::cursor_blink_interval(),
1928 cursor_style: CursorStyle::default(),
1929 cursor_color: crate::defaults::cursor_color(),
1930 cursor_text_color: None,
1931 lock_cursor_visibility: crate::defaults::bool_false(),
1932 lock_cursor_style: crate::defaults::bool_false(),
1933 lock_cursor_blink: crate::defaults::bool_false(),
1934 cursor_guide_enabled: crate::defaults::bool_false(),
1935 cursor_guide_color: crate::defaults::cursor_guide_color(),
1936 cursor_shadow_enabled: crate::defaults::bool_false(),
1937 cursor_shadow_color: crate::defaults::cursor_shadow_color(),
1938 cursor_shadow_offset: crate::defaults::cursor_shadow_offset(),
1939 cursor_shadow_blur: crate::defaults::cursor_shadow_blur(),
1940 cursor_boost: crate::defaults::cursor_boost(),
1941 cursor_boost_color: crate::defaults::cursor_boost_color(),
1942 unfocused_cursor_style: UnfocusedCursorStyle::default(),
1943 scrollbar_autohide_delay: crate::defaults::scrollbar_autohide_delay(),
1944 window_title: crate::defaults::window_title(),
1945 allow_title_change: crate::defaults::bool_true(),
1946 theme: crate::defaults::theme(),
1947 auto_dark_mode: false,
1948 light_theme: crate::defaults::light_theme(),
1949 dark_theme: crate::defaults::dark_theme(),
1950 left_option_key_mode: OptionKeyMode::default(),
1951 right_option_key_mode: OptionKeyMode::default(),
1952 modifier_remapping: ModifierRemapping::default(),
1953 use_physical_keys: crate::defaults::bool_false(),
1954 auto_copy_selection: crate::defaults::bool_true(),
1955 copy_trailing_newline: crate::defaults::bool_false(),
1956 middle_click_paste: crate::defaults::bool_true(),
1957 paste_delay_ms: crate::defaults::paste_delay_ms(),
1958 dropped_file_quote_style: DroppedFileQuoteStyle::default(),
1959 mouse_scroll_speed: crate::defaults::scroll_speed(),
1960 mouse_double_click_threshold: crate::defaults::double_click_threshold(),
1961 mouse_triple_click_threshold: crate::defaults::triple_click_threshold(),
1962 option_click_moves_cursor: crate::defaults::bool_true(),
1963 focus_follows_mouse: crate::defaults::bool_false(),
1964 report_horizontal_scroll: crate::defaults::bool_true(),
1965 word_characters: crate::defaults::word_characters(),
1966 smart_selection_enabled: crate::defaults::smart_selection_enabled(),
1967 smart_selection_rules: default_smart_selection_rules(),
1968 screenshot_format: crate::defaults::screenshot_format(),
1969 max_fps: crate::defaults::max_fps(),
1970 vsync_mode: VsyncMode::default(),
1971 power_preference: PowerPreference::default(),
1972 reduce_flicker: crate::defaults::reduce_flicker(),
1973 reduce_flicker_delay_ms: crate::defaults::reduce_flicker_delay_ms(),
1974 maximize_throughput: crate::defaults::maximize_throughput(),
1975 throughput_render_interval_ms: crate::defaults::throughput_render_interval_ms(),
1976 window_padding: crate::defaults::window_padding(),
1977 hide_window_padding_on_split: crate::defaults::bool_true(),
1978 window_opacity: crate::defaults::window_opacity(),
1979 window_always_on_top: crate::defaults::bool_false(),
1980 window_decorations: crate::defaults::bool_true(),
1981 window_type: WindowType::default(),
1982 target_monitor: None,
1983 target_space: None,
1984 lock_window_size: crate::defaults::bool_false(),
1985 show_window_number: crate::defaults::bool_false(),
1986 transparency_affects_only_default_background: crate::defaults::bool_true(),
1987 keep_text_opaque: crate::defaults::bool_true(),
1988 blur_enabled: crate::defaults::bool_false(),
1989 blur_radius: crate::defaults::blur_radius(),
1990 background_image: None,
1991 background_image_enabled: crate::defaults::bool_true(),
1992 background_image_mode: BackgroundImageMode::default(),
1993 background_image_opacity: crate::defaults::background_image_opacity(),
1994 image_scaling_mode: ImageScalingMode::default(),
1995 image_preserve_aspect_ratio: crate::defaults::bool_true(),
1996 background_mode: BackgroundMode::default(),
1997 pane_backgrounds: Vec::new(),
1998 background_color: crate::defaults::background_color(),
1999 download_save_location: DownloadSaveLocation::default(),
2000 last_download_directory: None,
2001 custom_shader: None,
2002 custom_shader_enabled: crate::defaults::bool_true(),
2003 custom_shader_animation: crate::defaults::bool_true(),
2004 custom_shader_animation_speed: crate::defaults::custom_shader_speed(),
2005 custom_shader_text_opacity: crate::defaults::text_opacity(),
2006 custom_shader_full_content: crate::defaults::bool_false(),
2007 custom_shader_brightness: crate::defaults::custom_shader_brightness(),
2008 custom_shader_channel0: None,
2009 custom_shader_channel1: None,
2010 custom_shader_channel2: None,
2011 custom_shader_channel3: None,
2012 custom_shader_cubemap: None,
2013 custom_shader_cubemap_enabled: crate::defaults::cubemap_enabled(),
2014 custom_shader_use_background_as_channel0: crate::defaults::use_background_as_channel0(),
2015 cursor_shader: None,
2016 cursor_shader_enabled: crate::defaults::bool_false(),
2017 cursor_shader_animation: crate::defaults::bool_true(),
2018 cursor_shader_animation_speed: crate::defaults::custom_shader_speed(),
2019 cursor_shader_color: crate::defaults::cursor_shader_color(),
2020 cursor_shader_trail_duration: crate::defaults::cursor_trail_duration(),
2021 cursor_shader_glow_radius: crate::defaults::cursor_glow_radius(),
2022 cursor_shader_glow_intensity: crate::defaults::cursor_glow_intensity(),
2023 cursor_shader_hides_cursor: crate::defaults::bool_false(),
2024 cursor_shader_disable_in_alt_screen:
2025 crate::defaults::cursor_shader_disable_in_alt_screen(),
2026 shell_exit_action: ShellExitAction::default(),
2027 custom_shell: None,
2028 shell_args: None,
2029 working_directory: None,
2030 startup_directory_mode: StartupDirectoryMode::default(),
2031 startup_directory: None,
2032 last_working_directory: None,
2033 shell_env: None,
2034 login_shell: crate::defaults::login_shell(),
2035 initial_text: crate::defaults::initial_text(),
2036 initial_text_delay_ms: crate::defaults::initial_text_delay_ms(),
2037 initial_text_send_newline: crate::defaults::initial_text_send_newline(),
2038 answerback_string: crate::defaults::answerback_string(),
2039 prompt_on_quit: crate::defaults::bool_false(),
2040 confirm_close_running_jobs: crate::defaults::bool_false(),
2041 jobs_to_ignore: crate::defaults::jobs_to_ignore(),
2042 semantic_history_enabled: crate::defaults::bool_true(),
2043 semantic_history_editor_mode: SemanticHistoryEditorMode::default(),
2044 semantic_history_editor: crate::defaults::semantic_history_editor(),
2045 link_highlight_color: crate::defaults::link_highlight_color(),
2046 link_highlight_underline: crate::defaults::bool_true(),
2047 link_underline_style: crate::types::LinkUnderlineStyle::default(),
2048 link_handler_command: String::new(),
2049 scrollbar_position: crate::defaults::scrollbar_position(),
2050 scrollbar_width: crate::defaults::scrollbar_width(),
2051 scrollbar_thumb_color: crate::defaults::scrollbar_thumb_color(),
2052 scrollbar_track_color: crate::defaults::scrollbar_track_color(),
2053 scrollbar_command_marks: crate::defaults::bool_true(),
2054 scrollbar_mark_tooltips: crate::defaults::bool_false(),
2055 command_separator_enabled: crate::defaults::bool_false(),
2056 command_separator_thickness: crate::defaults::command_separator_thickness(),
2057 command_separator_opacity: crate::defaults::command_separator_opacity(),
2058 command_separator_exit_color: crate::defaults::bool_true(),
2059 command_separator_color: crate::defaults::command_separator_color(),
2060 clipboard_max_sync_events: crate::defaults::clipboard_max_sync_events(),
2061 clipboard_max_event_bytes: crate::defaults::clipboard_max_event_bytes(),
2062 command_history_max_entries: crate::defaults::command_history_max_entries(),
2063 notification_bell_desktop: crate::defaults::bool_false(),
2064 notification_bell_sound: crate::defaults::bell_sound(),
2065 notification_bell_visual: crate::defaults::bool_true(),
2066 notification_activity_enabled: crate::defaults::bool_false(),
2067 notification_activity_threshold: crate::defaults::activity_threshold(),
2068 anti_idle_enabled: crate::defaults::bool_false(),
2069 anti_idle_seconds: crate::defaults::anti_idle_seconds(),
2070 anti_idle_code: crate::defaults::anti_idle_code(),
2071 notification_silence_enabled: crate::defaults::bool_false(),
2072 notification_silence_threshold: crate::defaults::silence_threshold(),
2073 notification_session_ended: crate::defaults::bool_false(),
2074 suppress_notifications_when_focused: crate::defaults::bool_true(),
2075 notification_max_buffer: crate::defaults::notification_max_buffer(),
2076 alert_sounds: HashMap::new(),
2077 enable_mdns_discovery: crate::defaults::bool_false(),
2078 mdns_scan_timeout_secs: crate::defaults::mdns_timeout(),
2079 ssh_auto_profile_switch: crate::defaults::bool_true(),
2080 ssh_revert_profile_on_disconnect: crate::defaults::bool_true(),
2081 tab_style: TabStyle::default(),
2082 light_tab_style: crate::defaults::light_tab_style(),
2083 dark_tab_style: crate::defaults::dark_tab_style(),
2084 tab_bar_mode: TabBarMode::default(),
2085 tab_title_mode: TabTitleMode::default(),
2086 tab_bar_height: crate::defaults::tab_bar_height(),
2087 tab_bar_position: TabBarPosition::default(),
2088 tab_bar_width: crate::defaults::tab_bar_width(),
2089 tab_show_close_button: crate::defaults::bool_true(),
2090 tab_show_index: crate::defaults::bool_false(),
2091 tab_inherit_cwd: crate::defaults::bool_true(),
2092 max_tabs: crate::defaults::zero(),
2093 show_profile_drawer_button: crate::defaults::bool_false(),
2094 new_tab_shortcut_shows_profiles: crate::defaults::bool_false(),
2095 tab_bar_background: crate::defaults::tab_bar_background(),
2096 tab_active_background: crate::defaults::tab_active_background(),
2097 tab_inactive_background: crate::defaults::tab_inactive_background(),
2098 tab_hover_background: crate::defaults::tab_hover_background(),
2099 tab_active_text: crate::defaults::tab_active_text(),
2100 tab_inactive_text: crate::defaults::tab_inactive_text(),
2101 tab_active_indicator: crate::defaults::tab_active_indicator(),
2102 tab_activity_indicator: crate::defaults::tab_activity_indicator(),
2103 tab_bell_indicator: crate::defaults::tab_bell_indicator(),
2104 tab_close_button: crate::defaults::tab_close_button(),
2105 tab_close_button_hover: crate::defaults::tab_close_button_hover(),
2106 dim_inactive_tabs: crate::defaults::bool_true(),
2107 inactive_tab_opacity: crate::defaults::inactive_tab_opacity(),
2108 tab_min_width: crate::defaults::tab_min_width(),
2109 tab_stretch_to_fill: crate::defaults::tab_stretch_to_fill(),
2110 tab_html_titles: crate::defaults::tab_html_titles(),
2111 tab_border_color: crate::defaults::tab_border_color(),
2112 tab_border_width: crate::defaults::tab_border_width(),
2113 tab_inactive_outline_only: crate::defaults::bool_false(),
2114 pane_divider_width: crate::defaults::pane_divider_width(),
2116 pane_divider_hit_width: crate::defaults::pane_divider_hit_width(),
2117 pane_padding: crate::defaults::pane_padding(),
2118 pane_min_size: crate::defaults::pane_min_size(),
2119 pane_background_opacity: crate::defaults::pane_background_opacity(),
2120 pane_divider_color: crate::defaults::pane_divider_color(),
2121 pane_divider_hover_color: crate::defaults::pane_divider_hover_color(),
2122 dim_inactive_panes: crate::defaults::bool_false(),
2123 inactive_pane_opacity: crate::defaults::inactive_pane_opacity(),
2124 show_pane_titles: crate::defaults::bool_false(),
2125 pane_title_height: crate::defaults::pane_title_height(),
2126 pane_title_position: PaneTitlePosition::default(),
2127 pane_title_color: crate::defaults::pane_title_color(),
2128 pane_title_bg_color: crate::defaults::pane_title_bg_color(),
2129 pane_title_font: String::new(),
2130 pane_divider_style: DividerStyle::default(),
2131 max_panes: crate::defaults::max_panes(),
2132 pane_focus_indicator: crate::defaults::bool_true(),
2133 pane_focus_color: crate::defaults::pane_focus_color(),
2134 pane_focus_width: crate::defaults::pane_focus_width(),
2135 tmux_enabled: crate::defaults::bool_false(),
2136 tmux_path: crate::defaults::tmux_path(),
2137 tmux_default_session: crate::defaults::tmux_default_session(),
2138 tmux_auto_attach: crate::defaults::bool_false(),
2139 tmux_auto_attach_session: crate::defaults::tmux_auto_attach_session(),
2140 tmux_clipboard_sync: crate::defaults::bool_true(),
2141 tmux_profile: None,
2142 tmux_show_status_bar: crate::defaults::bool_false(),
2143 tmux_status_bar_refresh_ms: crate::defaults::tmux_status_bar_refresh_ms(),
2144 tmux_prefix_key: crate::defaults::tmux_prefix_key(),
2145 tmux_status_bar_use_native_format: crate::defaults::bool_false(),
2146 tmux_status_bar_left: crate::defaults::tmux_status_bar_left(),
2147 tmux_status_bar_right: crate::defaults::tmux_status_bar_right(),
2148 pause_shaders_on_blur: crate::defaults::bool_true(),
2149 pause_refresh_on_blur: crate::defaults::bool_false(),
2150 unfocused_fps: crate::defaults::unfocused_fps(),
2151 inactive_tab_fps: crate::defaults::inactive_tab_fps(),
2152 shader_hot_reload: crate::defaults::bool_false(),
2153 shader_hot_reload_delay: crate::defaults::shader_hot_reload_delay(),
2154 shader_configs: HashMap::new(),
2155 cursor_shader_configs: HashMap::new(),
2156 keybindings: crate::defaults::keybindings(),
2157 shader_install_prompt: ShaderInstallPrompt::default(),
2158 shell_integration_state: InstallPromptState::default(),
2159 integration_versions: IntegrationVersions::default(),
2160 update_check_frequency: crate::defaults::update_check_frequency(),
2161 last_update_check: None,
2162 skipped_version: None,
2163 last_notified_version: None,
2164 auto_restore_arrangement: None,
2165 restore_session: crate::defaults::bool_false(),
2166 session_undo_timeout_secs: crate::defaults::session_undo_timeout_secs(),
2167 session_undo_max_entries: crate::defaults::session_undo_max_entries(),
2168 session_undo_preserve_shell: crate::defaults::session_undo_preserve_shell(),
2169 search_highlight_color: crate::defaults::search_highlight_color(),
2170 search_current_highlight_color: crate::defaults::search_current_highlight_color(),
2171 search_case_sensitive: crate::defaults::bool_false(),
2172 search_regex: crate::defaults::bool_false(),
2173 search_wrap_around: crate::defaults::bool_true(),
2174 auto_log_sessions: crate::defaults::bool_false(),
2176 session_log_format: SessionLogFormat::default(),
2177 session_log_directory: crate::defaults::session_log_directory(),
2178 archive_on_close: crate::defaults::bool_true(),
2179 log_level: LogLevel::default(),
2181 badge_enabled: crate::defaults::bool_false(),
2183 badge_format: crate::defaults::badge_format(),
2184 badge_color: crate::defaults::badge_color(),
2185 badge_color_alpha: crate::defaults::badge_color_alpha(),
2186 badge_font: crate::defaults::badge_font(),
2187 badge_font_bold: crate::defaults::bool_true(),
2188 badge_top_margin: crate::defaults::badge_top_margin(),
2189 badge_right_margin: crate::defaults::badge_right_margin(),
2190 badge_max_width: crate::defaults::badge_max_width(),
2191 badge_max_height: crate::defaults::badge_max_height(),
2192 status_bar_enabled: crate::defaults::bool_false(),
2194 status_bar_position: StatusBarPosition::default(),
2195 status_bar_height: crate::defaults::status_bar_height(),
2196 status_bar_bg_color: crate::defaults::status_bar_bg_color(),
2197 status_bar_bg_alpha: crate::defaults::status_bar_bg_alpha(),
2198 status_bar_fg_color: crate::defaults::status_bar_fg_color(),
2199 status_bar_font: String::new(),
2200 status_bar_font_size: crate::defaults::status_bar_font_size(),
2201 status_bar_separator: crate::defaults::status_bar_separator(),
2202 status_bar_auto_hide_fullscreen: crate::defaults::bool_true(),
2203 status_bar_auto_hide_mouse_inactive: crate::defaults::bool_false(),
2204 status_bar_mouse_inactive_timeout: crate::defaults::status_bar_mouse_inactive_timeout(),
2205 status_bar_system_poll_interval: crate::defaults::status_bar_system_poll_interval(),
2206 status_bar_git_poll_interval: crate::defaults::status_bar_git_poll_interval(),
2207 status_bar_time_format: crate::defaults::status_bar_time_format(),
2208 status_bar_git_show_status: crate::defaults::bool_true(),
2209 status_bar_widgets: crate::status_bar::default_widgets(),
2210 progress_bar_enabled: crate::defaults::bool_true(),
2212 progress_bar_style: ProgressBarStyle::default(),
2213 progress_bar_position: ProgressBarPosition::default(),
2214 progress_bar_height: crate::defaults::progress_bar_height(),
2215 progress_bar_opacity: crate::defaults::progress_bar_opacity(),
2216 progress_bar_normal_color: crate::defaults::progress_bar_normal_color(),
2217 progress_bar_warning_color: crate::defaults::progress_bar_warning_color(),
2218 progress_bar_error_color: crate::defaults::progress_bar_error_color(),
2219 progress_bar_indeterminate_color: crate::defaults::progress_bar_indeterminate_color(),
2220 triggers: Vec::new(),
2221 coprocesses: Vec::new(),
2222 scripts: Vec::new(),
2223 snippets: Vec::new(),
2224 actions: Vec::new(),
2225 enable_prettifier: crate::defaults::bool_true(),
2227 content_prettifier: prettifier::PrettifierYamlConfig::default(),
2228 collapsed_settings_sections: Vec::new(),
2229 dynamic_profile_sources: Vec::new(),
2230 ai_inspector_enabled: crate::defaults::ai_inspector_enabled(),
2232 ai_inspector_open_on_startup: crate::defaults::ai_inspector_open_on_startup(),
2233 ai_inspector_width: crate::defaults::ai_inspector_width(),
2234 ai_inspector_default_scope: crate::defaults::ai_inspector_default_scope(),
2235 ai_inspector_view_mode: crate::defaults::ai_inspector_view_mode(),
2236 ai_inspector_live_update: crate::defaults::ai_inspector_live_update(),
2237 ai_inspector_show_zones: crate::defaults::ai_inspector_show_zones(),
2238 ai_inspector_agent: crate::defaults::ai_inspector_agent(),
2239 ai_inspector_auto_launch: crate::defaults::ai_inspector_auto_launch(),
2240 ai_inspector_auto_context: crate::defaults::ai_inspector_auto_context(),
2241 ai_inspector_context_max_lines: crate::defaults::ai_inspector_context_max_lines(),
2242 ai_inspector_auto_approve: crate::defaults::ai_inspector_auto_approve(),
2243 ai_inspector_agent_terminal_access: crate::defaults::ai_inspector_agent_terminal_access(
2244 ),
2245 ai_inspector_agent_screenshot_access:
2246 crate::defaults::ai_inspector_agent_screenshot_access(),
2247 ai_inspector_custom_agents: Vec::new(),
2248 }
2249 }
2250}
2251
2252impl Config {
2253 pub fn apply_tab_style(&mut self) {
2258 match self.tab_style {
2259 TabStyle::Dark => {
2260 self.tab_bar_background = crate::defaults::tab_bar_background();
2262 self.tab_active_background = crate::defaults::tab_active_background();
2263 self.tab_inactive_background = crate::defaults::tab_inactive_background();
2264 self.tab_hover_background = crate::defaults::tab_hover_background();
2265 self.tab_active_text = crate::defaults::tab_active_text();
2266 self.tab_inactive_text = crate::defaults::tab_inactive_text();
2267 self.tab_active_indicator = crate::defaults::tab_active_indicator();
2268 self.tab_border_color = crate::defaults::tab_border_color();
2269 self.tab_border_width = crate::defaults::tab_border_width();
2270 self.tab_bar_height = crate::defaults::tab_bar_height();
2271 }
2272 TabStyle::Light => {
2273 self.tab_bar_background = [235, 235, 235];
2274 self.tab_active_background = [255, 255, 255];
2275 self.tab_inactive_background = [225, 225, 225];
2276 self.tab_hover_background = [240, 240, 240];
2277 self.tab_active_text = [30, 30, 30];
2278 self.tab_inactive_text = [100, 100, 100];
2279 self.tab_active_indicator = [50, 120, 220];
2280 self.tab_border_color = [200, 200, 200];
2281 self.tab_border_width = 1.0;
2282 self.tab_bar_height = crate::defaults::tab_bar_height();
2283 }
2284 TabStyle::Compact => {
2285 self.tab_bar_background = [35, 35, 35];
2287 self.tab_active_background = [55, 55, 55];
2288 self.tab_inactive_background = [35, 35, 35];
2289 self.tab_hover_background = [45, 45, 45];
2290 self.tab_active_text = [240, 240, 240];
2291 self.tab_inactive_text = [160, 160, 160];
2292 self.tab_active_indicator = [80, 140, 240];
2293 self.tab_border_color = [60, 60, 60];
2294 self.tab_border_width = 0.5;
2295 self.tab_bar_height = 22.0;
2296 }
2297 TabStyle::Minimal => {
2298 self.tab_bar_background = [30, 30, 30];
2300 self.tab_active_background = [30, 30, 30];
2301 self.tab_inactive_background = [30, 30, 30];
2302 self.tab_hover_background = [40, 40, 40];
2303 self.tab_active_text = [255, 255, 255];
2304 self.tab_inactive_text = [120, 120, 120];
2305 self.tab_active_indicator = [100, 150, 255];
2306 self.tab_border_color = [30, 30, 30]; self.tab_border_width = 0.0;
2308 self.tab_bar_height = 26.0;
2309 }
2310 TabStyle::HighContrast => {
2311 self.tab_bar_background = [0, 0, 0];
2313 self.tab_active_background = [255, 255, 255];
2314 self.tab_inactive_background = [30, 30, 30];
2315 self.tab_hover_background = [60, 60, 60];
2316 self.tab_active_text = [0, 0, 0];
2317 self.tab_inactive_text = [255, 255, 255];
2318 self.tab_active_indicator = [255, 255, 0];
2319 self.tab_border_color = [255, 255, 255];
2320 self.tab_border_width = 2.0;
2321 self.tab_bar_height = 30.0;
2322 }
2323 TabStyle::Automatic => {
2324 }
2326 }
2327 }
2328
2329 pub fn load() -> Result<Self> {
2331 let config_path = Self::config_path();
2332 log::info!("Config path: {:?}", config_path);
2333
2334 if config_path.exists() {
2335 log::info!("Loading existing config from {:?}", config_path);
2336 let contents = fs::read_to_string(&config_path)?;
2337 let contents = substitute_variables(&contents);
2338 let mut config: Config = serde_yaml::from_str(&contents)?;
2339
2340 config.merge_default_keybindings();
2342
2343 config.merge_default_widgets();
2345
2346 config.generate_snippet_action_keybindings();
2348
2349 config.load_last_working_directory();
2351
2352 Ok(config)
2353 } else {
2354 log::info!(
2355 "Config file not found, creating default at {:?}",
2356 config_path
2357 );
2358 let mut config = Self::default();
2360 config.generate_snippet_action_keybindings();
2362 if let Err(e) = config.save() {
2363 log::error!("Failed to save default config: {}", e);
2364 return Err(e);
2365 }
2366
2367 config.load_last_working_directory();
2369
2370 log::info!("Default config created successfully");
2371 Ok(config)
2372 }
2373 }
2374
2375 fn merge_default_keybindings(&mut self) {
2379 let default_keybindings = crate::defaults::keybindings();
2380
2381 let existing_actions: std::collections::HashSet<String> = self
2383 .keybindings
2384 .iter()
2385 .map(|kb| kb.action.clone())
2386 .collect();
2387
2388 let mut added_count = 0;
2390 for default_kb in default_keybindings {
2391 if !existing_actions.contains(&default_kb.action) {
2392 log::info!(
2393 "Adding new default keybinding: {} -> {}",
2394 default_kb.key,
2395 default_kb.action
2396 );
2397 self.keybindings.push(default_kb);
2398 added_count += 1;
2399 }
2400 }
2401
2402 if added_count > 0 {
2403 log::info!(
2404 "Merged {} new default keybinding(s) into user config",
2405 added_count
2406 );
2407 }
2408 }
2409
2410 fn merge_default_widgets(&mut self) {
2414 let default_widgets = crate::status_bar::default_widgets();
2415
2416 let existing_ids: std::collections::HashSet<crate::status_bar::WidgetId> = self
2417 .status_bar_widgets
2418 .iter()
2419 .map(|w| w.id.clone())
2420 .collect();
2421
2422 let mut added_count = 0;
2423 for default_widget in default_widgets {
2424 if !existing_ids.contains(&default_widget.id) {
2425 log::info!(
2426 "Adding new default status bar widget: {:?}",
2427 default_widget.id
2428 );
2429 self.status_bar_widgets.push(default_widget);
2430 added_count += 1;
2431 }
2432 }
2433
2434 if added_count > 0 {
2435 log::info!(
2436 "Merged {} new default status bar widget(s) into user config",
2437 added_count
2438 );
2439 }
2440 }
2441
2442 pub fn generate_snippet_action_keybindings(&mut self) {
2448 use crate::config::KeyBinding;
2449
2450 let mut seen_actions = std::collections::HashSet::new();
2452 let mut added_count = 0;
2453 let mut updated_count = 0;
2454
2455 for snippet in &self.snippets {
2457 if let Some(key) = &snippet.keybinding {
2458 let action = format!("snippet:{}", snippet.id);
2459 seen_actions.insert(action.clone());
2460
2461 if !key.is_empty() && snippet.enabled && snippet.keybinding_enabled {
2462 if let Some(existing) =
2464 self.keybindings.iter_mut().find(|kb| kb.action == action)
2465 {
2466 if existing.key != *key {
2468 log::info!(
2469 "Updating keybinding for snippet '{}': {} -> {} (was: {})",
2470 snippet.title,
2471 key,
2472 action,
2473 existing.key
2474 );
2475 existing.key = key.clone();
2476 updated_count += 1;
2477 }
2478 } else {
2479 log::info!(
2481 "Adding keybinding for snippet '{}': {} -> {} (enabled={}, keybinding_enabled={})",
2482 snippet.title,
2483 key,
2484 action,
2485 snippet.enabled,
2486 snippet.keybinding_enabled
2487 );
2488 self.keybindings.push(KeyBinding {
2489 key: key.clone(),
2490 action,
2491 });
2492 added_count += 1;
2493 }
2494 } else if !key.is_empty() {
2495 log::info!(
2496 "Skipping keybinding for snippet '{}': {} (enabled={}, keybinding_enabled={})",
2497 snippet.title,
2498 key,
2499 snippet.enabled,
2500 snippet.keybinding_enabled
2501 );
2502 }
2503 }
2504 }
2505
2506 for action_config in &self.actions {
2508 if let Some(key) = action_config.keybinding() {
2509 let action = format!("action:{}", action_config.id());
2510 seen_actions.insert(action.clone());
2511
2512 if !key.is_empty() && action_config.keybinding_enabled() {
2513 if let Some(existing) =
2515 self.keybindings.iter_mut().find(|kb| kb.action == action)
2516 {
2517 if existing.key != key {
2519 log::info!(
2520 "Updating keybinding for action '{}': {} -> {} (was: {})",
2521 action_config.title(),
2522 key,
2523 action,
2524 existing.key
2525 );
2526 existing.key = key.to_string();
2527 updated_count += 1;
2528 }
2529 } else {
2530 log::info!(
2532 "Adding keybinding for action '{}': {} -> {} (keybinding_enabled={})",
2533 action_config.title(),
2534 key,
2535 action,
2536 action_config.keybinding_enabled()
2537 );
2538 self.keybindings.push(KeyBinding {
2539 key: key.to_string(),
2540 action,
2541 });
2542 added_count += 1;
2543 }
2544 } else if !key.is_empty() {
2545 log::info!(
2546 "Skipping keybinding for action '{}': {} (keybinding_enabled={})",
2547 action_config.title(),
2548 key,
2549 action_config.keybinding_enabled()
2550 );
2551 }
2552 }
2553 }
2554
2555 let original_len = self.keybindings.len();
2557 self.keybindings.retain(|kb| {
2558 if !kb.action.starts_with("snippet:") && !kb.action.starts_with("action:") {
2560 return true;
2561 }
2562 seen_actions.contains(&kb.action)
2564 });
2565 let removed_count = original_len - self.keybindings.len();
2566
2567 if added_count > 0 || updated_count > 0 || removed_count > 0 {
2568 log::info!(
2569 "Snippet/Action keybindings: {} added, {} updated, {} removed",
2570 added_count,
2571 updated_count,
2572 removed_count
2573 );
2574 }
2575 }
2576
2577 pub fn save(&self) -> Result<()> {
2579 let config_path = Self::config_path();
2580
2581 if let Some(parent) = config_path.parent() {
2583 fs::create_dir_all(parent)?;
2584 }
2585
2586 let yaml = serde_yaml::to_string(self)?;
2587 fs::write(&config_path, yaml)?;
2588
2589 Ok(())
2590 }
2591
2592 pub fn config_path() -> PathBuf {
2594 #[cfg(target_os = "windows")]
2595 {
2596 if let Some(config_dir) = dirs::config_dir() {
2597 config_dir.join("par-term").join("config.yaml")
2598 } else {
2599 PathBuf::from("config.yaml")
2600 }
2601 }
2602 #[cfg(not(target_os = "windows"))]
2603 {
2604 if let Some(home_dir) = dirs::home_dir() {
2606 home_dir
2607 .join(".config")
2608 .join("par-term")
2609 .join("config.yaml")
2610 } else {
2611 PathBuf::from("config.yaml")
2613 }
2614 }
2615 }
2616
2617 pub fn resolve_tmux_path(&self) -> String {
2622 let configured = &self.tmux_path;
2623
2624 if configured.starts_with('/') && std::path::Path::new(configured).exists() {
2626 return configured.clone();
2627 }
2628
2629 if configured != "tmux" {
2631 return configured.clone();
2632 }
2633
2634 if let Ok(path_env) = std::env::var("PATH") {
2636 let separator = if cfg!(windows) { ';' } else { ':' };
2637 let executable = if cfg!(windows) { "tmux.exe" } else { "tmux" };
2638
2639 for dir in path_env.split(separator) {
2640 let candidate = std::path::Path::new(dir).join(executable);
2641 if candidate.exists() {
2642 return candidate.to_string_lossy().to_string();
2643 }
2644 }
2645 }
2646
2647 #[cfg(target_os = "macos")]
2649 {
2650 let macos_paths = [
2651 "/opt/homebrew/bin/tmux", "/usr/local/bin/tmux", ];
2654 for path in macos_paths {
2655 if std::path::Path::new(path).exists() {
2656 return path.to_string();
2657 }
2658 }
2659 }
2660
2661 #[cfg(target_os = "linux")]
2662 {
2663 let linux_paths = [
2664 "/usr/bin/tmux", "/usr/local/bin/tmux", "/snap/bin/tmux", ];
2668 for path in linux_paths {
2669 if std::path::Path::new(path).exists() {
2670 return path.to_string();
2671 }
2672 }
2673 }
2674
2675 configured.clone()
2677 }
2678
2679 pub fn logs_dir(&self) -> PathBuf {
2682 let path = if self.session_log_directory.starts_with("~/") {
2683 if let Some(home) = dirs::home_dir() {
2684 home.join(&self.session_log_directory[2..])
2685 } else {
2686 PathBuf::from(&self.session_log_directory)
2687 }
2688 } else {
2689 PathBuf::from(&self.session_log_directory)
2690 };
2691
2692 if !path.exists()
2694 && let Err(e) = std::fs::create_dir_all(&path)
2695 {
2696 log::warn!("Failed to create logs directory {:?}: {}", path, e);
2697 }
2698
2699 path
2700 }
2701
2702 pub fn shaders_dir() -> PathBuf {
2704 #[cfg(target_os = "windows")]
2705 {
2706 if let Some(config_dir) = dirs::config_dir() {
2707 config_dir.join("par-term").join("shaders")
2708 } else {
2709 PathBuf::from("shaders")
2710 }
2711 }
2712 #[cfg(not(target_os = "windows"))]
2713 {
2714 if let Some(home_dir) = dirs::home_dir() {
2715 home_dir.join(".config").join("par-term").join("shaders")
2716 } else {
2717 PathBuf::from("shaders")
2718 }
2719 }
2720 }
2721
2722 pub fn shader_path(shader_name: &str) -> PathBuf {
2726 let path = PathBuf::from(shader_name);
2727 if path.is_absolute() {
2728 path
2729 } else {
2730 Self::shaders_dir().join(shader_name)
2731 }
2732 }
2733
2734 pub fn resolve_texture_path(path: &str) -> PathBuf {
2738 if path.starts_with("~/")
2739 && let Some(home) = dirs::home_dir()
2740 {
2741 return home.join(&path[2..]);
2742 }
2743 let path_buf = PathBuf::from(path);
2744 if path_buf.is_absolute() {
2745 path_buf
2746 } else {
2747 Self::shaders_dir().join(path)
2748 }
2749 }
2750
2751 #[allow(dead_code)]
2754 pub fn shader_channel_paths(&self) -> [Option<PathBuf>; 4] {
2755 [
2756 self.custom_shader_channel0
2757 .as_ref()
2758 .map(|p| Self::resolve_texture_path(p)),
2759 self.custom_shader_channel1
2760 .as_ref()
2761 .map(|p| Self::resolve_texture_path(p)),
2762 self.custom_shader_channel2
2763 .as_ref()
2764 .map(|p| Self::resolve_texture_path(p)),
2765 self.custom_shader_channel3
2766 .as_ref()
2767 .map(|p| Self::resolve_texture_path(p)),
2768 ]
2769 }
2770
2771 #[allow(dead_code)]
2774 pub fn shader_cubemap_path(&self) -> Option<PathBuf> {
2775 self.custom_shader_cubemap
2776 .as_ref()
2777 .map(|p| Self::resolve_texture_path(p))
2778 }
2779
2780 #[allow(dead_code)]
2782 pub fn with_title(mut self, title: impl Into<String>) -> Self {
2783 self.window_title = title.into();
2784 self
2785 }
2786
2787 pub fn load_theme(&self) -> Theme {
2789 Theme::by_name(&self.theme).unwrap_or_default()
2790 }
2791
2792 pub fn apply_system_theme(&mut self, is_dark: bool) -> bool {
2795 if !self.auto_dark_mode {
2796 return false;
2797 }
2798 let new_theme = if is_dark {
2799 &self.dark_theme
2800 } else {
2801 &self.light_theme
2802 };
2803 if self.theme != *new_theme {
2804 self.theme = new_theme.clone();
2805 true
2806 } else {
2807 false
2808 }
2809 }
2810
2811 pub fn apply_system_tab_style(&mut self, is_dark: bool) -> bool {
2814 if self.tab_style != TabStyle::Automatic {
2815 return false;
2816 }
2817 let target = if is_dark {
2818 self.dark_tab_style
2819 } else {
2820 self.light_tab_style
2821 };
2822 self.tab_style = target;
2824 self.apply_tab_style();
2825 self.tab_style = TabStyle::Automatic;
2826 true
2827 }
2828
2829 pub fn get_shader_override(&self, shader_name: &str) -> Option<&ShaderConfig> {
2831 self.shader_configs.get(shader_name)
2832 }
2833
2834 pub fn get_cursor_shader_override(&self, shader_name: &str) -> Option<&CursorShaderConfig> {
2836 self.cursor_shader_configs.get(shader_name)
2837 }
2838
2839 pub fn get_or_create_shader_override(&mut self, shader_name: &str) -> &mut ShaderConfig {
2841 self.shader_configs
2842 .entry(shader_name.to_string())
2843 .or_default()
2844 }
2845
2846 pub fn get_or_create_cursor_shader_override(
2848 &mut self,
2849 shader_name: &str,
2850 ) -> &mut CursorShaderConfig {
2851 self.cursor_shader_configs
2852 .entry(shader_name.to_string())
2853 .or_default()
2854 }
2855
2856 pub fn remove_shader_override(&mut self, shader_name: &str) {
2858 self.shader_configs.remove(shader_name);
2859 }
2860
2861 pub fn remove_cursor_shader_override(&mut self, shader_name: &str) {
2863 self.cursor_shader_configs.remove(shader_name);
2864 }
2865
2866 pub fn should_prompt_shader_install(&self) -> bool {
2869 if self.shader_install_prompt != ShaderInstallPrompt::Ask {
2871 return false;
2872 }
2873
2874 let shaders_dir = Self::shaders_dir();
2875
2876 if !shaders_dir.exists() {
2878 return true;
2879 }
2880
2881 if let Ok(entries) = std::fs::read_dir(&shaders_dir) {
2883 for entry in entries.flatten() {
2884 if let Some(ext) = entry.path().extension()
2885 && ext == "glsl"
2886 {
2887 return false; }
2889 }
2890 }
2891
2892 true }
2894
2895 pub fn config_dir() -> PathBuf {
2897 #[cfg(target_os = "windows")]
2898 {
2899 if let Some(config_dir) = dirs::config_dir() {
2900 config_dir.join("par-term")
2901 } else {
2902 PathBuf::from(".")
2903 }
2904 }
2905 #[cfg(not(target_os = "windows"))]
2906 {
2907 if let Some(home_dir) = dirs::home_dir() {
2908 home_dir.join(".config").join("par-term")
2909 } else {
2910 PathBuf::from(".")
2911 }
2912 }
2913 }
2914
2915 pub fn shell_integration_dir() -> PathBuf {
2917 Self::config_dir()
2918 }
2919
2920 pub fn should_prompt_shell_integration(&self) -> bool {
2922 if self.shell_integration_state != InstallPromptState::Ask {
2923 return false;
2924 }
2925
2926 let current_version = env!("CARGO_PKG_VERSION");
2927
2928 if let Some(ref prompted) = self.integration_versions.shell_integration_prompted_version
2930 && prompted == current_version
2931 {
2932 return false;
2933 }
2934
2935 if let Some(ref installed) = self
2937 .integration_versions
2938 .shell_integration_installed_version
2939 && installed == current_version
2940 {
2941 return false;
2942 }
2943
2944 true
2945 }
2946
2947 pub fn should_prompt_shader_install_versioned(&self) -> bool {
2949 if self.shader_install_prompt != ShaderInstallPrompt::Ask {
2950 return false;
2951 }
2952
2953 let current_version = env!("CARGO_PKG_VERSION");
2954
2955 if let Some(ref prompted) = self.integration_versions.shaders_prompted_version
2957 && prompted == current_version
2958 {
2959 return false;
2960 }
2961
2962 if let Some(ref installed) = self.integration_versions.shaders_installed_version
2964 && installed == current_version
2965 {
2966 return false;
2967 }
2968
2969 let shaders_dir = Self::shaders_dir();
2971 !shaders_dir.exists() || !Self::has_shader_files(&shaders_dir)
2972 }
2973
2974 fn has_shader_files(dir: &PathBuf) -> bool {
2976 if let Ok(entries) = std::fs::read_dir(dir) {
2977 for entry in entries.flatten() {
2978 if let Some(ext) = entry.path().extension()
2979 && ext == "glsl"
2980 {
2981 return true;
2982 }
2983 }
2984 }
2985 false
2986 }
2987
2988 pub fn should_prompt_integrations(&self) -> bool {
2990 self.should_prompt_shader_install_versioned() || self.should_prompt_shell_integration()
2991 }
2992
2993 pub fn get_effective_startup_directory(&self) -> Option<String> {
3004 if let Some(ref wd) = self.working_directory {
3006 let expanded = Self::expand_home_dir(wd);
3007 if std::path::Path::new(&expanded).exists() {
3008 return Some(expanded);
3009 }
3010 log::warn!(
3011 "Configured working_directory '{}' does not exist, using default",
3012 wd
3013 );
3014 }
3015
3016 match self.startup_directory_mode {
3017 StartupDirectoryMode::Home => {
3018 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
3020 }
3021 StartupDirectoryMode::Previous => {
3022 if let Some(ref last_dir) = self.last_working_directory {
3024 let expanded = Self::expand_home_dir(last_dir);
3025 if std::path::Path::new(&expanded).exists() {
3026 return Some(expanded);
3027 }
3028 log::warn!(
3029 "Previous session directory '{}' no longer exists, using home",
3030 last_dir
3031 );
3032 }
3033 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
3035 }
3036 StartupDirectoryMode::Custom => {
3037 if let Some(ref custom_dir) = self.startup_directory {
3039 let expanded = Self::expand_home_dir(custom_dir);
3040 if std::path::Path::new(&expanded).exists() {
3041 return Some(expanded);
3042 }
3043 log::warn!(
3044 "Custom startup directory '{}' does not exist, using home",
3045 custom_dir
3046 );
3047 }
3048 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
3050 }
3051 }
3052 }
3053
3054 fn expand_home_dir(path: &str) -> String {
3056 if let Some(suffix) = path.strip_prefix("~/")
3057 && let Some(home) = dirs::home_dir()
3058 {
3059 return home.join(suffix).to_string_lossy().to_string();
3060 }
3061 path.to_string()
3062 }
3063
3064 pub fn state_file_path() -> PathBuf {
3066 #[cfg(target_os = "windows")]
3067 {
3068 if let Some(data_dir) = dirs::data_local_dir() {
3069 data_dir.join("par-term").join("state.yaml")
3070 } else {
3071 PathBuf::from("state.yaml")
3072 }
3073 }
3074 #[cfg(not(target_os = "windows"))]
3075 {
3076 if let Some(home_dir) = dirs::home_dir() {
3077 home_dir
3078 .join(".local")
3079 .join("share")
3080 .join("par-term")
3081 .join("state.yaml")
3082 } else {
3083 PathBuf::from("state.yaml")
3084 }
3085 }
3086 }
3087
3088 pub fn save_last_working_directory(&mut self, directory: &str) -> Result<()> {
3090 self.last_working_directory = Some(directory.to_string());
3091
3092 let state_path = Self::state_file_path();
3094 if let Some(parent) = state_path.parent() {
3095 fs::create_dir_all(parent)?;
3096 }
3097
3098 #[derive(Serialize)]
3100 struct SessionState {
3101 last_working_directory: Option<String>,
3102 }
3103
3104 let state = SessionState {
3105 last_working_directory: Some(directory.to_string()),
3106 };
3107
3108 let yaml = serde_yaml::to_string(&state)?;
3109 fs::write(&state_path, yaml)?;
3110
3111 log::debug!(
3112 "Saved last working directory to {:?}: {}",
3113 state_path,
3114 directory
3115 );
3116 Ok(())
3117 }
3118
3119 pub fn get_pane_background(
3122 &self,
3123 index: usize,
3124 ) -> Option<(String, BackgroundImageMode, f32, f32)> {
3125 self.pane_backgrounds
3126 .iter()
3127 .find(|pb| pb.index == index)
3128 .map(|pb| (pb.image.clone(), pb.mode, pb.opacity, pb.darken))
3129 }
3130
3131 pub fn load_last_working_directory(&mut self) {
3133 let state_path = Self::state_file_path();
3134 if !state_path.exists() {
3135 return;
3136 }
3137
3138 #[derive(Deserialize)]
3139 struct SessionState {
3140 last_working_directory: Option<String>,
3141 }
3142
3143 match fs::read_to_string(&state_path) {
3144 Ok(contents) => {
3145 if let Ok(state) = serde_yaml::from_str::<SessionState>(&contents)
3146 && let Some(dir) = state.last_working_directory
3147 {
3148 log::debug!("Loaded last working directory from state file: {}", dir);
3149 self.last_working_directory = Some(dir);
3150 }
3151 }
3152 Err(e) => {
3153 log::warn!("Failed to read state file {:?}: {}", state_path, e);
3154 }
3155 }
3156 }
3157}