First commit in rust

This commit is contained in:
2026-02-19 13:51:07 +08:00
commit c37ca9ba52
73 changed files with 9737 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
use ratatui::{
layout::{Constraint, Direction, Layout},
style::{Color, Modifier, Style},
text::{Line, Span},
widgets::{Block, Borders, List, ListItem, ListState, Paragraph},
Frame,
};
use crate::tui::app::App;
pub fn draw(frame: &mut Frame, app: &App) {
let area = frame.size();
// Split into sidebar | main column
let columns = Layout::default()
.direction(Direction::Horizontal)
.constraints([Constraint::Length(22), Constraint::Min(0)])
.split(area);
draw_sidebar(frame, app, columns[0]);
draw_main(frame, app, columns[1]);
}
fn draw_sidebar(frame: &mut Frame, app: &App, area: ratatui::layout::Rect) {
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Min(0), Constraint::Length(6)])
.split(area);
// ── Channel list ────────────────────────────────────────────────────
let items: Vec<ListItem> = app
.channels
.iter()
.enumerate()
.map(|(i, (_, name))| {
let style = if i == app.selected_channel {
Style::default().fg(Color::Yellow).add_modifier(Modifier::BOLD)
} else {
Style::default()
};
ListItem::new(format!("# {}", name)).style(style)
})
.collect();
let mut state = ListState::default();
state.select(Some(app.selected_channel));
let channels = List::new(items)
.block(
Block::default()
.title(" Channels ")
.borders(Borders::ALL)
.style(Style::default().fg(Color::Cyan)),
)
.highlight_style(Style::default().add_modifier(Modifier::BOLD));
frame.render_stateful_widget(channels, rows[0], &mut state);
// ── Online users ─────────────────────────────────────────────────────
let user_items: Vec<ListItem> = app
.online_users
.iter()
.map(|(_, name)| ListItem::new(format!("{}", name)))
.collect();
let online = List::new(user_items).block(
Block::default()
.title(" Online ")
.borders(Borders::ALL)
.style(Style::default().fg(Color::Green)),
);
frame.render_widget(online, rows[1]);
}
fn draw_main(frame: &mut Frame, app: &App, area: ratatui::layout::Rect) {
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(1), // channel title bar
Constraint::Min(0), // messages
Constraint::Length(3), // input box
Constraint::Length(1), // status bar
])
.split(area);
// ── Title bar ────────────────────────────────────────────────────────
let title = app
.current_channel()
.map(|(_, name)| format!(" # {} ", name))
.unwrap_or_else(|| " No channel selected ".into());
let title_bar = Paragraph::new(title)
.style(Style::default().fg(Color::White).bg(Color::DarkGray).add_modifier(Modifier::BOLD));
frame.render_widget(title_bar, rows[0]);
// ── Messages ─────────────────────────────────────────────────────────
let msgs: Vec<ListItem> = app
.current_channel_messages()
.map(|deque| {
deque
.iter()
.map(|m| {
ListItem::new(Line::from(vec![
Span::styled(
format!("{} ", m.timestamp),
Style::default().fg(Color::DarkGray),
),
Span::styled(
format!("{}: ", m.author),
Style::default().fg(Color::Cyan).add_modifier(Modifier::BOLD),
),
Span::raw(m.content.clone()),
]))
})
.collect()
})
.unwrap_or_default();
let message_count = msgs.len();
let mut list_state = ListState::default();
if message_count > 0 {
list_state.select(Some(message_count - 1));
}
let messages = List::new(msgs)
.block(Block::default().borders(Borders::LEFT))
.highlight_style(Style::default());
frame.render_stateful_widget(messages, rows[1], &mut list_state);
// ── Input box ─────────────────────────────────────────────────────────
let input = Paragraph::new(app.input.as_str())
.block(
Block::default()
.borders(Borders::ALL)
.title(" Message (Enter to send) "),
);
frame.render_widget(input, rows[2]);
// ── Status bar ────────────────────────────────────────────────────────
let status_text = app
.status_msg
.as_deref()
.unwrap_or("Ctrl-Q: quit | ↑↓: channels | Esc: dismiss");
let status = Paragraph::new(status_text)
.style(Style::default().fg(Color::DarkGray));
frame.render_widget(status, rows[3]);
}