birdcage/tui/scripts/take_screenshots.py
Ryan Malloy 3013eeee4c Add DemoCraftClient for complete offline demo mode
- DemoCraftClient in demo.py: duck-typed CraftClient replacement with
  8 canned satellites, synthetic pass predictions, and time-varying LEO
  arcs that drive real AOS/TCA/LOS pass events to the camera overlay
- Fix sky map EL signal fidelity: snap _el to _target_el at sweep start
  so 2D scans compute correct per-row RSSI (was using stale elevation)
- Branch on demo_mode in app.py _setup_craft_client() to inject
  DemoCraftClient instead of the HTTP CraftClient
- Add test_demo_craft.py: 5 tests exercising search, passes, tracking,
  and WAITING state through the full TUI without mocks
- Update take_screenshots.py to cover all 8 screens (dashboard, control,
  craft search, craft tracking, signal, system, console, camera)
2026-02-16 11:49:39 -07:00

147 lines
4.1 KiB
Python

"""Take screenshots of all TUI screens in demo mode for documentation.
Generates SVG snapshots of every screen and sub-mode including overlays.
All data comes from DemoDevice and DemoCraftClient — no hardware or
network required.
Usage:
cd tui && uv run python scripts/take_screenshots.py
"""
import asyncio
from birdcage_tui.app import BirdcageApp
from birdcage_tui.screens.control import ControlScreen
from birdcage_tui.widgets.craft_panel import CraftPanel
OUT_DIR = "/home/rpm/claude/ham/satellite/winegard-travler/site/public/screenshots"
async def _screenshot_basic_tabs():
"""F1 Dashboard, F2 Control (manual), F3 Signal, F4 System."""
tabs = {
"f1": "dashboard",
"f2": "control",
"f3": "signal",
"f4": "system",
}
for key, name in tabs.items():
app = BirdcageApp()
app.demo_mode = True
async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await pilot.press(key)
await pilot.pause()
await asyncio.sleep(1.5)
path = f"{OUT_DIR}/tui-{name}.svg"
app.save_screenshot(path)
print(f"Saved {path}")
async def _screenshot_craft_search():
"""F2 Control > Craft sub-mode with search results."""
app = BirdcageApp()
app.demo_mode = True
async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await pilot.press("f2")
await pilot.pause()
control = app.query_one("#control", ControlScreen)
control.switch_mode("craft")
await pilot.pause()
# Trigger a search — uses DemoCraftClient
panel = app.query_one("#ctrl-craft-panel", CraftPanel)
panel.post_message(CraftPanel.SearchRequested(""))
await asyncio.sleep(1.0)
path = f"{OUT_DIR}/tui-craft-search.svg"
app.save_screenshot(path)
print(f"Saved {path}")
async def _screenshot_craft_tracking():
"""F2 Control > Craft sub-mode actively tracking the Moon."""
app = BirdcageApp()
app.demo_mode = True
async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await pilot.press("f2")
await pilot.pause()
control = app.query_one("#control", ControlScreen)
control.switch_mode("craft")
await pilot.pause()
# Start tracking the Moon (always above horizon in demo)
panel = app.query_one("#ctrl-craft-panel", CraftPanel)
panel.post_message(
CraftPanel.TrackRequested(
target_type="celestial",
target_id="moon",
name="Moon",
min_el=5.0,
)
)
await asyncio.sleep(2.5)
path = f"{OUT_DIR}/tui-craft-tracking.svg"
app.save_screenshot(path)
print(f"Saved {path}")
control._stop_craft_tracking()
await pilot.pause()
async def _screenshot_console():
"""F5 Console overlay."""
app = BirdcageApp()
app.demo_mode = True
async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await asyncio.sleep(1.0)
await pilot.press("f5")
await pilot.pause()
await asyncio.sleep(0.5)
path = f"{OUT_DIR}/tui-console.svg"
app.save_screenshot(path)
print(f"Saved {path}")
async def _screenshot_camera():
"""F6 Camera overlay."""
app = BirdcageApp()
app.demo_mode = True
async with app.run_test(size=(120, 40)) as pilot:
await pilot.pause()
await asyncio.sleep(1.0)
await pilot.press("f6")
await pilot.pause()
await asyncio.sleep(0.5)
path = f"{OUT_DIR}/tui-camera.svg"
app.save_screenshot(path)
print(f"Saved {path}")
async def main():
await _screenshot_basic_tabs()
await _screenshot_craft_search()
await _screenshot_craft_tracking()
await _screenshot_console()
await _screenshot_camera()
print(f"\nAll screenshots saved to {OUT_DIR}/")
asyncio.run(main())