Initial commit: GSPro Remote MVP - Phase 1 complete

This commit is contained in:
Ryan Hill 2025-11-13 15:38:58 -06:00
commit 74ca4b38eb
50 changed files with 12818 additions and 0 deletions

191
Recommended kickoff plan.md Normal file
View file

@ -0,0 +1,191 @@
# Recommended kickoff plan
## 0) Toolchain (lock these down)
* **Python** 3.11 (for wheels availability + perf)
* **Node** 20.19+ and **npm** 10+
* **UV** for deps
* **Playwright** (later for UI smoke tests)
* **Git** repo from day 1
## 1) Make a tiny, opinionated repo scaffold
Create a new repo (you can copy/paste this tree into your README so the LLM follows it):
```
gspro-remote/
backend/
app/
__init__.py
main.py # FastAPI app factory + router mounts
api/
__init__.py
actions.py # /api/actions/*
vision.py # /api/vision/* (keep but V2-gated)
core/
config.py # AppConfig + load/save
input_ctrl.py # pydirectinput helpers
screen.py # mss capture utils (non-vision bits)
pkg.json # (optional) for uvicorn dev script via npm - or just use Makefile
pyproject.toml
README.md
frontend/
src/
main.tsx
App.tsx
pages/DynamicGolfUI.tsx
components/
AimPad.tsx
StatBar.tsx
MapPanel.tsx
styles/
index.html
package.json
vite.config.ts
README.md
scripts/
dev.ps1 # start both servers on Windows
.editorconfig
.gitignore
README.md
```
## 2) Bootstrap bare bones projects
### Backend (FastAPI)
```bash
cd backend
uv venv && uv pip install fastapi uvicorn pydantic-settings pydirectinput pywin32 mss opencv-python pillow zeroconf
# Or with pip:
# python -m venv .venv && .venv\Scripts\activate && pip install fastapi uvicorn pydirectinput pywin32 mss opencv-python pillow zeroconf
```
`app/main.py` (minimal)
```python
from fastapi import FastAPI
from .api.actions import router as actions_router
def create_app() -> FastAPI:
app = FastAPI(title="GSPro Remote", version="0.1.0")
app.include_router(actions_router, prefix="/api/actions", tags=["actions"])
return app
app = create_app()
```
`app/api/actions.py` (just enough to prove the vertical slice)
```python
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from ..core.input_ctrl import press_keys, focus_window
router = APIRouter()
class KeyReq(BaseModel):
keys: str
@router.post("/key")
def post_key(req: KeyReq):
if not focus_window("GSPro"):
raise HTTPException(409, "GSPro window not found/active")
press_keys(req.keys)
return {"ok": True}
```
Run:
```bash
uvicorn app.main:app --reload --host 0.0.0.0 --port 5005
```
### Frontend (Vite + React + TS)
```bash
cd ../frontend
npm create vite@latest . -- --template react-ts
npm i
npm run dev
```
Add a `.env` in frontend later if you want a configurable API base URL.
## 3) Migrate your existing code **incrementally**
You already have solid pieces. Bring them in slice by slice:
* Move `config.py``backend/app/core/config.py` (keep AppConfig + persistence).
* Move `input_ctrl.py``backend/app/core/input_ctrl.py` (unchanged).
* Create `backend/app/api/vision.py` and paste **only** the streaming endpoint youll use first (WebSocket or SSE). Keep OCR endpoints behind a `VISION_ENABLED` flag for V2.
* Defer `screen_watch.py`, `capture.py`, `streaming.py` until the “MapPanel” slice (below). Its okay if V1 ships with **no** OCR.
## 4) Implement one **vertical slice** end-to-end (MVP proof)
Start with the **Aim Pad + Mulligan** because it touches everything:
* Frontend:
* `components/AimPad.tsx`: buttons call `POST /api/actions/key` with `"left"`, `"right"`, `"up"`, `"down"`, and `"a"` (Reset).
* Add Mulligan button calling `"ctrl+m"`.
* Backend:
* The `post_key` route already exists.
* Make sure `focus_window("GSPro")` works on your machine.
* Test on Windows: you should see GSPro react from the tablet/phone.
Now you have a working remote!
## 5) Add the **MapPanel** as the second slice
* Backend:
* Introduce a simple `/api/vision/ws/stream` that returns a **downscaled JPEG** buffer of a fixed region; reuse your `mss` capture and JPEG base64 helpers. Keep the code minimal.
* Frontend:
* `components/MapPanel.tsx` opens a WS to `/api/vision/ws/stream`, paints frames onto a `<canvas>`, supports expand/collapse (no click mapping yet).
* Aim for **720p @ 30fps, \~7585 JPEG quality**. Measure latency, then optimize resize **before** JPEG encode.
## 6) Wire the rest of the “Essentials”
* Tee left/right (`c`/`v`)
* Scorecard (`t`) and Range finder (`r`) as secondary buttons under a kebab menu
* Stat row at bottom (hard-coded 0.0° up/right for now—wire later)
## 7) Keep OCR as **V2** but leave hooks
* Add a feature flag `VISION_ENABLED = False` in `app/core/config.py`.
* Keep `vision.py` imported but routes gated: if disabled, return 404 with a friendly message.
* This lets you merge OCR work later without reshaping the app.
## 8) Dev ergonomics on Windows
Create `scripts/dev.ps1` to run both servers:
```powershell
Start-Job { Set-Location backend; uvicorn app.main:app --reload --port 5005 }
Start-Sleep -Seconds 1
Start-Process powershell -ArgumentList 'cd frontend; npm run dev'
```
## 9) Packaging (when MVP is stable)
* **Backend**: PyInstaller or Nuitka into a single EXE, then wrap with **Inno Setup** to produce an installer that:
* Installs the EXE + config files to `C:\ProgramData\GSPro Remote\`
* Creates a Start Menu entry and an **auto-start shortcut** for the Windows user
* Opens firewall rule for your port
* **Frontend**: `npm run build` → copy `dist/` into `backend/ui/` and serve using FastAPI `StaticFiles`. (You already had `build-ui.py`; keep that idea.)
## 10) How to use the LLM effectively (so it helps, not hurts)
* **Give it the scaffold** and the **PRD** (and the Section-12 matrix).
* Ask for **one file at a time**: “Implement `components/AimPad.tsx` against `/api/actions/key`” with explicit props and return types.
* Paste back **real compiler/server errors** verbatim; ask it to fix **only those**.
* Freeze the public contracts early (API request/response shapes). LLMs drift if the interface is fuzzy.
---