From d62820dd80a9c991e93749dfdc89f32fff65958c Mon Sep 17 00:00:00 2001 From: mscrnt Date: Sun, 25 May 2025 09:50:33 -0700 Subject: [PATCH] Add game logo files and enhance home view layout with grid and button components --- agentm/assets/game_logos/doapp.txt | 10 ++ agentm/assets/game_logos/kof98umh.txt | 10 ++ agentm/assets/game_logos/mvsc.txt | 10 ++ agentm/assets/game_logos/samsh5sp.txt | 10 ++ agentm/assets/game_logos/sfiii3n.txt | 10 ++ agentm/assets/game_logos/soulclbr.txt | 10 ++ agentm/assets/game_logos/tektagt.txt | 10 ++ agentm/assets/game_logos/umk3.txt | 10 ++ agentm/assets/game_logos/xmvsf.txt | 10 ++ agentm/main.py | 10 +- agentm/theme/generate_theme_css.py | 16 ++- agentm/theme/styles.base.tcss | 139 ++++++++++++-------------- agentm/theme/styles.tcss | 139 ++++++++++++-------------- agentm/views/home.py | 114 ++++++++++----------- 14 files changed, 292 insertions(+), 216 deletions(-) create mode 100644 agentm/assets/game_logos/doapp.txt create mode 100644 agentm/assets/game_logos/kof98umh.txt create mode 100644 agentm/assets/game_logos/mvsc.txt create mode 100644 agentm/assets/game_logos/samsh5sp.txt create mode 100644 agentm/assets/game_logos/sfiii3n.txt create mode 100644 agentm/assets/game_logos/soulclbr.txt create mode 100644 agentm/assets/game_logos/tektagt.txt create mode 100644 agentm/assets/game_logos/umk3.txt create mode 100644 agentm/assets/game_logos/xmvsf.txt diff --git a/agentm/assets/game_logos/doapp.txt b/agentm/assets/game_logos/doapp.txt new file mode 100644 index 0000000..f672620 --- /dev/null +++ b/agentm/assets/game_logos/doapp.txt @@ -0,0 +1,10 @@ + + +██████╗ ███████╗ █████╗ ██████╗ ██████╗ ██████╗ █████╗ ██╗ ██╗██╗ ██╗███████╗ +██╔══██╗██╔════╝██╔══██╗██╔══██╗ ██╔═══██╗██╔══██╗ ██╔══██╗██║ ██║██║ ██║██╔════╝ +██║ ██║█████╗ ███████║██║ ██║ ██║ ██║██████╔╝ ███████║██║ ██║██║ ██║█████╗ +██║ ██║██╔══╝ ██╔══██║██║ ██║ ██║ ██║██╔══██╗ ██╔══██║██║ ██║╚██╗ ██╔╝██╔══╝ +██████╔╝███████╗██║ ██║██████╔╝ ╚██████╔╝██║ ██║ ██║ ██║███████╗██║ ╚████╔╝ ███████╗ +╚═════╝ ╚══════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚══════╝ + + diff --git a/agentm/assets/game_logos/kof98umh.txt b/agentm/assets/game_logos/kof98umh.txt new file mode 100644 index 0000000..3ffb8d8 --- /dev/null +++ b/agentm/assets/game_logos/kof98umh.txt @@ -0,0 +1,10 @@ + + +████████╗██╗ ██╗███████╗ ██╗ ██╗██╗███╗ ██╗ ██████╗ ██████╗ ███████╗ ███████╗██╗ ██████╗ ██╗ ██╗████████╗███████╗██████╗ ███████╗ +╚══██╔══╝██║ ██║██╔════╝ ██║ ██╔╝██║████╗ ██║██╔════╝ ██╔═══██╗██╔════╝ ██╔════╝██║██╔════╝ ██║ ██║╚══██╔══╝██╔════╝██╔══██╗██╔════╝ + ██║ ███████║█████╗ █████╔╝ ██║██╔██╗ ██║██║ ███╗ ██║ ██║█████╗ █████╗ ██║██║ ███╗███████║ ██║ █████╗ ██████╔╝███████╗ + ██║ ██╔══██║██╔══╝ ██╔═██╗ ██║██║╚██╗██║██║ ██║ ██║ ██║██╔══╝ ██╔══╝ ██║██║ ██║██╔══██║ ██║ ██╔══╝ ██╔══██╗╚════██║ + ██║ ██║ ██║███████╗ ██║ ██╗██║██║ ╚████║╚██████╔╝ ╚██████╔╝██║ ██║ ██║╚██████╔╝██║ ██║ ██║ ███████╗██║ ██║███████║ + ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ + + diff --git a/agentm/assets/game_logos/mvsc.txt b/agentm/assets/game_logos/mvsc.txt new file mode 100644 index 0000000..ccafabe --- /dev/null +++ b/agentm/assets/game_logos/mvsc.txt @@ -0,0 +1,10 @@ + + +███╗ ███╗ █████╗ ██████╗ ██╗ ██╗███████╗██╗ ██╗ ██╗███████╗ ██████╗ █████╗ ██████╗ ██████╗ ██████╗ ███╗ ███╗ +████╗ ████║██╔══██╗██╔══██╗██║ ██║██╔════╝██║ ██║ ██║██╔════╝ ██╔════╝██╔══██╗██╔══██╗██╔════╝██╔═══██╗████╗ ████║ +██╔████╔██║███████║██████╔╝██║ ██║█████╗ ██║ ██║ ██║███████╗ ██║ ███████║██████╔╝██║ ██║ ██║██╔████╔██║ +██║╚██╔╝██║██╔══██║██╔══██╗╚██╗ ██╔╝██╔══╝ ██║ ╚██╗ ██╔╝╚════██║ ██║ ██╔══██║██╔═══╝ ██║ ██║ ██║██║╚██╔╝██║ +██║ ╚═╝ ██║██║ ██║██║ ██║ ╚████╔╝ ███████╗███████╗ ╚████╔╝ ███████║██╗ ╚██████╗██║ ██║██║ ╚██████╗╚██████╔╝██║ ╚═╝ ██║ +╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═══╝ ╚══════╝╚══════╝ ╚═══╝ ╚══════╝╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ + + diff --git a/agentm/assets/game_logos/samsh5sp.txt b/agentm/assets/game_logos/samsh5sp.txt new file mode 100644 index 0000000..e5477ee --- /dev/null +++ b/agentm/assets/game_logos/samsh5sp.txt @@ -0,0 +1,10 @@ + + +███████╗ █████╗ ███╗ ███╗██╗ ██╗██████╗ █████╗ ██╗ ███████╗██╗ ██╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗███╗ ██╗ ██╗ ██╗ +██╔════╝██╔══██╗████╗ ████║██║ ██║██╔══██╗██╔══██╗██║ ██╔════╝██║ ██║██╔═══██╗██╔══██╗██╔═══██╗██║ ██║████╗ ██║ ██║ ██║ +███████╗███████║██╔████╔██║██║ ██║██████╔╝███████║██║ ███████╗███████║██║ ██║██║ ██║██║ ██║██║ █╗ ██║██╔██╗ ██║ ██║ ██║ +╚════██║██╔══██║██║╚██╔╝██║██║ ██║██╔══██╗██╔══██║██║ ╚════██║██╔══██║██║ ██║██║ ██║██║ ██║██║███╗██║██║╚██╗██║ ╚██╗ ██╔╝ +███████║██║ ██║██║ ╚═╝ ██║╚██████╔╝██║ ██║██║ ██║██║ ███████║██║ ██║╚██████╔╝██████╔╝╚██████╔╝╚███╔███╔╝██║ ╚████║ ╚████╔╝ +╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═══╝ ╚═══╝ + + diff --git a/agentm/assets/game_logos/sfiii3n.txt b/agentm/assets/game_logos/sfiii3n.txt new file mode 100644 index 0000000..8ef05c4 --- /dev/null +++ b/agentm/assets/game_logos/sfiii3n.txt @@ -0,0 +1,10 @@ + + +███████╗████████╗██████╗ ███████╗███████╗████████╗ ███████╗██╗ ██████╗ ██╗ ██╗████████╗███████╗██████╗ ██╗██╗██╗ +██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔════╝╚══██╔══╝ ██╔════╝██║██╔════╝ ██║ ██║╚══██╔══╝██╔════╝██╔══██╗ ██║██║██║ +███████╗ ██║ ██████╔╝█████╗ █████╗ ██║ █████╗ ██║██║ ███╗███████║ ██║ █████╗ ██████╔╝ ██║██║██║ +╚════██║ ██║ ██╔══██╗██╔══╝ ██╔══╝ ██║ ██╔══╝ ██║██║ ██║██╔══██║ ██║ ██╔══╝ ██╔══██╗ ██║██║██║ +███████║ ██║ ██║ ██║███████╗███████╗ ██║ ██║ ██║╚██████╔╝██║ ██║ ██║ ███████╗██║ ██║ ██║██║██║ +╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝╚═╝ + + diff --git a/agentm/assets/game_logos/soulclbr.txt b/agentm/assets/game_logos/soulclbr.txt new file mode 100644 index 0000000..93108a4 --- /dev/null +++ b/agentm/assets/game_logos/soulclbr.txt @@ -0,0 +1,10 @@ + + +███████╗ ██████╗ ██╗ ██╗██╗ ██████╗ █████╗ ██╗ ██╗██████╗ ██╗ ██╗██████╗ +██╔════╝██╔═══██╗██║ ██║██║ ██╔════╝██╔══██╗██║ ██║██╔══██╗██║ ██║██╔══██╗ +███████╗██║ ██║██║ ██║██║ ██║ ███████║██║ ██║██████╔╝██║ ██║██████╔╝ +╚════██║██║ ██║██║ ██║██║ ██║ ██╔══██║██║ ██║██╔══██╗██║ ██║██╔══██╗ +███████║╚██████╔╝╚██████╔╝███████╗ ╚██████╗██║ ██║███████╗██║██████╔╝╚██████╔╝██║ ██║ +╚══════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ + + diff --git a/agentm/assets/game_logos/tektagt.txt b/agentm/assets/game_logos/tektagt.txt new file mode 100644 index 0000000..d2ddf65 --- /dev/null +++ b/agentm/assets/game_logos/tektagt.txt @@ -0,0 +1,10 @@ + + +████████╗███████╗██╗ ██╗██╗ ██╗███████╗███╗ ██╗ ████████╗ █████╗ ██████╗ ████████╗ ██████╗ ██╗ ██╗██████╗ ███╗ ██╗ █████╗ ███╗ ███╗███████╗███╗ ██╗████████╗ +╚══██╔══╝██╔════╝██║ ██╔╝██║ ██╔╝██╔════╝████╗ ██║ ╚══██╔══╝██╔══██╗██╔════╝ ╚══██╔══╝██╔═══██╗██║ ██║██╔══██╗████╗ ██║██╔══██╗████╗ ████║██╔════╝████╗ ██║╚══██╔══╝ + ██║ █████╗ █████╔╝ █████╔╝ █████╗ ██╔██╗ ██║ ██║ ███████║██║ ███╗ ██║ ██║ ██║██║ ██║██████╔╝██╔██╗ ██║███████║██╔████╔██║█████╗ ██╔██╗ ██║ ██║ + ██║ ██╔══╝ ██╔═██╗ ██╔═██╗ ██╔══╝ ██║╚██╗██║ ██║ ██╔══██║██║ ██║ ██║ ██║ ██║██║ ██║██╔══██╗██║╚██╗██║██╔══██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ██║ + ██║ ███████╗██║ ██╗██║ ██╗███████╗██║ ╚████║ ██║ ██║ ██║╚██████╔╝ ██║ ╚██████╔╝╚██████╔╝██║ ██║██║ ╚████║██║ ██║██║ ╚═╝ ██║███████╗██║ ╚████║ ██║ + ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═╝ + + diff --git a/agentm/assets/game_logos/umk3.txt b/agentm/assets/game_logos/umk3.txt new file mode 100644 index 0000000..8ade7d5 --- /dev/null +++ b/agentm/assets/game_logos/umk3.txt @@ -0,0 +1,10 @@ + + +███╗ ███╗ ██████╗ ██████╗ ████████╗ █████╗ ██╗ ██╗ ██╗ ██████╗ ███╗ ███╗██████╗ █████╗ ████████╗ ██████╗ +████╗ ████║██╔═══██╗██╔══██╗╚══██╔══╝██╔══██╗██║ ██║ ██╔╝██╔═══██╗████╗ ████║██╔══██╗██╔══██╗╚══██╔══╝ ╚════██╗ +██╔████╔██║██║ ██║██████╔╝ ██║ ███████║██║ █████╔╝ ██║ ██║██╔████╔██║██████╔╝███████║ ██║ █████╔╝ +██║╚██╔╝██║██║ ██║██╔══██╗ ██║ ██╔══██║██║ ██╔═██╗ ██║ ██║██║╚██╔╝██║██╔══██╗██╔══██║ ██║ ╚═══██╗ +██║ ╚═╝ ██║╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗ ██║ ██╗╚██████╔╝██║ ╚═╝ ██║██████╔╝██║ ██║ ██║ ██████╔╝ +╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ + + diff --git a/agentm/assets/game_logos/xmvsf.txt b/agentm/assets/game_logos/xmvsf.txt new file mode 100644 index 0000000..d18487a --- /dev/null +++ b/agentm/assets/game_logos/xmvsf.txt @@ -0,0 +1,10 @@ + + +██╗ ██╗ ███╗ ███╗███████╗███╗ ██╗ ██╗ ██╗███████╗ ███████╗████████╗██████╗ ███████╗███████╗████████╗ ███████╗██╗ ██████╗ ██╗ ██╗████████╗███████╗██████╗ +╚██╗██╔╝ ████╗ ████║██╔════╝████╗ ██║ ██║ ██║██╔════╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██╔════╝╚══██╔══╝ ██╔════╝██║██╔════╝ ██║ ██║╚══██╔══╝██╔════╝██╔══██╗ + ╚███╔╝█████╗██╔████╔██║█████╗ ██╔██╗ ██║ ██║ ██║███████╗ ███████╗ ██║ ██████╔╝█████╗ █████╗ ██║ █████╗ ██║██║ ███╗███████║ ██║ █████╗ ██████╔╝ + ██╔██╗╚════╝██║╚██╔╝██║██╔══╝ ██║╚██╗██║ ╚██╗ ██╔╝╚════██║ ╚════██║ ██║ ██╔══██╗██╔══╝ ██╔══╝ ██║ ██╔══╝ ██║██║ ██║██╔══██║ ██║ ██╔══╝ ██╔══██╗ +██╔╝ ██╗ ██║ ╚═╝ ██║███████╗██║ ╚████║ ╚████╔╝ ███████║ ███████║ ██║ ██║ ██║███████╗███████╗ ██║ ██║ ██║╚██████╔╝██║ ██║ ██║ ███████╗██║ ██║ +╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═══╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + + diff --git a/agentm/main.py b/agentm/main.py index e96eb69..14656c7 100644 --- a/agentm/main.py +++ b/agentm/main.py @@ -1,7 +1,13 @@ -# agentm/main.py - +import subprocess from agentm.app import AgentMApp if __name__ == "__main__": + # Run the generate_theme_css module silently + subprocess.run( + ["python", "-m", "agentm.theme.generate_theme_css", "--silent"], + check=True + ) + + # Launch the app app = AgentMApp() app.run() diff --git a/agentm/theme/generate_theme_css.py b/agentm/theme/generate_theme_css.py index 6855311..bc6be60 100644 --- a/agentm/theme/generate_theme_css.py +++ b/agentm/theme/generate_theme_css.py @@ -1,6 +1,10 @@ -from agentm.theme.palette import get_theme -from pathlib import Path +import sys import re +from pathlib import Path +from agentm.theme.palette import get_theme + +# Check for --silent in command-line arguments +silent = "--silent" in sys.argv # Load dark theme instance theme = get_theme("dark") @@ -16,14 +20,16 @@ template = template_path.read_text() # Find all placeholders like {{FOREGROUND}}, {{BACKGROUND}}, etc. tokens = set(re.findall(r"{{\s*([A-Z0-9_]+)\s*}}", template)) -# Replace them with actual values from theme +# Replace tokens for token in tokens: value = getattr(theme, token, None) if value is not None: template = template.replace(f"{{{{{token}}}}}", value) - else: + elif not silent: print(f"⚠️ Warning: Theme token '{token}' not found in ThemeManager") # Write final output output_path.write_text(template) -print(f"✅ Synced themed CSS written to: {output_path}") + +if not silent: + print(f"✅ Synced themed CSS written to: {output_path}") diff --git a/agentm/theme/styles.base.tcss b/agentm/theme/styles.base.tcss index 2c938b0..d32907c 100644 --- a/agentm/theme/styles.base.tcss +++ b/agentm/theme/styles.base.tcss @@ -14,7 +14,7 @@ Screen { /* === Headers === */ -# Header, .header { +# .header { # dock: top; # height: 3; # content-align: center middle; @@ -31,10 +31,13 @@ Button { background: {{SURFACE_10}}; color: {{ACCENT}}; border: solid {{ACCENT}}; - padding: 1 2; - margin: 1; + padding: 1; + margin: 0; content-align: center middle; text-style: bold; + width: 100%; + min-height: 3; + text-align: center; } Button:hover { @@ -47,6 +50,42 @@ Button:focus { border: solid {{ACCENT}}; } +Button:disabled { + background: {{DISABLED_BG}}; + color: {{DISABLED}}; + border: solid {{DISABLED_BORDER}}; + text-style: dim; +} + +/* === Grid Layout === */ + +.rom_grid { + grid-size: 5; + grid-gutter: 1; + padding: 0 1; /* Was: 1 2 */ + margin: 0; + width: 100%; + align-horizontal: center; + align-vertical: middle; + height: auto; /* Ensure it can grow */ +} + +#rom_grid_scroll { + max-height: 30vh; /* was: height: 40vh */ + overflow-y: auto; + scrollbar-gutter: stable; + padding: 0; + margin: 0; + border-bottom: solid {{BORDER}}; +} + +/* Optionally ensure the parent container scrolls properly */ +#game_content_layout { + height: auto; + width: 100%; + padding: 1 2; +} + /* === Inputs === */ Input { @@ -115,7 +154,7 @@ Input:focus { content-align: center middle; } -/* === Game Layout === */ +/* === Layout === */ .centered_layout { layout: vertical; @@ -125,30 +164,6 @@ Input:focus { width: 100%; } -.rom_rows_container { - layout: vertical; - align-horizontal: center; -} - -.game_card { - width: auto; - height: auto; - max-width: 60; - margin: 1; - padding: 1; - background: {{SURFACE_10}}; - color: {{ACCENT}}; - border: solid {{BORDER}}; - content-align: center middle; - text-style: bold; - text-align: center; -} - -.game_card:hover { - background: {{SURFACE_20}}; - color: {{ACCENT_HOVER}}; -} - Static { text-align: center; } @@ -157,47 +172,16 @@ Horizontal { content-align: center middle; } -.game_card:focus { - background: {{SURFACE_20}}; - border: solid {{ACCENT}}; -} - -.game_card:disabled { - background: {{SURFACE_10}}; - color: {{BORDER}}; -} - -.game_card_clicked { - background: {{ACCENT}}; - color: {{BACKGROUND}}; -} - -.offset_card { - width: 11; - height: auto; - visibility: hidden; -} - -.rom_row { - layout: horizontal; - align-horizontal: center; - align-vertical: middle; - padding: 1 2; - width: 100%; -} - .info_confirm_row { - layout: horizontal; - width: 100%; height: auto; - padding: 1 2; - align-vertical: top; + padding: 0 0; + margin: 0; } #confirm_button { - width: 20%; - height: auto; - content-align: center middle; + width: 100%; /* Take full width of its container */ + min-width: 20; /* Increase minimum width if needed */ + max-width: 100%; /* Optional: avoid oversizing */ } .confirm_button:hover { @@ -205,21 +189,30 @@ Horizontal { color: {{BACKGROUND}}; } -Button:disabled { - background: {{DISABLED_BG}}; - color: {{DISABLED}}; - border: solid {{DISABLED_BORDER}}; - text-style: dim; -} - .game_info { - padding: 1 2; + padding: 0 1; + margin: 0; width: 100%; - margin-right: 1; color: {{ACCENT}}; } #game_info_box { width: 80%; height: auto; + max-height: 50vh; /* Adjusted for better visibility */ + padding: 1; + overflow-y: auto; } + +Button.game_button { + min-height: 3; +} + +#confirm_button_container { + width: 20%; + min-width: 20; + align-vertical: middle; + align-horizontal: center; + height: 100%; + padding-left: 1; +} \ No newline at end of file diff --git a/agentm/theme/styles.tcss b/agentm/theme/styles.tcss index c29051e..020185e 100644 --- a/agentm/theme/styles.tcss +++ b/agentm/theme/styles.tcss @@ -14,7 +14,7 @@ Screen { /* === Headers === */ -# Header, .header { +# .header { # dock: top; # height: 3; # content-align: center middle; @@ -31,10 +31,13 @@ Button { background: #282828; color: #ed7d3a; border: solid #ed7d3a; - padding: 1 2; - margin: 1; + padding: 1; + margin: 0; content-align: center middle; text-style: bold; + width: 100%; + min-height: 3; + text-align: center; } Button:hover { @@ -47,6 +50,42 @@ Button:focus { border: solid #ed7d3a; } +Button:disabled { + background: #444444; + color: #999999; + border: solid #666666; + text-style: dim; +} + +/* === Grid Layout === */ + +.rom_grid { + grid-size: 5; + grid-gutter: 1; + padding: 0 1; /* Was: 1 2 */ + margin: 0; + width: 100%; + align-horizontal: center; + align-vertical: middle; + height: auto; /* Ensure it can grow */ +} + +#rom_grid_scroll { + max-height: 30vh; /* was: height: 40vh */ + overflow-y: auto; + scrollbar-gutter: stable; + padding: 0; + margin: 0; + border-bottom: solid #3a9bed; +} + +/* Optionally ensure the parent container scrolls properly */ +#game_content_layout { + height: auto; + width: 100%; + padding: 1 2; +} + /* === Inputs === */ Input { @@ -115,7 +154,7 @@ Input:focus { content-align: center middle; } -/* === Game Layout === */ +/* === Layout === */ .centered_layout { layout: vertical; @@ -125,30 +164,6 @@ Input:focus { width: 100%; } -.rom_rows_container { - layout: vertical; - align-horizontal: center; -} - -.game_card { - width: auto; - height: auto; - max-width: 60; - margin: 1; - padding: 1; - background: #282828; - color: #ed7d3a; - border: solid #3a9bed; - content-align: center middle; - text-style: bold; - text-align: center; -} - -.game_card:hover { - background: #3f3f3f; - color: rgb(236, 194, 169); -} - Static { text-align: center; } @@ -157,47 +172,16 @@ Horizontal { content-align: center middle; } -.game_card:focus { - background: #3f3f3f; - border: solid #ed7d3a; -} - -.game_card:disabled { - background: #282828; - color: #3a9bed; -} - -.game_card_clicked { - background: #ed7d3a; - color: #0e0e0e; -} - -.offset_card { - width: 11; - height: auto; - visibility: hidden; -} - -.rom_row { - layout: horizontal; - align-horizontal: center; - align-vertical: middle; - padding: 1 2; - width: 100%; -} - .info_confirm_row { - layout: horizontal; - width: 100%; height: auto; - padding: 1 2; - align-vertical: top; + padding: 0 0; + margin: 0; } #confirm_button { - width: 20%; - height: auto; - content-align: center middle; + width: 100%; /* Take full width of its container */ + min-width: 20; /* Increase minimum width if needed */ + max-width: 100%; /* Optional: avoid oversizing */ } .confirm_button:hover { @@ -205,21 +189,30 @@ Horizontal { color: #0e0e0e; } -Button:disabled { - background: #444444; - color: #999999; - border: solid #666666; - text-style: dim; -} - .game_info { - padding: 1 2; + padding: 0 1; + margin: 0; width: 100%; - margin-right: 1; color: #ed7d3a; } #game_info_box { width: 80%; height: auto; + max-height: 50vh; /* Adjusted for better visibility */ + padding: 1; + overflow-y: auto; } + +Button.game_button { + min-height: 3; +} + +#confirm_button_container { + width: 20%; + min-width: 20; + align-vertical: middle; + align-horizontal: center; + height: 100%; + padding-left: 1; +} \ No newline at end of file diff --git a/agentm/views/home.py b/agentm/views/home.py index 6b63cab..c6a79a4 100644 --- a/agentm/views/home.py +++ b/agentm/views/home.py @@ -1,6 +1,6 @@ from textual.screen import Screen from textual.widgets import Static, Button -from textual.containers import Vertical, Horizontal, HorizontalScroll +from textual.containers import Vertical, Horizontal, VerticalScroll, Grid from textual.message import Message from textual.reactive import reactive from textual.widget import Widget @@ -8,18 +8,12 @@ from rich.panel import Panel from rich.console import Group from rich.table import Table from rich.rule import Rule -from rich.markup import escape -from PIL import Image, ImageFilter -import os - -from rich_pixels import Pixels -from rich_pixels._renderer import HalfcellRenderer +from rich.text import Text from agentm.utils.logger import log_with_caller from agentm.logic.roms import get_verified_roms, GAME_FILES from agentm.theme.palette import get_theme from agentm.components.footer import AgentMFooter -from agentm.components.game_image_preview import GameImagePreview palette = get_theme() @@ -37,30 +31,19 @@ class ProgressWidget(Widget): return f"[bold {palette.ACCENT}]{self.message}[/]" - -class GameAccordion(Static): - def __init__(self, title: str, rom_file: str, metadata: dict, parent_view): - super().__init__( - id=f"accordion_{rom_file.replace('.', '_').replace('-', '_')}", - classes="game_card" - ) - - self.title = title - self.rom_file = rom_file +class GameCardButton(Button): + def __init__(self, metadata: dict, parent_view): self.metadata = metadata self.parent_view = parent_view + safe_id = metadata["rom_file"].replace(".", "_").replace("-", "_") + label = Text(metadata["title"], style=f"bold {palette.ACCENT}") - self.title_label = Static( - f"[b {palette.ACCENT}]{escape(self.title.upper())}[/]\n", - classes="game_title", - markup=True + super().__init__( + label=label, + id=f"game_btn_{safe_id}", + classes="game_button" ) - - self.image_path = os.path.abspath(self.metadata.get("image_path", "")) - - def compose(self): - yield self.title_label - yield GameImagePreview(image_path=self.image_path) + self.styles.min_height = 3 # Ensures buttons stay visible even in constrained space async def on_click(self): await self.display_info() @@ -68,19 +51,19 @@ class GameAccordion(Static): async def display_info(self): meta = self.metadata - log_with_caller("debug", f"Showing shared info for {self.rom_file}") - table = Table.grid(expand=True) - table.add_column(ratio=1) - table.add_column() - table.add_row("[b]Title:[/b]", meta['title']) - table.add_row("[b]Game ID:[/b]", meta['game_id']) - table.add_row("[b]Difficulty:[/b]", f"{meta.get('difficulty_min')} - {meta.get('difficulty_max')}") - table.add_row("[b]Characters:[/b]", ", ".join(meta.get("characters", []))) - table.add_row("[b]Keywords:[/b]", ", ".join(meta.get("keywords", []))) - table.add_row("[b]SHA256:[/b]", meta["sha256"]) + table = Table.grid(padding=(0, 1)) + table.add_column("Key", style="bold underline", no_wrap=True) + table.add_column("Value", style=palette.ACCENT, overflow="fold") - self.parent_view.shared_info_box.update( + table.add_row("Title", meta["title"]) + table.add_row("Game ID", meta["game_id"]) + table.add_row("Difficulty", f"{meta.get('difficulty_min')} - {meta.get('difficulty_max')}") + table.add_row("Characters", ", ".join(meta.get("characters", []))) + table.add_row("Keywords", ", ".join(meta.get("keywords", []))) + table.add_row("SHA256", meta["sha256"]) + + self.parent_view.shared_info_content.update( Panel(Group(table, Rule(style="dim")), title="Game Info", border_style=palette.BORDER, expand=True) ) self.parent_view.shared_confirm_button.label = f"✅ Confirm {meta['title']}" @@ -88,13 +71,12 @@ class GameAccordion(Static): self.parent_view.selected_game = meta - class HomeView(Screen): BINDINGS = [("escape", "app.quit", "Quit")] - def highlight_selected(self, selected_widget: GameAccordion): - for card in self.rom_scroll_row.children: - if isinstance(card, GameAccordion): + def highlight_selected(self, selected_widget: GameCardButton): + for card in self.rom_grid.children: + if isinstance(card, GameCardButton): card.remove_class("game_card_clicked") selected_widget.add_class("game_card_clicked") @@ -125,7 +107,6 @@ class HomeView(Screen): id="loading_container" ) - # This will be the main container we later modify self.dynamic_container = Vertical(self.loading_container, id="dynamic_content") yield Vertical( @@ -148,7 +129,7 @@ class HomeView(Screen): for idx, rom in enumerate(verified_roms, start=1): self.app.call_from_thread( - lambda title=rom['title'], idx=idx: setattr( + lambda title=rom["title"], idx=idx: setattr( self.progress_text, "message", f"Processing {title} ({idx}/{total})" ) @@ -161,49 +142,56 @@ class HomeView(Screen): async def display_verified_roms(self, verified_roms): log_with_caller("info", f"ROM verification complete. Total: {len(verified_roms)}") - self.shared_info_box = Static( + self.shared_info_content = Static( Panel( - "[dim]Select a Game From Above to Start[/dim]", + "[dim]Select a Game From the Grid Below to Start[/dim]", title="Game Info", border_style=palette.BORDER, expand=True ), - id="game_info_box", - classes="game_info", - expand=True + id="info_panel_static" ) + + self.shared_info_box = VerticalScroll( + self.shared_info_content, + id="game_info_box", + classes="game_info" + ) + self.shared_info_box.styles.height = 7 # Around 5 visible rows + self.shared_confirm_button = Button( "✅ Confirm", id="confirm_button", classes="confirm_button", disabled=True ) - self.rom_scroll_row = HorizontalScroll(id="rom_scroll_row", classes="rom_row") + + self.rom_grid = Grid(id="rom_grid", classes="rom_grid") + self.rom_grid.styles.grid_columns = ["1fr"] * 5 + self.rom_grid.styles.grid_gap = (0, 1) + self.rom_grid.styles.width = "100%" + + rom_grid_scroll = VerticalScroll(self.rom_grid, id="rom_grid_scroll") + rom_grid_scroll.styles.max_height = "30vh" new_content = Vertical( - self.rom_scroll_row, + rom_grid_scroll, Horizontal( self.shared_info_box, - self.shared_confirm_button, + Vertical(self.shared_confirm_button, id="confirm_button_container"), id="info_row", classes="info_confirm_row" - ) + ), + id="game_content_layout", + classes="game_content_layout" ) - # Replace loading content with new UI below logo and welcome dynamic_container = self.query_one("#dynamic_content") await dynamic_container.remove_children() await dynamic_container.mount(new_content) - # Populate games for rom in verified_roms: - await self.rom_scroll_row.mount(GameAccordion( - title=rom["title"], - rom_file=rom["rom_file"], - metadata=rom, - parent_view=self - )) - + await self.rom_grid.mount(GameCardButton(metadata=rom, parent_view=self)) async def on_button_pressed(self, event: Button.Pressed) -> None: if event.button.id == "confirm_button" and self.selected_game: