from textual.screen import Screen from textual.containers import Vertical, Horizontal, VerticalScroll from textual.widgets import Static, Button from agentm.logic.db_functions import get_agents_for_game, delete_agent_by_id from agentm.theme.palette import get_theme from agentm.utils.logger import log_with_caller from agentm.components.footer import AgentMFooter from .create_agent import CreateAgentView from .select_run import SelectRunView from pathlib import Path palette = get_theme() class AgentCard(Static): def __init__(self, agent_data: dict, parent_view): super().__init__(classes="agent_card") self.agent_data = agent_data self.parent_view = parent_view def render(self) -> str: return f""" [bold {palette.ACCENT}]{self.agent_data['name']}[/] [dim]ID:[/] {self.agent_data['id']} [dim]Created:[/] {self.agent_data.get('created_at', '—')} [dim]Game ID:[/] {self.agent_data['game_id']} """.strip() async def on_click(self): await self.parent_view.display_agent_info(self.agent_data) class AgentSelectView(Screen): BINDINGS = [ ("escape", "app.pop_screen", "Back"), ("n", "create_agent", "New Agent"), ("r", "refresh_agents", "Refresh"), ("up", "scroll_up", "Scroll Up"), ("down", "scroll_down", "Scroll Down"), ] def __init__(self, game_metadata: dict): super().__init__() self.game_metadata = game_metadata self.selected_agent = None self.delete_mode = False def compose(self): ascii_path = Path(__file__).parent.parent / "assets" / "headers" / "agent_select.txt" try: header_text = ascii_path.read_text() except FileNotFoundError: header_text = "=== SELECT AGENT ===" self.header = Static(f"[{palette.ACCENT}]{header_text}[/{palette.ACCENT}]", classes="header") self.subheader = Static(f"[b]{self.game_metadata['title']}[/b]", classes="subheader") self.agent_list = VerticalScroll(id="agent_scroll") self.agent_info = Horizontal(id="agent_info_panel") self.confirm_button = Button("✅ Select Agent", id="confirm_agent_btn", classes="confirm_button", disabled=True) self.delete_button = Button("🗑️ Delete Agent", id="delete_agent_btn", classes="danger_button", disabled=True) self.create_button = Button("➕ Create Agent", id="create_agent_btn", classes="confirm_button") yield Vertical( self.header, self.subheader, self.agent_list, self.agent_info, self.create_button, AgentMFooter(compact=True), id="agent_select_layout" ) async def on_mount(self): log_with_caller("debug", f"Mounted AgentSelectView for game_id: {self.game_metadata['game_id']}") await self.refresh_agent_list() async def on_resume(self): await self.refresh_agent_list() async def refresh_agent_list(self): try: for child in list(self.agent_list.children): await child.remove() agents = get_agents_for_game(self.game_metadata["game_id"]) log_with_caller("info", f"Refreshed: {len(agents)} agents found for {self.game_metadata['game_id']}") if agents: for agent in agents: await self.agent_list.mount(AgentCard(agent, self)) else: await self.agent_list.mount(Static("[dim]No agents found for this game.[/dim]")) self.selected_agent = None self.delete_mode = False await self.display_agent_info(None) except Exception as e: log_with_caller("error", f"Error rendering agent list: {e}") async def display_agent_info(self, agent: dict | None): self.selected_agent = agent self.delete_mode = False await self.agent_info.remove_children() if not agent: await self.agent_info.mount(Static("[dim]Select an agent to view details[/dim]")) self.confirm_button.disabled = True self.delete_button.disabled = True return from rich.panel import Panel from rich.table import Table table = Table.grid(padding=(0, 1)) table.add_column("Key", style="bold underline") table.add_column("Value", style=palette.ACCENT, overflow="fold") table.add_row("Name", agent["name"]) table.add_row("Agent ID", str(agent["id"])) table.add_row("Game ID", agent["game_id"]) table.add_row("Created", agent.get("created_at", "—")) table.add_row("Type", agent.get("agent_type", "—")) table.add_row("Framework", agent.get("framework", "—")) table.add_row("Imitation", str(agent.get("is_imitation", False))) info_panel = Static(Panel(table, title="Agent Info", border_style=palette.BORDER), classes="agent_info_box") button_column = Vertical(self.confirm_button, self.delete_button, classes="agent_action_box") await self.agent_info.mount(info_panel, button_column) self.confirm_button.disabled = False self.delete_button.disabled = False self.delete_button.label = "🗑️ Delete Agent" async def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "create_agent_btn": log_with_caller("info", "Create Agent button pressed.") await self.app.push_screen(CreateAgentView(self.game_metadata)) elif event.button.id == "confirm_agent_btn" and self.selected_agent: log_with_caller("info", f"Agent confirmed: {self.selected_agent['name']}") await self.app.push_screen(SelectRunView(self.selected_agent)) elif event.button.id == "delete_agent_btn" and self.selected_agent: if not self.delete_mode: self.delete_mode = True self.delete_button.label = "❌ Confirm Delete" log_with_caller("warning", f"Pending deletion for agent: {self.selected_agent['name']}") else: log_with_caller("info", f"Deleting agent: {self.selected_agent['name']} ({self.selected_agent['id']})") delete_agent_by_id(self.selected_agent["id"]) await self.refresh_agent_list() async def action_create_agent(self): await self.app.push_screen(CreateAgentView(self.game_metadata)) async def action_refresh_agents(self): await self.refresh_agent_list() async def action_scroll_up(self): scroll = self.query_one("#agent_scroll", VerticalScroll) scroll.scroll_up() async def action_scroll_down(self): scroll = self.query_one("#agent_scroll", VerticalScroll) scroll.scroll_down()