Compare commits

...

5 commits

Author SHA1 Message Date
fd43a6165a Merge pull request 'frontend: Ghost-in-the-Shell overhaul — map-first landing, event blips, tickers' (#1) from frontend-overhaul into master
Some checks failed
build-and-deploy / build (push) Failing after 1m5s
Reviewed-on: #1
2026-08-27 17:48:58 -04:00
Sirius DevOps
2a433a41e9 map: port master popup-guard fix — real popup state via events, close on zoom/drag
Master's b2369bc fixed cameras vanishing after opening a popup and zooming:
Leaflet 1.9.4's Map.closePopup() never nulls map._popup, so the old
'if (map._popup) return' guard skipped every overlay reload forever after
the first popup. Port the fix into the redesigned frontend: track popup
state via popupopen/popupclose, close on user zoom/drag so moveend reloads
always run, and keep the autopan skip for the popup's own pan.
2026-08-27 17:48:39 -04:00
Sirius DevOps
8560884bbd Merge remote-tracking branch 'forgejo/master' into frontend-overhaul
# Conflicts:
#	app/static/index.html
2026-08-27 17:47:31 -04:00
Sirius DevOps
51e906c41f Merge remote-tracking branch 'forgejo/master' into frontend-overhaul
# Conflicts:
#	app/static/index.html
2026-08-27 17:33:12 -04:00
Sirius DevOps
12f01a17a9 frontend: Ghost-in-the-Shell overhaul — map-first landing, HUD chrome, event blips, tickers
- Map is now the landing view: full-viewport NASA GIBS globe, existing
  fires/cameras/HLS map machinery preserved verbatim
- New 'Event Blips' layer: geolocated ingest events, color-coded by source,
  bbox/since/has_coords filters added to GET /api/events
- Weather / Flights(ADS-B) / Vessels(AIS) layer slots reserved (feed pending)
- Market ticker strip with configurable symbols — auto-promotes to LIVE when
  GET /api/market returns {symbols:[...]} (contract documented in Settings)
- Breaking-news ticker: LLM exec-summary flash + headline marquee, 15-min cycle
- Dropdown nav (Map/News/Events/Alerts/Entities/Ingest/API Keys/Settings),
  Settings view (localStorage: symbols, map layer defaults), System panel
- Section-9 theme: near-black navy, cyan/magenta accents, Orbitron/Rajdhani/
  Share Tech Mono, chamfered HUD panels, scanlines, boot splash, UTC clock
- Fix: events.camera enum value missing from models.py/schemas.py caused 500s
  on every events query once camera events flowed in (migration 004 added it
  to the DB enum only)
2026-08-27 17:22:14 -04:00
4 changed files with 1410 additions and 496 deletions

View file

@ -189,6 +189,21 @@ async def update_source(source_id: UUID, payload: dict):
@app.get("/api/events", response_model=list[EventOut]) @app.get("/api/events", response_model=list[EventOut])
async def list_events( async def list_events(
source_type: SourceType | None = Query(None), source_type: SourceType | None = Query(None),
bbox: str | None = Query(
None,
description="Comma-separated 'minlon,minlat,maxlon,maxlat' to bound the "
"result set by event coordinates. Omit for all stored events.",
),
since: datetime | None = Query(
None,
description="Only events ingested at/after this UTC instant "
"(ISO 8601, e.g. '2026-08-24T12:00:00Z').",
),
has_coords: bool = Query(
False,
description="Only events that carry a location (location_lat/lon set). "
"Used by the map's event-blips layer.",
),
limit: int = Query(50, ge=1, le=500), limit: int = Query(50, ge=1, le=500),
offset: int = Query(0, ge=0), offset: int = Query(0, ge=0),
): ):
@ -197,6 +212,28 @@ async def list_events(
stmt = select(events).order_by(events.c.ingested_at.desc()) stmt = select(events).order_by(events.c.ingested_at.desc())
if source_type: if source_type:
stmt = stmt.where(events.c.source_type == source_type.value) stmt = stmt.where(events.c.source_type == source_type.value)
if since:
stmt = stmt.where(events.c.ingested_at >= since)
if has_coords:
stmt = stmt.where(events.c.location_lat.isnot(None))
if bbox:
parts = [p.strip() for p in bbox.split(",")]
if len(parts) != 4:
raise HTTPException(
422, "bbox must be 'minlon,minlat,maxlon,maxlat' (4 comma-separated values)"
)
try:
minlon, minlat, maxlon, maxlat = (float(p) for p in parts)
except ValueError:
raise HTTPException(
422, "bbox values must be floats: 'minlon,minlat,maxlon,maxlat'"
)
stmt = stmt.where(
and_(
events.c.location_lon >= minlon, events.c.location_lon <= maxlon,
events.c.location_lat >= minlat, events.c.location_lat <= maxlat,
)
)
stmt = stmt.limit(limit).offset(offset) stmt = stmt.limit(limit).offset(offset)
rows = (await session.execute(stmt)).mappings().all() rows = (await session.execute(stmt)).mappings().all()
return [event_to_out(r) for r in rows] return [event_to_out(r) for r in rows]

View file

@ -39,7 +39,7 @@ events = Table(
Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4), Column("id", UUID(as_uuid=True), primary_key=True, default=uuid4),
Column("source_type", Enum( Column("source_type", Enum(
"rss", "gdel-t2", "social", "earthquake", "disaster", "rss", "gdel-t2", "social", "earthquake", "disaster",
"weather", "fire", "satellite", name="event_source_type" "weather", "fire", "satellite", "camera", name="event_source_type"
), nullable=False, index=True), ), nullable=False, index=True),
Column("source_id", UUID(as_uuid=True)), Column("source_id", UUID(as_uuid=True)),
Column("title", Text), Column("title", Text),

View file

@ -21,6 +21,7 @@ class SourceType(str, Enum):
weather = "weather" weather = "weather"
fire = "fire" fire = "fire"
satellite = "satellite" satellite = "satellite"
camera = "camera"
class EntityKind(str, Enum): class EntityKind(str, Enum):

File diff suppressed because it is too large Load diff