1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "lowercase")]
15#[allow(dead_code)]
16pub enum KeyModifier {
17 Ctrl,
19 Alt,
21 Shift,
23 CmdOrCtrl,
25 Super,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
31pub struct KeyBinding {
32 pub key: String,
34 pub action: String,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
40#[serde(rename_all = "lowercase")]
41pub enum VsyncMode {
42 Immediate,
44 Mailbox,
46 #[default]
48 Fifo,
49}
50
51impl VsyncMode {
52 #[cfg(feature = "wgpu-types")]
54 pub fn to_present_mode(self) -> wgpu::PresentMode {
55 match self {
56 VsyncMode::Immediate => wgpu::PresentMode::Immediate,
57 VsyncMode::Mailbox => wgpu::PresentMode::Mailbox,
58 VsyncMode::Fifo => wgpu::PresentMode::Fifo,
59 }
60 }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
68#[serde(rename_all = "snake_case")]
69pub enum PowerPreference {
70 #[default]
72 None,
73 LowPower,
75 HighPerformance,
77}
78
79impl PowerPreference {
80 #[cfg(feature = "wgpu-types")]
82 pub fn to_wgpu(self) -> wgpu::PowerPreference {
83 match self {
84 PowerPreference::None => wgpu::PowerPreference::None,
85 PowerPreference::LowPower => wgpu::PowerPreference::LowPower,
86 PowerPreference::HighPerformance => wgpu::PowerPreference::HighPerformance,
87 }
88 }
89
90 pub fn display_name(&self) -> &'static str {
92 match self {
93 PowerPreference::None => "None (System Default)",
94 PowerPreference::LowPower => "Low Power (Integrated GPU)",
95 PowerPreference::HighPerformance => "High Performance (Discrete GPU)",
96 }
97 }
98
99 pub fn all() -> &'static [PowerPreference] {
101 &[
102 PowerPreference::None,
103 PowerPreference::LowPower,
104 PowerPreference::HighPerformance,
105 ]
106 }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
111#[serde(rename_all = "lowercase")]
112pub enum CursorStyle {
113 #[default]
115 Block,
116 Beam,
118 Underline,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
124#[serde(rename_all = "lowercase")]
125pub enum UnfocusedCursorStyle {
126 #[default]
128 Hollow,
129 Same,
131 Hidden,
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
139#[serde(rename_all = "lowercase")]
140pub enum ImageScalingMode {
141 Nearest,
143 #[default]
145 Linear,
146}
147
148impl ImageScalingMode {
149 pub fn display_name(&self) -> &'static str {
151 match self {
152 ImageScalingMode::Nearest => "Nearest (Sharp)",
153 ImageScalingMode::Linear => "Linear (Smooth)",
154 }
155 }
156
157 pub fn all() -> &'static [ImageScalingMode] {
159 &[ImageScalingMode::Nearest, ImageScalingMode::Linear]
160 }
161
162 #[cfg(feature = "wgpu-types")]
164 pub fn to_filter_mode(self) -> wgpu::FilterMode {
165 match self {
166 ImageScalingMode::Nearest => wgpu::FilterMode::Nearest,
167 ImageScalingMode::Linear => wgpu::FilterMode::Linear,
168 }
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
174#[serde(rename_all = "lowercase")]
175pub enum BackgroundImageMode {
176 Fit,
178 Fill,
180 #[default]
182 Stretch,
183 Tile,
185 Center,
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
191#[serde(rename_all = "snake_case")]
192pub enum DownloadSaveLocation {
193 #[default]
195 Downloads,
196 LastUsed,
198 Cwd,
200 Custom(String),
202}
203
204impl DownloadSaveLocation {
205 pub fn variants() -> &'static [DownloadSaveLocation] {
207 &[
208 DownloadSaveLocation::Downloads,
209 DownloadSaveLocation::LastUsed,
210 DownloadSaveLocation::Cwd,
211 ]
212 }
213
214 pub fn display_name(&self) -> &str {
216 match self {
217 DownloadSaveLocation::Downloads => "Downloads folder",
218 DownloadSaveLocation::LastUsed => "Last used directory",
219 DownloadSaveLocation::Cwd => "Current working directory",
220 DownloadSaveLocation::Custom(_) => "Custom directory",
221 }
222 }
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
227#[serde(rename_all = "lowercase")]
228pub enum BackgroundMode {
229 #[default]
231 Default,
232 Color,
234 Image,
236}
237
238#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct PaneBackgroundConfig {
241 pub index: usize,
243 pub image: String,
245 #[serde(default)]
247 pub mode: BackgroundImageMode,
248 #[serde(default = "crate::defaults::background_image_opacity")]
250 pub opacity: f32,
251 #[serde(default = "crate::defaults::pane_background_darken")]
253 pub darken: f32,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
261#[serde(rename_all = "snake_case")]
262pub enum TabStyle {
263 #[default]
265 Dark,
266 Light,
268 Compact,
270 Minimal,
272 HighContrast,
274 Automatic,
276}
277
278impl TabStyle {
279 pub fn display_name(&self) -> &'static str {
281 match self {
282 TabStyle::Dark => "Dark",
283 TabStyle::Light => "Light",
284 TabStyle::Compact => "Compact",
285 TabStyle::Minimal => "Minimal",
286 TabStyle::HighContrast => "High Contrast",
287 TabStyle::Automatic => "Automatic",
288 }
289 }
290
291 pub fn all() -> &'static [TabStyle] {
293 &[
294 TabStyle::Dark,
295 TabStyle::Light,
296 TabStyle::Compact,
297 TabStyle::Minimal,
298 TabStyle::HighContrast,
299 TabStyle::Automatic,
300 ]
301 }
302
303 pub fn all_concrete() -> &'static [TabStyle] {
305 &[
306 TabStyle::Dark,
307 TabStyle::Light,
308 TabStyle::Compact,
309 TabStyle::Minimal,
310 TabStyle::HighContrast,
311 ]
312 }
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
317#[serde(rename_all = "snake_case")]
318pub enum TabBarPosition {
319 #[default]
321 Top,
322 Bottom,
324 Left,
326}
327
328impl TabBarPosition {
329 pub fn display_name(&self) -> &'static str {
331 match self {
332 TabBarPosition::Top => "Top",
333 TabBarPosition::Bottom => "Bottom",
334 TabBarPosition::Left => "Left",
335 }
336 }
337
338 pub fn all() -> &'static [TabBarPosition] {
340 &[
341 TabBarPosition::Top,
342 TabBarPosition::Bottom,
343 TabBarPosition::Left,
344 ]
345 }
346
347 pub fn is_horizontal(&self) -> bool {
349 matches!(self, TabBarPosition::Top | TabBarPosition::Bottom)
350 }
351}
352
353#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
355#[serde(rename_all = "snake_case")]
356pub enum TabBarMode {
357 #[default]
359 Always,
360 WhenMultiple,
362 Never,
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
368#[serde(rename_all = "snake_case")]
369pub enum TabTitleMode {
370 #[default]
372 Auto,
373 OscOnly,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
379#[serde(rename_all = "lowercase")]
380pub enum StatusBarPosition {
381 Top,
383 #[default]
385 Bottom,
386}
387
388#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
390#[serde(rename_all = "snake_case")]
391pub enum WindowType {
392 #[default]
394 Normal,
395 Fullscreen,
397 EdgeTop,
400 EdgeBottom,
402 EdgeLeft,
404 EdgeRight,
406}
407
408impl WindowType {
409 pub fn display_name(&self) -> &'static str {
411 match self {
412 WindowType::Normal => "Normal",
413 WindowType::Fullscreen => "Fullscreen",
414 WindowType::EdgeTop => "Edge (Top)",
415 WindowType::EdgeBottom => "Edge (Bottom)",
416 WindowType::EdgeLeft => "Edge (Left)",
417 WindowType::EdgeRight => "Edge (Right)",
418 }
419 }
420
421 pub fn all() -> &'static [WindowType] {
423 &[
424 WindowType::Normal,
425 WindowType::Fullscreen,
426 WindowType::EdgeTop,
427 WindowType::EdgeBottom,
428 WindowType::EdgeLeft,
429 WindowType::EdgeRight,
430 ]
431 }
432
433 pub fn is_edge(&self) -> bool {
435 matches!(
436 self,
437 WindowType::EdgeTop
438 | WindowType::EdgeBottom
439 | WindowType::EdgeLeft
440 | WindowType::EdgeRight
441 )
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
449#[serde(rename_all = "snake_case")]
450pub enum DroppedFileQuoteStyle {
451 #[default]
454 SingleQuotes,
455 DoubleQuotes,
458 Backslash,
461 None,
463}
464
465impl DroppedFileQuoteStyle {
466 pub fn display_name(&self) -> &'static str {
468 match self {
469 DroppedFileQuoteStyle::SingleQuotes => "Single quotes ('...')",
470 DroppedFileQuoteStyle::DoubleQuotes => "Double quotes (\"...\")",
471 DroppedFileQuoteStyle::Backslash => "Backslash escaping (\\)",
472 DroppedFileQuoteStyle::None => "None (raw path)",
473 }
474 }
475
476 pub fn all() -> &'static [DroppedFileQuoteStyle] {
478 &[
479 DroppedFileQuoteStyle::SingleQuotes,
480 DroppedFileQuoteStyle::DoubleQuotes,
481 DroppedFileQuoteStyle::Backslash,
482 DroppedFileQuoteStyle::None,
483 ]
484 }
485}
486
487#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
493#[serde(rename_all = "lowercase")]
494pub enum OptionKeyMode {
495 Normal,
498 Meta,
501 #[default]
505 Esc,
506}
507
508#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
517#[serde(rename_all = "lowercase")]
518pub enum ModifierTarget {
519 #[default]
521 None,
522 Ctrl,
524 Alt,
526 Shift,
528 Super,
530}
531
532impl ModifierTarget {
533 pub fn display_name(&self) -> &'static str {
535 match self {
536 ModifierTarget::None => "None (disabled)",
537 ModifierTarget::Ctrl => "Ctrl",
538 ModifierTarget::Alt => "Alt/Option",
539 ModifierTarget::Shift => "Shift",
540 ModifierTarget::Super => "Super/Cmd",
541 }
542 }
543
544 pub fn all() -> &'static [ModifierTarget] {
546 &[
547 ModifierTarget::None,
548 ModifierTarget::Ctrl,
549 ModifierTarget::Alt,
550 ModifierTarget::Shift,
551 ModifierTarget::Super,
552 ]
553 }
554}
555
556#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
564pub struct ModifierRemapping {
565 #[serde(default)]
567 pub left_ctrl: ModifierTarget,
568 #[serde(default)]
570 pub right_ctrl: ModifierTarget,
571 #[serde(default)]
573 pub left_alt: ModifierTarget,
574 #[serde(default)]
576 pub right_alt: ModifierTarget,
577 #[serde(default)]
579 pub left_super: ModifierTarget,
580 #[serde(default)]
582 pub right_super: ModifierTarget,
583}
584
585#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct FontRange {
588 pub start: u32,
590 pub end: u32,
592 pub font_family: String,
594}
595
596#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
601#[serde(rename_all = "snake_case")]
602pub enum ThinStrokesMode {
603 Never,
605 #[default]
607 RetinaOnly,
608 DarkBackgroundsOnly,
610 RetinaDarkBackgroundsOnly,
612 Always,
614}
615
616#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
621#[serde(rename_all = "snake_case")]
622pub enum ShaderInstallPrompt {
623 #[default]
625 Ask,
626 Never,
628 Installed,
630}
631
632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
634#[serde(rename_all = "snake_case")]
635pub enum InstallPromptState {
636 #[default]
638 Ask,
639 Never,
641 Installed,
643}
644
645impl InstallPromptState {
646 pub fn display_name(&self) -> &'static str {
648 match self {
649 Self::Ask => "Ask",
650 Self::Never => "Never",
651 Self::Installed => "Installed",
652 }
653 }
654}
655
656#[derive(Debug, Clone, Default, Serialize, Deserialize)]
658pub struct IntegrationVersions {
659 pub shaders_installed_version: Option<String>,
661 pub shaders_prompted_version: Option<String>,
663 pub shell_integration_installed_version: Option<String>,
665 pub shell_integration_prompted_version: Option<String>,
667}
668
669#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
673#[serde(rename_all = "snake_case")]
674pub enum StartupDirectoryMode {
675 #[default]
677 Home,
678 Previous,
680 Custom,
682}
683
684impl StartupDirectoryMode {
685 pub fn display_name(&self) -> &'static str {
687 match self {
688 Self::Home => "Home Directory",
689 Self::Previous => "Previous Session",
690 Self::Custom => "Custom Directory",
691 }
692 }
693
694 pub fn all() -> &'static [StartupDirectoryMode] {
696 &[
697 StartupDirectoryMode::Home,
698 StartupDirectoryMode::Previous,
699 StartupDirectoryMode::Custom,
700 ]
701 }
702}
703
704#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
708#[serde(rename_all = "snake_case")]
709pub enum ShellExitAction {
710 #[default]
712 Close,
713 Keep,
715 RestartImmediately,
717 RestartWithPrompt,
719 RestartAfterDelay,
721}
722
723impl ShellExitAction {
724 pub fn display_name(&self) -> &'static str {
726 match self {
727 Self::Close => "Close tab/pane",
728 Self::Keep => "Keep open",
729 Self::RestartImmediately => "Restart immediately",
730 Self::RestartWithPrompt => "Restart with prompt",
731 Self::RestartAfterDelay => "Restart after 1s delay",
732 }
733 }
734
735 pub fn all() -> &'static [ShellExitAction] {
737 &[
738 ShellExitAction::Close,
739 ShellExitAction::Keep,
740 ShellExitAction::RestartImmediately,
741 ShellExitAction::RestartWithPrompt,
742 ShellExitAction::RestartAfterDelay,
743 ]
744 }
745
746 pub fn is_restart(&self) -> bool {
748 matches!(
749 self,
750 Self::RestartImmediately | Self::RestartWithPrompt | Self::RestartAfterDelay
751 )
752 }
753}
754
755#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
757#[serde(rename_all = "lowercase")]
758pub enum ShellType {
759 Bash,
760 Zsh,
761 Fish,
762 #[default]
763 Unknown,
764}
765
766impl ShellType {
767 fn from_path(path: &str) -> Self {
769 if path.contains("zsh") {
770 Self::Zsh
771 } else if path.contains("bash") {
772 Self::Bash
773 } else if path.contains("fish") {
774 Self::Fish
775 } else {
776 Self::Unknown
777 }
778 }
779
780 pub fn detect() -> Self {
786 if let Ok(shell) = std::env::var("SHELL") {
788 let t = Self::from_path(&shell);
789 if t != Self::Unknown {
790 return t;
791 }
792 }
793
794 #[cfg(target_os = "macos")]
796 {
797 if let Some(t) = Self::detect_via_dscl() {
798 return t;
799 }
800 }
801
802 #[cfg(unix)]
804 {
805 if let Some(t) = Self::detect_from_passwd() {
806 return t;
807 }
808 }
809
810 Self::Unknown
811 }
812
813 #[cfg(target_os = "macos")]
815 fn detect_via_dscl() -> Option<Self> {
816 let user = std::env::var("USER")
817 .or_else(|_| std::env::var("LOGNAME"))
818 .ok()?;
819 let output = std::process::Command::new("dscl")
820 .args([".", "-read", &format!("/Users/{}", user), "UserShell"])
821 .output()
822 .ok()?;
823 let text = String::from_utf8_lossy(&output.stdout);
824 let shell_path = text.split_whitespace().last()?;
826 let t = Self::from_path(shell_path);
827 if t != Self::Unknown { Some(t) } else { None }
828 }
829
830 #[cfg(unix)]
832 fn detect_from_passwd() -> Option<Self> {
833 let user = std::env::var("USER")
834 .or_else(|_| std::env::var("LOGNAME"))
835 .ok()?;
836 let contents = std::fs::read_to_string("/etc/passwd").ok()?;
837 for line in contents.lines() {
838 let parts: Vec<&str> = line.splitn(7, ':').collect();
839 if parts.len() == 7 && parts[0] == user {
840 let t = Self::from_path(parts[6]);
841 if t != Self::Unknown {
842 return Some(t);
843 }
844 }
845 }
846 None
847 }
848
849 pub fn display_name(&self) -> &'static str {
851 match self {
852 Self::Bash => "Bash",
853 Self::Zsh => "Zsh",
854 Self::Fish => "Fish",
855 Self::Unknown => "Unknown",
856 }
857 }
858
859 pub fn extension(&self) -> &'static str {
861 match self {
862 Self::Bash => "bash",
863 Self::Zsh => "zsh",
864 Self::Fish => "fish",
865 Self::Unknown => "sh",
866 }
867 }
868}
869
870#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
874#[serde(rename_all = "snake_case")]
875pub enum UpdateCheckFrequency {
876 Never,
878 Hourly,
880 #[default]
882 Daily,
883 Weekly,
885 Monthly,
887}
888
889impl UpdateCheckFrequency {
890 pub fn as_seconds(&self) -> Option<u64> {
892 match self {
893 UpdateCheckFrequency::Never => None,
894 UpdateCheckFrequency::Hourly => Some(3600),
895 UpdateCheckFrequency::Daily => Some(24 * 60 * 60), UpdateCheckFrequency::Weekly => Some(7 * 24 * 60 * 60), UpdateCheckFrequency::Monthly => Some(30 * 24 * 60 * 60), }
899 }
900
901 pub fn display_name(&self) -> &'static str {
903 match self {
904 UpdateCheckFrequency::Never => "Never",
905 UpdateCheckFrequency::Hourly => "Hourly",
906 UpdateCheckFrequency::Daily => "Daily",
907 UpdateCheckFrequency::Weekly => "Weekly",
908 UpdateCheckFrequency::Monthly => "Monthly",
909 }
910 }
911}
912
913#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
921#[serde(rename_all = "lowercase")]
922pub enum SessionLogFormat {
923 Plain,
925 Html,
927 #[default]
929 Asciicast,
930}
931
932impl SessionLogFormat {
933 pub fn display_name(&self) -> &'static str {
935 match self {
936 SessionLogFormat::Plain => "Plain Text",
937 SessionLogFormat::Html => "HTML",
938 SessionLogFormat::Asciicast => "Asciicast (asciinema)",
939 }
940 }
941
942 pub fn all() -> &'static [SessionLogFormat] {
944 &[
945 SessionLogFormat::Plain,
946 SessionLogFormat::Html,
947 SessionLogFormat::Asciicast,
948 ]
949 }
950
951 pub fn extension(&self) -> &'static str {
953 match self {
954 SessionLogFormat::Plain => "txt",
955 SessionLogFormat::Html => "html",
956 SessionLogFormat::Asciicast => "cast",
957 }
958 }
959}
960
961#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
966#[serde(rename_all = "lowercase")]
967pub enum LogLevel {
968 #[default]
970 Off,
971 Error,
973 Warn,
975 Info,
977 Debug,
979 Trace,
981}
982
983impl LogLevel {
984 pub fn display_name(&self) -> &'static str {
986 match self {
987 LogLevel::Off => "Off",
988 LogLevel::Error => "Error",
989 LogLevel::Warn => "Warn",
990 LogLevel::Info => "Info",
991 LogLevel::Debug => "Debug",
992 LogLevel::Trace => "Trace",
993 }
994 }
995
996 pub fn all() -> &'static [LogLevel] {
998 &[
999 LogLevel::Off,
1000 LogLevel::Error,
1001 LogLevel::Warn,
1002 LogLevel::Info,
1003 LogLevel::Debug,
1004 LogLevel::Trace,
1005 ]
1006 }
1007
1008 pub fn to_level_filter(self) -> log::LevelFilter {
1010 match self {
1011 LogLevel::Off => log::LevelFilter::Off,
1012 LogLevel::Error => log::LevelFilter::Error,
1013 LogLevel::Warn => log::LevelFilter::Warn,
1014 LogLevel::Info => log::LevelFilter::Info,
1015 LogLevel::Debug => log::LevelFilter::Debug,
1016 LogLevel::Trace => log::LevelFilter::Trace,
1017 }
1018 }
1019}
1020
1021#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1025#[serde(rename_all = "snake_case")]
1026pub enum SemanticHistoryEditorMode {
1027 Custom,
1029 #[default]
1031 EnvironmentVariable,
1032 SystemDefault,
1034}
1035
1036impl SemanticHistoryEditorMode {
1037 pub fn display_name(&self) -> &'static str {
1039 match self {
1040 SemanticHistoryEditorMode::Custom => "Custom Editor",
1041 SemanticHistoryEditorMode::EnvironmentVariable => "Environment Variable ($EDITOR)",
1042 SemanticHistoryEditorMode::SystemDefault => "System Default",
1043 }
1044 }
1045
1046 pub fn all() -> &'static [SemanticHistoryEditorMode] {
1048 &[
1049 SemanticHistoryEditorMode::Custom,
1050 SemanticHistoryEditorMode::EnvironmentVariable,
1051 SemanticHistoryEditorMode::SystemDefault,
1052 ]
1053 }
1054}
1055
1056#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1058#[serde(rename_all = "snake_case")]
1059pub enum LinkUnderlineStyle {
1060 Solid,
1062 #[default]
1064 Stipple,
1065}
1066
1067impl LinkUnderlineStyle {
1068 pub fn display_name(&self) -> &'static str {
1070 match self {
1071 LinkUnderlineStyle::Solid => "Solid",
1072 LinkUnderlineStyle::Stipple => "Stipple",
1073 }
1074 }
1075
1076 pub fn all() -> &'static [LinkUnderlineStyle] {
1078 &[LinkUnderlineStyle::Solid, LinkUnderlineStyle::Stipple]
1079 }
1080}
1081
1082#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1090pub struct ShaderMetadata {
1091 pub name: Option<String>,
1093 pub author: Option<String>,
1095 pub description: Option<String>,
1097 pub version: Option<String>,
1099 #[serde(default)]
1101 pub defaults: ShaderConfig,
1102}
1103
1104#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1109pub struct ShaderConfig {
1110 pub animation_speed: Option<f32>,
1112 pub brightness: Option<f32>,
1114 pub text_opacity: Option<f32>,
1116 pub full_content: Option<bool>,
1118 pub channel0: Option<String>,
1120 pub channel1: Option<String>,
1122 pub channel2: Option<String>,
1124 pub channel3: Option<String>,
1126 pub cubemap: Option<String>,
1128 pub cubemap_enabled: Option<bool>,
1130 pub use_background_as_channel0: Option<bool>,
1132}
1133
1134#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1138pub struct CursorShaderConfig {
1139 #[serde(flatten)]
1141 pub base: ShaderConfig,
1142 pub hides_cursor: Option<bool>,
1144 pub disable_in_alt_screen: Option<bool>,
1146 pub glow_radius: Option<f32>,
1148 pub glow_intensity: Option<f32>,
1150 pub trail_duration: Option<f32>,
1152 pub cursor_color: Option<[u8; 3]>,
1154}
1155
1156#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1161pub struct CursorShaderMetadata {
1162 pub name: Option<String>,
1164 pub author: Option<String>,
1166 pub description: Option<String>,
1168 pub version: Option<String>,
1170 #[serde(default)]
1172 pub defaults: CursorShaderConfig,
1173}
1174
1175#[derive(Debug, Clone)]
1179#[allow(dead_code)]
1180pub struct ResolvedShaderConfig {
1181 pub animation_speed: f32,
1183 pub brightness: f32,
1185 pub text_opacity: f32,
1187 pub full_content: bool,
1189 pub channel0: Option<PathBuf>,
1191 pub channel1: Option<PathBuf>,
1193 pub channel2: Option<PathBuf>,
1195 pub channel3: Option<PathBuf>,
1197 pub cubemap: Option<PathBuf>,
1199 pub cubemap_enabled: bool,
1201 pub use_background_as_channel0: bool,
1203}
1204
1205impl Default for ResolvedShaderConfig {
1206 fn default() -> Self {
1207 Self {
1208 animation_speed: 1.0,
1209 brightness: 1.0,
1210 text_opacity: 1.0,
1211 full_content: false,
1212 channel0: None,
1213 channel1: None,
1214 channel2: None,
1215 channel3: None,
1216 cubemap: None,
1217 cubemap_enabled: true,
1218 use_background_as_channel0: false,
1219 }
1220 }
1221}
1222
1223#[derive(Debug, Clone)]
1225#[allow(dead_code)]
1226pub struct ResolvedCursorShaderConfig {
1227 pub base: ResolvedShaderConfig,
1229 pub hides_cursor: bool,
1231 pub disable_in_alt_screen: bool,
1233 pub glow_radius: f32,
1235 pub glow_intensity: f32,
1237 pub trail_duration: f32,
1239 pub cursor_color: [u8; 3],
1241}
1242
1243impl Default for ResolvedCursorShaderConfig {
1244 fn default() -> Self {
1245 Self {
1246 base: ResolvedShaderConfig::default(),
1247 hides_cursor: false,
1248 disable_in_alt_screen: true,
1249 glow_radius: 80.0,
1250 glow_intensity: 0.3,
1251 trail_duration: 0.5,
1252 cursor_color: [255, 255, 255],
1253 }
1254 }
1255}
1256
1257#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1263#[serde(rename_all = "lowercase")]
1264pub enum ProgressBarStyle {
1265 #[default]
1267 Bar,
1268 BarWithText,
1270}
1271
1272impl ProgressBarStyle {
1273 pub fn display_name(&self) -> &'static str {
1275 match self {
1276 ProgressBarStyle::Bar => "Bar",
1277 ProgressBarStyle::BarWithText => "Bar with Text",
1278 }
1279 }
1280
1281 pub fn all() -> &'static [ProgressBarStyle] {
1283 &[ProgressBarStyle::Bar, ProgressBarStyle::BarWithText]
1284 }
1285}
1286
1287#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1289#[serde(rename_all = "lowercase")]
1290pub enum ProgressBarPosition {
1291 #[default]
1293 Top,
1294 Bottom,
1296}
1297
1298impl ProgressBarPosition {
1299 pub fn display_name(&self) -> &'static str {
1301 match self {
1302 ProgressBarPosition::Bottom => "Bottom",
1303 ProgressBarPosition::Top => "Top",
1304 }
1305 }
1306
1307 pub fn all() -> &'static [ProgressBarPosition] {
1309 &[ProgressBarPosition::Top, ProgressBarPosition::Bottom]
1310 }
1311}
1312
1313#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1322#[serde(rename_all = "snake_case")]
1323pub enum SmartSelectionPrecision {
1324 VeryLow,
1326 Low,
1328 #[default]
1330 Normal,
1331 High,
1333 VeryHigh,
1335}
1336
1337impl SmartSelectionPrecision {
1338 pub fn value(&self) -> f64 {
1340 match self {
1341 SmartSelectionPrecision::VeryLow => 0.00001,
1342 SmartSelectionPrecision::Low => 0.001,
1343 SmartSelectionPrecision::Normal => 1.0,
1344 SmartSelectionPrecision::High => 1000.0,
1345 SmartSelectionPrecision::VeryHigh => 1_000_000.0,
1346 }
1347 }
1348
1349 pub fn display_name(&self) -> &'static str {
1351 match self {
1352 SmartSelectionPrecision::VeryLow => "Very Low",
1353 SmartSelectionPrecision::Low => "Low",
1354 SmartSelectionPrecision::Normal => "Normal",
1355 SmartSelectionPrecision::High => "High",
1356 SmartSelectionPrecision::VeryHigh => "Very High",
1357 }
1358 }
1359}
1360
1361#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1363#[serde(rename_all = "snake_case")]
1364pub enum PaneTitlePosition {
1365 #[default]
1367 Top,
1368 Bottom,
1370}
1371
1372impl PaneTitlePosition {
1373 pub const ALL: &'static [PaneTitlePosition] =
1375 &[PaneTitlePosition::Top, PaneTitlePosition::Bottom];
1376
1377 pub fn display_name(&self) -> &'static str {
1379 match self {
1380 PaneTitlePosition::Top => "Top",
1381 PaneTitlePosition::Bottom => "Bottom",
1382 }
1383 }
1384}
1385
1386#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1388#[serde(rename_all = "snake_case")]
1389pub enum DividerStyle {
1390 #[default]
1392 Solid,
1393 Double,
1395 Dashed,
1397 Shadow,
1399}
1400
1401impl DividerStyle {
1402 pub const ALL: &'static [DividerStyle] = &[
1404 DividerStyle::Solid,
1405 DividerStyle::Double,
1406 DividerStyle::Dashed,
1407 DividerStyle::Shadow,
1408 ];
1409
1410 pub fn display_name(&self) -> &'static str {
1412 match self {
1413 DividerStyle::Solid => "Solid",
1414 DividerStyle::Double => "Double",
1415 DividerStyle::Dashed => "Dashed",
1416 DividerStyle::Shadow => "Shadow",
1417 }
1418 }
1419}
1420
1421#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1423#[serde(rename_all = "snake_case")]
1424pub enum AlertEvent {
1425 Bell,
1427 CommandComplete,
1429 NewTab,
1431 TabClose,
1433}
1434
1435impl AlertEvent {
1436 pub fn display_name(&self) -> &'static str {
1438 match self {
1439 AlertEvent::Bell => "Bell",
1440 AlertEvent::CommandComplete => "Command Complete",
1441 AlertEvent::NewTab => "New Tab",
1442 AlertEvent::TabClose => "Tab Close",
1443 }
1444 }
1445
1446 pub fn all() -> &'static [AlertEvent] {
1448 &[
1449 AlertEvent::Bell,
1450 AlertEvent::CommandComplete,
1451 AlertEvent::NewTab,
1452 AlertEvent::TabClose,
1453 ]
1454 }
1455}
1456
1457#[derive(Debug, Clone, Serialize, Deserialize)]
1459pub struct AlertSoundConfig {
1460 #[serde(default = "crate::defaults::bool_true")]
1462 pub enabled: bool,
1463 #[serde(default = "crate::defaults::bell_sound")]
1465 pub volume: u8,
1466 #[serde(default)]
1469 pub sound_file: Option<String>,
1470 #[serde(default = "default_alert_frequency")]
1472 pub frequency: f32,
1473 #[serde(default = "default_alert_duration_ms")]
1475 pub duration_ms: u64,
1476}
1477
1478fn default_alert_frequency() -> f32 {
1479 800.0
1480}
1481
1482fn default_alert_duration_ms() -> u64 {
1483 100
1484}
1485
1486impl Default for AlertSoundConfig {
1487 fn default() -> Self {
1488 Self {
1489 enabled: true,
1490 volume: 50,
1491 sound_file: None,
1492 frequency: 800.0,
1493 duration_ms: 100,
1494 }
1495 }
1496}
1497
1498#[derive(Debug, Clone, Serialize, Deserialize)]
1503pub struct SmartSelectionRule {
1504 pub name: String,
1506 pub regex: String,
1508 #[serde(default)]
1510 pub precision: SmartSelectionPrecision,
1511 #[serde(default = "default_enabled")]
1513 pub enabled: bool,
1514}
1515
1516fn default_enabled() -> bool {
1517 true
1518}
1519
1520impl SmartSelectionRule {
1521 pub fn new(
1523 name: impl Into<String>,
1524 regex: impl Into<String>,
1525 precision: SmartSelectionPrecision,
1526 ) -> Self {
1527 Self {
1528 name: name.into(),
1529 regex: regex.into(),
1530 precision,
1531 enabled: true,
1532 }
1533 }
1534}
1535
1536pub fn default_smart_selection_rules() -> Vec<SmartSelectionRule> {
1538 vec![
1539 SmartSelectionRule::new(
1541 "HTTP URL",
1542 r"https?://[^\s<>\[\]{}|\\^`\x00-\x1f]+",
1543 SmartSelectionPrecision::VeryHigh,
1544 ),
1545 SmartSelectionRule::new(
1546 "SSH URL",
1547 r"\bssh://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1548 SmartSelectionPrecision::VeryHigh,
1549 ),
1550 SmartSelectionRule::new(
1551 "Git URL",
1552 r"\bgit://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1553 SmartSelectionPrecision::VeryHigh,
1554 ),
1555 SmartSelectionRule::new(
1556 "File URL",
1557 r"file://[^\s]+",
1558 SmartSelectionPrecision::VeryHigh,
1559 ),
1560 SmartSelectionRule::new(
1562 "Email address",
1563 r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b",
1564 SmartSelectionPrecision::High,
1565 ),
1566 SmartSelectionRule::new(
1567 "IPv4 address",
1568 r"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b",
1569 SmartSelectionPrecision::High,
1570 ),
1571 SmartSelectionRule::new(
1573 "File path",
1574 r"~?/?(?:[a-zA-Z0-9._-]+/)+[a-zA-Z0-9._-]+/?",
1575 SmartSelectionPrecision::Normal,
1576 ),
1577 SmartSelectionRule::new(
1578 "Java/Python import",
1579 r"(?:[a-zA-Z_][a-zA-Z0-9_]*\.){2,}[a-zA-Z_][a-zA-Z0-9_]*",
1581 SmartSelectionPrecision::Normal,
1582 ),
1583 SmartSelectionRule::new(
1584 "C++ namespace",
1585 r"(?:[a-zA-Z_][a-zA-Z0-9_]*::)+[a-zA-Z_][a-zA-Z0-9_]*",
1586 SmartSelectionPrecision::Normal,
1587 ),
1588 SmartSelectionRule::new(
1589 "Quoted string",
1590 r#""(?:[^"\\]|\\.)*""#,
1591 SmartSelectionPrecision::Normal,
1592 ),
1593 SmartSelectionRule::new(
1594 "UUID",
1595 r"\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b",
1596 SmartSelectionPrecision::Normal,
1597 ),
1598 ]
1602}
1603
1604#[derive(Debug, Clone, Default)]
1610pub struct PaneBackground {
1611 pub image_path: Option<String>,
1613 pub mode: BackgroundImageMode,
1615 pub opacity: f32,
1617 pub darken: f32,
1619}
1620
1621impl PaneBackground {
1622 pub fn new() -> Self {
1624 Self {
1625 image_path: None,
1626 mode: BackgroundImageMode::default(),
1627 opacity: 1.0,
1628 darken: 0.0,
1629 }
1630 }
1631
1632 pub fn has_image(&self) -> bool {
1634 self.image_path.is_some()
1635 }
1636}
1637
1638#[derive(Debug, Clone, Copy)]
1640pub struct DividerRect {
1641 pub x: f32,
1643 pub y: f32,
1645 pub width: f32,
1647 pub height: f32,
1649 pub is_horizontal: bool,
1651}
1652
1653impl DividerRect {
1654 pub fn new(x: f32, y: f32, width: f32, height: f32, is_horizontal: bool) -> Self {
1656 Self {
1657 x,
1658 y,
1659 width,
1660 height,
1661 is_horizontal,
1662 }
1663 }
1664
1665 pub fn contains(&self, px: f32, py: f32, padding: f32) -> bool {
1667 px >= self.x - padding
1668 && px < self.x + self.width + padding
1669 && py >= self.y - padding
1670 && py < self.y + self.height + padding
1671 }
1672}
1673
1674pub type SeparatorMark = (usize, Option<i32>, Option<(u8, u8, u8)>);
1676
1677pub type PaneId = u64;
1683
1684pub type TabId = u64;