1use crate::snippets::{CustomActionConfig, SnippetConfig};
7use crate::themes::Theme;
8use crate::types::{
9 AlertEvent, AlertSoundConfig, BackgroundImageMode, BackgroundMode, CursorShaderConfig,
10 CursorStyle, DividerStyle, DownloadSaveLocation, DroppedFileQuoteStyle, FontRange,
11 ImageScalingMode, InstallPromptState, IntegrationVersions, KeyBinding, LogLevel,
12 ModifierRemapping, OptionKeyMode, PaneBackgroundConfig, PaneTitlePosition, PowerPreference,
13 ProgressBarPosition, ProgressBarStyle, SemanticHistoryEditorMode, SessionLogFormat,
14 ShaderConfig, ShaderInstallPrompt, ShellExitAction, SmartSelectionRule, StartupDirectoryMode,
15 StatusBarPosition, TabBarMode, TabBarPosition, TabStyle, ThinStrokesMode, UnfocusedCursorStyle,
16 UpdateCheckFrequency, VsyncMode, WindowType, default_smart_selection_rules,
17};
18
19use anyhow::Result;
20use regex::Regex;
21use serde::{Deserialize, Serialize};
22use std::collections::HashMap;
23use std::fs;
24use std::path::PathBuf;
25
26pub fn substitute_variables(input: &str) -> String {
36 let escaped_placeholder = "\x00ESC_DOLLAR\x00";
38 let working = input.replace("$${", escaped_placeholder);
39
40 let re = Regex::new(r"\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-((?:[^}\\]|\\.)*))?}")
42 .expect("invalid regex");
43
44 let result = re.replace_all(&working, |caps: ®ex::Captures| {
45 let var_name = &caps[1];
46 match std::env::var(var_name) {
47 Ok(val) => val,
48 Err(_) => {
49 caps.get(2)
51 .map(|m| m.as_str().replace("\\}", "}"))
52 .unwrap_or_else(|| caps[0].to_string())
53 }
54 }
55 });
56
57 result.replace(escaped_placeholder, "${")
59}
60
61fn deserialize_shell_exit_action<'de, D>(deserializer: D) -> Result<ShellExitAction, D::Error>
67where
68 D: serde::Deserializer<'de>,
69{
70 #[derive(Deserialize)]
71 #[serde(untagged)]
72 enum BoolOrAction {
73 Bool(bool),
74 Action(ShellExitAction),
75 }
76
77 match BoolOrAction::deserialize(deserializer)? {
78 BoolOrAction::Bool(true) => Ok(ShellExitAction::Close),
79 BoolOrAction::Bool(false) => Ok(ShellExitAction::Keep),
80 BoolOrAction::Action(action) => Ok(action),
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct Config {
88 #[serde(default = "crate::defaults::cols")]
93 pub cols: usize,
94
95 #[serde(default = "crate::defaults::rows")]
97 pub rows: usize,
98
99 #[serde(default = "crate::defaults::font_size")]
101 pub font_size: f32,
102
103 #[serde(default = "crate::defaults::font_family")]
105 pub font_family: String,
106
107 #[serde(default)]
109 pub font_family_bold: Option<String>,
110
111 #[serde(default)]
113 pub font_family_italic: Option<String>,
114
115 #[serde(default)]
117 pub font_family_bold_italic: Option<String>,
118
119 #[serde(default)]
123 pub font_ranges: Vec<FontRange>,
124
125 #[serde(default = "crate::defaults::line_spacing")]
127 pub line_spacing: f32,
128
129 #[serde(default = "crate::defaults::char_spacing")]
131 pub char_spacing: f32,
132
133 #[serde(default = "crate::defaults::text_shaping")]
136 pub enable_text_shaping: bool,
137
138 #[serde(default = "crate::defaults::bool_true")]
140 pub enable_ligatures: bool,
141
142 #[serde(default = "crate::defaults::bool_true")]
144 pub enable_kerning: bool,
145
146 #[serde(default = "crate::defaults::bool_true")]
149 pub font_antialias: bool,
150
151 #[serde(default = "crate::defaults::bool_true")]
155 pub font_hinting: bool,
156
157 #[serde(default)]
165 pub font_thin_strokes: ThinStrokesMode,
166
167 #[serde(default = "crate::defaults::minimum_contrast")]
175 pub minimum_contrast: f32,
176
177 #[serde(default = "crate::defaults::window_title")]
179 pub window_title: String,
180
181 #[serde(default = "crate::defaults::bool_true")]
184 pub allow_title_change: bool,
185
186 #[serde(default = "crate::defaults::max_fps", alias = "refresh_rate")]
192 pub max_fps: u32,
193
194 #[serde(default)]
201 pub vsync_mode: VsyncMode,
202
203 #[serde(default)]
210 pub power_preference: PowerPreference,
211
212 #[serde(default = "crate::defaults::reduce_flicker")]
215 pub reduce_flicker: bool,
216
217 #[serde(default = "crate::defaults::reduce_flicker_delay_ms")]
221 pub reduce_flicker_delay_ms: u32,
222
223 #[serde(default = "crate::defaults::maximize_throughput")]
227 pub maximize_throughput: bool,
228
229 #[serde(default = "crate::defaults::throughput_render_interval_ms")]
232 pub throughput_render_interval_ms: u32,
233
234 #[serde(default = "crate::defaults::window_padding")]
236 pub window_padding: f32,
237
238 #[serde(default = "crate::defaults::window_opacity")]
240 pub window_opacity: f32,
241
242 #[serde(default = "crate::defaults::bool_false")]
244 pub window_always_on_top: bool,
245
246 #[serde(default = "crate::defaults::bool_true")]
248 pub window_decorations: bool,
249
250 #[serde(default)]
255 pub window_type: WindowType,
256
257 #[serde(default)]
260 pub target_monitor: Option<usize>,
261
262 #[serde(default)]
266 pub target_space: Option<u32>,
267
268 #[serde(default = "crate::defaults::bool_false")]
271 pub lock_window_size: bool,
272
273 #[serde(default = "crate::defaults::bool_false")]
276 pub show_window_number: bool,
277
278 #[serde(default = "crate::defaults::bool_true")]
282 pub transparency_affects_only_default_background: bool,
283
284 #[serde(default = "crate::defaults::bool_true")]
287 pub keep_text_opaque: bool,
288
289 #[serde(default = "crate::defaults::bool_false")]
292 pub blur_enabled: bool,
293
294 #[serde(default = "crate::defaults::blur_radius")]
297 pub blur_radius: u32,
298
299 #[serde(default)]
301 pub background_image: Option<String>,
302
303 #[serde(default = "crate::defaults::bool_true")]
305 pub background_image_enabled: bool,
306
307 #[serde(default)]
314 pub background_image_mode: BackgroundImageMode,
315
316 #[serde(default = "crate::defaults::background_image_opacity")]
318 pub background_image_opacity: f32,
319
320 #[serde(default)]
325 pub image_scaling_mode: ImageScalingMode,
326
327 #[serde(default = "crate::defaults::bool_true")]
329 pub image_preserve_aspect_ratio: bool,
330
331 #[serde(default)]
333 pub background_mode: BackgroundMode,
334
335 #[serde(default)]
337 pub pane_backgrounds: Vec<crate::config::PaneBackgroundConfig>,
338
339 #[serde(default = "crate::defaults::background_color")]
343 pub background_color: [u8; 3],
344
345 #[serde(default)]
350 pub download_save_location: DownloadSaveLocation,
351
352 #[serde(default, skip_serializing_if = "Option::is_none")]
354 pub last_download_directory: Option<String>,
355
356 #[serde(default)]
360 pub custom_shader: Option<String>,
361
362 #[serde(default = "crate::defaults::bool_true")]
364 pub custom_shader_enabled: bool,
365
366 #[serde(default = "crate::defaults::bool_true")]
369 pub custom_shader_animation: bool,
370
371 #[serde(default = "crate::defaults::custom_shader_speed")]
373 pub custom_shader_animation_speed: f32,
374
375 #[serde(default = "crate::defaults::text_opacity")]
378 pub custom_shader_text_opacity: f32,
379
380 #[serde(default = "crate::defaults::bool_false")]
384 pub custom_shader_full_content: bool,
385
386 #[serde(default = "crate::defaults::custom_shader_brightness")]
389 pub custom_shader_brightness: f32,
390
391 #[serde(default)]
394 pub custom_shader_channel0: Option<String>,
395
396 #[serde(default)]
398 pub custom_shader_channel1: Option<String>,
399
400 #[serde(default)]
402 pub custom_shader_channel2: Option<String>,
403
404 #[serde(default)]
406 pub custom_shader_channel3: Option<String>,
407
408 #[serde(default)]
413 pub custom_shader_cubemap: Option<String>,
414
415 #[serde(default = "crate::defaults::cubemap_enabled")]
418 pub custom_shader_cubemap_enabled: bool,
419
420 #[serde(default = "crate::defaults::use_background_as_channel0")]
425 pub custom_shader_use_background_as_channel0: bool,
426
427 #[serde(default)]
433 pub cursor_shader: Option<String>,
434
435 #[serde(default = "crate::defaults::bool_false")]
437 pub cursor_shader_enabled: bool,
438
439 #[serde(default = "crate::defaults::bool_true")]
441 pub cursor_shader_animation: bool,
442
443 #[serde(default = "crate::defaults::custom_shader_speed")]
445 pub cursor_shader_animation_speed: f32,
446
447 #[serde(default = "crate::defaults::cursor_shader_color")]
450 pub cursor_shader_color: [u8; 3],
451
452 #[serde(default = "crate::defaults::cursor_trail_duration")]
455 pub cursor_shader_trail_duration: f32,
456
457 #[serde(default = "crate::defaults::cursor_glow_radius")]
460 pub cursor_shader_glow_radius: f32,
461
462 #[serde(default = "crate::defaults::cursor_glow_intensity")]
465 pub cursor_shader_glow_intensity: f32,
466
467 #[serde(default = "crate::defaults::bool_false")]
471 pub cursor_shader_hides_cursor: bool,
472
473 #[serde(default = "crate::defaults::cursor_shader_disable_in_alt_screen")]
476 pub cursor_shader_disable_in_alt_screen: bool,
477
478 #[serde(default)]
486 pub left_option_key_mode: OptionKeyMode,
487
488 #[serde(default)]
494 pub right_option_key_mode: OptionKeyMode,
495
496 #[serde(default)]
499 pub modifier_remapping: ModifierRemapping,
500
501 #[serde(default = "crate::defaults::bool_false")]
506 pub use_physical_keys: bool,
507
508 #[serde(default = "crate::defaults::bool_true")]
513 pub auto_copy_selection: bool,
514
515 #[serde(
518 default = "crate::defaults::bool_false",
519 alias = "strip_trailing_newline_on_copy"
520 )]
521 pub copy_trailing_newline: bool,
522
523 #[serde(default = "crate::defaults::bool_true")]
525 pub middle_click_paste: bool,
526
527 #[serde(default = "crate::defaults::paste_delay_ms")]
530 pub paste_delay_ms: u64,
531
532 #[serde(default)]
538 pub dropped_file_quote_style: DroppedFileQuoteStyle,
539
540 #[serde(default = "crate::defaults::scroll_speed")]
545 pub mouse_scroll_speed: f32,
546
547 #[serde(default = "crate::defaults::double_click_threshold")]
549 pub mouse_double_click_threshold: u64,
550
551 #[serde(default = "crate::defaults::triple_click_threshold")]
553 pub mouse_triple_click_threshold: u64,
554
555 #[serde(default = "crate::defaults::bool_true")]
559 pub option_click_moves_cursor: bool,
560
561 #[serde(default = "crate::defaults::bool_false")]
564 pub focus_follows_mouse: bool,
565
566 #[serde(default = "crate::defaults::bool_true")]
569 pub report_horizontal_scroll: bool,
570
571 #[serde(default = "crate::defaults::word_characters")]
578 pub word_characters: String,
579
580 #[serde(default = "crate::defaults::smart_selection_enabled")]
584 pub smart_selection_enabled: bool,
585
586 #[serde(default = "crate::types::default_smart_selection_rules")]
590 pub smart_selection_rules: Vec<SmartSelectionRule>,
591
592 #[serde(default = "crate::defaults::bool_true")]
599 pub copy_mode_enabled: bool,
600
601 #[serde(default = "crate::defaults::bool_true")]
605 pub copy_mode_auto_exit_on_yank: bool,
606
607 #[serde(default = "crate::defaults::bool_true")]
611 pub copy_mode_show_status: bool,
612
613 #[serde(default = "crate::defaults::scrollback", alias = "scrollback_size")]
618 pub scrollback_lines: usize,
619
620 #[serde(default = "crate::defaults::unicode_version")]
627 pub unicode_version: par_term_emu_core_rust::UnicodeVersion,
628
629 #[serde(default = "crate::defaults::ambiguous_width")]
633 pub ambiguous_width: par_term_emu_core_rust::AmbiguousWidth,
634
635 #[serde(default = "crate::defaults::normalization_form")]
643 pub normalization_form: par_term_emu_core_rust::NormalizationForm,
644
645 #[serde(default = "crate::defaults::bool_false")]
647 pub cursor_blink: bool,
648
649 #[serde(default = "crate::defaults::cursor_blink_interval")]
651 pub cursor_blink_interval: u64,
652
653 #[serde(default)]
655 pub cursor_style: CursorStyle,
656
657 #[serde(default = "crate::defaults::cursor_color")]
659 pub cursor_color: [u8; 3],
660
661 #[serde(default)]
665 pub cursor_text_color: Option<[u8; 3]>,
666
667 #[serde(default = "crate::defaults::bool_false")]
670 pub lock_cursor_visibility: bool,
671
672 #[serde(default = "crate::defaults::bool_false")]
675 pub lock_cursor_style: bool,
676
677 #[serde(default = "crate::defaults::bool_false")]
680 pub lock_cursor_blink: bool,
681
682 #[serde(default = "crate::defaults::bool_false")]
687 pub cursor_guide_enabled: bool,
688
689 #[serde(default = "crate::defaults::cursor_guide_color")]
691 pub cursor_guide_color: [u8; 4],
692
693 #[serde(default = "crate::defaults::bool_false")]
695 pub cursor_shadow_enabled: bool,
696
697 #[serde(default = "crate::defaults::cursor_shadow_color")]
699 pub cursor_shadow_color: [u8; 4],
700
701 #[serde(default = "crate::defaults::cursor_shadow_offset")]
703 pub cursor_shadow_offset: [f32; 2],
704
705 #[serde(default = "crate::defaults::cursor_shadow_blur")]
707 pub cursor_shadow_blur: f32,
708
709 #[serde(default = "crate::defaults::cursor_boost")]
712 pub cursor_boost: f32,
713
714 #[serde(default = "crate::defaults::cursor_boost_color")]
716 pub cursor_boost_color: [u8; 3],
717
718 #[serde(default)]
723 pub unfocused_cursor_style: UnfocusedCursorStyle,
724
725 #[serde(default = "crate::defaults::scrollbar_autohide_delay")]
730 pub scrollbar_autohide_delay: u64,
731
732 #[serde(default = "crate::defaults::theme")]
737 pub theme: String,
738
739 #[serde(default)]
741 pub auto_dark_mode: bool,
742
743 #[serde(default = "crate::defaults::light_theme")]
745 pub light_theme: String,
746
747 #[serde(default = "crate::defaults::dark_theme")]
749 pub dark_theme: String,
750
751 #[serde(default = "crate::defaults::screenshot_format")]
756 pub screenshot_format: String,
757
758 #[serde(
765 default,
766 deserialize_with = "deserialize_shell_exit_action",
767 alias = "exit_on_shell_exit",
768 alias = "close_on_shell_exit"
769 )]
770 pub shell_exit_action: ShellExitAction,
771
772 #[serde(default)]
774 pub custom_shell: Option<String>,
775
776 #[serde(default)]
778 pub shell_args: Option<Vec<String>>,
779
780 #[serde(default)]
783 pub working_directory: Option<String>,
784
785 #[serde(default)]
790 pub startup_directory_mode: StartupDirectoryMode,
791
792 #[serde(default)]
795 pub startup_directory: Option<String>,
796
797 #[serde(default)]
800 pub last_working_directory: Option<String>,
801
802 #[serde(default)]
804 pub shell_env: Option<std::collections::HashMap<String, String>>,
805
806 #[serde(default = "crate::defaults::login_shell")]
810 pub login_shell: bool,
811
812 #[serde(default = "crate::defaults::initial_text")]
815 pub initial_text: String,
816
817 #[serde(default = "crate::defaults::initial_text_delay_ms")]
819 pub initial_text_delay_ms: u64,
820
821 #[serde(default = "crate::defaults::initial_text_send_newline")]
823 pub initial_text_send_newline: bool,
824
825 #[serde(default = "crate::defaults::answerback_string")]
831 pub answerback_string: String,
832
833 #[serde(default = "crate::defaults::bool_false")]
838 pub prompt_on_quit: bool,
839
840 #[serde(default = "crate::defaults::bool_false")]
844 pub confirm_close_running_jobs: bool,
845
846 #[serde(default = "crate::defaults::jobs_to_ignore")]
851 pub jobs_to_ignore: Vec<String>,
852
853 #[serde(default = "crate::defaults::bool_true")]
859 pub semantic_history_enabled: bool,
860
861 #[serde(default)]
867 pub semantic_history_editor_mode: SemanticHistoryEditorMode,
868
869 #[serde(default = "crate::defaults::semantic_history_editor")]
879 pub semantic_history_editor: String,
880
881 #[serde(default = "crate::defaults::link_highlight_color")]
883 pub link_highlight_color: [u8; 3],
884
885 #[serde(default = "crate::defaults::bool_true")]
887 pub link_highlight_underline: bool,
888
889 #[serde(default)]
891 pub link_underline_style: crate::types::LinkUnderlineStyle,
892
893 #[serde(default)]
904 pub link_handler_command: String,
905
906 #[serde(default = "crate::defaults::scrollbar_position")]
911 pub scrollbar_position: String,
912
913 #[serde(default = "crate::defaults::scrollbar_width")]
915 pub scrollbar_width: f32,
916
917 #[serde(default = "crate::defaults::scrollbar_thumb_color")]
919 pub scrollbar_thumb_color: [f32; 4],
920
921 #[serde(default = "crate::defaults::scrollbar_track_color")]
923 pub scrollbar_track_color: [f32; 4],
924
925 #[serde(default = "crate::defaults::bool_true")]
927 pub scrollbar_command_marks: bool,
928
929 #[serde(default = "crate::defaults::bool_false")]
931 pub scrollbar_mark_tooltips: bool,
932
933 #[serde(default = "crate::defaults::bool_false")]
938 pub command_separator_enabled: bool,
939
940 #[serde(default = "crate::defaults::command_separator_thickness")]
942 pub command_separator_thickness: f32,
943
944 #[serde(default = "crate::defaults::command_separator_opacity")]
946 pub command_separator_opacity: f32,
947
948 #[serde(default = "crate::defaults::bool_true")]
950 pub command_separator_exit_color: bool,
951
952 #[serde(default = "crate::defaults::command_separator_color")]
954 pub command_separator_color: [u8; 3],
955
956 #[serde(
961 default = "crate::defaults::clipboard_max_sync_events",
962 alias = "max_clipboard_sync_events"
963 )]
964 pub clipboard_max_sync_events: usize,
965
966 #[serde(
968 default = "crate::defaults::clipboard_max_event_bytes",
969 alias = "max_clipboard_event_bytes"
970 )]
971 pub clipboard_max_event_bytes: usize,
972
973 #[serde(default = "crate::defaults::command_history_max_entries")]
978 pub command_history_max_entries: usize,
979
980 #[serde(default = "crate::defaults::bool_false", alias = "bell_desktop")]
985 pub notification_bell_desktop: bool,
986
987 #[serde(default = "crate::defaults::bell_sound", alias = "bell_sound")]
989 pub notification_bell_sound: u8,
990
991 #[serde(default = "crate::defaults::bool_true", alias = "bell_visual")]
993 pub notification_bell_visual: bool,
994
995 #[serde(
997 default = "crate::defaults::bool_false",
998 alias = "activity_notifications"
999 )]
1000 pub notification_activity_enabled: bool,
1001
1002 #[serde(
1004 default = "crate::defaults::activity_threshold",
1005 alias = "activity_threshold"
1006 )]
1007 pub notification_activity_threshold: u64,
1008
1009 #[serde(default = "crate::defaults::bool_false")]
1011 pub anti_idle_enabled: bool,
1012
1013 #[serde(default = "crate::defaults::anti_idle_seconds")]
1015 pub anti_idle_seconds: u64,
1016
1017 #[serde(default = "crate::defaults::anti_idle_code")]
1019 pub anti_idle_code: u8,
1020
1021 #[serde(
1023 default = "crate::defaults::bool_false",
1024 alias = "silence_notifications"
1025 )]
1026 pub notification_silence_enabled: bool,
1027
1028 #[serde(
1030 default = "crate::defaults::silence_threshold",
1031 alias = "silence_threshold"
1032 )]
1033 pub notification_silence_threshold: u64,
1034
1035 #[serde(default = "crate::defaults::bool_false", alias = "session_ended")]
1037 pub notification_session_ended: bool,
1038
1039 #[serde(default = "crate::defaults::bool_true")]
1041 pub suppress_notifications_when_focused: bool,
1042
1043 #[serde(
1045 default = "crate::defaults::notification_max_buffer",
1046 alias = "max_notifications"
1047 )]
1048 pub notification_max_buffer: usize,
1049
1050 #[serde(default)]
1053 pub alert_sounds: HashMap<AlertEvent, AlertSoundConfig>,
1054
1055 #[serde(default = "crate::defaults::bool_false")]
1060 pub enable_mdns_discovery: bool,
1061
1062 #[serde(default = "crate::defaults::mdns_timeout")]
1064 pub mdns_scan_timeout_secs: u32,
1065
1066 #[serde(default = "crate::defaults::bool_true")]
1068 pub ssh_auto_profile_switch: bool,
1069
1070 #[serde(default = "crate::defaults::bool_true")]
1072 pub ssh_revert_profile_on_disconnect: bool,
1073
1074 #[serde(default)]
1080 pub tab_style: TabStyle,
1081
1082 #[serde(default = "crate::defaults::light_tab_style")]
1084 pub light_tab_style: TabStyle,
1085
1086 #[serde(default = "crate::defaults::dark_tab_style")]
1088 pub dark_tab_style: TabStyle,
1089
1090 #[serde(default)]
1092 pub tab_bar_mode: TabBarMode,
1093
1094 #[serde(default = "crate::defaults::tab_bar_height")]
1096 pub tab_bar_height: f32,
1097
1098 #[serde(default)]
1100 pub tab_bar_position: TabBarPosition,
1101
1102 #[serde(default = "crate::defaults::tab_bar_width")]
1104 pub tab_bar_width: f32,
1105
1106 #[serde(default = "crate::defaults::bool_true")]
1108 pub tab_show_close_button: bool,
1109
1110 #[serde(default = "crate::defaults::bool_false")]
1112 pub tab_show_index: bool,
1113
1114 #[serde(default = "crate::defaults::bool_true")]
1116 pub tab_inherit_cwd: bool,
1117
1118 #[serde(default = "crate::defaults::zero")]
1120 pub max_tabs: usize,
1121
1122 #[serde(default = "crate::defaults::bool_false")]
1125 pub show_profile_drawer_button: bool,
1126
1127 #[serde(default = "crate::defaults::bool_false")]
1130 pub new_tab_shortcut_shows_profiles: bool,
1131
1132 #[serde(default = "crate::defaults::tab_bar_background")]
1137 pub tab_bar_background: [u8; 3],
1138
1139 #[serde(default = "crate::defaults::tab_active_background")]
1141 pub tab_active_background: [u8; 3],
1142
1143 #[serde(default = "crate::defaults::tab_inactive_background")]
1145 pub tab_inactive_background: [u8; 3],
1146
1147 #[serde(default = "crate::defaults::tab_hover_background")]
1149 pub tab_hover_background: [u8; 3],
1150
1151 #[serde(default = "crate::defaults::tab_active_text")]
1153 pub tab_active_text: [u8; 3],
1154
1155 #[serde(default = "crate::defaults::tab_inactive_text")]
1157 pub tab_inactive_text: [u8; 3],
1158
1159 #[serde(default = "crate::defaults::tab_active_indicator")]
1161 pub tab_active_indicator: [u8; 3],
1162
1163 #[serde(default = "crate::defaults::tab_activity_indicator")]
1165 pub tab_activity_indicator: [u8; 3],
1166
1167 #[serde(default = "crate::defaults::tab_bell_indicator")]
1169 pub tab_bell_indicator: [u8; 3],
1170
1171 #[serde(default = "crate::defaults::tab_close_button")]
1173 pub tab_close_button: [u8; 3],
1174
1175 #[serde(default = "crate::defaults::tab_close_button_hover")]
1177 pub tab_close_button_hover: [u8; 3],
1178
1179 #[serde(default = "crate::defaults::bool_true")]
1182 pub dim_inactive_tabs: bool,
1183
1184 #[serde(default = "crate::defaults::inactive_tab_opacity")]
1188 pub inactive_tab_opacity: f32,
1189
1190 #[serde(default = "crate::defaults::tab_min_width")]
1193 pub tab_min_width: f32,
1194
1195 #[serde(default = "crate::defaults::tab_stretch_to_fill")]
1198 pub tab_stretch_to_fill: bool,
1199
1200 #[serde(default = "crate::defaults::tab_html_titles")]
1203 pub tab_html_titles: bool,
1204
1205 #[serde(default = "crate::defaults::tab_border_color")]
1208 pub tab_border_color: [u8; 3],
1209
1210 #[serde(default = "crate::defaults::tab_border_width")]
1212 pub tab_border_width: f32,
1213
1214 #[serde(default = "crate::defaults::pane_divider_width")]
1219 pub pane_divider_width: Option<f32>,
1220
1221 #[serde(default = "crate::defaults::pane_divider_hit_width")]
1224 pub pane_divider_hit_width: f32,
1225
1226 #[serde(default = "crate::defaults::pane_padding")]
1228 pub pane_padding: f32,
1229
1230 #[serde(default = "crate::defaults::pane_min_size")]
1233 pub pane_min_size: usize,
1234
1235 #[serde(default = "crate::defaults::pane_background_opacity")]
1238 pub pane_background_opacity: f32,
1239
1240 #[serde(default = "crate::defaults::pane_divider_color")]
1242 pub pane_divider_color: [u8; 3],
1243
1244 #[serde(default = "crate::defaults::pane_divider_hover_color")]
1246 pub pane_divider_hover_color: [u8; 3],
1247
1248 #[serde(default = "crate::defaults::bool_false")]
1250 pub dim_inactive_panes: bool,
1251
1252 #[serde(default = "crate::defaults::inactive_pane_opacity")]
1254 pub inactive_pane_opacity: f32,
1255
1256 #[serde(default = "crate::defaults::bool_false")]
1258 pub show_pane_titles: bool,
1259
1260 #[serde(default = "crate::defaults::pane_title_height")]
1262 pub pane_title_height: f32,
1263
1264 #[serde(default)]
1266 pub pane_title_position: PaneTitlePosition,
1267
1268 #[serde(default = "crate::defaults::pane_title_color")]
1270 pub pane_title_color: [u8; 3],
1271
1272 #[serde(default = "crate::defaults::pane_title_bg_color")]
1274 pub pane_title_bg_color: [u8; 3],
1275
1276 #[serde(default)]
1278 pub pane_title_font: String,
1279
1280 #[serde(default)]
1282 pub pane_divider_style: DividerStyle,
1283
1284 #[serde(default = "crate::defaults::max_panes")]
1286 pub max_panes: usize,
1287
1288 #[serde(default = "crate::defaults::bool_true")]
1290 pub pane_focus_indicator: bool,
1291
1292 #[serde(default = "crate::defaults::pane_focus_color")]
1294 pub pane_focus_color: [u8; 3],
1295
1296 #[serde(default = "crate::defaults::pane_focus_width")]
1298 pub pane_focus_width: f32,
1299
1300 #[serde(default = "crate::defaults::bool_false")]
1305 pub tmux_enabled: bool,
1306
1307 #[serde(default = "crate::defaults::tmux_path")]
1309 pub tmux_path: String,
1310
1311 #[serde(default = "crate::defaults::tmux_default_session")]
1313 pub tmux_default_session: Option<String>,
1314
1315 #[serde(default = "crate::defaults::bool_false")]
1317 pub tmux_auto_attach: bool,
1318
1319 #[serde(default = "crate::defaults::tmux_auto_attach_session")]
1321 pub tmux_auto_attach_session: Option<String>,
1322
1323 #[serde(default = "crate::defaults::bool_true")]
1326 pub tmux_clipboard_sync: bool,
1327
1328 #[serde(default)]
1332 pub tmux_profile: Option<String>,
1333
1334 #[serde(default = "crate::defaults::bool_false")]
1337 pub tmux_show_status_bar: bool,
1338
1339 #[serde(default = "crate::defaults::tmux_status_bar_refresh_ms")]
1344 pub tmux_status_bar_refresh_ms: u64,
1345
1346 #[serde(default = "crate::defaults::tmux_prefix_key")]
1351 pub tmux_prefix_key: String,
1352
1353 #[serde(default = "crate::defaults::bool_false")]
1358 pub tmux_status_bar_use_native_format: bool,
1359
1360 #[serde(default = "crate::defaults::tmux_status_bar_left")]
1372 pub tmux_status_bar_left: String,
1373
1374 #[serde(default = "crate::defaults::tmux_status_bar_right")]
1380 pub tmux_status_bar_right: String,
1381
1382 #[serde(default = "crate::defaults::bool_true")]
1388 pub pause_shaders_on_blur: bool,
1389
1390 #[serde(default = "crate::defaults::bool_false")]
1393 pub pause_refresh_on_blur: bool,
1394
1395 #[serde(default = "crate::defaults::unfocused_fps")]
1398 pub unfocused_fps: u32,
1399
1400 #[serde(default = "crate::defaults::bool_false")]
1406 pub shader_hot_reload: bool,
1407
1408 #[serde(default = "crate::defaults::shader_hot_reload_delay")]
1411 pub shader_hot_reload_delay: u64,
1412
1413 #[serde(default)]
1419 pub shader_configs: HashMap<String, ShaderConfig>,
1420
1421 #[serde(default)]
1423 pub cursor_shader_configs: HashMap<String, CursorShaderConfig>,
1424
1425 #[serde(default = "crate::defaults::keybindings")]
1431 pub keybindings: Vec<KeyBinding>,
1432
1433 #[serde(default)]
1441 pub shader_install_prompt: ShaderInstallPrompt,
1442
1443 #[serde(default)]
1445 pub shell_integration_state: InstallPromptState,
1446
1447 #[serde(default)]
1449 pub integration_versions: IntegrationVersions,
1450
1451 #[serde(default = "crate::defaults::update_check_frequency")]
1460 pub update_check_frequency: UpdateCheckFrequency,
1461
1462 #[serde(default)]
1464 pub last_update_check: Option<String>,
1465
1466 #[serde(default)]
1468 pub skipped_version: Option<String>,
1469
1470 #[serde(default)]
1472 pub last_notified_version: Option<String>,
1473
1474 #[serde(default, skip_serializing_if = "Option::is_none")]
1479 pub auto_restore_arrangement: Option<String>,
1480
1481 #[serde(default = "crate::defaults::bool_false")]
1483 pub restore_session: bool,
1484
1485 #[serde(default = "crate::defaults::session_undo_timeout_secs")]
1487 pub session_undo_timeout_secs: u32,
1488
1489 #[serde(default = "crate::defaults::session_undo_max_entries")]
1491 pub session_undo_max_entries: usize,
1492
1493 #[serde(default = "crate::defaults::session_undo_preserve_shell")]
1496 pub session_undo_preserve_shell: bool,
1497
1498 #[serde(default = "crate::defaults::search_highlight_color")]
1503 pub search_highlight_color: [u8; 4],
1504
1505 #[serde(default = "crate::defaults::search_current_highlight_color")]
1507 pub search_current_highlight_color: [u8; 4],
1508
1509 #[serde(default = "crate::defaults::bool_false")]
1511 pub search_case_sensitive: bool,
1512
1513 #[serde(default = "crate::defaults::bool_false")]
1515 pub search_regex: bool,
1516
1517 #[serde(default = "crate::defaults::bool_true")]
1519 pub search_wrap_around: bool,
1520
1521 #[serde(default = "crate::defaults::bool_false")]
1527 pub auto_log_sessions: bool,
1528
1529 #[serde(default)]
1534 pub session_log_format: SessionLogFormat,
1535
1536 #[serde(default = "crate::defaults::session_log_directory")]
1539 pub session_log_directory: String,
1540
1541 #[serde(default = "crate::defaults::bool_true")]
1544 pub archive_on_close: bool,
1545
1546 #[serde(default)]
1553 pub log_level: LogLevel,
1554
1555 #[serde(default = "crate::defaults::bool_false")]
1560 pub badge_enabled: bool,
1561
1562 #[serde(default = "crate::defaults::badge_format")]
1565 pub badge_format: String,
1566
1567 #[serde(default = "crate::defaults::badge_color")]
1569 pub badge_color: [u8; 3],
1570
1571 #[serde(default = "crate::defaults::badge_color_alpha")]
1573 pub badge_color_alpha: f32,
1574
1575 #[serde(default = "crate::defaults::badge_font")]
1577 pub badge_font: String,
1578
1579 #[serde(default = "crate::defaults::bool_true")]
1581 pub badge_font_bold: bool,
1582
1583 #[serde(default = "crate::defaults::badge_top_margin")]
1585 pub badge_top_margin: f32,
1586
1587 #[serde(default = "crate::defaults::badge_right_margin")]
1589 pub badge_right_margin: f32,
1590
1591 #[serde(default = "crate::defaults::badge_max_width")]
1593 pub badge_max_width: f32,
1594
1595 #[serde(default = "crate::defaults::badge_max_height")]
1597 pub badge_max_height: f32,
1598
1599 #[serde(default = "crate::defaults::bool_false")]
1604 pub status_bar_enabled: bool,
1605
1606 #[serde(default)]
1608 pub status_bar_position: StatusBarPosition,
1609
1610 #[serde(default = "crate::defaults::status_bar_height")]
1612 pub status_bar_height: f32,
1613
1614 #[serde(default = "crate::defaults::status_bar_bg_color")]
1616 pub status_bar_bg_color: [u8; 3],
1617
1618 #[serde(default = "crate::defaults::status_bar_bg_alpha")]
1620 pub status_bar_bg_alpha: f32,
1621
1622 #[serde(default = "crate::defaults::status_bar_fg_color")]
1624 pub status_bar_fg_color: [u8; 3],
1625
1626 #[serde(default)]
1628 pub status_bar_font: String,
1629
1630 #[serde(default = "crate::defaults::status_bar_font_size")]
1632 pub status_bar_font_size: f32,
1633
1634 #[serde(default = "crate::defaults::status_bar_separator")]
1636 pub status_bar_separator: String,
1637
1638 #[serde(default = "crate::defaults::bool_true")]
1640 pub status_bar_auto_hide_fullscreen: bool,
1641
1642 #[serde(default = "crate::defaults::bool_false")]
1644 pub status_bar_auto_hide_mouse_inactive: bool,
1645
1646 #[serde(default = "crate::defaults::status_bar_mouse_inactive_timeout")]
1648 pub status_bar_mouse_inactive_timeout: f32,
1649
1650 #[serde(default = "crate::defaults::status_bar_system_poll_interval")]
1652 pub status_bar_system_poll_interval: f32,
1653
1654 #[serde(default = "crate::defaults::status_bar_git_poll_interval")]
1656 pub status_bar_git_poll_interval: f32,
1657
1658 #[serde(default = "crate::defaults::status_bar_time_format")]
1660 pub status_bar_time_format: String,
1661
1662 #[serde(default = "crate::defaults::bool_true")]
1664 pub status_bar_git_show_status: bool,
1665
1666 #[serde(default = "crate::status_bar::default_widgets")]
1668 pub status_bar_widgets: Vec<crate::status_bar::StatusBarWidgetConfig>,
1669
1670 #[serde(default = "crate::defaults::bool_true")]
1676 pub progress_bar_enabled: bool,
1677
1678 #[serde(default)]
1682 pub progress_bar_style: ProgressBarStyle,
1683
1684 #[serde(default)]
1688 pub progress_bar_position: ProgressBarPosition,
1689
1690 #[serde(default = "crate::defaults::progress_bar_height")]
1692 pub progress_bar_height: f32,
1693
1694 #[serde(default = "crate::defaults::progress_bar_opacity")]
1696 pub progress_bar_opacity: f32,
1697
1698 #[serde(default = "crate::defaults::progress_bar_normal_color")]
1700 pub progress_bar_normal_color: [u8; 3],
1701
1702 #[serde(default = "crate::defaults::progress_bar_warning_color")]
1704 pub progress_bar_warning_color: [u8; 3],
1705
1706 #[serde(default = "crate::defaults::progress_bar_error_color")]
1708 pub progress_bar_error_color: [u8; 3],
1709
1710 #[serde(default = "crate::defaults::progress_bar_indeterminate_color")]
1712 pub progress_bar_indeterminate_color: [u8; 3],
1713
1714 #[serde(default)]
1719 pub triggers: Vec<crate::automation::TriggerConfig>,
1720
1721 #[serde(default)]
1723 pub coprocesses: Vec<crate::automation::CoprocessDefConfig>,
1724
1725 #[serde(default)]
1727 pub scripts: Vec<crate::scripting::ScriptConfig>,
1728
1729 #[serde(default)]
1734 pub snippets: Vec<SnippetConfig>,
1735
1736 #[serde(default)]
1738 pub actions: Vec<CustomActionConfig>,
1739
1740 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1746 pub collapsed_settings_sections: Vec<String>,
1747
1748 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1753 pub dynamic_profile_sources: Vec<crate::profile::DynamicProfileSource>,
1754
1755 #[serde(default = "crate::defaults::ai_inspector_enabled")]
1760 pub ai_inspector_enabled: bool,
1761
1762 #[serde(default = "crate::defaults::ai_inspector_open_on_startup")]
1764 pub ai_inspector_open_on_startup: bool,
1765
1766 #[serde(default = "crate::defaults::ai_inspector_width")]
1768 pub ai_inspector_width: f32,
1769
1770 #[serde(default = "crate::defaults::ai_inspector_default_scope")]
1772 pub ai_inspector_default_scope: String,
1773
1774 #[serde(default = "crate::defaults::ai_inspector_view_mode")]
1776 pub ai_inspector_view_mode: String,
1777
1778 #[serde(default = "crate::defaults::ai_inspector_live_update")]
1780 pub ai_inspector_live_update: bool,
1781
1782 #[serde(default = "crate::defaults::ai_inspector_show_zones")]
1784 pub ai_inspector_show_zones: bool,
1785
1786 #[serde(default = "crate::defaults::ai_inspector_agent")]
1788 pub ai_inspector_agent: String,
1789
1790 #[serde(default = "crate::defaults::ai_inspector_auto_launch")]
1792 pub ai_inspector_auto_launch: bool,
1793
1794 #[serde(default = "crate::defaults::ai_inspector_auto_context")]
1796 pub ai_inspector_auto_context: bool,
1797
1798 #[serde(default = "crate::defaults::ai_inspector_context_max_lines")]
1800 pub ai_inspector_context_max_lines: usize,
1801
1802 #[serde(default = "crate::defaults::ai_inspector_auto_approve")]
1804 pub ai_inspector_auto_approve: bool,
1805
1806 #[serde(default = "crate::defaults::ai_inspector_agent_terminal_access")]
1808 pub ai_inspector_agent_terminal_access: bool,
1809}
1810
1811impl Default for Config {
1812 fn default() -> Self {
1813 Self {
1814 cols: crate::defaults::cols(),
1815 rows: crate::defaults::rows(),
1816 font_size: crate::defaults::font_size(),
1817 font_family: crate::defaults::font_family(),
1818 font_family_bold: None,
1819 font_family_italic: None,
1820 font_family_bold_italic: None,
1821 font_ranges: Vec::new(),
1822 line_spacing: crate::defaults::line_spacing(),
1823 char_spacing: crate::defaults::char_spacing(),
1824 enable_text_shaping: crate::defaults::text_shaping(),
1825 enable_ligatures: crate::defaults::bool_true(),
1826 enable_kerning: crate::defaults::bool_true(),
1827 font_antialias: crate::defaults::bool_true(),
1828 font_hinting: crate::defaults::bool_true(),
1829 font_thin_strokes: ThinStrokesMode::default(),
1830 minimum_contrast: crate::defaults::minimum_contrast(),
1831 copy_mode_enabled: crate::defaults::bool_true(),
1832 copy_mode_auto_exit_on_yank: crate::defaults::bool_true(),
1833 copy_mode_show_status: crate::defaults::bool_true(),
1834 scrollback_lines: crate::defaults::scrollback(),
1835 unicode_version: crate::defaults::unicode_version(),
1836 ambiguous_width: crate::defaults::ambiguous_width(),
1837 normalization_form: crate::defaults::normalization_form(),
1838 cursor_blink: crate::defaults::bool_false(),
1839 cursor_blink_interval: crate::defaults::cursor_blink_interval(),
1840 cursor_style: CursorStyle::default(),
1841 cursor_color: crate::defaults::cursor_color(),
1842 cursor_text_color: None,
1843 lock_cursor_visibility: crate::defaults::bool_false(),
1844 lock_cursor_style: crate::defaults::bool_false(),
1845 lock_cursor_blink: crate::defaults::bool_false(),
1846 cursor_guide_enabled: crate::defaults::bool_false(),
1847 cursor_guide_color: crate::defaults::cursor_guide_color(),
1848 cursor_shadow_enabled: crate::defaults::bool_false(),
1849 cursor_shadow_color: crate::defaults::cursor_shadow_color(),
1850 cursor_shadow_offset: crate::defaults::cursor_shadow_offset(),
1851 cursor_shadow_blur: crate::defaults::cursor_shadow_blur(),
1852 cursor_boost: crate::defaults::cursor_boost(),
1853 cursor_boost_color: crate::defaults::cursor_boost_color(),
1854 unfocused_cursor_style: UnfocusedCursorStyle::default(),
1855 scrollbar_autohide_delay: crate::defaults::scrollbar_autohide_delay(),
1856 window_title: crate::defaults::window_title(),
1857 allow_title_change: crate::defaults::bool_true(),
1858 theme: crate::defaults::theme(),
1859 auto_dark_mode: false,
1860 light_theme: crate::defaults::light_theme(),
1861 dark_theme: crate::defaults::dark_theme(),
1862 left_option_key_mode: OptionKeyMode::default(),
1863 right_option_key_mode: OptionKeyMode::default(),
1864 modifier_remapping: ModifierRemapping::default(),
1865 use_physical_keys: crate::defaults::bool_false(),
1866 auto_copy_selection: crate::defaults::bool_true(),
1867 copy_trailing_newline: crate::defaults::bool_false(),
1868 middle_click_paste: crate::defaults::bool_true(),
1869 paste_delay_ms: crate::defaults::paste_delay_ms(),
1870 dropped_file_quote_style: DroppedFileQuoteStyle::default(),
1871 mouse_scroll_speed: crate::defaults::scroll_speed(),
1872 mouse_double_click_threshold: crate::defaults::double_click_threshold(),
1873 mouse_triple_click_threshold: crate::defaults::triple_click_threshold(),
1874 option_click_moves_cursor: crate::defaults::bool_true(),
1875 focus_follows_mouse: crate::defaults::bool_false(),
1876 report_horizontal_scroll: crate::defaults::bool_true(),
1877 word_characters: crate::defaults::word_characters(),
1878 smart_selection_enabled: crate::defaults::smart_selection_enabled(),
1879 smart_selection_rules: default_smart_selection_rules(),
1880 screenshot_format: crate::defaults::screenshot_format(),
1881 max_fps: crate::defaults::max_fps(),
1882 vsync_mode: VsyncMode::default(),
1883 power_preference: PowerPreference::default(),
1884 reduce_flicker: crate::defaults::reduce_flicker(),
1885 reduce_flicker_delay_ms: crate::defaults::reduce_flicker_delay_ms(),
1886 maximize_throughput: crate::defaults::maximize_throughput(),
1887 throughput_render_interval_ms: crate::defaults::throughput_render_interval_ms(),
1888 window_padding: crate::defaults::window_padding(),
1889 window_opacity: crate::defaults::window_opacity(),
1890 window_always_on_top: crate::defaults::bool_false(),
1891 window_decorations: crate::defaults::bool_true(),
1892 window_type: WindowType::default(),
1893 target_monitor: None,
1894 target_space: None,
1895 lock_window_size: crate::defaults::bool_false(),
1896 show_window_number: crate::defaults::bool_false(),
1897 transparency_affects_only_default_background: crate::defaults::bool_true(),
1898 keep_text_opaque: crate::defaults::bool_true(),
1899 blur_enabled: crate::defaults::bool_false(),
1900 blur_radius: crate::defaults::blur_radius(),
1901 background_image: None,
1902 background_image_enabled: crate::defaults::bool_true(),
1903 background_image_mode: BackgroundImageMode::default(),
1904 background_image_opacity: crate::defaults::background_image_opacity(),
1905 image_scaling_mode: ImageScalingMode::default(),
1906 image_preserve_aspect_ratio: crate::defaults::bool_true(),
1907 background_mode: BackgroundMode::default(),
1908 pane_backgrounds: Vec::new(),
1909 background_color: crate::defaults::background_color(),
1910 download_save_location: DownloadSaveLocation::default(),
1911 last_download_directory: None,
1912 custom_shader: None,
1913 custom_shader_enabled: crate::defaults::bool_true(),
1914 custom_shader_animation: crate::defaults::bool_true(),
1915 custom_shader_animation_speed: crate::defaults::custom_shader_speed(),
1916 custom_shader_text_opacity: crate::defaults::text_opacity(),
1917 custom_shader_full_content: crate::defaults::bool_false(),
1918 custom_shader_brightness: crate::defaults::custom_shader_brightness(),
1919 custom_shader_channel0: None,
1920 custom_shader_channel1: None,
1921 custom_shader_channel2: None,
1922 custom_shader_channel3: None,
1923 custom_shader_cubemap: None,
1924 custom_shader_cubemap_enabled: crate::defaults::cubemap_enabled(),
1925 custom_shader_use_background_as_channel0: crate::defaults::use_background_as_channel0(),
1926 cursor_shader: None,
1927 cursor_shader_enabled: crate::defaults::bool_false(),
1928 cursor_shader_animation: crate::defaults::bool_true(),
1929 cursor_shader_animation_speed: crate::defaults::custom_shader_speed(),
1930 cursor_shader_color: crate::defaults::cursor_shader_color(),
1931 cursor_shader_trail_duration: crate::defaults::cursor_trail_duration(),
1932 cursor_shader_glow_radius: crate::defaults::cursor_glow_radius(),
1933 cursor_shader_glow_intensity: crate::defaults::cursor_glow_intensity(),
1934 cursor_shader_hides_cursor: crate::defaults::bool_false(),
1935 cursor_shader_disable_in_alt_screen:
1936 crate::defaults::cursor_shader_disable_in_alt_screen(),
1937 shell_exit_action: ShellExitAction::default(),
1938 custom_shell: None,
1939 shell_args: None,
1940 working_directory: None,
1941 startup_directory_mode: StartupDirectoryMode::default(),
1942 startup_directory: None,
1943 last_working_directory: None,
1944 shell_env: None,
1945 login_shell: crate::defaults::login_shell(),
1946 initial_text: crate::defaults::initial_text(),
1947 initial_text_delay_ms: crate::defaults::initial_text_delay_ms(),
1948 initial_text_send_newline: crate::defaults::initial_text_send_newline(),
1949 answerback_string: crate::defaults::answerback_string(),
1950 prompt_on_quit: crate::defaults::bool_false(),
1951 confirm_close_running_jobs: crate::defaults::bool_false(),
1952 jobs_to_ignore: crate::defaults::jobs_to_ignore(),
1953 semantic_history_enabled: crate::defaults::bool_true(),
1954 semantic_history_editor_mode: SemanticHistoryEditorMode::default(),
1955 semantic_history_editor: crate::defaults::semantic_history_editor(),
1956 link_highlight_color: crate::defaults::link_highlight_color(),
1957 link_highlight_underline: crate::defaults::bool_true(),
1958 link_underline_style: crate::types::LinkUnderlineStyle::default(),
1959 link_handler_command: String::new(),
1960 scrollbar_position: crate::defaults::scrollbar_position(),
1961 scrollbar_width: crate::defaults::scrollbar_width(),
1962 scrollbar_thumb_color: crate::defaults::scrollbar_thumb_color(),
1963 scrollbar_track_color: crate::defaults::scrollbar_track_color(),
1964 scrollbar_command_marks: crate::defaults::bool_true(),
1965 scrollbar_mark_tooltips: crate::defaults::bool_false(),
1966 command_separator_enabled: crate::defaults::bool_false(),
1967 command_separator_thickness: crate::defaults::command_separator_thickness(),
1968 command_separator_opacity: crate::defaults::command_separator_opacity(),
1969 command_separator_exit_color: crate::defaults::bool_true(),
1970 command_separator_color: crate::defaults::command_separator_color(),
1971 clipboard_max_sync_events: crate::defaults::clipboard_max_sync_events(),
1972 clipboard_max_event_bytes: crate::defaults::clipboard_max_event_bytes(),
1973 command_history_max_entries: crate::defaults::command_history_max_entries(),
1974 notification_bell_desktop: crate::defaults::bool_false(),
1975 notification_bell_sound: crate::defaults::bell_sound(),
1976 notification_bell_visual: crate::defaults::bool_true(),
1977 notification_activity_enabled: crate::defaults::bool_false(),
1978 notification_activity_threshold: crate::defaults::activity_threshold(),
1979 anti_idle_enabled: crate::defaults::bool_false(),
1980 anti_idle_seconds: crate::defaults::anti_idle_seconds(),
1981 anti_idle_code: crate::defaults::anti_idle_code(),
1982 notification_silence_enabled: crate::defaults::bool_false(),
1983 notification_silence_threshold: crate::defaults::silence_threshold(),
1984 notification_session_ended: crate::defaults::bool_false(),
1985 suppress_notifications_when_focused: crate::defaults::bool_true(),
1986 notification_max_buffer: crate::defaults::notification_max_buffer(),
1987 alert_sounds: HashMap::new(),
1988 enable_mdns_discovery: crate::defaults::bool_false(),
1989 mdns_scan_timeout_secs: crate::defaults::mdns_timeout(),
1990 ssh_auto_profile_switch: crate::defaults::bool_true(),
1991 ssh_revert_profile_on_disconnect: crate::defaults::bool_true(),
1992 tab_style: TabStyle::default(),
1993 light_tab_style: crate::defaults::light_tab_style(),
1994 dark_tab_style: crate::defaults::dark_tab_style(),
1995 tab_bar_mode: TabBarMode::default(),
1996 tab_bar_height: crate::defaults::tab_bar_height(),
1997 tab_bar_position: TabBarPosition::default(),
1998 tab_bar_width: crate::defaults::tab_bar_width(),
1999 tab_show_close_button: crate::defaults::bool_true(),
2000 tab_show_index: crate::defaults::bool_false(),
2001 tab_inherit_cwd: crate::defaults::bool_true(),
2002 max_tabs: crate::defaults::zero(),
2003 show_profile_drawer_button: crate::defaults::bool_false(),
2004 new_tab_shortcut_shows_profiles: crate::defaults::bool_false(),
2005 tab_bar_background: crate::defaults::tab_bar_background(),
2006 tab_active_background: crate::defaults::tab_active_background(),
2007 tab_inactive_background: crate::defaults::tab_inactive_background(),
2008 tab_hover_background: crate::defaults::tab_hover_background(),
2009 tab_active_text: crate::defaults::tab_active_text(),
2010 tab_inactive_text: crate::defaults::tab_inactive_text(),
2011 tab_active_indicator: crate::defaults::tab_active_indicator(),
2012 tab_activity_indicator: crate::defaults::tab_activity_indicator(),
2013 tab_bell_indicator: crate::defaults::tab_bell_indicator(),
2014 tab_close_button: crate::defaults::tab_close_button(),
2015 tab_close_button_hover: crate::defaults::tab_close_button_hover(),
2016 dim_inactive_tabs: crate::defaults::bool_true(),
2017 inactive_tab_opacity: crate::defaults::inactive_tab_opacity(),
2018 tab_min_width: crate::defaults::tab_min_width(),
2019 tab_stretch_to_fill: crate::defaults::tab_stretch_to_fill(),
2020 tab_html_titles: crate::defaults::tab_html_titles(),
2021 tab_border_color: crate::defaults::tab_border_color(),
2022 tab_border_width: crate::defaults::tab_border_width(),
2023 pane_divider_width: crate::defaults::pane_divider_width(),
2025 pane_divider_hit_width: crate::defaults::pane_divider_hit_width(),
2026 pane_padding: crate::defaults::pane_padding(),
2027 pane_min_size: crate::defaults::pane_min_size(),
2028 pane_background_opacity: crate::defaults::pane_background_opacity(),
2029 pane_divider_color: crate::defaults::pane_divider_color(),
2030 pane_divider_hover_color: crate::defaults::pane_divider_hover_color(),
2031 dim_inactive_panes: crate::defaults::bool_false(),
2032 inactive_pane_opacity: crate::defaults::inactive_pane_opacity(),
2033 show_pane_titles: crate::defaults::bool_false(),
2034 pane_title_height: crate::defaults::pane_title_height(),
2035 pane_title_position: PaneTitlePosition::default(),
2036 pane_title_color: crate::defaults::pane_title_color(),
2037 pane_title_bg_color: crate::defaults::pane_title_bg_color(),
2038 pane_title_font: String::new(),
2039 pane_divider_style: DividerStyle::default(),
2040 max_panes: crate::defaults::max_panes(),
2041 pane_focus_indicator: crate::defaults::bool_true(),
2042 pane_focus_color: crate::defaults::pane_focus_color(),
2043 pane_focus_width: crate::defaults::pane_focus_width(),
2044 tmux_enabled: crate::defaults::bool_false(),
2045 tmux_path: crate::defaults::tmux_path(),
2046 tmux_default_session: crate::defaults::tmux_default_session(),
2047 tmux_auto_attach: crate::defaults::bool_false(),
2048 tmux_auto_attach_session: crate::defaults::tmux_auto_attach_session(),
2049 tmux_clipboard_sync: crate::defaults::bool_true(),
2050 tmux_profile: None,
2051 tmux_show_status_bar: crate::defaults::bool_false(),
2052 tmux_status_bar_refresh_ms: crate::defaults::tmux_status_bar_refresh_ms(),
2053 tmux_prefix_key: crate::defaults::tmux_prefix_key(),
2054 tmux_status_bar_use_native_format: crate::defaults::bool_false(),
2055 tmux_status_bar_left: crate::defaults::tmux_status_bar_left(),
2056 tmux_status_bar_right: crate::defaults::tmux_status_bar_right(),
2057 pause_shaders_on_blur: crate::defaults::bool_true(),
2058 pause_refresh_on_blur: crate::defaults::bool_false(),
2059 unfocused_fps: crate::defaults::unfocused_fps(),
2060 shader_hot_reload: crate::defaults::bool_false(),
2061 shader_hot_reload_delay: crate::defaults::shader_hot_reload_delay(),
2062 shader_configs: HashMap::new(),
2063 cursor_shader_configs: HashMap::new(),
2064 keybindings: crate::defaults::keybindings(),
2065 shader_install_prompt: ShaderInstallPrompt::default(),
2066 shell_integration_state: InstallPromptState::default(),
2067 integration_versions: IntegrationVersions::default(),
2068 update_check_frequency: crate::defaults::update_check_frequency(),
2069 last_update_check: None,
2070 skipped_version: None,
2071 last_notified_version: None,
2072 auto_restore_arrangement: None,
2073 restore_session: crate::defaults::bool_false(),
2074 session_undo_timeout_secs: crate::defaults::session_undo_timeout_secs(),
2075 session_undo_max_entries: crate::defaults::session_undo_max_entries(),
2076 session_undo_preserve_shell: crate::defaults::session_undo_preserve_shell(),
2077 search_highlight_color: crate::defaults::search_highlight_color(),
2078 search_current_highlight_color: crate::defaults::search_current_highlight_color(),
2079 search_case_sensitive: crate::defaults::bool_false(),
2080 search_regex: crate::defaults::bool_false(),
2081 search_wrap_around: crate::defaults::bool_true(),
2082 auto_log_sessions: crate::defaults::bool_false(),
2084 session_log_format: SessionLogFormat::default(),
2085 session_log_directory: crate::defaults::session_log_directory(),
2086 archive_on_close: crate::defaults::bool_true(),
2087 log_level: LogLevel::default(),
2089 badge_enabled: crate::defaults::bool_false(),
2091 badge_format: crate::defaults::badge_format(),
2092 badge_color: crate::defaults::badge_color(),
2093 badge_color_alpha: crate::defaults::badge_color_alpha(),
2094 badge_font: crate::defaults::badge_font(),
2095 badge_font_bold: crate::defaults::bool_true(),
2096 badge_top_margin: crate::defaults::badge_top_margin(),
2097 badge_right_margin: crate::defaults::badge_right_margin(),
2098 badge_max_width: crate::defaults::badge_max_width(),
2099 badge_max_height: crate::defaults::badge_max_height(),
2100 status_bar_enabled: crate::defaults::bool_false(),
2102 status_bar_position: StatusBarPosition::default(),
2103 status_bar_height: crate::defaults::status_bar_height(),
2104 status_bar_bg_color: crate::defaults::status_bar_bg_color(),
2105 status_bar_bg_alpha: crate::defaults::status_bar_bg_alpha(),
2106 status_bar_fg_color: crate::defaults::status_bar_fg_color(),
2107 status_bar_font: String::new(),
2108 status_bar_font_size: crate::defaults::status_bar_font_size(),
2109 status_bar_separator: crate::defaults::status_bar_separator(),
2110 status_bar_auto_hide_fullscreen: crate::defaults::bool_true(),
2111 status_bar_auto_hide_mouse_inactive: crate::defaults::bool_false(),
2112 status_bar_mouse_inactive_timeout: crate::defaults::status_bar_mouse_inactive_timeout(),
2113 status_bar_system_poll_interval: crate::defaults::status_bar_system_poll_interval(),
2114 status_bar_git_poll_interval: crate::defaults::status_bar_git_poll_interval(),
2115 status_bar_time_format: crate::defaults::status_bar_time_format(),
2116 status_bar_git_show_status: crate::defaults::bool_true(),
2117 status_bar_widgets: crate::status_bar::default_widgets(),
2118 progress_bar_enabled: crate::defaults::bool_true(),
2120 progress_bar_style: ProgressBarStyle::default(),
2121 progress_bar_position: ProgressBarPosition::default(),
2122 progress_bar_height: crate::defaults::progress_bar_height(),
2123 progress_bar_opacity: crate::defaults::progress_bar_opacity(),
2124 progress_bar_normal_color: crate::defaults::progress_bar_normal_color(),
2125 progress_bar_warning_color: crate::defaults::progress_bar_warning_color(),
2126 progress_bar_error_color: crate::defaults::progress_bar_error_color(),
2127 progress_bar_indeterminate_color: crate::defaults::progress_bar_indeterminate_color(),
2128 triggers: Vec::new(),
2129 coprocesses: Vec::new(),
2130 scripts: Vec::new(),
2131 snippets: Vec::new(),
2132 actions: Vec::new(),
2133 collapsed_settings_sections: Vec::new(),
2134 dynamic_profile_sources: Vec::new(),
2135 ai_inspector_enabled: crate::defaults::ai_inspector_enabled(),
2137 ai_inspector_open_on_startup: crate::defaults::ai_inspector_open_on_startup(),
2138 ai_inspector_width: crate::defaults::ai_inspector_width(),
2139 ai_inspector_default_scope: crate::defaults::ai_inspector_default_scope(),
2140 ai_inspector_view_mode: crate::defaults::ai_inspector_view_mode(),
2141 ai_inspector_live_update: crate::defaults::ai_inspector_live_update(),
2142 ai_inspector_show_zones: crate::defaults::ai_inspector_show_zones(),
2143 ai_inspector_agent: crate::defaults::ai_inspector_agent(),
2144 ai_inspector_auto_launch: crate::defaults::ai_inspector_auto_launch(),
2145 ai_inspector_auto_context: crate::defaults::ai_inspector_auto_context(),
2146 ai_inspector_context_max_lines: crate::defaults::ai_inspector_context_max_lines(),
2147 ai_inspector_auto_approve: crate::defaults::ai_inspector_auto_approve(),
2148 ai_inspector_agent_terminal_access: crate::defaults::ai_inspector_agent_terminal_access(
2149 ),
2150 }
2151 }
2152}
2153
2154impl Config {
2155 pub fn apply_tab_style(&mut self) {
2160 match self.tab_style {
2161 TabStyle::Dark => {
2162 self.tab_bar_background = crate::defaults::tab_bar_background();
2164 self.tab_active_background = crate::defaults::tab_active_background();
2165 self.tab_inactive_background = crate::defaults::tab_inactive_background();
2166 self.tab_hover_background = crate::defaults::tab_hover_background();
2167 self.tab_active_text = crate::defaults::tab_active_text();
2168 self.tab_inactive_text = crate::defaults::tab_inactive_text();
2169 self.tab_active_indicator = crate::defaults::tab_active_indicator();
2170 self.tab_border_color = crate::defaults::tab_border_color();
2171 self.tab_border_width = crate::defaults::tab_border_width();
2172 self.tab_bar_height = crate::defaults::tab_bar_height();
2173 }
2174 TabStyle::Light => {
2175 self.tab_bar_background = [235, 235, 235];
2176 self.tab_active_background = [255, 255, 255];
2177 self.tab_inactive_background = [225, 225, 225];
2178 self.tab_hover_background = [240, 240, 240];
2179 self.tab_active_text = [30, 30, 30];
2180 self.tab_inactive_text = [100, 100, 100];
2181 self.tab_active_indicator = [50, 120, 220];
2182 self.tab_border_color = [200, 200, 200];
2183 self.tab_border_width = 1.0;
2184 self.tab_bar_height = crate::defaults::tab_bar_height();
2185 }
2186 TabStyle::Compact => {
2187 self.tab_bar_background = [35, 35, 35];
2189 self.tab_active_background = [55, 55, 55];
2190 self.tab_inactive_background = [35, 35, 35];
2191 self.tab_hover_background = [45, 45, 45];
2192 self.tab_active_text = [240, 240, 240];
2193 self.tab_inactive_text = [160, 160, 160];
2194 self.tab_active_indicator = [80, 140, 240];
2195 self.tab_border_color = [60, 60, 60];
2196 self.tab_border_width = 0.5;
2197 self.tab_bar_height = 22.0;
2198 }
2199 TabStyle::Minimal => {
2200 self.tab_bar_background = [30, 30, 30];
2202 self.tab_active_background = [30, 30, 30];
2203 self.tab_inactive_background = [30, 30, 30];
2204 self.tab_hover_background = [40, 40, 40];
2205 self.tab_active_text = [255, 255, 255];
2206 self.tab_inactive_text = [120, 120, 120];
2207 self.tab_active_indicator = [100, 150, 255];
2208 self.tab_border_color = [30, 30, 30]; self.tab_border_width = 0.0;
2210 self.tab_bar_height = 26.0;
2211 }
2212 TabStyle::HighContrast => {
2213 self.tab_bar_background = [0, 0, 0];
2215 self.tab_active_background = [255, 255, 255];
2216 self.tab_inactive_background = [30, 30, 30];
2217 self.tab_hover_background = [60, 60, 60];
2218 self.tab_active_text = [0, 0, 0];
2219 self.tab_inactive_text = [255, 255, 255];
2220 self.tab_active_indicator = [255, 255, 0];
2221 self.tab_border_color = [255, 255, 255];
2222 self.tab_border_width = 2.0;
2223 self.tab_bar_height = 30.0;
2224 }
2225 TabStyle::Automatic => {
2226 }
2228 }
2229 }
2230
2231 pub fn load() -> Result<Self> {
2233 let config_path = Self::config_path();
2234 log::info!("Config path: {:?}", config_path);
2235
2236 if config_path.exists() {
2237 log::info!("Loading existing config from {:?}", config_path);
2238 let contents = fs::read_to_string(&config_path)?;
2239 let contents = substitute_variables(&contents);
2240 let mut config: Config = serde_yaml::from_str(&contents)?;
2241
2242 config.merge_default_keybindings();
2244
2245 config.generate_snippet_action_keybindings();
2247
2248 config.load_last_working_directory();
2250
2251 Ok(config)
2252 } else {
2253 log::info!(
2254 "Config file not found, creating default at {:?}",
2255 config_path
2256 );
2257 let mut config = Self::default();
2259 config.generate_snippet_action_keybindings();
2261 if let Err(e) = config.save() {
2262 log::error!("Failed to save default config: {}", e);
2263 return Err(e);
2264 }
2265
2266 config.load_last_working_directory();
2268
2269 log::info!("Default config created successfully");
2270 Ok(config)
2271 }
2272 }
2273
2274 fn merge_default_keybindings(&mut self) {
2278 let default_keybindings = crate::defaults::keybindings();
2279
2280 let existing_actions: std::collections::HashSet<String> = self
2282 .keybindings
2283 .iter()
2284 .map(|kb| kb.action.clone())
2285 .collect();
2286
2287 let mut added_count = 0;
2289 for default_kb in default_keybindings {
2290 if !existing_actions.contains(&default_kb.action) {
2291 log::info!(
2292 "Adding new default keybinding: {} -> {}",
2293 default_kb.key,
2294 default_kb.action
2295 );
2296 self.keybindings.push(default_kb);
2297 added_count += 1;
2298 }
2299 }
2300
2301 if added_count > 0 {
2302 log::info!(
2303 "Merged {} new default keybinding(s) into user config",
2304 added_count
2305 );
2306 }
2307 }
2308
2309 pub fn generate_snippet_action_keybindings(&mut self) {
2315 use crate::config::KeyBinding;
2316
2317 let mut seen_actions = std::collections::HashSet::new();
2319 let mut added_count = 0;
2320 let mut updated_count = 0;
2321
2322 for snippet in &self.snippets {
2324 if let Some(key) = &snippet.keybinding {
2325 let action = format!("snippet:{}", snippet.id);
2326 seen_actions.insert(action.clone());
2327
2328 if !key.is_empty() && snippet.enabled && snippet.keybinding_enabled {
2329 if let Some(existing) =
2331 self.keybindings.iter_mut().find(|kb| kb.action == action)
2332 {
2333 if existing.key != *key {
2335 log::info!(
2336 "Updating keybinding for snippet '{}': {} -> {} (was: {})",
2337 snippet.title,
2338 key,
2339 action,
2340 existing.key
2341 );
2342 existing.key = key.clone();
2343 updated_count += 1;
2344 }
2345 } else {
2346 log::info!(
2348 "Adding keybinding for snippet '{}': {} -> {} (enabled={}, keybinding_enabled={})",
2349 snippet.title,
2350 key,
2351 action,
2352 snippet.enabled,
2353 snippet.keybinding_enabled
2354 );
2355 self.keybindings.push(KeyBinding {
2356 key: key.clone(),
2357 action,
2358 });
2359 added_count += 1;
2360 }
2361 } else if !key.is_empty() {
2362 log::info!(
2363 "Skipping keybinding for snippet '{}': {} (enabled={}, keybinding_enabled={})",
2364 snippet.title,
2365 key,
2366 snippet.enabled,
2367 snippet.keybinding_enabled
2368 );
2369 }
2370 }
2371 }
2372
2373 for action_config in &self.actions {
2375 if let Some(key) = action_config.keybinding() {
2376 let action = format!("action:{}", action_config.id());
2377 seen_actions.insert(action.clone());
2378
2379 if !key.is_empty() && action_config.keybinding_enabled() {
2380 if let Some(existing) =
2382 self.keybindings.iter_mut().find(|kb| kb.action == action)
2383 {
2384 if existing.key != key {
2386 log::info!(
2387 "Updating keybinding for action '{}': {} -> {} (was: {})",
2388 action_config.title(),
2389 key,
2390 action,
2391 existing.key
2392 );
2393 existing.key = key.to_string();
2394 updated_count += 1;
2395 }
2396 } else {
2397 log::info!(
2399 "Adding keybinding for action '{}': {} -> {} (keybinding_enabled={})",
2400 action_config.title(),
2401 key,
2402 action,
2403 action_config.keybinding_enabled()
2404 );
2405 self.keybindings.push(KeyBinding {
2406 key: key.to_string(),
2407 action,
2408 });
2409 added_count += 1;
2410 }
2411 } else if !key.is_empty() {
2412 log::info!(
2413 "Skipping keybinding for action '{}': {} (keybinding_enabled={})",
2414 action_config.title(),
2415 key,
2416 action_config.keybinding_enabled()
2417 );
2418 }
2419 }
2420 }
2421
2422 let original_len = self.keybindings.len();
2424 self.keybindings.retain(|kb| {
2425 if !kb.action.starts_with("snippet:") && !kb.action.starts_with("action:") {
2427 return true;
2428 }
2429 seen_actions.contains(&kb.action)
2431 });
2432 let removed_count = original_len - self.keybindings.len();
2433
2434 if added_count > 0 || updated_count > 0 || removed_count > 0 {
2435 log::info!(
2436 "Snippet/Action keybindings: {} added, {} updated, {} removed",
2437 added_count,
2438 updated_count,
2439 removed_count
2440 );
2441 }
2442 }
2443
2444 pub fn save(&self) -> Result<()> {
2446 let config_path = Self::config_path();
2447
2448 if let Some(parent) = config_path.parent() {
2450 fs::create_dir_all(parent)?;
2451 }
2452
2453 let yaml = serde_yaml::to_string(self)?;
2454 fs::write(&config_path, yaml)?;
2455
2456 Ok(())
2457 }
2458
2459 pub fn config_path() -> PathBuf {
2461 #[cfg(target_os = "windows")]
2462 {
2463 if let Some(config_dir) = dirs::config_dir() {
2464 config_dir.join("par-term").join("config.yaml")
2465 } else {
2466 PathBuf::from("config.yaml")
2467 }
2468 }
2469 #[cfg(not(target_os = "windows"))]
2470 {
2471 if let Some(home_dir) = dirs::home_dir() {
2473 home_dir
2474 .join(".config")
2475 .join("par-term")
2476 .join("config.yaml")
2477 } else {
2478 PathBuf::from("config.yaml")
2480 }
2481 }
2482 }
2483
2484 pub fn resolve_tmux_path(&self) -> String {
2489 let configured = &self.tmux_path;
2490
2491 if configured.starts_with('/') && std::path::Path::new(configured).exists() {
2493 return configured.clone();
2494 }
2495
2496 if configured != "tmux" {
2498 return configured.clone();
2499 }
2500
2501 if let Ok(path_env) = std::env::var("PATH") {
2503 let separator = if cfg!(windows) { ';' } else { ':' };
2504 let executable = if cfg!(windows) { "tmux.exe" } else { "tmux" };
2505
2506 for dir in path_env.split(separator) {
2507 let candidate = std::path::Path::new(dir).join(executable);
2508 if candidate.exists() {
2509 return candidate.to_string_lossy().to_string();
2510 }
2511 }
2512 }
2513
2514 #[cfg(target_os = "macos")]
2516 {
2517 let macos_paths = [
2518 "/opt/homebrew/bin/tmux", "/usr/local/bin/tmux", ];
2521 for path in macos_paths {
2522 if std::path::Path::new(path).exists() {
2523 return path.to_string();
2524 }
2525 }
2526 }
2527
2528 #[cfg(target_os = "linux")]
2529 {
2530 let linux_paths = [
2531 "/usr/bin/tmux", "/usr/local/bin/tmux", "/snap/bin/tmux", ];
2535 for path in linux_paths {
2536 if std::path::Path::new(path).exists() {
2537 return path.to_string();
2538 }
2539 }
2540 }
2541
2542 configured.clone()
2544 }
2545
2546 pub fn logs_dir(&self) -> PathBuf {
2549 let path = if self.session_log_directory.starts_with("~/") {
2550 if let Some(home) = dirs::home_dir() {
2551 home.join(&self.session_log_directory[2..])
2552 } else {
2553 PathBuf::from(&self.session_log_directory)
2554 }
2555 } else {
2556 PathBuf::from(&self.session_log_directory)
2557 };
2558
2559 if !path.exists()
2561 && let Err(e) = std::fs::create_dir_all(&path)
2562 {
2563 log::warn!("Failed to create logs directory {:?}: {}", path, e);
2564 }
2565
2566 path
2567 }
2568
2569 pub fn shaders_dir() -> PathBuf {
2571 #[cfg(target_os = "windows")]
2572 {
2573 if let Some(config_dir) = dirs::config_dir() {
2574 config_dir.join("par-term").join("shaders")
2575 } else {
2576 PathBuf::from("shaders")
2577 }
2578 }
2579 #[cfg(not(target_os = "windows"))]
2580 {
2581 if let Some(home_dir) = dirs::home_dir() {
2582 home_dir.join(".config").join("par-term").join("shaders")
2583 } else {
2584 PathBuf::from("shaders")
2585 }
2586 }
2587 }
2588
2589 pub fn shader_path(shader_name: &str) -> PathBuf {
2593 let path = PathBuf::from(shader_name);
2594 if path.is_absolute() {
2595 path
2596 } else {
2597 Self::shaders_dir().join(shader_name)
2598 }
2599 }
2600
2601 pub fn resolve_texture_path(path: &str) -> PathBuf {
2605 if path.starts_with("~/")
2606 && let Some(home) = dirs::home_dir()
2607 {
2608 return home.join(&path[2..]);
2609 }
2610 let path_buf = PathBuf::from(path);
2611 if path_buf.is_absolute() {
2612 path_buf
2613 } else {
2614 Self::shaders_dir().join(path)
2615 }
2616 }
2617
2618 #[allow(dead_code)]
2621 pub fn shader_channel_paths(&self) -> [Option<PathBuf>; 4] {
2622 [
2623 self.custom_shader_channel0
2624 .as_ref()
2625 .map(|p| Self::resolve_texture_path(p)),
2626 self.custom_shader_channel1
2627 .as_ref()
2628 .map(|p| Self::resolve_texture_path(p)),
2629 self.custom_shader_channel2
2630 .as_ref()
2631 .map(|p| Self::resolve_texture_path(p)),
2632 self.custom_shader_channel3
2633 .as_ref()
2634 .map(|p| Self::resolve_texture_path(p)),
2635 ]
2636 }
2637
2638 #[allow(dead_code)]
2641 pub fn shader_cubemap_path(&self) -> Option<PathBuf> {
2642 self.custom_shader_cubemap
2643 .as_ref()
2644 .map(|p| Self::resolve_texture_path(p))
2645 }
2646
2647 #[allow(dead_code)]
2649 pub fn with_title(mut self, title: impl Into<String>) -> Self {
2650 self.window_title = title.into();
2651 self
2652 }
2653
2654 pub fn load_theme(&self) -> Theme {
2656 Theme::by_name(&self.theme).unwrap_or_default()
2657 }
2658
2659 pub fn apply_system_theme(&mut self, is_dark: bool) -> bool {
2662 if !self.auto_dark_mode {
2663 return false;
2664 }
2665 let new_theme = if is_dark {
2666 &self.dark_theme
2667 } else {
2668 &self.light_theme
2669 };
2670 if self.theme != *new_theme {
2671 self.theme = new_theme.clone();
2672 true
2673 } else {
2674 false
2675 }
2676 }
2677
2678 pub fn apply_system_tab_style(&mut self, is_dark: bool) -> bool {
2681 if self.tab_style != TabStyle::Automatic {
2682 return false;
2683 }
2684 let target = if is_dark {
2685 self.dark_tab_style
2686 } else {
2687 self.light_tab_style
2688 };
2689 self.tab_style = target;
2691 self.apply_tab_style();
2692 self.tab_style = TabStyle::Automatic;
2693 true
2694 }
2695
2696 pub fn get_shader_override(&self, shader_name: &str) -> Option<&ShaderConfig> {
2698 self.shader_configs.get(shader_name)
2699 }
2700
2701 pub fn get_cursor_shader_override(&self, shader_name: &str) -> Option<&CursorShaderConfig> {
2703 self.cursor_shader_configs.get(shader_name)
2704 }
2705
2706 pub fn get_or_create_shader_override(&mut self, shader_name: &str) -> &mut ShaderConfig {
2708 self.shader_configs
2709 .entry(shader_name.to_string())
2710 .or_default()
2711 }
2712
2713 pub fn get_or_create_cursor_shader_override(
2715 &mut self,
2716 shader_name: &str,
2717 ) -> &mut CursorShaderConfig {
2718 self.cursor_shader_configs
2719 .entry(shader_name.to_string())
2720 .or_default()
2721 }
2722
2723 pub fn remove_shader_override(&mut self, shader_name: &str) {
2725 self.shader_configs.remove(shader_name);
2726 }
2727
2728 pub fn remove_cursor_shader_override(&mut self, shader_name: &str) {
2730 self.cursor_shader_configs.remove(shader_name);
2731 }
2732
2733 pub fn should_prompt_shader_install(&self) -> bool {
2736 if self.shader_install_prompt != ShaderInstallPrompt::Ask {
2738 return false;
2739 }
2740
2741 let shaders_dir = Self::shaders_dir();
2742
2743 if !shaders_dir.exists() {
2745 return true;
2746 }
2747
2748 if let Ok(entries) = std::fs::read_dir(&shaders_dir) {
2750 for entry in entries.flatten() {
2751 if let Some(ext) = entry.path().extension()
2752 && ext == "glsl"
2753 {
2754 return false; }
2756 }
2757 }
2758
2759 true }
2761
2762 pub fn config_dir() -> PathBuf {
2764 #[cfg(target_os = "windows")]
2765 {
2766 if let Some(config_dir) = dirs::config_dir() {
2767 config_dir.join("par-term")
2768 } else {
2769 PathBuf::from(".")
2770 }
2771 }
2772 #[cfg(not(target_os = "windows"))]
2773 {
2774 if let Some(home_dir) = dirs::home_dir() {
2775 home_dir.join(".config").join("par-term")
2776 } else {
2777 PathBuf::from(".")
2778 }
2779 }
2780 }
2781
2782 pub fn shell_integration_dir() -> PathBuf {
2784 Self::config_dir()
2785 }
2786
2787 pub fn should_prompt_shell_integration(&self) -> bool {
2789 if self.shell_integration_state != InstallPromptState::Ask {
2790 return false;
2791 }
2792
2793 let current_version = env!("CARGO_PKG_VERSION");
2794
2795 if let Some(ref prompted) = self.integration_versions.shell_integration_prompted_version
2797 && prompted == current_version
2798 {
2799 return false;
2800 }
2801
2802 if let Some(ref installed) = self
2804 .integration_versions
2805 .shell_integration_installed_version
2806 && installed == current_version
2807 {
2808 return false;
2809 }
2810
2811 true
2812 }
2813
2814 pub fn should_prompt_shader_install_versioned(&self) -> bool {
2816 if self.shader_install_prompt != ShaderInstallPrompt::Ask {
2817 return false;
2818 }
2819
2820 let current_version = env!("CARGO_PKG_VERSION");
2821
2822 if let Some(ref prompted) = self.integration_versions.shaders_prompted_version
2824 && prompted == current_version
2825 {
2826 return false;
2827 }
2828
2829 if let Some(ref installed) = self.integration_versions.shaders_installed_version
2831 && installed == current_version
2832 {
2833 return false;
2834 }
2835
2836 let shaders_dir = Self::shaders_dir();
2838 !shaders_dir.exists() || !Self::has_shader_files(&shaders_dir)
2839 }
2840
2841 fn has_shader_files(dir: &PathBuf) -> bool {
2843 if let Ok(entries) = std::fs::read_dir(dir) {
2844 for entry in entries.flatten() {
2845 if let Some(ext) = entry.path().extension()
2846 && ext == "glsl"
2847 {
2848 return true;
2849 }
2850 }
2851 }
2852 false
2853 }
2854
2855 pub fn should_prompt_integrations(&self) -> bool {
2857 self.should_prompt_shader_install_versioned() || self.should_prompt_shell_integration()
2858 }
2859
2860 pub fn get_effective_startup_directory(&self) -> Option<String> {
2871 if let Some(ref wd) = self.working_directory {
2873 let expanded = Self::expand_home_dir(wd);
2874 if std::path::Path::new(&expanded).exists() {
2875 return Some(expanded);
2876 }
2877 log::warn!(
2878 "Configured working_directory '{}' does not exist, using default",
2879 wd
2880 );
2881 }
2882
2883 match self.startup_directory_mode {
2884 StartupDirectoryMode::Home => {
2885 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
2887 }
2888 StartupDirectoryMode::Previous => {
2889 if let Some(ref last_dir) = self.last_working_directory {
2891 let expanded = Self::expand_home_dir(last_dir);
2892 if std::path::Path::new(&expanded).exists() {
2893 return Some(expanded);
2894 }
2895 log::warn!(
2896 "Previous session directory '{}' no longer exists, using home",
2897 last_dir
2898 );
2899 }
2900 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
2902 }
2903 StartupDirectoryMode::Custom => {
2904 if let Some(ref custom_dir) = self.startup_directory {
2906 let expanded = Self::expand_home_dir(custom_dir);
2907 if std::path::Path::new(&expanded).exists() {
2908 return Some(expanded);
2909 }
2910 log::warn!(
2911 "Custom startup directory '{}' does not exist, using home",
2912 custom_dir
2913 );
2914 }
2915 dirs::home_dir().map(|p| p.to_string_lossy().to_string())
2917 }
2918 }
2919 }
2920
2921 fn expand_home_dir(path: &str) -> String {
2923 if let Some(suffix) = path.strip_prefix("~/")
2924 && let Some(home) = dirs::home_dir()
2925 {
2926 return home.join(suffix).to_string_lossy().to_string();
2927 }
2928 path.to_string()
2929 }
2930
2931 pub fn state_file_path() -> PathBuf {
2933 #[cfg(target_os = "windows")]
2934 {
2935 if let Some(data_dir) = dirs::data_local_dir() {
2936 data_dir.join("par-term").join("state.yaml")
2937 } else {
2938 PathBuf::from("state.yaml")
2939 }
2940 }
2941 #[cfg(not(target_os = "windows"))]
2942 {
2943 if let Some(home_dir) = dirs::home_dir() {
2944 home_dir
2945 .join(".local")
2946 .join("share")
2947 .join("par-term")
2948 .join("state.yaml")
2949 } else {
2950 PathBuf::from("state.yaml")
2951 }
2952 }
2953 }
2954
2955 pub fn save_last_working_directory(&mut self, directory: &str) -> Result<()> {
2957 self.last_working_directory = Some(directory.to_string());
2958
2959 let state_path = Self::state_file_path();
2961 if let Some(parent) = state_path.parent() {
2962 fs::create_dir_all(parent)?;
2963 }
2964
2965 #[derive(Serialize)]
2967 struct SessionState {
2968 last_working_directory: Option<String>,
2969 }
2970
2971 let state = SessionState {
2972 last_working_directory: Some(directory.to_string()),
2973 };
2974
2975 let yaml = serde_yaml::to_string(&state)?;
2976 fs::write(&state_path, yaml)?;
2977
2978 log::debug!(
2979 "Saved last working directory to {:?}: {}",
2980 state_path,
2981 directory
2982 );
2983 Ok(())
2984 }
2985
2986 pub fn get_pane_background(&self, index: usize) -> Option<(String, BackgroundImageMode, f32)> {
2989 self.pane_backgrounds
2990 .iter()
2991 .find(|pb| pb.index == index)
2992 .map(|pb| (pb.image.clone(), pb.mode, pb.opacity))
2993 }
2994
2995 pub fn load_last_working_directory(&mut self) {
2997 let state_path = Self::state_file_path();
2998 if !state_path.exists() {
2999 return;
3000 }
3001
3002 #[derive(Deserialize)]
3003 struct SessionState {
3004 last_working_directory: Option<String>,
3005 }
3006
3007 match fs::read_to_string(&state_path) {
3008 Ok(contents) => {
3009 if let Ok(state) = serde_yaml::from_str::<SessionState>(&contents)
3010 && let Some(dir) = state.last_working_directory
3011 {
3012 log::debug!("Loaded last working directory from state file: {}", dir);
3013 self.last_working_directory = Some(dir);
3014 }
3015 }
3016 Err(e) => {
3017 log::warn!("Failed to read state file {:?}: {}", state_path, e);
3018 }
3019 }
3020 }
3021}