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, Default, Serialize, Deserialize)]
1046pub struct ShaderMetadata {
1047 pub name: Option<String>,
1049 pub author: Option<String>,
1051 pub description: Option<String>,
1053 pub version: Option<String>,
1055 #[serde(default)]
1057 pub defaults: ShaderConfig,
1058}
1059
1060#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
1065pub struct ShaderConfig {
1066 pub animation_speed: Option<f32>,
1068 pub brightness: Option<f32>,
1070 pub text_opacity: Option<f32>,
1072 pub full_content: Option<bool>,
1074 pub channel0: Option<String>,
1076 pub channel1: Option<String>,
1078 pub channel2: Option<String>,
1080 pub channel3: Option<String>,
1082 pub cubemap: Option<String>,
1084 pub cubemap_enabled: Option<bool>,
1086 pub use_background_as_channel0: Option<bool>,
1088}
1089
1090#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1094pub struct CursorShaderConfig {
1095 #[serde(flatten)]
1097 pub base: ShaderConfig,
1098 pub hides_cursor: Option<bool>,
1100 pub disable_in_alt_screen: Option<bool>,
1102 pub glow_radius: Option<f32>,
1104 pub glow_intensity: Option<f32>,
1106 pub trail_duration: Option<f32>,
1108 pub cursor_color: Option<[u8; 3]>,
1110}
1111
1112#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1117pub struct CursorShaderMetadata {
1118 pub name: Option<String>,
1120 pub author: Option<String>,
1122 pub description: Option<String>,
1124 pub version: Option<String>,
1126 #[serde(default)]
1128 pub defaults: CursorShaderConfig,
1129}
1130
1131#[derive(Debug, Clone)]
1135#[allow(dead_code)]
1136pub struct ResolvedShaderConfig {
1137 pub animation_speed: f32,
1139 pub brightness: f32,
1141 pub text_opacity: f32,
1143 pub full_content: bool,
1145 pub channel0: Option<PathBuf>,
1147 pub channel1: Option<PathBuf>,
1149 pub channel2: Option<PathBuf>,
1151 pub channel3: Option<PathBuf>,
1153 pub cubemap: Option<PathBuf>,
1155 pub cubemap_enabled: bool,
1157 pub use_background_as_channel0: bool,
1159}
1160
1161impl Default for ResolvedShaderConfig {
1162 fn default() -> Self {
1163 Self {
1164 animation_speed: 1.0,
1165 brightness: 1.0,
1166 text_opacity: 1.0,
1167 full_content: false,
1168 channel0: None,
1169 channel1: None,
1170 channel2: None,
1171 channel3: None,
1172 cubemap: None,
1173 cubemap_enabled: true,
1174 use_background_as_channel0: false,
1175 }
1176 }
1177}
1178
1179#[derive(Debug, Clone)]
1181#[allow(dead_code)]
1182pub struct ResolvedCursorShaderConfig {
1183 pub base: ResolvedShaderConfig,
1185 pub hides_cursor: bool,
1187 pub disable_in_alt_screen: bool,
1189 pub glow_radius: f32,
1191 pub glow_intensity: f32,
1193 pub trail_duration: f32,
1195 pub cursor_color: [u8; 3],
1197}
1198
1199impl Default for ResolvedCursorShaderConfig {
1200 fn default() -> Self {
1201 Self {
1202 base: ResolvedShaderConfig::default(),
1203 hides_cursor: false,
1204 disable_in_alt_screen: true,
1205 glow_radius: 80.0,
1206 glow_intensity: 0.3,
1207 trail_duration: 0.5,
1208 cursor_color: [255, 255, 255],
1209 }
1210 }
1211}
1212
1213#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1219#[serde(rename_all = "lowercase")]
1220pub enum ProgressBarStyle {
1221 #[default]
1223 Bar,
1224 BarWithText,
1226}
1227
1228impl ProgressBarStyle {
1229 pub fn display_name(&self) -> &'static str {
1231 match self {
1232 ProgressBarStyle::Bar => "Bar",
1233 ProgressBarStyle::BarWithText => "Bar with Text",
1234 }
1235 }
1236
1237 pub fn all() -> &'static [ProgressBarStyle] {
1239 &[ProgressBarStyle::Bar, ProgressBarStyle::BarWithText]
1240 }
1241}
1242
1243#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1245#[serde(rename_all = "lowercase")]
1246pub enum ProgressBarPosition {
1247 #[default]
1249 Top,
1250 Bottom,
1252}
1253
1254impl ProgressBarPosition {
1255 pub fn display_name(&self) -> &'static str {
1257 match self {
1258 ProgressBarPosition::Bottom => "Bottom",
1259 ProgressBarPosition::Top => "Top",
1260 }
1261 }
1262
1263 pub fn all() -> &'static [ProgressBarPosition] {
1265 &[ProgressBarPosition::Top, ProgressBarPosition::Bottom]
1266 }
1267}
1268
1269#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1278#[serde(rename_all = "snake_case")]
1279pub enum SmartSelectionPrecision {
1280 VeryLow,
1282 Low,
1284 #[default]
1286 Normal,
1287 High,
1289 VeryHigh,
1291}
1292
1293impl SmartSelectionPrecision {
1294 pub fn value(&self) -> f64 {
1296 match self {
1297 SmartSelectionPrecision::VeryLow => 0.00001,
1298 SmartSelectionPrecision::Low => 0.001,
1299 SmartSelectionPrecision::Normal => 1.0,
1300 SmartSelectionPrecision::High => 1000.0,
1301 SmartSelectionPrecision::VeryHigh => 1_000_000.0,
1302 }
1303 }
1304
1305 pub fn display_name(&self) -> &'static str {
1307 match self {
1308 SmartSelectionPrecision::VeryLow => "Very Low",
1309 SmartSelectionPrecision::Low => "Low",
1310 SmartSelectionPrecision::Normal => "Normal",
1311 SmartSelectionPrecision::High => "High",
1312 SmartSelectionPrecision::VeryHigh => "Very High",
1313 }
1314 }
1315}
1316
1317#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1319#[serde(rename_all = "snake_case")]
1320pub enum PaneTitlePosition {
1321 #[default]
1323 Top,
1324 Bottom,
1326}
1327
1328impl PaneTitlePosition {
1329 pub const ALL: &'static [PaneTitlePosition] =
1331 &[PaneTitlePosition::Top, PaneTitlePosition::Bottom];
1332
1333 pub fn display_name(&self) -> &'static str {
1335 match self {
1336 PaneTitlePosition::Top => "Top",
1337 PaneTitlePosition::Bottom => "Bottom",
1338 }
1339 }
1340}
1341
1342#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
1344#[serde(rename_all = "snake_case")]
1345pub enum DividerStyle {
1346 #[default]
1348 Solid,
1349 Double,
1351 Dashed,
1353 Shadow,
1355}
1356
1357impl DividerStyle {
1358 pub const ALL: &'static [DividerStyle] = &[
1360 DividerStyle::Solid,
1361 DividerStyle::Double,
1362 DividerStyle::Dashed,
1363 DividerStyle::Shadow,
1364 ];
1365
1366 pub fn display_name(&self) -> &'static str {
1368 match self {
1369 DividerStyle::Solid => "Solid",
1370 DividerStyle::Double => "Double",
1371 DividerStyle::Dashed => "Dashed",
1372 DividerStyle::Shadow => "Shadow",
1373 }
1374 }
1375}
1376
1377#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
1379#[serde(rename_all = "snake_case")]
1380pub enum AlertEvent {
1381 Bell,
1383 CommandComplete,
1385 NewTab,
1387 TabClose,
1389}
1390
1391impl AlertEvent {
1392 pub fn display_name(&self) -> &'static str {
1394 match self {
1395 AlertEvent::Bell => "Bell",
1396 AlertEvent::CommandComplete => "Command Complete",
1397 AlertEvent::NewTab => "New Tab",
1398 AlertEvent::TabClose => "Tab Close",
1399 }
1400 }
1401
1402 pub fn all() -> &'static [AlertEvent] {
1404 &[
1405 AlertEvent::Bell,
1406 AlertEvent::CommandComplete,
1407 AlertEvent::NewTab,
1408 AlertEvent::TabClose,
1409 ]
1410 }
1411}
1412
1413#[derive(Debug, Clone, Serialize, Deserialize)]
1415pub struct AlertSoundConfig {
1416 #[serde(default = "crate::defaults::bool_true")]
1418 pub enabled: bool,
1419 #[serde(default = "crate::defaults::bell_sound")]
1421 pub volume: u8,
1422 #[serde(default)]
1425 pub sound_file: Option<String>,
1426 #[serde(default = "default_alert_frequency")]
1428 pub frequency: f32,
1429 #[serde(default = "default_alert_duration_ms")]
1431 pub duration_ms: u64,
1432}
1433
1434fn default_alert_frequency() -> f32 {
1435 800.0
1436}
1437
1438fn default_alert_duration_ms() -> u64 {
1439 100
1440}
1441
1442impl Default for AlertSoundConfig {
1443 fn default() -> Self {
1444 Self {
1445 enabled: true,
1446 volume: 50,
1447 sound_file: None,
1448 frequency: 800.0,
1449 duration_ms: 100,
1450 }
1451 }
1452}
1453
1454#[derive(Debug, Clone, Serialize, Deserialize)]
1459pub struct SmartSelectionRule {
1460 pub name: String,
1462 pub regex: String,
1464 #[serde(default)]
1466 pub precision: SmartSelectionPrecision,
1467 #[serde(default = "default_enabled")]
1469 pub enabled: bool,
1470}
1471
1472fn default_enabled() -> bool {
1473 true
1474}
1475
1476impl SmartSelectionRule {
1477 pub fn new(
1479 name: impl Into<String>,
1480 regex: impl Into<String>,
1481 precision: SmartSelectionPrecision,
1482 ) -> Self {
1483 Self {
1484 name: name.into(),
1485 regex: regex.into(),
1486 precision,
1487 enabled: true,
1488 }
1489 }
1490}
1491
1492pub fn default_smart_selection_rules() -> Vec<SmartSelectionRule> {
1494 vec![
1495 SmartSelectionRule::new(
1497 "HTTP URL",
1498 r"https?://[^\s<>\[\]{}|\\^`\x00-\x1f]+",
1499 SmartSelectionPrecision::VeryHigh,
1500 ),
1501 SmartSelectionRule::new(
1502 "SSH URL",
1503 r"\bssh://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1504 SmartSelectionPrecision::VeryHigh,
1505 ),
1506 SmartSelectionRule::new(
1507 "Git URL",
1508 r"\bgit://([a-zA-Z0-9_]+@)?([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+(/[^\s]*)?",
1509 SmartSelectionPrecision::VeryHigh,
1510 ),
1511 SmartSelectionRule::new(
1512 "File URL",
1513 r"file://[^\s]+",
1514 SmartSelectionPrecision::VeryHigh,
1515 ),
1516 SmartSelectionRule::new(
1518 "Email address",
1519 r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b",
1520 SmartSelectionPrecision::High,
1521 ),
1522 SmartSelectionRule::new(
1523 "IPv4 address",
1524 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",
1525 SmartSelectionPrecision::High,
1526 ),
1527 SmartSelectionRule::new(
1529 "File path",
1530 r"~?/?(?:[a-zA-Z0-9._-]+/)+[a-zA-Z0-9._-]+/?",
1531 SmartSelectionPrecision::Normal,
1532 ),
1533 SmartSelectionRule::new(
1534 "Java/Python import",
1535 r"(?:[a-zA-Z_][a-zA-Z0-9_]*\.){2,}[a-zA-Z_][a-zA-Z0-9_]*",
1537 SmartSelectionPrecision::Normal,
1538 ),
1539 SmartSelectionRule::new(
1540 "C++ namespace",
1541 r"(?:[a-zA-Z_][a-zA-Z0-9_]*::)+[a-zA-Z_][a-zA-Z0-9_]*",
1542 SmartSelectionPrecision::Normal,
1543 ),
1544 SmartSelectionRule::new(
1545 "Quoted string",
1546 r#""(?:[^"\\]|\\.)*""#,
1547 SmartSelectionPrecision::Normal,
1548 ),
1549 SmartSelectionRule::new(
1550 "UUID",
1551 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",
1552 SmartSelectionPrecision::Normal,
1553 ),
1554 ]
1558}
1559
1560#[derive(Debug, Clone, Default)]
1566pub struct PaneBackground {
1567 pub image_path: Option<String>,
1569 pub mode: BackgroundImageMode,
1571 pub opacity: f32,
1573}
1574
1575impl PaneBackground {
1576 pub fn new() -> Self {
1578 Self {
1579 image_path: None,
1580 mode: BackgroundImageMode::default(),
1581 opacity: 1.0,
1582 }
1583 }
1584
1585 pub fn has_image(&self) -> bool {
1587 self.image_path.is_some()
1588 }
1589}
1590
1591#[derive(Debug, Clone, Copy)]
1593pub struct DividerRect {
1594 pub x: f32,
1596 pub y: f32,
1598 pub width: f32,
1600 pub height: f32,
1602 pub is_horizontal: bool,
1604}
1605
1606impl DividerRect {
1607 pub fn new(x: f32, y: f32, width: f32, height: f32, is_horizontal: bool) -> Self {
1609 Self {
1610 x,
1611 y,
1612 width,
1613 height,
1614 is_horizontal,
1615 }
1616 }
1617
1618 pub fn contains(&self, px: f32, py: f32, padding: f32) -> bool {
1620 px >= self.x - padding
1621 && px < self.x + self.width + padding
1622 && py >= self.y - padding
1623 && py < self.y + self.height + padding
1624 }
1625}
1626
1627pub type SeparatorMark = (usize, Option<i32>, Option<(u8, u8, u8)>);
1629
1630pub type PaneId = u64;
1636
1637pub type TabId = u64;