diff --git a/web/src/api-types.ts b/web/src/api-types.ts index 992be15..a90169f 100644 --- a/web/src/api-types.ts +++ b/web/src/api-types.ts @@ -99,23 +99,6 @@ export interface RegisterResponse { } // Evolution dashboard types (re-exported from types.ts for convenience) -import type { - LiveJSON, - EvolutionIslandStat, - EvolutionParentInfo, - EvolutionStageResult, - EvolutionValidationStatus, - EvaluationMatchResult, - EvolutionEvaluationStatus, - EvolutionCandidate, - EvolutionCycleInfo, - EvolutionActivityEntry, - EvolutionTotals, - EvolutionGenerationEntry, - EvolutionLineageNode, - EvolutionMetaSnapshot, -} from './types'; - export type { LiveJSON, EvolutionIslandStat as IslandStat, @@ -134,6 +117,7 @@ export type { } from './types'; // Convenience alias: the full live.json document +import type { LiveJSON } from './types'; export type EvolutionLiveData = LiveJSON; // Full island stat (legacy format, not in live.json schema) diff --git a/web/src/components/annotation.ts b/web/src/components/annotation.ts index 37d1d8a..eb32472 100644 --- a/web/src/components/annotation.ts +++ b/web/src/components/annotation.ts @@ -93,7 +93,7 @@ export class AnnotationOverlay { private annotations: Annotation[] = []; private currentTurn: number = 0; private totalTurns: number = 0; - private _matchId: string = ''; + private matchId: string = ''; private options: AnnotationOverlayOptions; constructor(container: HTMLElement, options: AnnotationOverlayOptions = {}) { @@ -104,7 +104,7 @@ export class AnnotationOverlay { loadAnnotations(matchId: string, annotations: Annotation[], totalTurns: number): void { this.matchId = matchId; this.totalTurns = totalTurns; - this.annotations = annotations.filter(a => a.match_id === matchId); + this.annotations = annotations.filter(a => a.match_id === this.matchId); this.render(); } diff --git a/web/src/pages/feedback.ts b/web/src/pages/feedback.ts index 54ef3eb..8dfce0c 100644 --- a/web/src/pages/feedback.ts +++ b/web/src/pages/feedback.ts @@ -1,19 +1,19 @@ // Community replay feedback: users annotate replay turns with tags. // Annotations feed the evolution pipeline by surfacing interesting moments. +// Types consolidated to match plan §8.3 replay_feedback schema. import { fetchMatchIndex, API_BASE, type MatchSummary } from '../api-types'; import { ReplayViewer } from '../replay-viewer'; import type { Replay } from '../types'; // ─── Types ──────────────────────────────────────────────────────────────────── +// Aligned with plan §8.3: insight, mistake, idea, highlight export const ANNOTATION_TAGS = [ - { id: 'turning_point', label: 'Turning Point', color: '#ef4444', desc: 'A moment that decisively changed the outcome' }, - { id: 'tactical_insight', label: 'Tactical Insight', color: '#3b82f6', desc: 'A clever or instructive strategy in action' }, - { id: 'impressive', label: 'Impressive', color: '#a78bfa', desc: 'Exceptional performance or execution' }, - { id: 'funny', label: 'Funny', color: '#f59e0b', desc: 'An unexpected or humorous sequence' }, - { id: 'bug', label: 'Possible Bug', color: '#f97316', desc: 'Behaviour that looks unintended' }, - { id: 'evolution_seed', label: 'Evolution Seed', color: '#22c55e', desc: 'A sequence worth propagating in the evolution pipeline' }, + { id: 'insight', label: 'Tactical Insight', color: '#3b82f6', desc: 'A clever or instructive strategy in action' }, + { id: 'mistake', label: 'Mistake Spotted', color: '#ef4444', desc: 'Behaviour that looks unintended or suboptimal' }, + { id: 'idea', label: 'Strategy Idea', color: '#22c55e', desc: 'A novel approach worth propagating in the evolution pipeline' }, + { id: 'highlight', label: 'Highlight', color: '#fbbf24', desc: 'An impressive or noteworthy moment' }, ]; export interface ReplayAnnotation { @@ -415,13 +415,13 @@ function initFeedback(): void { // ─── Local storage for offline annotations ──────────────────────────────────── -const LS_KEY = 'acb_annotations'; +const LS_KEY = 'acb_annotations_v2'; function saveLocalAnnotation(ann: ReplayAnnotation): void { try { const existing: ReplayAnnotation[] = JSON.parse(localStorage.getItem(LS_KEY) ?? '[]'); existing.push(ann); - localStorage.setItem(LS_KEY, JSON.stringify(existing.slice(-200))); // keep last 200 + localStorage.setItem(LS_KEY, JSON.stringify(existing.slice(-200))); } catch {} } diff --git a/web/src/pages/replay.ts b/web/src/pages/replay.ts index 81f3993..30b789b 100644 --- a/web/src/pages/replay.ts +++ b/web/src/pages/replay.ts @@ -1,6 +1,14 @@ // Standalone replay viewer page - lazy loaded from app.ts -import type { Replay, GameEvent, DebugInfo } from '../types'; +import type { Replay, GameEvent, DebugInfo, Position } from '../types'; import { fetchCommentary } from '../api-types'; +import { + AnnotationOverlay, + createAnnotationForm, + fetchFeedback, + loadLocalAnnotations, + ANNOTATION_OVERLAY_STYLES, + type Annotation, +} from '../components/annotation'; const loadReplayViewer = () => import('../replay-viewer'); @@ -170,6 +178,12 @@ function initReplayViewerWithClass(ReplayViewerClass: any, initialUrl?: string): +
+

Annotations

+
+
+
+
Space Play/Pause Step @@ -347,7 +361,6 @@ function initReplayViewer(ReplayViewerClass: any, initialUrl?: string): void { const commentaryToggle = document.getElementById('commentary-toggle') as HTMLButtonElement; const debugPanel = document.getElementById('debug-panel') as HTMLDivElement; const debugPanelToggleBtn = document.getElementById('debug-panel-toggle-btn') as HTMLDivElement; - const debugPanelChevron = document.getElementById('debug-panel-chevron') as HTMLSpanElement; const debugPlayerToggles = document.getElementById('debug-player-toggles') as HTMLDivElement; const debugInfoDisplay = document.getElementById('debug-info-display') as HTMLDivElement;