import { useEffect, useMemo, useRef } from "react"; import { eventIcon, severityClass } from "../eventDecor"; import type { CountrySnapshot, EntitySnapshot, GameLogEventSnapshot } from "../types"; type EventFeedPanelProps = { events: GameLogEventSnapshot[]; countries?: CountrySnapshot[]; entities?: EntitySnapshot[]; }; export function EventFeedPanel({ events, countries = [], entities = [] }: EventFeedPanelProps) { const listRef = useRef(null); const isPinnedToBottomRef = useRef(true); // Resolve a speaker's nation colour (matching the scoreboard: Nemotron blue, // Qwen purple) so chronicle speech lines are tinted by who is talking. const colorByCountry = useMemo( () => new Map(countries.map((country) => [country.id, country.color])), [countries], ); const countryByActor = useMemo( () => new Map(entities.map((entity) => [entity.id, entity.country_id ?? null])), [entities], ); const speechColor = (event: GameLogEventSnapshot): string | undefined => { if (event.type !== "speech" || !event.actor_id) { return undefined; } // Fall back to the id prefix when the speaker has since died/left the snapshot. let countryId = countryByActor.get(event.actor_id) ?? null; if (!countryId) { if (event.actor_id.startsWith("qwen")) countryId = "qwen"; else if (event.actor_id.startsWith("npc")) countryId = "nemotron"; } return countryId ? colorByCountry.get(countryId) : undefined; }; useEffect(() => { const list = listRef.current; if (list && isPinnedToBottomRef.current) { list.scrollTop = list.scrollHeight; } }, [events]); return ( ); }