Replace content CLI with Textual TUI
This commit is contained in:
1 parent
0d58ddd430
commit
737700a9a8
28 files changed
+2403
-167
No files matched your search
@@ -0,0 +1,110 @@
|
||||
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.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"),
|
||||
("profile", "Profile"),
|
||||
("experience", "Experience"),
|
||||
("leadership", "Leadership"),
|
||||
("projects", "Projects"),
|
||||
("themes", "Themes"),
|
||||
]
|
||||
|
||||
|
||||
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(str(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 Profile", id="profile"):
|
||||
yield ProfilePane()
|
||||
with TabPane("6 Experience", id="experience"):
|
||||
yield RecordListPane(portfolio.EXPERIENCE)
|
||||
with TabPane("7 Leadership", id="leadership"):
|
||||
yield RecordListPane(portfolio.LEADERSHIP)
|
||||
with TabPane("8 Projects", id="projects"):
|
||||
yield ProjectsPane()
|
||||
with TabPane("9 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())
|
||||
Reference in new issue
Block a user