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}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
258#[serde(rename_all = "snake_case")]
259pub enum TabStyle {
260 #[default]
262 Dark,
263 Light,
265 Compact,
267 Minimal,
269 HighContrast,
271 Automatic,
273}
274
275impl TabStyle {
276 pub fn display_name(&self) -> &'static str {
278 match self {
279 TabStyle::Dark => "Dark",
280 TabStyle::Light => "Light",
281 TabStyle::Compact => "Compact",
282 TabStyle::Minimal => "Minimal",
283 TabStyle::HighContrast => "High Contrast",
284 TabStyle::Automatic => "Automatic",
285 }
286 }
287
288 pub fn all() -> &'static [TabStyle] {
290 &[
291 TabStyle::Dark,
292 TabStyle::Light,
293 TabStyle::Compact,
294 TabStyle::Minimal,
295 TabStyle::HighContrast,
296 TabStyle::Automatic,
297 ]
298 }
299
300 pub fn all_concrete() -> &'static [TabStyle] {
302 &[
303 TabStyle::Dark,
304 TabStyle::Light,
305 TabStyle::Compact,
306 TabStyle::Minimal,
307 TabStyle::HighContrast,
308 ]
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
314#[serde(rename_all = "snake_case")]
315pub enum TabBarPosition {
316 #[default]
318 Top,
319 Bottom,
321 Left,
323}
324
325impl TabBarPosition {
326 pub fn display_name(&self) -> &'static str {
328 match self {
329 TabBarPosition::Top => "Top",
330 TabBarPosition::Bottom => "Bottom",
331 TabBarPosition::Left => "Left",
332 }
333 }
334
335 pub fn all() -> &'static [TabBarPosition] {
337 &[
338 TabBarPosition::Top,
339 TabBarPosition::Bottom,
340 TabBarPosition::Left,
341 ]
342 }
343
344 pub fn is_horizontal(&self) -> bool {
346 matches!(self, TabBarPosition::Top | TabBarPosition::Bottom)
347 }
348}
349
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
352#[serde(rename_all = "snake_case")]
353pub enum TabBarMode {
354 Always,
356 #[default]
358 WhenMultiple,
359 Never,
361}
362
363#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, Default)]
365#[serde(rename_all = "lowercase")]
366pub enum StatusBarPosition {
367 Top,
369 #[default]
371 Bottom,
372}
373
374#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
376#[serde(rename_all = "snake_case")]
377pub enum WindowType {
378 #[default]
380 Normal,
381 Fullscreen,
383 EdgeTop,
386 EdgeBottom,
388 EdgeLeft,
390 EdgeRight,
392}
393
394impl WindowType {
395 pub fn display_name(&self) -> &'static str {
397 match self {
398 WindowType::Normal => "Normal",
399 WindowType::Fullscreen => "Fullscreen",
400 WindowType::EdgeTop => "Edge (Top)",
401 WindowType::EdgeBottom => "Edge (Bottom)",
402 WindowType::EdgeLeft => "Edge (Left)",
403 WindowType::EdgeRight => "Edge (Right)",
404 }
405 }
406
407 pub fn all() -> &'static [WindowType] {
409 &[
410 WindowType::Normal,
411 WindowType::Fullscreen,
412 WindowType::EdgeTop,
413 WindowType::EdgeBottom,
414 WindowType::EdgeLeft,
415 WindowType::EdgeRight,
416 ]
417 }
418
419 pub fn is_edge(&self) -> bool {
421 matches!(
422 self,
423 WindowType::EdgeTop
424 | WindowType::EdgeBottom
425 | WindowType::EdgeLeft
426 | WindowType::EdgeRight
427 )
428 }
429}
430
431#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
435#[serde(rename_all = "snake_case")]
436pub enum DroppedFileQuoteStyle {
437 #[default]
440 SingleQuotes,
441 DoubleQuotes,
444 Backslash,
447 None,
449}
450
451impl DroppedFileQuoteStyle {
452 pub fn display_name(&self) -> &'static str {
454 match self {
455 DroppedFileQuoteStyle::SingleQuotes => "Single quotes ('...')",
456 DroppedFileQuoteStyle::DoubleQuotes => "Double quotes (\"...\")",
457 DroppedFileQuoteStyle::Backslash => "Backslash escaping (\\)",
458 DroppedFileQuoteStyle::None => "None (raw path)",
459 }
460 }
461
462 pub fn all() -> &'static [DroppedFileQuoteStyle] {
464 &[
465 DroppedFileQuoteStyle::SingleQuotes,
466 DroppedFileQuoteStyle::DoubleQuotes,
467 DroppedFileQuoteStyle::Backslash,
468 DroppedFileQuoteStyle::None,
469 ]
470 }
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
479#[serde(rename_all = "lowercase")]
480pub enum OptionKeyMode {
481 Normal,
484 Meta,
487 #[default]
491 Esc,
492}
493
494#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
503#[serde(rename_all = "lowercase")]
504pub enum ModifierTarget {
505 #[default]
507 None,
508 Ctrl,
510 Alt,
512 Shift,
514 Super,
516}
517
518impl ModifierTarget {
519 pub fn display_name(&self) -> &'static str {
521 match self {
522 ModifierTarget::None => "None (disabled)",
523 ModifierTarget::Ctrl => "Ctrl",
524 ModifierTarget::Alt => "Alt/Option",
525 ModifierTarget::Shift => "Shift",
526 ModifierTarget::Super => "Super/Cmd",
527 }
528 }
529
530 pub fn all() -> &'static [ModifierTarget] {
532 &[
533 ModifierTarget::None,
534 ModifierTarget::Ctrl,
535 ModifierTarget::Alt,
536 ModifierTarget::Shift,
537 ModifierTarget::Super,
538 ]
539 }
540}
541
542#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
550pub struct ModifierRemapping {
551 #[serde(default)]
553 pub left_ctrl: ModifierTarget,
554 #[serde(default)]
556 pub right_ctrl: ModifierTarget,
557 #[serde(default)]
559 pub left_alt: ModifierTarget,
560 #[serde(default)]
562 pub right_alt: ModifierTarget,
563 #[serde(default)]
565 pub left_super: ModifierTarget,
566 #[serde(default)]
568 pub right_super: ModifierTarget,
569}
570
571#[derive(Debug, Clone, Serialize, Deserialize)]
573pub struct FontRange {
574 pub start: u32,
576 pub end: u32,
578 pub font_family: String,
580}
581
582#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
587#[serde(rename_all = "snake_case")]
588pub enum ThinStrokesMode {
589 Never,
591 #[default]
593 RetinaOnly,
594 DarkBackgroundsOnly,
596 RetinaDarkBackgroundsOnly,
598 Always,
600}
601
602#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
607#[serde(rename_all = "snake_case")]
608pub enum ShaderInstallPrompt {
609 #[default]
611 Ask,
612 Never,
614 Installed,
616}
617
618#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
620#[serde(rename_all = "snake_case")]
621pub enum InstallPromptState {
622 #[default]
624 Ask,
625 Never,
627 Installed,
629}
630
631impl InstallPromptState {
632 pub fn display_name(&self) -> &'static str {
634 match self {
635 Self::Ask => "Ask",
636 Self::Never => "Never",
637 Self::Installed => "Installed",
638 }
639 }
640}
641
642#[derive(Debug, Clone, Default, Serialize, Deserialize)]
644pub struct IntegrationVersions {
645 pub shaders_installed_version: Option<String>,
647 pub shaders_prompted_version: Option<String>,
649 pub shell_integration_installed_version: Option<String>,
651 pub shell_integration_prompted_version: Option<String>,
653}
654
655#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
659#[serde(rename_all = "snake_case")]
660pub enum StartupDirectoryMode {
661 #[default]
663 Home,
664 Previous,
666 Custom,
668}
669
670impl StartupDirectoryMode {
671 pub fn display_name(&self) -> &'static str {
673 match self {
674 Self::Home => "Home Directory",
675 Self::Previous => "Previous Session",
676 Self::Custom => "Custom Directory",
677 }
678 }
679
680 pub fn all() -> &'static [StartupDirectoryMode] {
682 &[
683 StartupDirectoryMode::Home,
684 StartupDirectoryMode::Previous,
685 StartupDirectoryMode::Custom,
686 ]
687 }
688}
689
690#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
694#[serde(rename_all = "snake_case")]
695pub enum ShellExitAction {
696 #[default]
698 Close,
699 Keep,
701 RestartImmediately,
703 RestartWithPrompt,
705 RestartAfterDelay,
707}
708
709impl ShellExitAction {
710 pub fn display_name(&self) -> &'static str {
712 match self {
713 Self::Close => "Close tab/pane",
714 Self::Keep => "Keep open",
715 Self::RestartImmediately => "Restart immediately",
716 Self::RestartWithPrompt => "Restart with prompt",
717 Self::RestartAfterDelay => "Restart after 1s delay",
718 }
719 }
720
721 pub fn all() -> &'static [ShellExitAction] {
723 &[
724 ShellExitAction::Close,
725 ShellExitAction::Keep,
726 ShellExitAction::RestartImmediately,
727 ShellExitAction::RestartWithPrompt,
728 ShellExitAction::RestartAfterDelay,
729 ]
730 }
731
732 pub fn is_restart(&self) -> bool {
734 matches!(
735 self,
736 Self::RestartImmediately | Self::RestartWithPrompt | Self::RestartAfterDelay
737 )
738 }
739}
740
741#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
743#[serde(rename_all = "lowercase")]
744pub enum ShellType {
745 Bash,
746 Zsh,
747 Fish,
748 #[default]
749 Unknown,
750}
751
752impl ShellType {
753 fn from_path(path: &str) -> Self {
755 if path.contains("zsh") {
756 Self::Zsh
757 } else if path.contains("bash") {
758 Self::Bash
759 } else if path.contains("fish") {
760 Self::Fish
761 } else {
762 Self::Unknown
763 }
764 }
765
766 pub fn detect() -> Self {
772 if let Ok(shell) = std::env::var("SHELL") {
774 let t = Self::from_path(&shell);
775 if t != Self::Unknown {
776 return t;
777 }
778 }
779
780 #[cfg(target_os = "macos")]
782 {
783 if let Some(t) = Self::detect_via_dscl() {
784 return t;
785 }
786 }
787
788 #[cfg(unix)]
790 {
791 if let Some(t) = Self::detect_from_passwd() {
792 return t;
793 }
794 }
795
796 Self::Unknown
797 }
798
799 #[cfg(target_os = "macos")]
801 fn detect_via_dscl() -> Option<Self> {
802 let user = std::env::var("USER")
803 .or_else(|_| std::env::var("LOGNAME"))
804 .ok()?;
805 let output = std::process::Command::new("dscl")
806 .args([".", "-read", &format!("/Users/{}", user), "UserShell"])
807 .output()
808 .ok()?;
809 let text = String::from_utf8_lossy(&output.stdout);
810 let shell_path = text.split_whitespace().last()?;
812 let t = Self::from_path(shell_path);
813 if t != Self::Unknown { Some(t) } else { None }
814 }
815
816 #[cfg(unix)]
818 fn detect_from_passwd() -> Option<Self> {
819 let user = std::env::var("USER")
820 .or_else(|_| std::env::var("LOGNAME"))
821 .ok()?;
822 let contents = std::fs::read_to_string("/etc/passwd").ok()?;
823 for line in contents.lines() {
824 let parts: Vec<&str> = line.splitn(7, ':').collect();
825 if parts.len() == 7 && parts[0] == user {
826 let t = Self::from_path(parts[6]);
827 if t != Self::Unknown {
828 return Some(t);
829 }
830 }
831 }
832 None
833 }
834
835 pub fn display_name(&self) -> &'static str {
837 match self {
838 Self::Bash => "Bash",
839 Self::Zsh => "Zsh",
840 Self::Fish => "Fish",
841 Self::Unknown => "Unknown",
842 }
843 }
844
845 pub fn extension(&self) -> &'static str {
847 match self {
848 Self::Bash => "bash",
849 Self::Zsh => "zsh",
850 Self::Fish => "fish",
851 Self::Unknown => "sh",
852 }
853 }
854}
855
856#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
860#[serde(rename_all = "snake_case")]
861pub enum UpdateCheckFrequency {
862 Never,
864 #[default]
866 Daily,
867 Weekly,
869 Monthly,
871}
872
873impl UpdateCheckFrequency {
874 pub fn as_seconds(&self) -> Option<u64> {
876 match self {
877 UpdateCheckFrequency::Never => None,
878 UpdateCheckFrequency::Daily => Some(24 * 60 * 60), UpdateCheckFrequency::Weekly => Some(7 * 24 * 60 * 60), UpdateCheckFrequency::Monthly => Some(30 * 24 * 60 * 60), }
882 }
883
884 pub fn display_name(&self) -> &'static str {
886 match self {
887 UpdateCheckFrequency::Never => "Never",
888 UpdateCheckFrequency::Daily => "Daily",
889 UpdateCheckFrequency::Weekly => "Weekly",
890 UpdateCheckFrequency::Monthly => "Monthly",
891 }
892 }
893}
894
895#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
903#[serde(rename_all = "lowercase")]
904pub enum SessionLogFormat {
905 Plain,
907 Html,
909 #[default]
911 Asciicast,
912}
913
914impl SessionLogFormat {
915 pub fn display_name(&self) -> &'static str {
917 match self {
918 SessionLogFormat::Plain => "Plain Text",
919 SessionLogFormat::Html => "HTML",
920 SessionLogFormat::Asciicast => "Asciicast (asciinema)",
921 }
922 }
923
924 pub fn all() -> &'static [SessionLogFormat] {
926 &[
927 SessionLogFormat::Plain,
928 SessionLogFormat::Html,
929 SessionLogFormat::Asciicast,
930 ]
931 }
932
933 pub fn extension(&self) -> &'static str {
935 match self {
936 SessionLogFormat::Plain => "txt",
937 SessionLogFormat::Html => "html",
938 SessionLogFormat::Asciicast => "cast",
939 }
940 }
941}
942
943#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
948#[serde(rename_all = "lowercase")]
949pub enum LogLevel {
950 #[default]
952 Off,
953 Error,
955 Warn,
957 Info,
959 Debug,
961 Trace,
963}
964
965impl LogLevel {
966 pub fn display_name(&self) -> &'static str {
968 match self {
969 LogLevel::Off => "Off",
970 LogLevel::Error => "Error",
971 LogLevel::Warn => "Warn",
972 LogLevel::Info => "Info",
973 LogLevel::Debug => "Debug",
974 LogLevel::Trace => "Trace",
975 }
976 }
977
978 pub fn all() -> &'static [LogLevel] {
980 &[
981 LogLevel::Off,
982 LogLevel::Error,
983 LogLevel::Warn,
984 LogLevel::Info,
985 LogLevel::Debug,
986 LogLevel::Trace,
987 ]
988 }
989
990 pub fn to_level_filter(self) -> log::LevelFilter {
992 match self {
993 LogLevel::Off => log::LevelFilter::Off,
994 LogLevel::Error => log::LevelFilter::Error,
995 LogLevel::Warn => log::LevelFilter::Warn,
996 LogLevel::Info => log::LevelFilter::Info,
997 LogLevel::Debug => log::LevelFilter::Debug,
998 LogLevel::Trace => log::LevelFilter::Trace,
999 }
1000 }
1001}
1002
1003#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1007#[serde(rename_all = "snake_case")]
1008pub enum SemanticHistoryEditorMode {
1009 Custom,
1011 #[default]
1013 EnvironmentVariable,
1014 SystemDefault,
1016}
1017
1018impl SemanticHistoryEditorMode {
1019 pub fn display_name(&self) -> &'static str {
1021 match self {
1022 SemanticHistoryEditorMode::Custom => "Custom Editor",
1023 SemanticHistoryEditorMode::EnvironmentVariable => "Environment Variable ($EDITOR)",
1024 SemanticHistoryEditorMode::SystemDefault => "System Default",
1025 }
1026 }
1027
1028 pub fn all() -> &'static [SemanticHistoryEditorMode] {
1030 &[
1031 SemanticHistoryEditorMode::Custom,
1032 SemanticHistoryEditorMode::EnvironmentVariable,
1033 SemanticHistoryEditorMode::SystemDefault,
1034 ]
1035 }
1036}
1037
1038#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1040#[serde(rename_all = "snake_case")]
1041pub enum LinkUnderlineStyle {
1042 Solid,
1044 #[default]
1046 Stipple,
1047}
1048
1049impl LinkUnderlineStyle {
1050 pub fn display_name(&self) -> &'static str {
1052 match self {
1053 LinkUnderlineStyle::Solid => "Solid",
1054 LinkUnderlineStyle::Stipple => "Stipple",
1055 }
1056 }
1057
1058 pub fn all() -> &'static [LinkUnderlineStyle] {
1060 &[LinkUnderlineStyle::Solid, LinkUnderlineStyle::Stipple]
1061 }
1062}
1063
1064#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1072pub struct ShaderMetadata {
1073 pub name: Option<String>,
1075 pub author: Option<String>,
1077 pub description: Option<String>,
1079 pub version: Option<String>,
1081 #[serde(default)]
1083 pub defaults: ShaderConfig,
1084}
1085
1086#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1091pub struct ShaderConfig {
1092 pub animation_speed: Option<f32>,
1094 pub brightness: Option<f32>,
1096 pub text_opacity: Option<f32>,
1098 pub full_content: Option<bool>,
1100 pub channel0: Option<String>,
1102 pub channel1: Option<String>,
1104 pub channel2: Option<String>,
1106 pub channel3: Option<String>,
1108 pub cubemap: Option<String>,
1110 pub cubemap_enabled: Option<bool>,
1112 pub use_background_as_channel0: Option<bool>,
1114}
1115
1116#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1120pub struct CursorShaderConfig {
1121 #[serde(flatten)]
1123 pub base: ShaderConfig,
1124 pub hides_cursor: Option<bool>,
1126 pub disable_in_alt_screen: Option<bool>,
1128 pub glow_radius: Option<f32>,
1130 pub glow_intensity: Option<f32>,
1132 pub trail_duration: Option<f32>,
1134 pub cursor_color: Option<[u8; 3]>,
1136}
1137
1138#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1143pub struct CursorShaderMetadata {
1144 pub name: Option<String>,
1146 pub author: Option<String>,
1148 pub description: Option<String>,
1150 pub version: Option<String>,
1152 #[serde(default)]
1154 pub defaults: CursorShaderConfig,
1155}
1156
1157#[derive(Debug, Clone)]
1161#[allow(dead_code)]
1162pub struct ResolvedShaderConfig {
1163 pub animation_speed: f32,
1165 pub brightness: f32,
1167 pub text_opacity: f32,
1169 pub full_content: bool,
1171 pub channel0: Option<PathBuf>,
1173 pub channel1: Option<PathBuf>,
1175 pub channel2: Option<PathBuf>,
1177 pub channel3: Option<PathBuf>,
1179 pub cubemap: Option<PathBuf>,
1181 pub cubemap_enabled: bool,
1183 pub use_background_as_channel0: bool,
1185}
1186
1187impl Default for ResolvedShaderConfig {
1188 fn default() -> Self {
1189 Self {
1190 animation_speed: 1.0,
1191 brightness: 1.0,
1192 text_opacity: 1.0,
1193 full_content: false,
1194 channel0: None,
1195 channel1: None,
1196 channel2: None,
1197 channel3: None,
1198 cubemap: None,
1199 cubemap_enabled: true,
1200 use_background_as_channel0: false,
1201 }
1202 }
1203}
1204
1205#[derive(Debug, Clone)]
1207#[allow(dead_code)]
1208pub struct ResolvedCursorShaderConfig {
1209 pub base: ResolvedShaderConfig,
1211 pub hides_cursor: bool,
1213 pub disable_in_alt_screen: bool,
1215 pub glow_radius: f32,
1217 pub glow_intensity: f32,
1219 pub trail_duration: f32,
1221 pub cursor_color: [u8; 3],
1223}
1224
1225impl Default for ResolvedCursorShaderConfig {
1226 fn default() -> Self {
1227 Self {
1228 base: ResolvedShaderConfig::default(),
1229 hides_cursor: false,
1230 disable_in_alt_screen: true,
1231 glow_radius: 80.0,
1232 glow_intensity: 0.3,
1233 trail_duration: 0.5,
1234 cursor_color: [255, 255, 255],
1235 }
1236 }
1237}
1238
1239#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1245#[serde(rename_all = "lowercase")]
1246pub enum ProgressBarStyle {
1247 #[default]
1249 Bar,
1250 BarWithText,
1252}
1253
1254impl ProgressBarStyle {
1255 pub fn display_name(&self) -> &'static str {
1257 match self {
1258 ProgressBarStyle::Bar => "Bar",
1259 ProgressBarStyle::BarWithText => "Bar with Text",
1260 }
1261 }
1262
1263 pub fn all() -> &'static [ProgressBarStyle] {
1265 &[ProgressBarStyle::Bar, ProgressBarStyle::BarWithText]
1266 }
1267}
1268
1269#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1271#[serde(rename_all = "lowercase")]
1272pub enum ProgressBarPosition {
1273 #[default]
1275 Top,
1276 Bottom,
1278}
1279
1280impl ProgressBarPosition {
1281 pub fn display_name(&self) -> &'static str {
1283 match self {
1284 ProgressBarPosition::Bottom => "Bottom",
1285 ProgressBarPosition::Top => "Top",
1286 }
1287 }
1288
1289 pub fn all() -> &'static [ProgressBarPosition] {
1291 &[ProgressBarPosition::Top, ProgressBarPosition::Bottom]
1292 }
1293}
1294
1295#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1304#[serde(rename_all = "snake_case")]
1305pub enum SmartSelectionPrecision {
1306 VeryLow,
1308 Low,
1310 #[default]
1312 Normal,
1313 High,
1315 VeryHigh,
1317}
1318
1319impl SmartSelectionPrecision {
1320 pub fn value(&self) -> f64 {
1322 match self {
1323 SmartSelectionPrecision::VeryLow => 0.00001,
1324 SmartSelectionPrecision::Low => 0.001,
1325 SmartSelectionPrecision::Normal => 1.0,
1326 SmartSelectionPrecision::High => 1000.0,
1327 SmartSelectionPrecision::VeryHigh => 1_000_000.0,
1328 }
1329 }
1330
1331 pub fn display_name(&self) -> &'static str {
1333 match self {
1334 SmartSelectionPrecision::VeryLow => "Very Low",
1335 SmartSelectionPrecision::Low => "Low",
1336 SmartSelectionPrecision::Normal => "Normal",
1337 SmartSelectionPrecision::High => "High",
1338 SmartSelectionPrecision::VeryHigh => "Very High",
1339 }
1340 }
1341}
1342
1343#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1345#[serde(rename_all = "snake_case")]
1346pub enum PaneTitlePosition {
1347 #[default]
1349 Top,
1350 Bottom,
1352}
1353
1354impl PaneTitlePosition {
1355 pub const ALL: &'static [PaneTitlePosition] =
1357 &[PaneTitlePosition::Top, PaneTitlePosition::Bottom];
1358
1359 pub fn display_name(&self) -> &'static str {
1361 match self {
1362 PaneTitlePosition::Top => "Top",
1363 PaneTitlePosition::Bottom => "Bottom",
1364 }
1365 }
1366}
1367
1368#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1370#[serde(rename_all = "snake_case")]
1371pub enum DividerStyle {
1372 #[default]
1374 Solid,
1375 Double,
1377 Dashed,
1379 Shadow,
1381}
1382
1383impl DividerStyle {
1384 pub const ALL: &'static [DividerStyle] = &[
1386 DividerStyle::Solid,
1387 DividerStyle::Double,
1388 DividerStyle::Dashed,
1389 DividerStyle::Shadow,
1390 ];
1391
1392 pub fn display_name(&self) -> &'static str {
1394 match self {
1395 DividerStyle::Solid => "Solid",
1396 DividerStyle::Double => "Double",
1397 DividerStyle::Dashed => "Dashed",
1398 DividerStyle::Shadow => "Shadow",
1399 }
1400 }
1401}
1402
1403#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1405#[serde(rename_all = "snake_case")]
1406pub enum AlertEvent {
1407 Bell,
1409 CommandComplete,
1411 NewTab,
1413 TabClose,
1415}
1416
1417impl AlertEvent {
1418 pub fn display_name(&self) -> &'static str {
1420 match self {
1421 AlertEvent::Bell => "Bell",
1422 AlertEvent::CommandComplete => "Command Complete",
1423 AlertEvent::NewTab => "New Tab",
1424 AlertEvent::TabClose => "Tab Close",
1425 }
1426 }
1427
1428 pub fn all() -> &'static [AlertEvent] {
1430 &[
1431 AlertEvent::Bell,
1432 AlertEvent::CommandComplete,
1433 AlertEvent::NewTab,
1434 AlertEvent::TabClose,
1435 ]
1436 }
1437}
1438
1439#[derive(Debug, Clone, Serialize, Deserialize)]
1441pub struct AlertSoundConfig {
1442 #[serde(default = "crate::defaults::bool_true")]
1444 pub enabled: bool,
1445 #[serde(default = "crate::defaults::bell_sound")]
1447 pub volume: u8,
1448 #[serde(default)]
1451 pub sound_file: Option<String>,
1452 #[serde(default = "default_alert_frequency")]
1454 pub frequency: f32,
1455 #[serde(default = "default_alert_duration_ms")]
1457 pub duration_ms: u64,
1458}
1459
1460fn default_alert_frequency() -> f32 {
1461 800.0
1462}
1463
1464fn default_alert_duration_ms() -> u64 {
1465 100
1466}
1467
1468impl Default for AlertSoundConfig {
1469 fn default() -> Self {
1470 Self {
1471 enabled: true,
1472 volume: 50,
1473 sound_file: None,
1474 frequency: 800.0,
1475 duration_ms: 100,
1476 }
1477 }
1478}
1479
1480#[derive(Debug, Clone, Serialize, Deserialize)]
1485pub struct SmartSelectionRule {
1486 pub name: String,
1488 pub regex: String,
1490 #[serde(default)]
1492 pub precision: SmartSelectionPrecision,
1493 #[serde(default = "default_enabled")]
1495 pub enabled: bool,
1496}
1497
1498fn default_enabled() -> bool {
1499 true
1500}
1501
1502impl SmartSelectionRule {
1503 pub fn new(
1505 name: impl Into<String>,
1506 regex: impl Into<String>,
1507 precision: SmartSelectionPrecision,
1508 ) -> Self {
1509 Self {
1510 name: name.into(),
1511 regex: regex.into(),
1512 precision,
1513 enabled: true,
1514 }
1515 }
1516}
1517
1518pub fn default_smart_selection_rules() -> Vec<SmartSelectionRule> {
1520 vec![
1521 SmartSelectionRule::new(
1523 "HTTP URL",
1524 r"https?://[^\s<>\[\]{}|\\^`\x00-\x1f]+",
1525 SmartSelectionPrecision::VeryHigh,
1526 ),
1527 SmartSelectionRule::new(
1528 "SSH URL",
1529 r"\bssh://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1530 SmartSelectionPrecision::VeryHigh,
1531 ),
1532 SmartSelectionRule::new(
1533 "Git URL",
1534 r"\bgit://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1535 SmartSelectionPrecision::VeryHigh,
1536 ),
1537 SmartSelectionRule::new(
1538 "File URL",
1539 r"file://[^\s]+",
1540 SmartSelectionPrecision::VeryHigh,
1541 ),
1542 SmartSelectionRule::new(
1544 "Email address",
1545 r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b",
1546 SmartSelectionPrecision::High,
1547 ),
1548 SmartSelectionRule::new(
1549 "IPv4 address",
1550 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",
1551 SmartSelectionPrecision::High,
1552 ),
1553 SmartSelectionRule::new(
1555 "File path",
1556 r"~?/?(?:[a-zA-Z0-9._-]+/)+[a-zA-Z0-9._-]+/?",
1557 SmartSelectionPrecision::Normal,
1558 ),
1559 SmartSelectionRule::new(
1560 "Java/Python import",
1561 r"(?:[a-zA-Z_][a-zA-Z0-9_]*\.){2,}[a-zA-Z_][a-zA-Z0-9_]*",
1563 SmartSelectionPrecision::Normal,
1564 ),
1565 SmartSelectionRule::new(
1566 "C++ namespace",
1567 r"(?:[a-zA-Z_][a-zA-Z0-9_]*::)+[a-zA-Z_][a-zA-Z0-9_]*",
1568 SmartSelectionPrecision::Normal,
1569 ),
1570 SmartSelectionRule::new(
1571 "Quoted string",
1572 r#""(?:[^"\\]|\\.)*""#,
1573 SmartSelectionPrecision::Normal,
1574 ),
1575 SmartSelectionRule::new(
1576 "UUID",
1577 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",
1578 SmartSelectionPrecision::Normal,
1579 ),
1580 ]
1584}
1585
1586#[derive(Debug, Clone, Default)]
1592pub struct PaneBackground {
1593 pub image_path: Option<String>,
1595 pub mode: BackgroundImageMode,
1597 pub opacity: f32,
1599}
1600
1601impl PaneBackground {
1602 pub fn new() -> Self {
1604 Self {
1605 image_path: None,
1606 mode: BackgroundImageMode::default(),
1607 opacity: 1.0,
1608 }
1609 }
1610
1611 pub fn has_image(&self) -> bool {
1613 self.image_path.is_some()
1614 }
1615}
1616
1617#[derive(Debug, Clone, Copy)]
1619pub struct DividerRect {
1620 pub x: f32,
1622 pub y: f32,
1624 pub width: f32,
1626 pub height: f32,
1628 pub is_horizontal: bool,
1630}
1631
1632impl DividerRect {
1633 pub fn new(x: f32, y: f32, width: f32, height: f32, is_horizontal: bool) -> Self {
1635 Self {
1636 x,
1637 y,
1638 width,
1639 height,
1640 is_horizontal,
1641 }
1642 }
1643
1644 pub fn contains(&self, px: f32, py: f32, padding: f32) -> bool {
1646 px >= self.x - padding
1647 && px < self.x + self.width + padding
1648 && py >= self.y - padding
1649 && py < self.y + self.height + padding
1650 }
1651}
1652
1653pub type SeparatorMark = (usize, Option<i32>, Option<(u8, u8, u8)>);
1655
1656pub type PaneId = u64;
1662
1663pub type TabId = u64;