use crate::app::App;
use crate::ui::theme::{CATPPUCCIN_MOCHA, mocha};
use ratatui::{
Frame,
layout::{Alignment, Constraint, Direction, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
widgets::{Block, BorderType, Borders, Clear, Paragraph},
};
pub fn draw(f: &mut Frame, app: &App, area: Rect) {
let lang = &app.tool_config.language;
let block = Block::default()
.title(Span::styled(
crate::ui::tr("auth_title", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.danger))
.style(Style::default().bg(mocha::MANTLE));
let inner_area = block.inner(area);
f.render_widget(Clear, area);
f.render_widget(block, area);
let text_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Length(3), Constraint::Length(1), Constraint::Length(1), Constraint::Min(0),
])
.split(inner_area);
let msg = Paragraph::new(crate::ui::tr("auth_msg", lang))
.style(Style::default().fg(CATPPUCCIN_MOCHA.text).bg(mocha::MANTLE));
f.render_widget(msg, text_chunks[0]);
let masked_input: String = std::iter::repeat('*')
.take(app.password_input.len())
.collect();
let input_widget = Paragraph::new(masked_input)
.block(
Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border_focus)),
)
.style(Style::default().fg(CATPPUCCIN_MOCHA.text).bg(mocha::MANTLE));
f.render_widget(input_widget, text_chunks[1]);
let cancel_msg = Paragraph::new(crate::ui::tr("auth_cancel", lang)).style(
Style::default()
.fg(CATPPUCCIN_MOCHA.text_muted)
.bg(mocha::MANTLE),
);
f.render_widget(cancel_msg, text_chunks[2]);
if app.password_incorrect {
let err_msg = Paragraph::new(crate::ui::tr("auth_error", lang)).style(
Style::default()
.fg(CATPPUCCIN_MOCHA.danger)
.bg(mocha::MANTLE),
);
f.render_widget(err_msg, text_chunks[3]);
}
}
pub fn draw_exit_menu(f: &mut Frame, app: &App, area: Rect) {
let block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border_focus))
.style(Style::default().bg(mocha::MANTLE));
let inner_area = block.inner(area);
f.render_widget(Clear, area);
f.render_widget(block, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Length(2), Constraint::Length(3), Constraint::Min(0), ])
.split(inner_area);
let lang = &app.tool_config.language;
let title = Paragraph::new(Line::from(vec![
Span::styled("[!] ", Style::default()),
Span::styled(
crate::ui::tr("exit_title", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
),
]))
.alignment(Alignment::Center)
.style(Style::default().bg(mocha::MANTLE));
f.render_widget(title, chunks[0]);
let question = Paragraph::new(crate::ui::tr("exit_question", lang))
.alignment(Alignment::Center)
.style(Style::default().fg(CATPPUCCIN_MOCHA.text).bg(mocha::MANTLE));
f.render_widget(question, chunks[1]);
let card_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(chunks[2]);
let is_settings_selected = app.exit_menu_selected == 0;
let settings_border_style = if is_settings_selected {
Style::default().fg(CATPPUCCIN_MOCHA.border_focus)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.border)
};
let settings_block = Block::default()
.borders(Borders::ALL)
.border_type(if is_settings_selected {
BorderType::Double
} else {
BorderType::Rounded
})
.border_style(settings_border_style)
.style(if is_settings_selected {
Style::default().bg(CATPPUCCIN_MOCHA.selection_bg)
} else {
Style::default().bg(mocha::MANTLE)
});
let settings_text = Paragraph::new(crate::ui::tr("exit_settings", lang))
.alignment(Alignment::Center)
.block(settings_block)
.style(if is_settings_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text_muted)
});
f.render_widget(settings_text, card_chunks[0]);
let is_quit_selected = app.exit_menu_selected == 1;
let quit_border_style = if is_quit_selected {
Style::default().fg(CATPPUCCIN_MOCHA.danger)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.border)
};
let quit_block = Block::default()
.borders(Borders::ALL)
.border_type(if is_quit_selected {
BorderType::Double
} else {
BorderType::Rounded
})
.border_style(quit_border_style)
.style(if is_quit_selected {
Style::default().bg(CATPPUCCIN_MOCHA.selection_bg)
} else {
Style::default().bg(mocha::MANTLE)
});
let quit_text = Paragraph::new(crate::ui::tr("exit_quit", lang))
.alignment(Alignment::Center)
.block(quit_block)
.style(if is_quit_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.danger)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text_muted)
});
f.render_widget(quit_text, card_chunks[1]);
let hint = Paragraph::new(crate::ui::tr("exit_hint", lang))
.alignment(Alignment::Center)
.style(
Style::default()
.fg(CATPPUCCIN_MOCHA.text_muted)
.bg(mocha::MANTLE),
);
f.render_widget(hint, chunks[3]);
}
pub fn draw_tool_settings(f: &mut Frame, app: &App, area: Rect) {
let block = Block::default()
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border_focus))
.style(Style::default().bg(mocha::MANTLE));
let inner_area = block.inner(area);
f.render_widget(Clear, area);
f.render_widget(block, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), Constraint::Length(2), Constraint::Length(3), Constraint::Min(0), ])
.split(inner_area);
let lang = &app.tool_config.language;
let title = Paragraph::new(Line::from(vec![
Span::styled("[⚙] ", Style::default()),
Span::styled(
crate::ui::tr("tool_settings_title", lang),
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
),
]))
.alignment(Alignment::Center)
.style(Style::default().bg(mocha::MANTLE));
f.render_widget(title, chunks[0]);
let question = Paragraph::new(crate::ui::tr("tool_settings_question", lang))
.alignment(Alignment::Center)
.style(Style::default().fg(CATPPUCCIN_MOCHA.text).bg(mocha::MANTLE));
f.render_widget(question, chunks[1]);
let card_chunks = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(chunks[2]);
let is_en_selected = app.tool_settings_selected == 0;
let en_border_style = if is_en_selected {
Style::default().fg(CATPPUCCIN_MOCHA.border_focus)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.border)
};
let en_block = Block::default()
.borders(Borders::ALL)
.border_type(if is_en_selected {
BorderType::Double
} else {
BorderType::Rounded
})
.border_style(en_border_style)
.style(if is_en_selected {
Style::default().bg(CATPPUCCIN_MOCHA.selection_bg)
} else {
Style::default().bg(mocha::MANTLE)
});
let en_text = Paragraph::new(crate::ui::tr("tool_settings_en", lang))
.alignment(Alignment::Center)
.block(en_block)
.style(if is_en_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text_muted)
});
f.render_widget(en_text, card_chunks[0]);
let is_zh_selected = app.tool_settings_selected == 1;
let zh_border_style = if is_zh_selected {
Style::default().fg(CATPPUCCIN_MOCHA.border_focus)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.border)
};
let zh_block = Block::default()
.borders(Borders::ALL)
.border_type(if is_zh_selected {
BorderType::Double
} else {
BorderType::Rounded
})
.border_style(zh_border_style)
.style(if is_zh_selected {
Style::default().bg(CATPPUCCIN_MOCHA.selection_bg)
} else {
Style::default().bg(mocha::MANTLE)
});
let zh_text = Paragraph::new(crate::ui::tr("tool_settings_zh", lang))
.alignment(Alignment::Center)
.block(zh_block)
.style(if is_zh_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.text)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text_muted)
});
f.render_widget(zh_text, card_chunks[1]);
let hint = Paragraph::new(crate::ui::tr("tool_settings_hint", lang))
.alignment(Alignment::Center)
.style(
Style::default()
.fg(CATPPUCCIN_MOCHA.text_muted)
.bg(mocha::MANTLE),
);
f.render_widget(hint, chunks[3]);
}
pub fn draw_port_menu(f: &mut Frame, app: &App, area: Rect) {
let lang = &app.tool_config.language;
let block = Block::default()
.title(Span::styled(
format!(" {} ", crate::ui::tr("port_menu_title", lang)),
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.add_modifier(Modifier::BOLD),
))
.borders(Borders::ALL)
.border_type(BorderType::Rounded)
.border_style(Style::default().fg(CATPPUCCIN_MOCHA.border_focus))
.style(Style::default().bg(mocha::MANTLE));
let inner_area = block.inner(area);
f.render_widget(Clear, area);
f.render_widget(block, area);
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Min(1), Constraint::Length(1), ])
.split(inner_area);
let mut items = Vec::new();
for channel in &app.channels {
let display_name = if let Some(ref prod) = channel.usb_product {
format!("{} ({})", channel.port, prod)
} else {
channel.port.clone()
};
items.push(display_name);
}
let list_items: Vec<ratatui::widgets::ListItem> = items
.iter()
.enumerate()
.map(|(idx, name)| {
let is_selected = idx == app.port_menu_selected;
let style = if is_selected {
Style::default()
.fg(CATPPUCCIN_MOCHA.accent)
.bg(CATPPUCCIN_MOCHA.selection_bg)
.add_modifier(Modifier::BOLD)
} else {
Style::default().fg(CATPPUCCIN_MOCHA.text)
};
let prefix = if is_selected { "● " } else { " " };
ratatui::widgets::ListItem::new(format!("{}{}", prefix, name)).style(style)
})
.collect();
let list = ratatui::widgets::List::new(list_items).style(Style::default().bg(mocha::MANTLE));
f.render_widget(list, chunks[0]);
let hint = Paragraph::new(crate::ui::tr("port_menu_hint", lang))
.alignment(Alignment::Center)
.style(
Style::default()
.fg(CATPPUCCIN_MOCHA.text_muted)
.bg(mocha::MANTLE),
);
f.render_widget(hint, chunks[1]);
}