Add automated TUI screenshot capture script
Textual Pilot-based script that launches demo mode, navigates all screens, and exports SVG + PNG via rsvg-convert.
This commit is contained in:
parent
f8bfd69ceb
commit
b0aee4e5a6
160
tui/scripts/capture_screenshots.py
Normal file
160
tui/scripts/capture_screenshots.py
Normal file
@ -0,0 +1,160 @@
|
||||
"""Capture all TUI screenshots for documentation.
|
||||
|
||||
Runs the Birdcage TUI in demo mode using Textual's Pilot API,
|
||||
navigates to each screen/sub-mode, and exports SVG + PNG screenshots.
|
||||
|
||||
Usage:
|
||||
cd tui && uv run python scripts/capture_screenshots.py
|
||||
|
||||
Output goes to ../site/public/screenshots/
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
|
||||
|
||||
from birdcage_tui.app import BirdcageApp
|
||||
|
||||
OUTPUT_DIR = (
|
||||
Path(__file__).resolve().parent.parent.parent / "site" / "public" / "screenshots"
|
||||
)
|
||||
TERMINAL_SIZE = (120, 42)
|
||||
|
||||
# Demo search results for craft-search screenshot
|
||||
DEMO_SEARCH_RESULTS = [
|
||||
{
|
||||
"name": "ISS (ZARYA)",
|
||||
"target_type": "satellite",
|
||||
"target_id": "25544",
|
||||
"groups": ["stations"],
|
||||
},
|
||||
{
|
||||
"name": "NOAA 19",
|
||||
"target_type": "satellite",
|
||||
"target_id": "33591",
|
||||
"groups": ["weather"],
|
||||
},
|
||||
{
|
||||
"name": "SO-50 (SAUDISAT 1C)",
|
||||
"target_type": "satellite",
|
||||
"target_id": "27607",
|
||||
"groups": ["amateur"],
|
||||
},
|
||||
{
|
||||
"name": "TEVEL-2",
|
||||
"target_type": "satellite",
|
||||
"target_id": "50988",
|
||||
"groups": ["amateur"],
|
||||
},
|
||||
{
|
||||
"name": "AO-91 (FOX-1B)",
|
||||
"target_type": "satellite",
|
||||
"target_id": "43017",
|
||||
"groups": ["amateur"],
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def save(name: str, svg: str) -> None:
|
||||
"""Write SVG and convert to PNG via rsvg-convert."""
|
||||
svg_path = OUTPUT_DIR / f"{name}.svg"
|
||||
png_path = OUTPUT_DIR / f"{name}.png"
|
||||
svg_path.write_text(svg)
|
||||
print(f" {svg_path.name}")
|
||||
try:
|
||||
subprocess.run(
|
||||
["rsvg-convert", "-o", str(png_path), str(svg_path)],
|
||||
check=True,
|
||||
capture_output=True,
|
||||
)
|
||||
print(f" {png_path.name}")
|
||||
except FileNotFoundError:
|
||||
print(" WARNING: rsvg-convert not found, skipping PNG")
|
||||
|
||||
|
||||
async def capture_all() -> None:
|
||||
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
app = BirdcageApp()
|
||||
app.demo_mode = True
|
||||
|
||||
async with app.run_test(size=TERMINAL_SIZE) as pilot:
|
||||
await pilot.pause(1.0)
|
||||
|
||||
# ---- F1 Dashboard ----
|
||||
print("F1 Dashboard")
|
||||
await pilot.press("f1")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-dashboard", app.export_screenshot())
|
||||
|
||||
# ---- F2 Control (Manual mode) ----
|
||||
print("F2 Control")
|
||||
await pilot.press("f2")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-control", app.export_screenshot())
|
||||
|
||||
# ---- F2 Control > Craft (search results) ----
|
||||
print("F2 Control > Craft (search)")
|
||||
craft_mode_btn = app.query_one("#mode-craft")
|
||||
craft_mode_btn.press()
|
||||
await pilot.pause(0.5)
|
||||
|
||||
# Populate search results directly via widget API
|
||||
craft_panel = app.query_one("#ctrl-craft-panel")
|
||||
craft_panel.set_search_results(DEMO_SEARCH_RESULTS)
|
||||
await pilot.pause(0.3)
|
||||
save("tui-craft-search", app.export_screenshot())
|
||||
|
||||
# ---- F2 Control > Craft (tracking Moon) ----
|
||||
print("F2 Control > Craft (tracking)")
|
||||
# Clear results and set tracking state
|
||||
craft_panel.set_search_results([])
|
||||
craft_panel.set_tracking_status(
|
||||
state="TRACKING",
|
||||
target_name="Moon",
|
||||
azimuth=145.00,
|
||||
elevation=32.01,
|
||||
distance_km=384400,
|
||||
range_rate=-0.3,
|
||||
moves=47,
|
||||
rate=1.0,
|
||||
)
|
||||
await pilot.pause(0.3)
|
||||
save("tui-craft-tracking", app.export_screenshot())
|
||||
|
||||
# ---- F3 Signal (Monitor mode) ----
|
||||
print("F3 Signal")
|
||||
await pilot.press("f3")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-signal", app.export_screenshot())
|
||||
|
||||
# ---- F4 System ----
|
||||
print("F4 System")
|
||||
await pilot.press("f4")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-system", app.export_screenshot())
|
||||
|
||||
# ---- F5 Console overlay ----
|
||||
print("F5 Console")
|
||||
await pilot.press("f5")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-console", app.export_screenshot())
|
||||
await pilot.press("f5")
|
||||
await pilot.pause(0.3)
|
||||
|
||||
# ---- F6 Camera overlay ----
|
||||
print("F6 Camera")
|
||||
await pilot.press("f6")
|
||||
await pilot.pause(0.5)
|
||||
save("tui-camera", app.export_screenshot())
|
||||
await pilot.press("f6")
|
||||
await pilot.pause(0.3)
|
||||
|
||||
print("\nDone!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(capture_all())
|
||||
Loading…
x
Reference in New Issue
Block a user