youtube-tui/widgets/command_palette.py

200 lines
5.4 KiB
Python

"""
Command Palette Widget for YouTube TUI
"""
from textual.app import ComposeResult
from textual.containers import Container
from textual.screen import ModalScreen
from textual.widgets import Footer, Header, Input, ListView, ListItem, Static
class CommandPalette(ModalScreen):
"""Command palette for quick access to actions"""
DEFAULT_COMMANDS = [
("Search", "Search for videos"),
("Download", "Quick download mode"),
("History", "Show search history"),
("Settings", "Open settings"),
("Help", "Show help screen"),
("Quit", "Quit application"),
]
CSS = """
CommandPalette {
align: center middle;
}
#palette-container {
width: 60%;
height: auto;
border: solid #555555;
background: $surface;
padding: 1;
}
#palette-title {
width: 100%;
height: 3;
dock: top;
background: $primary;
content-align: center middle;
color: $text;
}
#palette-input {
width: 100%;
margin: 1 0;
}
#palette-list {
width: 100%;
height: 20;
margin: 1 0;
}
#palette-actions {
width: 100%;
height: auto;
dock: bottom;
margin-top: 1;
}
ListItem {
height: 3;
padding: 0 1;
}
ListItem:hover {
background: $primary-darken-2;
}
ListItem.--highlight {
background: $primary;
}
.command-description {
color: $text-muted;
}
"""
BINDINGS = [
("escape", "close_palette", "Close"),
("up", "cursor_up", "Cursor Up"),
("down", "cursor_down", "Cursor Down"),
("enter", "select_command", "Select"),
]
def __init__(self):
super().__init__()
self.selected_index = 0
self.commands = self.DEFAULT_COMMANDS.copy()
def compose(self) -> ComposeResult:
"""Compose the command palette"""
yield Header()
yield Container(
Static("Command Palette", id="palette-title"),
Input(placeholder="Type to filter commands...", id="palette-input"),
ListView(id="palette-list-view"),
id="palette-container",
)
yield Footer()
def on_mount(self) -> None:
"""Called when palette is mounted"""
self.update_list()
self.query_one(Input).focus()
def update_list(self) -> None:
"""Update the command list"""
input_widget = self.query_one("#palette-input", Input)
filter_text = input_widget.value.lower()
# Filter commands
if filter_text:
self.commands = [
(cmd, desc)
for cmd, desc in self.DEFAULT_COMMANDS
if filter_text in cmd.lower() or filter_text in desc.lower()
]
else:
self.commands = self.DEFAULT_COMMANDS.copy()
# Update the list view
list_view = self.query_one("#palette-list-view", ListView)
list_view.clear()
for i, (command, description) in enumerate(self.commands):
item = ListItem(
Static(f"[bold]{command}[/bold]\n{description}"),
)
list_view.append(item)
# Update selected index if needed
if self.selected_index >= len(self.commands):
self.selected_index = max(0, len(self.commands) - 1)
# Highlight selected item
self.highlight_selected()
def highlight_selected(self) -> None:
"""Highlight the selected item"""
list_view = self.query_one("#palette-list-view", ListView)
for i, item in enumerate(list_view.children):
if i == self.selected_index:
item.add_class("--highlight")
else:
item.remove_class("--highlight")
def action_cursor_up(self) -> None:
"""Move selection up"""
if self.commands:
self.selected_index = max(0, self.selected_index - 1)
self.highlight_selected()
def action_cursor_down(self) -> None:
"""Move selection down"""
if self.commands:
self.selected_index = min(len(self.commands) - 1, self.selected_index + 1)
self.highlight_selected()
def action_select_command(self) -> None:
"""Execute the selected command"""
if not self.commands:
return
command = self.commands[self.selected_index][0]
self.execute_command(command)
def execute_command(self, command: str) -> None:
"""Execute a command"""
if command == "Search":
self.app.push_screen("search")
self.app.pop_screen()
elif command == "Download":
# Go to search for quick download
self.app.push_screen("search")
self.app.pop_screen()
elif command == "History":
self.app.push_screen("history")
elif command == "Settings":
self.app.push_screen("settings")
elif command == "Help":
self.app.push_screen("help")
elif command == "Quit":
self.app.exit()
def action_close_palette(self) -> None:
"""Close the palette"""
self.app.pop_screen()
def on_input_changed(self, event: Input.Changed) -> None:
"""Handle input changes"""
self.update_list()
def on_data_table_row_selected(self, event) -> None:
"""Handle row selection"""
self.action_select_command()