gsproremote/Recommended kickoff plan.md

5.9 KiB
Raw Permalink Blame History

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)

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)

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)

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:

uvicorn app.main:app --reload --host 0.0.0.0 --port 5005

Frontend (Vite + React + TS)

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.pybackend/app/core/config.py (keep AppConfig + persistence).
  • Move input_ctrl.pybackend/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:

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.