Skip to main content

par_term_config/
defaults.rs

1//! Default value functions for configuration.
2
3pub fn cols() -> usize {
4    80
5}
6
7pub fn rows() -> usize {
8    24
9}
10
11pub fn font_size() -> f32 {
12    12.0
13}
14
15pub fn font_family() -> String {
16    "JetBrains Mono".to_string()
17}
18
19pub fn line_spacing() -> f32 {
20    1.0 // Default line height multiplier
21}
22
23pub fn char_spacing() -> f32 {
24    1.0 // Default character width multiplier
25}
26
27pub fn text_shaping() -> bool {
28    true // Enabled by default - OpenType features now properly configured via Feature::from_str()
29}
30
31pub fn minimum_contrast() -> f32 {
32    1.0 // Disabled by default (1.0 = no adjustment)
33}
34
35pub fn scrollback() -> usize {
36    10000
37}
38
39pub fn window_title() -> String {
40    "par-term".to_string()
41}
42
43pub fn theme() -> String {
44    "dark-background".to_string()
45}
46
47pub fn light_theme() -> String {
48    "light-background".to_string()
49}
50
51pub fn dark_theme() -> String {
52    "dark-background".to_string()
53}
54
55pub fn screenshot_format() -> String {
56    "png".to_string()
57}
58
59pub fn max_fps() -> u32 {
60    60
61}
62
63pub fn window_padding() -> f32 {
64    0.0
65}
66
67pub fn login_shell() -> bool {
68    true
69}
70
71pub fn initial_text() -> String {
72    String::new()
73}
74
75pub fn initial_text_delay_ms() -> u64 {
76    100
77}
78
79pub fn initial_text_send_newline() -> bool {
80    true
81}
82
83pub fn scrollbar_position() -> String {
84    "right".to_string()
85}
86
87pub fn scrollbar_width() -> f32 {
88    15.0
89}
90
91pub fn scrollbar_thumb_color() -> [f32; 4] {
92    [0.4, 0.4, 0.4, 0.95] // Medium gray, nearly opaque
93}
94
95pub fn scrollbar_track_color() -> [f32; 4] {
96    [0.15, 0.15, 0.15, 0.6] // Dark gray, semi-transparent
97}
98
99pub fn command_separator_thickness() -> f32 {
100    1.0 // 1 pixel line
101}
102
103pub fn command_separator_opacity() -> f32 {
104    0.4 // Subtle by default
105}
106
107pub fn command_separator_color() -> [u8; 3] {
108    [128, 128, 128] // Medium gray
109}
110
111pub fn link_highlight_color() -> [u8; 3] {
112    [79, 195, 247] // Bright cyan (#4FC3F7)
113}
114
115pub fn paste_delay_ms() -> u64 {
116    0 // No delay by default
117}
118
119pub fn clipboard_max_sync_events() -> usize {
120    64 // Aligned with sister project
121}
122
123pub fn clipboard_max_event_bytes() -> usize {
124    2048 // Aligned with sister project
125}
126
127pub fn activity_threshold() -> u64 {
128    10 // Aligned with sister project (10 seconds)
129}
130
131pub fn anti_idle_seconds() -> u64 {
132    60 // Default keep-alive interval: 60 seconds
133}
134
135pub fn anti_idle_code() -> u8 {
136    0 // Default keep-alive code: NUL (0x00)
137}
138
139pub fn silence_threshold() -> u64 {
140    300 // 5 minutes
141}
142
143pub fn notification_max_buffer() -> usize {
144    64 // Aligned with sister project
145}
146
147pub fn scroll_speed() -> f32 {
148    3.0 // Lines per scroll tick
149}
150
151pub fn double_click_threshold() -> u64 {
152    500 // 500 milliseconds
153}
154
155pub fn triple_click_threshold() -> u64 {
156    500 // 500 milliseconds (same as double-click)
157}
158
159pub fn cursor_blink_interval() -> u64 {
160    500 // 500 milliseconds (blink twice per second)
161}
162
163pub fn cursor_color() -> [u8; 3] {
164    [255, 255, 255] // White cursor
165}
166
167pub fn scrollbar_autohide_delay() -> u64 {
168    0 // 0 = never auto-hide (always visible when scrollback exists)
169}
170
171pub fn window_opacity() -> f32 {
172    1.0 // Fully opaque by default
173}
174
175pub fn background_image_opacity() -> f32 {
176    1.0 // Fully opaque by default
177}
178
179pub fn pane_background_darken() -> f32 {
180    0.0 // No darkening by default
181}
182
183pub fn background_color() -> [u8; 3] {
184    [30, 30, 30] // Dark gray
185}
186
187pub fn bool_false() -> bool {
188    false
189}
190
191pub fn bool_true() -> bool {
192    true
193}
194
195pub fn mdns_timeout() -> u32 {
196    3
197}
198
199pub fn text_opacity() -> f32 {
200    1.0 // Fully opaque text by default
201}
202
203pub fn custom_shader_speed() -> f32 {
204    1.0 // Normal animation speed
205}
206
207pub fn custom_shader_brightness() -> f32 {
208    0.15 // 15% brightness by default for better text readability
209}
210
211pub fn cursor_shader_color() -> [u8; 3] {
212    [255, 255, 255] // White cursor for shader effects
213}
214
215pub fn cursor_trail_duration() -> f32 {
216    0.5 // 500ms trail duration
217}
218
219pub fn cursor_glow_radius() -> f32 {
220    80.0 // 80 pixel glow radius
221}
222
223pub fn cursor_glow_intensity() -> f32 {
224    0.3 // 30% glow intensity
225}
226
227pub fn cursor_shader_disable_in_alt_screen() -> bool {
228    true // Preserve current behavior: disable cursor shader in alt screen by default
229}
230
231pub fn bell_sound() -> u8 {
232    50 // Default to 50% volume
233}
234
235pub fn tab_bar_height() -> f32 {
236    28.0 // Default tab bar height in pixels
237}
238
239pub fn tab_bar_width() -> f32 {
240    160.0 // Default tab bar width in pixels (for left position)
241}
242
243pub fn zero() -> usize {
244    0
245}
246
247pub fn unfocused_fps() -> u32 {
248    30 // Reduced FPS when window is not focused
249}
250
251/// Default for reduce_flicker option
252pub fn reduce_flicker() -> bool {
253    true
254}
255
256/// Default delay in milliseconds for reduce_flicker
257pub fn reduce_flicker_delay_ms() -> u32 {
258    16 // ~1 frame at 60fps
259}
260
261/// Default for maximize_throughput option
262pub fn maximize_throughput() -> bool {
263    false // Off by default
264}
265
266/// Default render interval in milliseconds when maximize_throughput is enabled
267pub fn throughput_render_interval_ms() -> u32 {
268    100 // 100ms default (~10 fps during bulk output)
269}
270
271pub fn shader_hot_reload_delay() -> u64 {
272    100 // Debounce delay in milliseconds
273}
274
275// Tab bar color defaults
276pub fn tab_bar_background() -> [u8; 3] {
277    [40, 40, 40] // Dark gray background
278}
279
280pub fn tab_active_background() -> [u8; 3] {
281    [60, 60, 60] // Slightly lighter for active tab
282}
283
284pub fn tab_inactive_background() -> [u8; 3] {
285    [40, 40, 40] // Same as bar background
286}
287
288pub fn tab_hover_background() -> [u8; 3] {
289    [50, 50, 50] // Between inactive and active
290}
291
292pub fn tab_active_text() -> [u8; 3] {
293    [255, 255, 255] // White text for active tab
294}
295
296pub fn tab_inactive_text() -> [u8; 3] {
297    [180, 180, 180] // Gray text for inactive tabs
298}
299
300pub fn tab_active_indicator() -> [u8; 3] {
301    [100, 150, 255] // Blue underline for active tab
302}
303
304pub fn tab_activity_indicator() -> [u8; 3] {
305    [100, 180, 255] // Light blue activity dot
306}
307
308pub fn tab_bell_indicator() -> [u8; 3] {
309    [255, 200, 100] // Orange/yellow bell icon
310}
311
312pub fn tab_close_button() -> [u8; 3] {
313    [150, 150, 150] // Gray close button
314}
315
316pub fn tab_close_button_hover() -> [u8; 3] {
317    [255, 100, 100] // Red on hover
318}
319
320pub fn cubemap_enabled() -> bool {
321    true // Cubemap sampling enabled by default when a path is configured
322}
323
324pub fn inactive_tab_opacity() -> f32 {
325    0.6 // Default opacity for inactive tabs (60%)
326}
327
328pub fn tab_min_width() -> f32 {
329    120.0 // Minimum tab width in pixels before scrolling kicks in
330}
331
332pub fn tab_stretch_to_fill() -> bool {
333    true // Tabs stretch to share available width by default
334}
335
336pub fn tab_html_titles() -> bool {
337    false // Render tab titles as plain text unless explicitly enabled
338}
339
340pub fn tab_border_color() -> [u8; 3] {
341    [80, 80, 80] // Subtle gray border between tabs
342}
343
344pub fn tab_border_width() -> f32 {
345    1.0 // 1 pixel border
346}
347
348pub fn blur_radius() -> u32 {
349    8 // Default blur radius in points (macOS only)
350}
351
352pub fn light_tab_style() -> crate::types::TabStyle {
353    crate::types::TabStyle::Light
354}
355
356pub fn dark_tab_style() -> crate::types::TabStyle {
357    crate::types::TabStyle::Dark
358}
359
360pub fn use_background_as_channel0() -> bool {
361    false // By default, use configured channel0 texture, not background image
362}
363
364pub fn keybindings() -> Vec<crate::types::KeyBinding> {
365    // macOS: Cmd+key is safe because Cmd is separate from Ctrl (terminal control codes).
366    // Windows/Linux: Ctrl+key conflicts with terminal control codes (Ctrl+C=SIGINT, Ctrl+D=EOF, etc.)
367    // so we use Ctrl+Shift+key following standard terminal emulator conventions
368    // (WezTerm, Kitty, Alacritty, GNOME Terminal, Windows Terminal).
369    #[cfg(target_os = "macos")]
370    let bindings = vec![
371        crate::types::KeyBinding {
372            key: "CmdOrCtrl+Shift+B".to_string(),
373            action: "toggle_background_shader".to_string(),
374        },
375        crate::types::KeyBinding {
376            key: "CmdOrCtrl+Shift+U".to_string(),
377            action: "toggle_cursor_shader".to_string(),
378        },
379        crate::types::KeyBinding {
380            key: "CmdOrCtrl+Shift+V".to_string(),
381            action: "paste_special".to_string(),
382        },
383        crate::types::KeyBinding {
384            key: "CmdOrCtrl+Shift+R".to_string(),
385            action: "toggle_session_logging".to_string(),
386        },
387        // Split pane shortcuts (Cmd+D / Cmd+Shift+D matches iTerm2)
388        crate::types::KeyBinding {
389            key: "CmdOrCtrl+D".to_string(),
390            action: "split_horizontal".to_string(),
391        },
392        crate::types::KeyBinding {
393            key: "CmdOrCtrl+Shift+D".to_string(),
394            action: "split_vertical".to_string(),
395        },
396        crate::types::KeyBinding {
397            key: "CmdOrCtrl+Shift+W".to_string(),
398            action: "close_pane".to_string(),
399        },
400        // Pane navigation shortcuts
401        crate::types::KeyBinding {
402            key: "CmdOrCtrl+Alt+Left".to_string(),
403            action: "navigate_pane_left".to_string(),
404        },
405        crate::types::KeyBinding {
406            key: "CmdOrCtrl+Alt+Right".to_string(),
407            action: "navigate_pane_right".to_string(),
408        },
409        crate::types::KeyBinding {
410            key: "CmdOrCtrl+Alt+Up".to_string(),
411            action: "navigate_pane_up".to_string(),
412        },
413        crate::types::KeyBinding {
414            key: "CmdOrCtrl+Alt+Down".to_string(),
415            action: "navigate_pane_down".to_string(),
416        },
417        // Pane resize shortcuts
418        crate::types::KeyBinding {
419            key: "CmdOrCtrl+Alt+Shift+Left".to_string(),
420            action: "resize_pane_left".to_string(),
421        },
422        crate::types::KeyBinding {
423            key: "CmdOrCtrl+Alt+Shift+Right".to_string(),
424            action: "resize_pane_right".to_string(),
425        },
426        crate::types::KeyBinding {
427            key: "CmdOrCtrl+Alt+Shift+Up".to_string(),
428            action: "resize_pane_up".to_string(),
429        },
430        crate::types::KeyBinding {
431            key: "CmdOrCtrl+Alt+Shift+Down".to_string(),
432            action: "resize_pane_down".to_string(),
433        },
434        // Broadcast input mode
435        crate::types::KeyBinding {
436            key: "CmdOrCtrl+Alt+I".to_string(),
437            action: "toggle_broadcast_input".to_string(),
438        },
439        // Throughput mode toggle
440        crate::types::KeyBinding {
441            key: "CmdOrCtrl+Shift+T".to_string(),
442            action: "toggle_throughput_mode".to_string(),
443        },
444        // tmux session picker
445        crate::types::KeyBinding {
446            key: "CmdOrCtrl+Alt+T".to_string(),
447            action: "toggle_tmux_session_picker".to_string(),
448        },
449        // Copy mode (vi-style keyboard-driven selection) - matches iTerm2
450        crate::types::KeyBinding {
451            key: "CmdOrCtrl+Shift+C".to_string(),
452            action: "toggle_copy_mode".to_string(),
453        },
454        // Command history fuzzy search
455        crate::types::KeyBinding {
456            key: "CmdOrCtrl+R".to_string(),
457            action: "toggle_command_history".to_string(),
458        },
459        // Reopen recently closed tab
460        crate::types::KeyBinding {
461            key: "CmdOrCtrl+Z".to_string(),
462            action: "reopen_closed_tab".to_string(),
463        },
464        // SSH Quick Connect
465        crate::types::KeyBinding {
466            key: "CmdOrCtrl+Shift+S".to_string(),
467            action: "ssh_quick_connect".to_string(),
468        },
469    ];
470
471    #[cfg(not(target_os = "macos"))]
472    let bindings = vec![
473        crate::types::KeyBinding {
474            key: "Ctrl+Shift+B".to_string(),
475            action: "toggle_background_shader".to_string(),
476        },
477        crate::types::KeyBinding {
478            key: "Ctrl+Shift+U".to_string(),
479            action: "toggle_cursor_shader".to_string(),
480        },
481        // Ctrl+Shift+V is standard paste on Linux terminals, so use Ctrl+Alt+V for paste special
482        crate::types::KeyBinding {
483            key: "Ctrl+Alt+V".to_string(),
484            action: "paste_special".to_string(),
485        },
486        crate::types::KeyBinding {
487            key: "Ctrl+Shift+R".to_string(),
488            action: "toggle_session_logging".to_string(),
489        },
490        // Split pane shortcuts
491        // Ctrl+D is EOF/logout - use Ctrl+Shift+D for horizontal split
492        crate::types::KeyBinding {
493            key: "Ctrl+Shift+D".to_string(),
494            action: "split_horizontal".to_string(),
495        },
496        // Ctrl+Shift+E for vertical split (Tilix/Terminator convention)
497        crate::types::KeyBinding {
498            key: "Ctrl+Shift+E".to_string(),
499            action: "split_vertical".to_string(),
500        },
501        // Ctrl+Shift+W is standard close tab - use Ctrl+Shift+X for close pane
502        crate::types::KeyBinding {
503            key: "Ctrl+Shift+X".to_string(),
504            action: "close_pane".to_string(),
505        },
506        // Pane navigation shortcuts
507        crate::types::KeyBinding {
508            key: "Ctrl+Alt+Left".to_string(),
509            action: "navigate_pane_left".to_string(),
510        },
511        crate::types::KeyBinding {
512            key: "Ctrl+Alt+Right".to_string(),
513            action: "navigate_pane_right".to_string(),
514        },
515        crate::types::KeyBinding {
516            key: "Ctrl+Alt+Up".to_string(),
517            action: "navigate_pane_up".to_string(),
518        },
519        crate::types::KeyBinding {
520            key: "Ctrl+Alt+Down".to_string(),
521            action: "navigate_pane_down".to_string(),
522        },
523        // Pane resize shortcuts
524        crate::types::KeyBinding {
525            key: "Ctrl+Alt+Shift+Left".to_string(),
526            action: "resize_pane_left".to_string(),
527        },
528        crate::types::KeyBinding {
529            key: "Ctrl+Alt+Shift+Right".to_string(),
530            action: "resize_pane_right".to_string(),
531        },
532        crate::types::KeyBinding {
533            key: "Ctrl+Alt+Shift+Up".to_string(),
534            action: "resize_pane_up".to_string(),
535        },
536        crate::types::KeyBinding {
537            key: "Ctrl+Alt+Shift+Down".to_string(),
538            action: "resize_pane_down".to_string(),
539        },
540        // Broadcast input mode
541        crate::types::KeyBinding {
542            key: "Ctrl+Alt+I".to_string(),
543            action: "toggle_broadcast_input".to_string(),
544        },
545        // Ctrl+Shift+T is standard new tab - use Ctrl+Shift+M for throughput mode
546        crate::types::KeyBinding {
547            key: "Ctrl+Shift+M".to_string(),
548            action: "toggle_throughput_mode".to_string(),
549        },
550        // tmux session picker
551        crate::types::KeyBinding {
552            key: "Ctrl+Alt+T".to_string(),
553            action: "toggle_tmux_session_picker".to_string(),
554        },
555        // Copy mode (vi-style keyboard-driven selection)
556        // Ctrl+Shift+C is standard copy on Linux, so use Ctrl+Shift+Space
557        crate::types::KeyBinding {
558            key: "Ctrl+Shift+Space".to_string(),
559            action: "toggle_copy_mode".to_string(),
560        },
561        // Command history fuzzy search
562        // Ctrl+R conflicts with terminal reverse search, so use Ctrl+Shift+R
563        // Note: Ctrl+Shift+R is session logging on Linux; users can reassign
564        crate::types::KeyBinding {
565            key: "Ctrl+Alt+R".to_string(),
566            action: "toggle_command_history".to_string(),
567        },
568        // Reopen recently closed tab
569        crate::types::KeyBinding {
570            key: "Ctrl+Shift+Z".to_string(),
571            action: "reopen_closed_tab".to_string(),
572        },
573        // SSH Quick Connect
574        crate::types::KeyBinding {
575            key: "Ctrl+Shift+S".to_string(),
576            action: "ssh_quick_connect".to_string(),
577        },
578    ];
579
580    bindings
581}
582
583// Progress bar defaults
584pub fn progress_bar_height() -> f32 {
585    4.0 // Height in pixels
586}
587
588pub fn progress_bar_normal_color() -> [u8; 3] {
589    [80, 180, 255] // Blue for normal progress
590}
591
592pub fn progress_bar_warning_color() -> [u8; 3] {
593    [255, 200, 50] // Yellow for warning
594}
595
596pub fn progress_bar_error_color() -> [u8; 3] {
597    [255, 80, 80] // Red for error
598}
599
600pub fn progress_bar_indeterminate_color() -> [u8; 3] {
601    [150, 150, 150] // Gray for indeterminate
602}
603
604pub fn progress_bar_opacity() -> f32 {
605    0.8
606}
607
608// Cursor enhancement defaults
609pub fn cursor_guide_color() -> [u8; 4] {
610    [255, 255, 255, 20] // Subtle white highlight
611}
612
613pub fn cursor_shadow_color() -> [u8; 4] {
614    [0, 0, 0, 128] // Semi-transparent black
615}
616
617pub fn cursor_shadow_offset() -> [f32; 2] {
618    [2.0, 2.0] // 2 pixels offset in both directions
619}
620
621pub fn cursor_shadow_blur() -> f32 {
622    3.0 // 3 pixel blur radius
623}
624
625pub fn cursor_boost() -> f32 {
626    0.0 // Disabled by default
627}
628
629pub fn cursor_boost_color() -> [u8; 3] {
630    [255, 255, 255] // White glow
631}
632
633pub fn update_check_frequency() -> crate::types::UpdateCheckFrequency {
634    crate::types::UpdateCheckFrequency::Daily
635}
636
637// Search defaults
638pub fn search_highlight_color() -> [u8; 4] {
639    [255, 200, 0, 180] // Yellow with some transparency
640}
641
642pub fn search_current_highlight_color() -> [u8; 4] {
643    [255, 100, 0, 220] // Orange, more visible for current match
644}
645
646// Selection defaults
647pub fn word_characters() -> String {
648    // Default characters considered part of a word (in addition to alphanumeric)
649    // Matches iTerm2's default: /-+\~_.
650    "/-+\\~_.".to_string()
651}
652
653pub fn smart_selection_enabled() -> bool {
654    true // Smart selection enabled by default
655}
656
657pub fn answerback_string() -> String {
658    String::new() // Empty/disabled by default for security
659}
660
661/// Default semantic history editor command
662/// Empty string means auto-detect from $EDITOR or use system default
663pub fn semantic_history_editor() -> String {
664    String::new() // Auto-detect by default
665}
666
667/// Default list of jobs/processes to ignore when checking for running jobs
668/// These are common shells and utilities that shouldn't block tab close
669pub fn jobs_to_ignore() -> Vec<String> {
670    vec![
671        // Common shells - these are the parent process, not "jobs"
672        "bash".to_string(),
673        "zsh".to_string(),
674        "fish".to_string(),
675        "sh".to_string(),
676        "dash".to_string(),
677        "ksh".to_string(),
678        "tcsh".to_string(),
679        "csh".to_string(),
680        // Common pagers and viewers
681        "less".to_string(),
682        "more".to_string(),
683        "man".to_string(),
684        // Common utilities that are often left running
685        "cat".to_string(),
686        "sleep".to_string(),
687    ]
688}
689
690pub fn unicode_version() -> par_term_emu_core_rust::UnicodeVersion {
691    par_term_emu_core_rust::UnicodeVersion::Auto
692}
693
694pub fn ambiguous_width() -> par_term_emu_core_rust::AmbiguousWidth {
695    par_term_emu_core_rust::AmbiguousWidth::Narrow
696}
697
698pub fn normalization_form() -> par_term_emu_core_rust::NormalizationForm {
699    par_term_emu_core_rust::NormalizationForm::NFC
700}
701
702// Split pane defaults
703pub fn pane_divider_width() -> Option<f32> {
704    Some(2.0) // 2 pixel divider between panes
705}
706
707pub fn pane_divider_hit_width() -> f32 {
708    8.0 // 8 pixel hit area for drag-to-resize (larger than visual for easier grabbing)
709}
710
711pub fn pane_padding() -> f32 {
712    4.0 // 4 pixel padding inside panes (space between content and border/divider)
713}
714
715pub fn pane_min_size() -> usize {
716    10 // Minimum pane size in cells (columns or rows)
717}
718
719pub fn pane_background_opacity() -> f32 {
720    0.85 // 85% opacity allows background/shader to show through slightly
721}
722
723pub fn pane_divider_color() -> [u8; 3] {
724    [80, 80, 80] // Subtle gray divider
725}
726
727pub fn pane_divider_hover_color() -> [u8; 3] {
728    [120, 150, 200] // Brighter color on hover for resize feedback
729}
730
731pub fn inactive_pane_opacity() -> f32 {
732    0.7 // 70% opacity for inactive panes
733}
734
735pub fn max_panes() -> usize {
736    16 // Maximum panes per tab
737}
738
739pub fn pane_title_height() -> f32 {
740    20.0 // 20 pixel title bar height for panes
741}
742
743pub fn pane_title_color() -> [u8; 3] {
744    [200, 200, 200] // Light gray text for pane titles
745}
746
747pub fn pane_title_bg_color() -> [u8; 3] {
748    [40, 40, 50] // Dark background for pane title bars
749}
750
751pub fn pane_focus_color() -> [u8; 3] {
752    [100, 150, 255] // Blue highlight for focused pane
753}
754
755pub fn pane_focus_width() -> f32 {
756    2.0 // 2 pixel border around focused pane
757}
758
759// tmux integration defaults
760pub fn tmux_path() -> String {
761    // First, try to find tmux in the user's PATH environment variable
762    if let Ok(path_env) = std::env::var("PATH") {
763        let separator = if cfg!(windows) { ';' } else { ':' };
764        let executable = if cfg!(windows) { "tmux.exe" } else { "tmux" };
765
766        for dir in path_env.split(separator) {
767            let candidate = std::path::Path::new(dir).join(executable);
768            if candidate.exists() {
769                return candidate.to_string_lossy().to_string();
770            }
771        }
772    }
773
774    // Fall back to common paths for environments where PATH might be incomplete
775    // (e.g., macOS app bundles launched from Finder)
776    #[cfg(target_os = "macos")]
777    {
778        let macos_paths = [
779            "/opt/homebrew/bin/tmux", // Homebrew on Apple Silicon
780            "/usr/local/bin/tmux",    // Homebrew on Intel / MacPorts
781        ];
782        for path in macos_paths {
783            if std::path::Path::new(path).exists() {
784                return path.to_string();
785            }
786        }
787    }
788
789    #[cfg(target_os = "linux")]
790    {
791        let linux_paths = [
792            "/usr/bin/tmux",       // Most distros
793            "/usr/local/bin/tmux", // Manual install
794            "/snap/bin/tmux",      // Snap package
795        ];
796        for path in linux_paths {
797            if std::path::Path::new(path).exists() {
798                return path.to_string();
799            }
800        }
801    }
802
803    // Final fallback - let the OS try to find it
804    "tmux".to_string()
805}
806
807pub fn tmux_default_session() -> Option<String> {
808    None // No default session name
809}
810
811pub fn tmux_auto_attach_session() -> Option<String> {
812    None // No auto-attach session
813}
814
815pub fn tmux_prefix_key() -> String {
816    "C-b".to_string() // Standard tmux prefix (Ctrl+B)
817}
818
819pub fn tmux_status_bar_refresh_ms() -> u64 {
820    1000 // Default: 1 second refresh interval
821}
822
823pub fn tmux_status_bar_left() -> String {
824    "[{session}] {windows}".to_string()
825}
826
827pub fn tmux_status_bar_right() -> String {
828    "{pane} | {time:%H:%M}".to_string()
829}
830
831// Badge defaults
832pub fn badge_format() -> String {
833    "\\(session.username)@\\(session.hostname)".to_string()
834}
835
836pub fn badge_color() -> [u8; 3] {
837    [255, 0, 0] // Red text (matches iTerm2 default)
838}
839
840pub fn badge_color_alpha() -> f32 {
841    0.5 // 50% opacity (semi-transparent)
842}
843
844pub fn badge_font() -> String {
845    "Helvetica".to_string()
846}
847
848pub fn badge_top_margin() -> f32 {
849    0.0 // 0 pixels from top
850}
851
852pub fn badge_right_margin() -> f32 {
853    16.0 // 16 pixels from right
854}
855
856pub fn badge_max_width() -> f32 {
857    0.5 // 50% of terminal width
858}
859
860pub fn badge_max_height() -> f32 {
861    0.2 // 20% of terminal height
862}
863
864// Session logging defaults
865pub fn command_history_max_entries() -> usize {
866    1000 // Maximum number of commands to persist across sessions
867}
868
869pub fn session_undo_timeout_secs() -> u32 {
870    5
871}
872
873pub fn session_undo_max_entries() -> usize {
874    10
875}
876
877pub fn session_undo_preserve_shell() -> bool {
878    false
879}
880
881// Status bar defaults
882pub fn status_bar_height() -> f32 {
883    22.0
884}
885
886pub fn status_bar_bg_color() -> [u8; 3] {
887    [30, 30, 30]
888}
889
890pub fn status_bar_bg_alpha() -> f32 {
891    0.95
892}
893
894pub fn status_bar_fg_color() -> [u8; 3] {
895    [200, 200, 200]
896}
897
898pub fn status_bar_font_size() -> f32 {
899    12.0
900}
901
902pub fn status_bar_separator() -> String {
903    " \u{2502} ".to_string() // " │ "
904}
905
906pub fn status_bar_mouse_inactive_timeout() -> f32 {
907    3.0
908}
909
910pub fn status_bar_system_poll_interval() -> f32 {
911    2.0
912}
913
914pub fn status_bar_git_poll_interval() -> f32 {
915    5.0
916}
917
918pub fn status_bar_time_format() -> String {
919    "%H:%M:%S".to_string()
920}
921
922pub fn session_log_directory() -> String {
923    // XDG-compliant default: ~/.local/share/par-term/logs/
924    if let Some(home) = dirs::home_dir() {
925        home.join(".local")
926            .join("share")
927            .join("par-term")
928            .join("logs")
929            .to_string_lossy()
930            .to_string()
931    } else {
932        "logs".to_string()
933    }
934}
935
936// AI Inspector defaults
937pub fn ai_inspector_enabled() -> bool {
938    true
939}
940
941pub fn ai_inspector_width() -> f32 {
942    300.0
943}
944
945pub fn ai_inspector_open_on_startup() -> bool {
946    false
947}
948
949pub fn ai_inspector_default_scope() -> String {
950    "visible".to_string()
951}
952
953pub fn ai_inspector_view_mode() -> String {
954    "tree".to_string()
955}
956
957pub fn ai_inspector_live_update() -> bool {
958    false
959}
960
961pub fn ai_inspector_show_zones() -> bool {
962    true
963}
964
965pub fn ai_inspector_agent() -> String {
966    "claude.com".to_string()
967}
968
969pub fn ai_inspector_auto_launch() -> bool {
970    false
971}
972
973pub fn ai_inspector_auto_context() -> bool {
974    false
975}
976
977pub fn ai_inspector_context_max_lines() -> usize {
978    200
979}
980
981pub fn ai_inspector_auto_approve() -> bool {
982    false
983}
984
985pub fn ai_inspector_agent_terminal_access() -> bool {
986    false
987}