alphai-tui 0.34.0

Terminal stock dashboard: live quotes and charts plus AI-scored financial news and SEC Form 4 insider activity from AlphAI
use ratatui::Frame;
use ratatui::layout::{Constraint, Layout, Rect};

use crate::app::{App, FeedKind};
use crate::keymap::Action;
use crate::ui::{Hint, View, ViewId};
use crate::ui::{chart, news, table};

/// Below this body height the news strip is dropped and the table and the
/// chart share the row, so both keep usable space on tiny terminals.
const NEWS_MIN_BODY_HEIGHT: u16 = 16;

pub struct SplitView;

impl View for SplitView {
    fn id(&self) -> ViewId {
        ViewId::Split
    }

    fn title(&self) -> &'static str {
        "Split"
    }

    fn hints(&self) -> &'static [Hint] {
        const HINTS: &[Hint] = &[
            Hint::act(&[Action::Quit], "quit"),
            Hint::fixed("tab/1-9", "view"),
            Hint::act(&[Action::Up, Action::Down], "select"),
            Hint::act(&[Action::CycleScope], "scope"),
            Hint::act(
                &[
                    Action::ChartStyle,
                    Action::ToggleSma,
                    Action::ToggleRsi,
                    Action::ToggleVolume,
                    Action::MaType,
                    Action::NewsMarkers,
                ],
                "chart",
            ),
            Hint::act(&[Action::NextPreset], "interval"),
            Hint::act(&[Action::FindTicker], "find"),
            // No settings hint: with the news marks key this line is the
            // widest in the app after the News one, it has to fit 110
            // columns, and the help overlay lists s anyway.
            Hint::act(&[Action::Help], "help"),
        ];
        HINTS
    }

    /// The embedded news strip drives the same demand-driven fetch as the
    /// full News view; jk still navigate the watchlist, not the strip.
    fn feed_shown(&self) -> Option<FeedKind> {
        Some(FeedKind::News)
    }

    fn has_chart_panel(&self) -> bool {
        true
    }

    /// The full-width chart gets 70% of the height, with the watchlist
    /// and news strip sharing the remaining 30% below it.
    fn render(&self, f: &mut Frame, area: Rect, app: &mut App) {
        if area.height < NEWS_MIN_BODY_HEIGHT {
            let [left, right] =
                Layout::horizontal([Constraint::Percentage(45), Constraint::Percentage(55)])
                    .areas(area);
            table::render_table(f, left, app);
            chart::render_chart(f, right, app);
            return;
        }
        let [top, bottom] =
            Layout::vertical([Constraint::Percentage(70), Constraint::Percentage(30)]).areas(area);
        chart::render_chart(f, top, app);
        let [left, right] =
            Layout::horizontal([Constraint::Percentage(45), Constraint::Percentage(55)])
                .areas(bottom);
        table::render_table(f, left, app);
        news::render_panel(f, right, app);
    }
}