zik 0.1.0

A TUI web radio player with audio spectrum visualizer
use ratatui::Frame;
use ratatui::layout::{Constraint, Direction, Layout};
use ratatui::style::{Color, Modifier, Style};
use ratatui::symbols::border;
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph};

use crate::app::App;
use crate::visualizer::Visualizer;

/// Draw the full UI including visualizer.
pub fn draw(frame: &mut Frame, app: &App, viz: &mut Visualizer) {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([Constraint::Min(10), Constraint::Length(1)])
        .split(frame.area());

    let visualizer_area = if app.show_selector {
        let main_chunks = Layout::default()
            .direction(Direction::Horizontal)
            .constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
            .split(chunks[0]);

        let items: Vec<ListItem> = app
            .radio_names
            .iter()
            .enumerate()
            .map(|(i, name)| {
                let prefix = if app.playing.as_deref() == Some(name.as_str()) {
                    ">> "
                } else {
                    "   "
                };
                let style = if i == app.selected {
                    Style::default()
                        .fg(Color::Yellow)
                        .add_modifier(Modifier::BOLD)
                } else if app.playing.as_deref() == Some(name.as_str()) {
                    Style::default().fg(Color::Green)
                } else {
                    Style::default()
                };
                ListItem::new(format!("{prefix}{name}")).style(style)
            })
            .collect();

        let list = List::new(items).block(
            Block::default()
                .borders(Borders::ALL)
                .border_set(border::ROUNDED)
                .title(" Radios "),
        );
        frame.render_widget(list, main_chunks[0]);

        // Render border for visualizer pane
        let block = Block::default()
            .borders(Borders::ALL)
            .border_set(border::ROUNDED)
            .title(" Visualizer ");
        let inner = block.inner(main_chunks[1]);
        frame.render_widget(block, main_chunks[1]);

        inner
    } else {
        chunks[0]
    };

    // Status bar
    let left_spans = if let Some(ref name) = app.playing {
        vec![
            Span::raw(" Playing: "),
            Span::styled(
                name.clone(),
                Style::default()
                    .fg(Color::Green)
                    .add_modifier(Modifier::BOLD),
            ),
        ]
    } else {
        vec![Span::styled(
            " Stopped",
            Style::default().fg(Color::DarkGray),
        )]
    };

    let status_bar = Paragraph::new(Line::from(left_spans));
    frame.render_widget(status_bar, chunks[1]);

    let right_text = if app.show_help {
        "q: quit | Enter: play/stop | s: radios | j/k: navigate | ?: help "
    } else {
        "?: help "
    };
    let help_hint = Paragraph::new(Line::from(Span::styled(
        right_text,
        Style::default().fg(Color::DarkGray),
    )))
    .alignment(ratatui::layout::Alignment::Right);
    frame.render_widget(help_hint, chunks[1]);

    if app.player.is_playing() {
        viz.render(frame.buffer_mut(), visualizer_area, &app.spectrum);
    }
}