169 lines
5.8 KiB
Rust
169 lines
5.8 KiB
Rust
use ratatui::{
|
|
layout::{Constraint, Direction, Layout, Rect},
|
|
style::{Color, Modifier, Style},
|
|
widgets::{Block, Borders, List, ListItem, Paragraph, Clear},
|
|
Frame,
|
|
};
|
|
|
|
use crate::app::{App, InputMode};
|
|
|
|
pub fn ui(f: &mut Frame, app: &mut App) {
|
|
let size = f.size();
|
|
|
|
let chunks = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints([Constraint::Min(0), Constraint::Length(3)].as_ref())
|
|
.split(size);
|
|
|
|
let items: Vec<ListItem> = app
|
|
.servers
|
|
.iter()
|
|
.map(|s| {
|
|
let content = format!("{} ({}) - {}:{}", s.name, s.user, s.host, s.port);
|
|
ListItem::new(content).style(Style::default())
|
|
})
|
|
.collect();
|
|
|
|
let list = List::new(items)
|
|
.block(Block::default().borders(Borders::ALL).title(" SSHX - Servers "))
|
|
.highlight_style(Style::default().add_modifier(Modifier::BOLD).fg(Color::Yellow))
|
|
.highlight_symbol("> ");
|
|
|
|
f.render_stateful_widget(list, chunks[0], &mut app.state);
|
|
|
|
// Help text
|
|
let help_text = match app.input_mode {
|
|
InputMode::Normal => "Enter: SSH Connect | m: Mosh Connect | n: New | Shift+n: Copy ID | i: Edit | d: Delete | q: Quit",
|
|
InputMode::Adding(_) => "Enter: Save | Esc: Cancel | Tab: Next Field",
|
|
InputMode::Editing(_) => "Enter: Save | Esc: Cancel | Tab: Next Field",
|
|
};
|
|
let help = Paragraph::new(help_text)
|
|
.style(Style::default().fg(Color::Gray))
|
|
.block(Block::default().borders(Borders::ALL).title(" Help "));
|
|
f.render_widget(help, chunks[1]);
|
|
|
|
// Popup for Adding Server
|
|
if let InputMode::Adding(state) = &app.input_mode {
|
|
let block = Block::default().borders(Borders::ALL).title(" Add New Connection ");
|
|
let area = centered_rect(60, 40, size);
|
|
f.render_widget(Clear, area); // Clear the background
|
|
f.render_widget(block, area);
|
|
|
|
let input_layout = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.margin(2)
|
|
.constraints(
|
|
[
|
|
Constraint::Length(3), // Name
|
|
Constraint::Length(3), // User
|
|
Constraint::Length(3), // Host
|
|
Constraint::Length(3), // Port
|
|
Constraint::Min(1),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(area);
|
|
|
|
let fields = [
|
|
("Name", &state.name),
|
|
("User (default: root)", &state.user),
|
|
("Host/IP", &state.host),
|
|
("Port (default: 22)", &state.port),
|
|
];
|
|
|
|
for (i, (label, value)) in fields.iter().enumerate() {
|
|
let mut style = Style::default();
|
|
if state.field_idx == i {
|
|
style = style.fg(Color::Yellow);
|
|
}
|
|
let input = Paragraph::new(value.as_str())
|
|
.style(style)
|
|
.block(Block::default().borders(Borders::ALL).title(*label));
|
|
f.render_widget(input, input_layout[i]);
|
|
|
|
// Show cursor in the active input field
|
|
if state.field_idx == i {
|
|
f.set_cursor(
|
|
input_layout[i].x + value.len() as u16 + 1, // Position after the text
|
|
input_layout[i].y + 1, // Middle of the input area
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Popup for Editing Server
|
|
if let InputMode::Editing(state) = &app.input_mode {
|
|
let block = Block::default().borders(Borders::ALL).title(" Edit Connection ");
|
|
let area = centered_rect(60, 40, size);
|
|
f.render_widget(Clear, area); // Clear the background
|
|
f.render_widget(block, area);
|
|
|
|
let input_layout = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.margin(2)
|
|
.constraints(
|
|
[
|
|
Constraint::Length(3), // Name
|
|
Constraint::Length(3), // User
|
|
Constraint::Length(3), // Host
|
|
Constraint::Length(3), // Port
|
|
Constraint::Min(1),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(area);
|
|
|
|
let fields = [
|
|
("Name", &state.name),
|
|
("User (default: root)", &state.user),
|
|
("Host/IP", &state.host),
|
|
("Port (default: 22)", &state.port),
|
|
];
|
|
|
|
for (i, (label, value)) in fields.iter().enumerate() {
|
|
let mut style = Style::default();
|
|
if state.field_idx == i {
|
|
style = style.fg(Color::Yellow);
|
|
}
|
|
let input = Paragraph::new(value.as_str())
|
|
.style(style)
|
|
.block(Block::default().borders(Borders::ALL).title(*label));
|
|
f.render_widget(input, input_layout[i]);
|
|
|
|
// Show cursor in the active input field
|
|
if state.field_idx == i {
|
|
f.set_cursor(
|
|
input_layout[i].x + value.len() as u16 + 1, // Position after the text
|
|
input_layout[i].y + 1, // Middle of the input area
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
|
let popup_layout = Layout::default()
|
|
.direction(Direction::Vertical)
|
|
.constraints(
|
|
[
|
|
Constraint::Percentage((100 - percent_y) / 2),
|
|
Constraint::Percentage(percent_y),
|
|
Constraint::Percentage((100 - percent_y) / 2),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(r);
|
|
|
|
Layout::default()
|
|
.direction(Direction::Horizontal)
|
|
.constraints(
|
|
[
|
|
Constraint::Percentage((100 - percent_x) / 2),
|
|
Constraint::Percentage(percent_x),
|
|
Constraint::Percentage((100 - percent_x) / 2),
|
|
]
|
|
.as_ref(),
|
|
)
|
|
.split(popup_layout[1])[1]
|
|
}
|