Add Command Center web UI (no build step)

Static HTML/CSS/JS served by FastAPI (mounted at /ui, / redirects there),
talking to the existing JSON API — no node/npm, no bundler.

- games/versions sidebar with A/B version selectors
- visual 4-axis diff (types/methods/events/fields, +/- struct_layout) with
  +/-/~ rows, per-axis counts, class (owner) filter, moved-methods section
- single-snapshot browser (tabs + live filter)
- app.py mounts StaticFiles(html=True) last so API routes win; / -> /ui/

Smoke-tested live on uvicorn: /, /ui/ and assets serve 200; UI wiring drives
the same /games and /diff endpoints verified end-to-end. app.js passes
`node --check`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Patryk Gensch
2026-05-30 23:37:03 +02:00
parent bd03c56e98
commit 4542763936
5 changed files with 352 additions and 0 deletions

View File

@@ -7,12 +7,18 @@ Run with uvicorn's factory mode (no import-time DB side effects):
from __future__ import annotations
from pathlib import Path
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from .. import __version__
from .db import configure, init_db
from .routes import diff, games, snapshots
_STATIC = Path(__file__).parent / "static"
def create_app(database_url: str | None = None) -> FastAPI:
configure(database_url)
@@ -27,4 +33,11 @@ def create_app(database_url: str | None = None) -> FastAPI:
def health() -> dict[str, str]:
return {"status": "ok", "version": __version__}
@app.get("/", include_in_schema=False)
def root() -> RedirectResponse:
return RedirectResponse("/ui/")
# The command-center UI (static HTML/CSS/JS, no build step) is served last so API
# routes take precedence.
app.mount("/ui", StaticFiles(directory=str(_STATIC), html=True), name="ui")
return app