refactor: round borders, Y/N delete confirm, fix selected row styling
This commit is contained in:
@@ -25,7 +25,7 @@ pub fn draw_dialog(
|
|||||||
Block::default()
|
Block::default()
|
||||||
.title(title.to_string())
|
.title(title.to_string())
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Plain)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(border_color))
|
.border_style(Style::default().fg(border_color))
|
||||||
.bg(PANEL),
|
.bg(PANEL),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ pub fn draw_input(
|
|||||||
Block::default()
|
Block::default()
|
||||||
.title(title.to_string())
|
.title(title.to_string())
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Plain)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(if focused {
|
.border_style(if focused {
|
||||||
Style::default().fg(ACCENT)
|
Style::default().fg(ACCENT)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ pub fn panel(title: impl Into<String>) -> Block<'static> {
|
|||||||
Block::default()
|
Block::default()
|
||||||
.title(Line::from(format!(" {} ", title.into())).fg(TEXT).bold())
|
.title(Line::from(format!(" {} ", title.into())).fg(TEXT).bold())
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Plain)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(DIM_BORDER))
|
.border_style(Style::default().fg(DIM_BORDER))
|
||||||
.bg(PANEL)
|
.bg(PANEL)
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ pub fn panel_accent(title: impl Into<String>) -> Block<'static> {
|
|||||||
Block::default()
|
Block::default()
|
||||||
.title(Line::from(format!(" {} ", title.into())).fg(ACCENT).bold())
|
.title(Line::from(format!(" {} ", title.into())).fg(ACCENT).bold())
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Plain)
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(ACCENT))
|
.border_style(Style::default().fg(ACCENT))
|
||||||
.bg(PANEL)
|
.bg(PANEL)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::ui::{GREEN, PANEL_ALT, RED};
|
use crate::ui::{GREEN, PANEL_ALT, RED};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
style::{Style, Stylize},
|
style::{Style, Stylize},
|
||||||
widgets::{Block, Borders, Clear, Paragraph, Widget},
|
widgets::{Block, BorderType, Borders, Clear, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn draw_toast(frame: &mut ratatui::Frame<'_>, message: &str, success: bool) {
|
pub fn draw_toast(frame: &mut ratatui::Frame<'_>, message: &str, success: bool) {
|
||||||
@@ -19,6 +19,7 @@ pub fn draw_toast(frame: &mut ratatui::Frame<'_>, message: &str, success: bool)
|
|||||||
.block(
|
.block(
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(border_color))
|
.border_style(Style::default().fg(border_color))
|
||||||
.bg(PANEL_ALT),
|
.bg(PANEL_ALT),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ pub fn draw_delete_confirm(frame: &mut ratatui::Frame<'_>, app: &App) {
|
|||||||
7,
|
7,
|
||||||
RED,
|
RED,
|
||||||
" Confirm Delete ",
|
" Confirm Delete ",
|
||||||
&format!("Delete connection '{name}'?\n\n Enter confirm · Esc cancel"),
|
&format!("Delete connection '{name}'?\n\n [Y] Yes · [N] No"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ fn form_hints() -> Vec<Hint> {
|
|||||||
|
|
||||||
fn delete_hints() -> Vec<Hint> {
|
fn delete_hints() -> Vec<Hint> {
|
||||||
vec![
|
vec![
|
||||||
Hint { key: "Enter", desc: "confirm" },
|
Hint { key: "Y", desc: "yes" },
|
||||||
Hint { key: "Esc", desc: "cancel" },
|
Hint { key: "N", desc: "no" },
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-11
@@ -1,6 +1,5 @@
|
|||||||
use crate::app::{App, Mode};
|
use crate::app::{App, Mode};
|
||||||
use crate::ui::component::common::layout::centered_rect;
|
use crate::ui::{ACCENT, MUTED, PANEL, SELECTED_BG, TEXT};
|
||||||
use crate::ui::{ACCENT, PANEL, SELECTED_BG, TEXT};
|
|
||||||
|
|
||||||
use super::View;
|
use super::View;
|
||||||
|
|
||||||
@@ -10,7 +9,8 @@ use ratatui::{
|
|||||||
Frame,
|
Frame,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
style::{Modifier, Style, Stylize},
|
style::{Modifier, Style, Stylize},
|
||||||
widgets::{Block, Borders, Clear, ListItem, ListState},
|
text::{Line, Span},
|
||||||
|
widgets::{Block, BorderType, Borders, Clear, ListState},
|
||||||
};
|
};
|
||||||
|
|
||||||
const ACTIONS: &[(&str, &str)] = &[
|
const ACTIONS: &[(&str, &str)] = &[
|
||||||
@@ -24,17 +24,27 @@ const ACTIONS: &[(&str, &str)] = &[
|
|||||||
pub struct ActionMenuView;
|
pub struct ActionMenuView;
|
||||||
|
|
||||||
impl View for ActionMenuView {
|
impl View for ActionMenuView {
|
||||||
fn draw(&self, frame: &mut Frame<'_>, _app: &App, _area: Rect) {
|
fn draw(&self, frame: &mut Frame<'_>, _app: &App, area: Rect) {
|
||||||
let width = 44u16;
|
let list_width = 52u16.min(area.width.saturating_sub(4));
|
||||||
let height = (ACTIONS.len() as u16) + 4;
|
let list_height = (ACTIONS.len() as u16 + 4).min(area.height);
|
||||||
let area = centered_rect(width, height, frame.area());
|
let x = area.x + (area.width.saturating_sub(list_width)) / 2;
|
||||||
|
let y = area.y + (area.height.saturating_sub(list_height)) / 2;
|
||||||
|
let list_area = Rect {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width: list_width,
|
||||||
|
height: list_height,
|
||||||
|
};
|
||||||
|
|
||||||
frame.render_widget(Clear, area);
|
frame.render_widget(Clear, list_area);
|
||||||
|
|
||||||
let items: Vec<ListItem<'_>> = ACTIONS
|
let items: Vec<Line<'_>> = ACTIONS
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(label, desc)| {
|
.map(|(label, desc)| {
|
||||||
ListItem::new(format!(" {label:<14}{desc}"))
|
Line::from(vec![
|
||||||
|
Span::styled(format!(" {label:<14}"), Style::default().fg(TEXT)),
|
||||||
|
Span::styled(desc.to_string(), Style::default().fg(MUTED)),
|
||||||
|
])
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -44,6 +54,7 @@ impl View for ActionMenuView {
|
|||||||
.title(" Actions ")
|
.title(" Actions ")
|
||||||
.title_style(Style::default().fg(ACCENT).bold())
|
.title_style(Style::default().fg(ACCENT).bold())
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
|
.border_type(BorderType::Rounded)
|
||||||
.border_style(Style::default().fg(ACCENT))
|
.border_style(Style::default().fg(ACCENT))
|
||||||
.bg(PANEL),
|
.bg(PANEL),
|
||||||
)
|
)
|
||||||
@@ -58,7 +69,7 @@ impl View for ActionMenuView {
|
|||||||
let mut state = ListState::default();
|
let mut state = ListState::default();
|
||||||
state.select(Some(_app.session.action_menu.cursor));
|
state.select(Some(_app.session.action_menu.cursor));
|
||||||
|
|
||||||
frame.render_stateful_widget(list, area, &mut state);
|
frame.render_stateful_widget(list, list_area, &mut state);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key(&self, app: &mut App, key: KeyEvent) -> Result<()> {
|
fn handle_key(&self, app: &mut App, key: KeyEvent) -> Result<()> {
|
||||||
|
|||||||
@@ -74,7 +74,11 @@ fn draw_credentials(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
Row::new([
|
Row::new([
|
||||||
Cell::from(format!("{} {}", if selected { ">" } else { " " }, (*name)))
|
Cell::from(format!("{} {}", if selected { ">" } else { " " }, (*name)))
|
||||||
.style(style),
|
.style(style),
|
||||||
Cell::from(format!("● {kind_text}")).style(Style::default().fg(dot_color)),
|
Cell::from(format!("● {kind_text}")).style(if selected {
|
||||||
|
Style::default().fg(dot_color).bg(SELECTED_BG)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(dot_color)
|
||||||
|
}),
|
||||||
Cell::from(ref_text).style(style),
|
Cell::from(ref_text).style(style),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@@ -95,7 +99,7 @@ fn draw_credentials(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
||||||
)
|
)
|
||||||
.block(panel("Credentials"))
|
.block(panel("Credentials"))
|
||||||
.column_spacing(2)
|
.column_spacing(0)
|
||||||
.row_highlight_style(Style::default().bg(SELECTED_BG));
|
.row_highlight_style(Style::default().bg(SELECTED_BG));
|
||||||
frame.render_widget(table, area);
|
frame.render_widget(table, area);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-12
@@ -181,7 +181,7 @@ pub fn draw_connection_list(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
.style(Style::default().fg(BLUE).add_modifier(Modifier::BOLD)),
|
||||||
)
|
)
|
||||||
.block(panel(title))
|
.block(panel(title))
|
||||||
.column_spacing(2)
|
.column_spacing(0)
|
||||||
.row_highlight_style(Style::default().bg(SELECTED_BG));
|
.row_highlight_style(Style::default().bg(SELECTED_BG));
|
||||||
frame.render_widget(table, area);
|
frame.render_widget(table, area);
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ fn connection_row(
|
|||||||
Style::default().fg(TEXT)
|
Style::default().fg(TEXT)
|
||||||
};
|
};
|
||||||
let is_shell = matches!(profile.kind, ConnectionType::Shell { .. });
|
let is_shell = matches!(profile.kind, ConnectionType::Shell { .. });
|
||||||
let type_badge = if is_shell { "[SHL]" } else { "[SSH]" };
|
let type_badge = if is_shell { "SHL" } else { "SSH" };
|
||||||
let badge_color = if is_shell { PURPLE } else { ACCENT };
|
let badge_color = if is_shell { PURPLE } else { ACCENT };
|
||||||
|
|
||||||
let target = match &profile.kind {
|
let target = match &profile.kind {
|
||||||
@@ -259,16 +259,23 @@ fn connection_row(
|
|||||||
_ => MUTED,
|
_ => MUTED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let badge_style = if selected {
|
||||||
|
Style::default().fg(badge_color).bg(SELECTED_BG).add_modifier(Modifier::BOLD)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(crate::ui::BG).bg(badge_color).add_modifier(Modifier::BOLD)
|
||||||
|
};
|
||||||
|
|
||||||
Row::new([
|
Row::new([
|
||||||
Cell::from(format!("{marker} {}", display_name(name))).style(row_style),
|
Cell::from(format!("{marker} {}", display_name(name))).style(row_style),
|
||||||
Cell::from(type_badge).style(
|
Cell::from(Line::from(vec![
|
||||||
Style::default()
|
Span::styled(format!(" {} ", type_badge), badge_style),
|
||||||
.fg(crate::ui::BG)
|
])).style(row_style),
|
||||||
.bg(badge_color)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Cell::from(target).style(row_style),
|
Cell::from(target).style(row_style),
|
||||||
Cell::from(auth_state).style(Style::default().fg(auth_color)),
|
Cell::from(auth_state).style(if selected {
|
||||||
|
Style::default().fg(auth_color).bg(SELECTED_BG)
|
||||||
|
} else {
|
||||||
|
Style::default().fg(auth_color)
|
||||||
|
}),
|
||||||
])
|
])
|
||||||
.height(1)
|
.height(1)
|
||||||
}
|
}
|
||||||
@@ -295,7 +302,7 @@ pub fn draw_detail_panel(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
let mut lines: Vec<Line> = Vec::new();
|
let mut lines: Vec<Line> = Vec::new();
|
||||||
|
|
||||||
let is_shell = matches!(profile.kind, ConnectionType::Shell { .. });
|
let is_shell = matches!(profile.kind, ConnectionType::Shell { .. });
|
||||||
let badge = if is_shell { "[SHL]" } else { "[SSH]" };
|
let badge = if is_shell { "SHL" } else { "SSH" };
|
||||||
let badge_color = if is_shell { PURPLE } else { ACCENT };
|
let badge_color = if is_shell { PURPLE } else { ACCENT };
|
||||||
lines.push(Line::raw(""));
|
lines.push(Line::raw(""));
|
||||||
lines.push(Line::from(vec![
|
lines.push(Line::from(vec![
|
||||||
@@ -553,14 +560,16 @@ impl View for DeleteConfirmView {
|
|||||||
|
|
||||||
fn handle_key(&self, app: &mut App, key: KeyEvent) -> Result<()> {
|
fn handle_key(&self, app: &mut App, key: KeyEvent) -> Result<()> {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Esc => app.session.mode = Mode::Home,
|
KeyCode::Char('y') | KeyCode::Char('Y') => {
|
||||||
KeyCode::Enter => {
|
|
||||||
match app.delete_selected() {
|
match app.delete_selected() {
|
||||||
Ok(()) => app.toast("deleted", true),
|
Ok(()) => app.toast("deleted", true),
|
||||||
Err(err) => app.toast(err.to_string(), false),
|
Err(err) => app.toast(err.to_string(), false),
|
||||||
}
|
}
|
||||||
app.session.mode = Mode::Home;
|
app.session.mode = Mode::Home;
|
||||||
}
|
}
|
||||||
|
KeyCode::Esc | KeyCode::Char('n') | KeyCode::Char('N') => {
|
||||||
|
app.session.mode = Mode::Home;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+14
-3
@@ -69,8 +69,13 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|c| format!("conflict: {}", c.name))
|
.map(|c| format!("conflict: {}", c.name))
|
||||||
.unwrap_or_else(|| "-".to_string());
|
.unwrap_or_else(|| "-".to_string());
|
||||||
|
let check_cell_style = if selected_row {
|
||||||
|
Style::default().bg(SELECTED_BG)
|
||||||
|
} else {
|
||||||
|
Style::default()
|
||||||
|
};
|
||||||
rows.push(Row::new([
|
rows.push(Row::new([
|
||||||
Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style),
|
Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style.patch(check_cell_style)),
|
||||||
Cell::from(item.name.clone()).style(style),
|
Cell::from(item.name.clone()).style(style),
|
||||||
Cell::from(item.path.display().to_string()).style(style),
|
Cell::from(item.path.display().to_string()).style(style),
|
||||||
Cell::from(status).style(style),
|
Cell::from(status).style(style),
|
||||||
@@ -98,8 +103,13 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
} else {
|
} else {
|
||||||
Style::default().fg(MUTED)
|
Style::default().fg(MUTED)
|
||||||
};
|
};
|
||||||
|
let check_cell_style = if selected_row {
|
||||||
|
Style::default().bg(SELECTED_BG)
|
||||||
|
} else {
|
||||||
|
Style::default()
|
||||||
|
};
|
||||||
rows.push(Row::new([
|
rows.push(Row::new([
|
||||||
Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style),
|
Cell::from(if checked { " [x]" } else { " [ ]" }).style(check_style.patch(check_cell_style)),
|
||||||
Cell::from(item.name.clone()).style(style),
|
Cell::from(item.name.clone()).style(style),
|
||||||
Cell::from(format!("{}@{}:{}", item.user, item.host, item.port)).style(style),
|
Cell::from(format!("{}@{}:{}", item.user, item.host, item.port)).style(style),
|
||||||
Cell::from(
|
Cell::from(
|
||||||
@@ -146,7 +156,8 @@ fn draw_import(frame: &mut Frame<'_>, app: &App, area: Rect) {
|
|||||||
"Import",
|
"Import",
|
||||||
"Space toggle a all A none Enter import Esc cancel",
|
"Space toggle a all A none Enter import Esc cancel",
|
||||||
))
|
))
|
||||||
.column_spacing(2);
|
.column_spacing(0)
|
||||||
|
.row_highlight_style(Style::default().bg(SELECTED_BG));
|
||||||
frame.render_widget(table, area);
|
frame.render_widget(table, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user