"""Full-screen keyboard help.""" from __future__ import annotations import textwrap from rich.console import Group from rich.table import Table from rich.text import Text from textual.app import ComposeResult from textual.screen import ModalScreen from textual.widgets import Static from .common import SPECTRUM_STOPS, gradient HELP_ROWS: tuple[tuple[str, str], ...] = ( ("↑ / ↓ j / k", "move through the station list"), ("enter", "tune in to the selected station"), ("space", "play / pause the current station"), ("s", "stop playback"), ("] / [", "next / previous station (tunes immediately)"), ("+ / -", "volume up / down"), ("m", "mute / unmute"), ("f", "favourite — favourites float to the top"), ("/", "filter stations by title, genre or description"), ("esc", "clear the filter / close overlays"), ("v", "cycle visualiser: mirrored bars → bars → wave"), ("r", "refresh station list and song history"), ("g / G", "jump to the top / bottom of the list"), ("? ", "this help"), ("q", "quit"), ) ABOUT = ( ( "Stations and song history come from somafm.com " "(channels.json + songs/.json)." ), ( "The spectrum captures your actual PulseAudio/PipeWire output with " "parec (SYNC); otherwise it falls back to a muted ffmpeg stream tap " "(FFT, can drift) or a synthesized display (SIM)." ), ( "The desktop picks us up over MPRIS as a " "somafmtui media player — try `playerctl -l` / `playerctl metadata`." ), ) class HelpScreen(ModalScreen[None]): def compose(self) -> ComposeResult: table = Table.grid(padding=(0, 2), expand=False) table.add_column(style="bold cyan", no_wrap=True, justify="right") table.add_column(style="") for key, description in HELP_ROWS: table.add_row(key, description) header = Text() header.append("◤ ", style=f"bold {gradient(SPECTRUM_STOPS, 0.9)}") header.append("SOMAFM TUI", style=f"bold {gradient(SPECTRUM_STOPS, 0.55)}") header.append(" keyboard & credits", style="dim") footer = Text() for paragraph in ABOUT: for line in textwrap.wrap(paragraph, width=70): footer.append(line + "\n", style="dim") body = Group(header, Text(""), table, Text(""), footer) yield Static(body, id="help") def on_key(self, event) -> None: event.stop() self.dismiss(None)