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
|
|
|
"""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
|
|
|
|
|
"""
|
2026-02-15 15:52:49 -07:00
|
|
|
|
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
from birdcage_tui.app import BirdcageApp
|
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
|
|
|
from birdcage_tui.screens.control import ControlScreen
|
|
|
|
|
from birdcage_tui.widgets.craft_panel import CraftPanel
|
2026-02-15 15:52:49 -07:00
|
|
|
|
|
|
|
|
OUT_DIR = "/home/rpm/claude/ham/satellite/winegard-travler/site/public/screenshots"
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
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():
|
2026-02-15 15:52:49 -07:00
|
|
|
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}")
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
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}/")
|
|
|
|
|
|
|
|
|
|
|
2026-02-15 15:52:49 -07:00
|
|
|
asyncio.run(main())
|