tenseleyflow/fackr / ef6efb7

Browse files

feat: render terminal tabs in title bar

Update terminal title bar to show tabs when multiple sessions exist:
- Single session: show CWD or "Terminal" centered with separators
- Multiple sessions: tab bar with numbers and directory names
- Active tab highlighted with bold white on darker background
- Add extract_dirname() helper for path display
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
ef6efb7a592aadb84e2252ac3c0118881cdf3392
Parents
cae12ce
Tree
a4027d9

1 changed file

StatusFile+-
M src/render/screen.rs 122 17
src/render/screen.rsmodified
@@ -71,6 +71,27 @@ const INACTIVE_CURRENT_LINE_BG: Color = Color::AnsiValue(234); // Dimmed current
7171
 const INACTIVE_LINE_NUM_COLOR: Color = Color::AnsiValue(240);  // Dimmed line numbers
7272
 const INACTIVE_TEXT_COLOR: Color = Color::AnsiValue(245);      // Dimmed text
7373
 
74
+/// Extract the last component of a path for display
75
+fn extract_dirname(path: &str) -> String {
76
+    // Handle home directory
77
+    if path == "/" {
78
+        return "/".to_string();
79
+    }
80
+
81
+    // Get the last path component
82
+    path.rsplit('/')
83
+        .find(|s| !s.is_empty())
84
+        .map(|s| {
85
+            // If it starts with ~, keep it
86
+            if path.starts_with('~') || path == "/" {
87
+                s.to_string()
88
+            } else {
89
+                s.to_string()
90
+            }
91
+        })
92
+        .unwrap_or_else(|| path.to_string())
93
+}
94
+
7495
 /// Terminal screen renderer
7596
 pub struct Screen {
7697
     stdout: Stdout,
@@ -3728,24 +3749,108 @@ impl Screen {
37283749
             SetForegroundColor(Color::White),
37293750
         )?;
37303751
 
3731
-        // Terminal title bar
3732
-        let title = " Terminal ";
3733
-        let separator = "─".repeat((self.cols as usize).saturating_sub(title.len() + 2) / 2);
3734
-        execute!(
3735
-            self.stdout,
3736
-            Print(&separator),
3737
-            SetAttribute(Attribute::Bold),
3738
-            Print(title),
3739
-            SetAttribute(Attribute::Reset),
3740
-            SetBackgroundColor(Color::AnsiValue(237)),
3741
-            SetForegroundColor(Color::White),
3742
-            Print(&separator),
3743
-        )?;
3752
+        // Terminal title bar with tabs
3753
+        let session_count = terminal.session_count();
3754
+        let active_idx = terminal.active_session_index();
3755
+
3756
+        if session_count <= 1 {
3757
+            // Single session: show CWD or "Terminal" centered
3758
+            let name = terminal.active_cwd()
3759
+                .map(|p| extract_dirname(p))
3760
+                .unwrap_or_else(|| "Terminal".to_string());
3761
+            let title = format!(" {} ", name);
3762
+            let separator = "─".repeat((self.cols as usize).saturating_sub(title.len() + 2) / 2);
3763
+            execute!(
3764
+                self.stdout,
3765
+                Print(&separator),
3766
+                SetAttribute(Attribute::Bold),
3767
+                Print(&title),
3768
+                SetAttribute(Attribute::Reset),
3769
+                SetBackgroundColor(Color::AnsiValue(237)),
3770
+                SetForegroundColor(Color::White),
3771
+                Print(&separator),
3772
+            )?;
3773
+
3774
+            // Pad to end of line
3775
+            let printed = separator.chars().count() * 2 + title.len();
3776
+            if printed < self.cols as usize {
3777
+                execute!(self.stdout, Print(" ".repeat(self.cols as usize - printed)))?;
3778
+            }
3779
+        } else {
3780
+            // Multiple sessions: render tab bar
3781
+            let sessions = terminal.sessions();
3782
+            let available_width = self.cols as usize;
3783
+            let tab_width = (available_width / session_count).max(8).min(25);
3784
+
3785
+            let mut printed = 0;
3786
+            for (i, session) in sessions.iter().enumerate() {
3787
+                let is_active = i == active_idx;
3788
+                let name = session.cwd()
3789
+                    .map(|p| extract_dirname(p))
3790
+                    .unwrap_or_else(|| format!("Term {}", i + 1));
3791
+
3792
+                // Format: "[n] name" with truncation
3793
+                let prefix = format!("{} ", i + 1);
3794
+                let max_name_len = tab_width.saturating_sub(prefix.len() + 1);
3795
+                let display_name = if name.len() > max_name_len {
3796
+                    format!("{}…", &name[..max_name_len.saturating_sub(1)])
3797
+                } else {
3798
+                    name
3799
+                };
3800
+                let tab_content = format!("{}{}", prefix, display_name);
3801
+
3802
+                // Set colors based on active state
3803
+                if is_active {
3804
+                    execute!(
3805
+                        self.stdout,
3806
+                        SetBackgroundColor(Color::AnsiValue(238)),
3807
+                        SetForegroundColor(Color::White),
3808
+                        SetAttribute(Attribute::Bold),
3809
+                    )?;
3810
+                } else {
3811
+                    execute!(
3812
+                        self.stdout,
3813
+                        SetBackgroundColor(Color::AnsiValue(235)),
3814
+                        SetForegroundColor(Color::AnsiValue(245)),
3815
+                        SetAttribute(Attribute::Reset),
3816
+                    )?;
3817
+                }
3818
+
3819
+                // Print tab with padding
3820
+                let padding = tab_width.saturating_sub(tab_content.len());
3821
+                let left_pad = padding / 2;
3822
+                let right_pad = padding - left_pad;
3823
+                execute!(
3824
+                    self.stdout,
3825
+                    Print(" ".repeat(left_pad)),
3826
+                    Print(&tab_content),
3827
+                    Print(" ".repeat(right_pad)),
3828
+                )?;
3829
+                printed += tab_width;
37443830
 
3745
-        // Pad to end of line
3746
-        let printed = separator.chars().count() * 2 + title.len();
3747
-        if printed < self.cols as usize {
3748
-            execute!(self.stdout, Print(" ".repeat(self.cols as usize - printed)))?;
3831
+                // Separator between tabs
3832
+                if i < session_count - 1 {
3833
+                    execute!(
3834
+                        self.stdout,
3835
+                        SetBackgroundColor(Color::AnsiValue(237)),
3836
+                        SetForegroundColor(Color::AnsiValue(240)),
3837
+                        SetAttribute(Attribute::Reset),
3838
+                        Print("│"),
3839
+                    )?;
3840
+                    printed += 1;
3841
+                }
3842
+            }
3843
+
3844
+            // Fill remaining space
3845
+            if printed < available_width {
3846
+                execute!(
3847
+                    self.stdout,
3848
+                    SetBackgroundColor(Color::AnsiValue(237)),
3849
+                    SetForegroundColor(Color::White),
3850
+                    SetAttribute(Attribute::Reset),
3851
+                    Print(" ".repeat(available_width - printed)),
3852
+                )?;
3853
+            }
37493854
         }
37503855
 
37513856
         // Terminal content area - use batched rendering to reduce flicker