Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 44 additions & 2 deletions src/ui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::db::{
ConnectionConfig, ConnectionManager, DatabaseInfo, QueryResult, SchemaInfo, SslMode, TableInfo,
};
use crate::editor::{HistoryEntry, QueryHistory, TextBuffer};
use crate::ui::Theme;
use crate::ui::{Theme, ThemeName};

pub const SPINNER_FRAMES: &[char] = &['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];

Expand Down Expand Up @@ -198,8 +198,13 @@ impl App {
initial_config.password.len(),
];

let theme = match Self::load_theme_preference() {
ThemeName::Dark => Theme::dark(),
ThemeName::Light => Theme::light(),
};

Self {
theme: Theme::dark(),
theme,
focus: Focus::ConnectionDialog,
should_quit: false,

Expand Down Expand Up @@ -298,9 +303,46 @@ impl App {
}
}

pub fn toggle_theme(&mut self) {
let new_name = self.theme.name.toggle();
self.theme = match new_name {
ThemeName::Dark => Theme::dark(),
ThemeName::Light => Theme::light(),
};
Self::save_theme_preference(new_name);
}

fn save_theme_preference(theme_name: ThemeName) {
if let Some(config_dir) = dirs::config_dir() {
let pgrsql_dir = config_dir.join("pgrsql");
let _ = std::fs::create_dir_all(&pgrsql_dir);
let _ = std::fs::write(pgrsql_dir.join("theme"), theme_name.as_str());
}
}

fn load_theme_preference() -> ThemeName {
if let Some(config_dir) = dirs::config_dir() {
let path = config_dir.join("pgrsql").join("theme");
if let Ok(content) = std::fs::read_to_string(path) {
return match content.trim() {
"light" => ThemeName::Light,
_ => ThemeName::Dark,
};
}
}
ThemeName::Dark
}

pub async fn handle_input(&mut self, key: KeyEvent) -> Result<()> {
// Global shortcuts
match (key.code, key.modifiers) {
// Ctrl+Shift+T: Toggle theme (works in all contexts except connection dialog text input)
// Note: most terminals report Ctrl+Shift+T as Char('T') with only CONTROL;
// the SHIFT modifier is implicit in the uppercase letter.
(KeyCode::Char('T'), m) if m.contains(KeyModifiers::CONTROL) => {
self.toggle_theme();
return Ok(());
}
(KeyCode::Char('?'), _) if self.focus != Focus::Editor => {
self.show_help = !self.show_help;
if self.show_help {
Expand Down
9 changes: 7 additions & 2 deletions src/ui/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,12 @@ fn draw_status_bar(frame: &mut Frame, app: &App, area: Rect) {
Style::default().fg(theme.text_muted).bg(theme.bg_secondary)
};

// Right section: help hints
let right_text = "? Help | Ctrl+Q/D Quit ";
// Right section: theme indicator + help hints
let theme_indicator = match app.theme.name {
crate::ui::ThemeName::Dark => "Dark",
crate::ui::ThemeName::Light => "Light",
};
let right_text = format!("{} | ? Help | Ctrl+Q/D Quit ", theme_indicator);

// Calculate padding
let left_len = left_text.len() as u16;
Expand Down Expand Up @@ -945,6 +949,7 @@ fn draw_help_overlay(frame: &mut Frame, app: &App) {
" GLOBAL",
" Ctrl+Q/D Quit",
" Ctrl+C Connect dialog",
" Ctrl+Shift+T Toggle theme",
" ? Toggle help",
"",
" NAVIGATION",
Expand Down
25 changes: 25 additions & 0 deletions src/ui/theme.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
use ratatui::style::{Color, Modifier, Style};

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ThemeName {
Dark,
Light,
}

#[allow(dead_code)]
pub struct Theme {
pub name: ThemeName,
// Background colors
pub bg_primary: Color,
pub bg_secondary: Color,
Expand Down Expand Up @@ -43,10 +50,27 @@ impl Default for Theme {
}
}

impl ThemeName {
pub fn toggle(self) -> Self {
match self {
ThemeName::Dark => ThemeName::Light,
ThemeName::Light => ThemeName::Dark,
}
}

pub fn as_str(&self) -> &'static str {
match self {
ThemeName::Dark => "dark",
ThemeName::Light => "light",
}
}
}

#[allow(dead_code)]
impl Theme {
pub fn dark() -> Self {
Self {
name: ThemeName::Dark,
// Background colors - dark blue-gray palette
bg_primary: Color::Rgb(24, 26, 33),
bg_secondary: Color::Rgb(30, 33, 43),
Expand Down Expand Up @@ -85,6 +109,7 @@ impl Theme {

pub fn light() -> Self {
Self {
name: ThemeName::Light,
bg_primary: Color::Rgb(250, 250, 252),
bg_secondary: Color::Rgb(240, 240, 245),
bg_tertiary: Color::Rgb(230, 230, 238),
Expand Down