fix(ui): keep the help and catalogue menus inside the screen

This commit is contained in:
beefcarver committed 2026-11-04 08:56:04 -05:00
1 parent 43ddd8666b
commit 7ba2d50503
4 files changed
+151 -70

No files matched your search

+29 -17
View File
@@ -5,11 +5,12 @@ from __future__ import annotations
from rich.console import Group
from rich.text import Text
from textual.app import ComposeResult
from textual.containers import VerticalScroll
from textual.screen import ModalScreen
from textual.widgets import Static
from ..providers import PROVIDERS, available_providers
from .common import SPECTRUM_STOPS, gradient, truncate
from .common import active_stops, gradient, truncate
class ProviderScreen(ModalScreen[str | None]):
@@ -22,12 +23,19 @@ class ProviderScreen(ModalScreen[str | None]):
self.index = self.slugs.index(current_id) if current_id in self.slugs else 0
def compose(self) -> ComposeResult:
yield Static(self._body(), id="providers")
# A scroll container keeps thirteen catalogues navigable on a short
# terminal; the body is given its exact line count so it measures.
with VerticalScroll(id="providers"):
yield Static(self._body(), id="providers-body")
def on_mount(self) -> None:
body = self.query_one("#providers-body", Static)
body.styles.height = len(self.slugs) + 5 # heading, blank, rows, footer
def _body(self) -> Group:
header = Text()
header.append("◤ ", style=f"bold {gradient(SPECTRUM_STOPS, 0.9)}")
header.append("CATALOGUES", style=f"bold {gradient(SPECTRUM_STOPS, 0.55)}")
header.append("◤ ", style=f"bold {gradient(active_stops(self.app), 0.9)}")
header.append("CATALOGUES", style=f"bold {gradient(active_stops(self.app), 0.55)}")
header.append(" · pick a station catalogue", style="dim")
name_width = (
@@ -55,26 +63,20 @@ class ProviderScreen(ModalScreen[str | None]):
f"{truncate(provider.name, name_width - 2):<{name_width}}",
style="bold underline" if selected else "bold",
)
row.append(truncate(provider.brand, 14), style="dim")
row.append(" ")
# marker(2) + number(5) + name + gap(2) + tagline + current dot(3)
room = max(8, 58 - 2 - 5 - name_width - 2 - 3)
row.append(truncate(provider.tagline, room), style="italic dim")
if slug == self.current_id:
row.append(" ●", style="bold green")
rows.append(row)
if selected:
rows.append(
Text(
" " + truncate(provider.tagline, 54),
style="italic dim",
no_wrap=True,
)
)
current = PROVIDERS.get(self.current_id)
footer = Text()
footer.append("\n")
if current is not None:
footer.append(
f"now: {current.name} — {current.station_source}\n", style="dim"
)
line = truncate(f"now: {current.name} — {current.station_source}", 58)
footer.append(line + "\n", style="dim")
footer.append(
"↑ ↓ choose · enter switch · esc cancel",
style="dim",
@@ -85,7 +87,17 @@ class ProviderScreen(ModalScreen[str | None]):
def _move(self, delta: int) -> None:
self.index = (self.index + delta) % len(self.slugs)
self.query_one("#providers", Static).update(self._body())
body = self.query_one("#providers-body", Static)
body.update(self._body())
box = self.query_one("#providers", VerticalScroll)
def follow() -> None:
visible = max(1, box.size.height)
target = max(0, 2 + self.index - (visible - 3) // 2)
box.scroll_to(y=target, animate=False)
box.call_after_refresh(follow)
def on_key(self, event) -> None:
key = event.key