import sys from textual import on from textual.app import App, ComposeResult from textual.binding import Binding from textual.widgets import DataTable, Footer, Header, TabbedContent, TabPane import portfolio from tui.books_pane import BooksPane from tui.games_pane import GamesPane from tui.media_pane import MediaPane from tui.music_pane import MusicPane from tui.profile_pane import ProfilePane from tui.projects_pane import ProjectsPane from tui.record_pane import RecordListPane from tui.themes import default_theme_id, load_site_themes from tui.themes_pane import ThemesPane TABS = [ ("music", "Music"), ("shows", "Shows"), ("movies", "Movies"), ("books", "Books"), ("games", "Games"), ("profile", "Profile"), ("experience", "Experience"), ("leadership", "Leadership"), ("projects", "Projects"), ("themes", "Themes"), ] def tab_key(number: int) -> str: return str(number % 10) class ContentApp(App): TITLE = "sirblob@engineering" SUB_TITLE = "~/content" CSS_PATH = "tui/app.tcss" BINDINGS = [ Binding("q", "quit", "Quit"), Binding("t", "next_theme", "Theme"), *[Binding(tab_key(number), f"show_tab('{tab_id}')", label, show=False) for number, (tab_id, label) in enumerate(TABS, start=1)], ] def __init__(self): super().__init__() self.site_theme_ids: list[str] = [] self.reload_site_themes(default_theme_id()) def reload_site_themes(self, show_theme_id: str | None = None): self.site_theme_ids = [] for theme in load_site_themes(): self.register_theme(theme) self.site_theme_ids.append(theme.name) if not self.site_theme_ids: return target = show_theme_id if show_theme_id in self.site_theme_ids else self.site_theme_ids[0] if target == self.theme: fallback = next(theme for theme in self.available_themes if theme != target) self.theme = fallback self.theme = target def action_next_theme(self): if not self.site_theme_ids: return current = self.site_theme_ids.index(self.theme) if self.theme in self.site_theme_ids else -1 self.theme = self.site_theme_ids[(current + 1) % len(self.site_theme_ids)] self.notify(f"Theme: {self.theme}") def compose(self) -> ComposeResult: yield Header() with TabbedContent(initial="music"): with TabPane("1 Music", id="music"): yield MusicPane() with TabPane("2 Shows", id="shows"): yield MediaPane("show") with TabPane("3 Movies", id="movies"): yield MediaPane("movie") with TabPane("4 Books", id="books"): yield BooksPane() with TabPane("5 Games", id="games"): yield GamesPane() with TabPane("6 Profile", id="profile"): yield ProfilePane() with TabPane("7 Experience", id="experience"): yield RecordListPane(portfolio.EXPERIENCE) with TabPane("8 Leadership", id="leadership"): yield RecordListPane(portfolio.LEADERSHIP) with TabPane("9 Projects", id="projects"): yield ProjectsPane() with TabPane("0 Themes", id="themes"): yield ThemesPane() yield Footer() def on_mount(self): self.focus_active_table() def action_show_tab(self, tab_id: str): self.query_one(TabbedContent).active = tab_id @on(TabbedContent.TabActivated) def tab_changed(self): self.focus_active_table() def focus_active_table(self): tabs = self.query_one(TabbedContent) tabs.get_pane(tabs.active).query_one(DataTable).focus() def main() -> int: ContentApp().run() return 0 if __name__ == "__main__": sys.exit(main())