feat(replay): wire AnnotationOverlay to EventTimeline with annotation badges per §16.8

Sync annotations to both the canvas renderer (spatial markers) and the
event timeline (colored badge markers) so user feedback appears in both
the replay canvas and the timeline ribbon.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jedarden 2026-04-21 18:07:44 -04:00
parent d3f2068f8b
commit 80733f673e

View file

@ -652,15 +652,42 @@ function initReplayViewer(ReplayViewerClass: any, initialUrl?: string): void {
// ── Annotation overlay integration (§16.8) ──────────────────────────────────────
let annotationOverlay: AnnotationOverlay | null = null;
let eventTimeline: EventTimeline | null = null;
let allAnnotations: Annotation[] = [];
let clickedGridPosition: Position | undefined;
let canvasAnnotationHint: HTMLDivElement | null = null;
function syncAnnotationsToViewer(): void {
// Push annotations to the canvas renderer for marker drawing
viewer.setAnnotations(allAnnotations);
// Push annotations to the event timeline for badge rendering
eventTimeline?.setAnnotations(allAnnotations);
}
function initAnnotations(replay: Replay): void {
const overlayContainer = document.getElementById('annotation-overlay-container');
const formContainer = document.getElementById('annotation-form-container');
if (!overlayContainer || !formContainer) return;
// Initialize EventTimeline (desktop)
const timelineContainer = document.getElementById('event-timeline-container');
if (timelineContainer) {
eventTimeline = new EventTimeline(timelineContainer, {
onTurnClick: (turn: number) => {
viewer.setTurn(turn);
updateUI();
updateEventLog();
},
});
// Extract events from replay turns and feed to timeline
const timelineTurns = replay.turns.map((t: any, i: number) => ({
turn: i,
events: t.events ?? [],
}));
eventTimeline.setEvents(timelineTurns);
timelineContainer.style.display = '';
}
annotationOverlay = new AnnotationOverlay(overlayContainer, {
onTurnClick: (turn: number) => {
viewer.setTurn(turn);
@ -675,9 +702,11 @@ function initReplayViewer(ReplayViewerClass: any, initialUrl?: string): void {
fetchFeedback(replay.match_id).then(remote => {
allAnnotations = [...remote, ...local];
annotationOverlay?.loadAnnotations(replay.match_id, allAnnotations, replay.turns.length);
syncAnnotationsToViewer();
}).catch(() => {
allAnnotations = local;
annotationOverlay?.loadAnnotations(replay.match_id, allAnnotations, replay.turns.length);
syncAnnotationsToViewer();
});
// Create the annotation form
@ -688,6 +717,7 @@ function initReplayViewer(ReplayViewerClass: any, initialUrl?: string): void {
const ann = e.detail as Annotation;
allAnnotations.push(ann);
annotationOverlay?.addAnnotation(ann);
syncAnnotationsToViewer();
clickedGridPosition = undefined;
if (canvasAnnotationHint) canvasAnnotationHint.classList.remove('visible');
}) as EventListener);
@ -709,6 +739,9 @@ function initReplayViewer(ReplayViewerClass: any, initialUrl?: string): void {
if (annotationOverlay) {
annotationOverlay.setCurrentTurn(viewer.getTurn());
}
if (eventTimeline) {
eventTimeline.setCurrentTurn(viewer.getTurn());
}
}
// Handle canvas clicks for spatial annotation position