"""Full-screen keyboard help.""" from __future__ import annotations import sys import textwrap from typing import TYPE_CHECKING, cast 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 if TYPE_CHECKING: from ..app import BeefyPlayerApp 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: mirror → bars → wave → waterfall → radial → braille", ), ("r", "refresh station list and song history"), ("g / G", "jump to the top / bottom of the list"), ("? ", "this help"), ("q", "quit"), ) ABOUT_LINUX = ( ( "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 " "beefyplayer media player — try `playerctl -l` / `playerctl metadata`." ), ) ABOUT_MACOS = ( ( "The spectrum captures your Mac's real audio output (SYNC) with a " "native Core Audio process tap — nothing to install. macOS asks for " "System Audio Recording permission the first time; without it the " "analyser falls back to a muted ffmpeg stream tap (FFT, can drift) " "or a synthesized display (SIM)." ), ( "mpv publishes what is playing to the macOS Now Playing session, so " "Control Center and the keyboard media keys control this app." ), ) class HelpScreen(ModalScreen[None]): def compose(self) -> ComposeResult: provider = cast("BeefyPlayerApp", self.app).provider about = ( f"Stations and song history come from {provider.name} " f"({provider.station_source} + {provider.history_source}).", ) + (ABOUT_MACOS if sys.platform == "darwin" else ABOUT_LINUX) 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("BEEFY PLAYER", 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)