feat: NCWF-5/6/7/8/9 chess analysis page and engine integration

- NCWF-5: scaffold /analysis route with ChessBoard viewer and navigation
- NCWF-6: FEN / PGN / Game-ID input form with depth selector
- NCWF-7: extend GameApiService with analyzePosition(); add AnalysisService
  with game-wide annotation pipeline; proxy /api/analysis -> :8087
- NCWF-8: EvalTimelineComponent — SVG win-chance chart per ply
- NCWF-9: AnnotatedMoveListComponent — quality labels (!! ! ?! ? ??)
  derived from win-chance delta

Also fix pre-existing app.spec.ts failure (missing provideHttpClient).
Apply project-wide prettier formatting pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janis Eccarius
2026-06-10 15:55:14 +02:00
parent ae952d70b0
commit 8e2afb93f3
115 changed files with 6134 additions and 2378 deletions
+23 -11
View File
@@ -11,7 +11,7 @@ import { Bot, BotWithToken } from '../../models/bot.models';
standalone: true,
imports: [CommonModule, RouterLink, FormsModule],
templateUrl: './bots.component.html',
styleUrl: './bots.component.css'
styleUrl: './bots.component.css',
})
export class BotsComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
@@ -36,11 +36,17 @@ export class BotsComponent implements OnInit {
loadBots(): void {
this.loading = true;
this.botService.list()
this.botService
.list()
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: bots => { this.bots = bots; this.loading = false; },
error: () => { this.loading = false; }
next: (bots) => {
this.bots = bots;
this.loading = false;
},
error: () => {
this.loading = false;
},
});
}
@@ -66,10 +72,10 @@ export class BotsComponent implements OnInit {
this.bots = [bot, ...this.bots];
this.revealedTokens[bot.id] = bot.token;
},
error: err => {
error: (err) => {
this.creating = false;
this.createError = err.error?.message ?? err.error?.error ?? 'Failed to create bot.';
}
},
});
}
@@ -80,11 +86,13 @@ export class BotsComponent implements OnInit {
}
this.revealingId = botId;
this.botService.rotateToken(botId).subscribe({
next: token => {
next: (token) => {
this.revealingId = null;
this.revealedTokens[botId] = token;
},
error: () => { this.revealingId = null; }
error: () => {
this.revealingId = null;
},
});
}
@@ -93,7 +101,9 @@ export class BotsComponent implements OnInit {
if (!token) return;
navigator.clipboard.writeText(token).then(() => {
this.copiedId = botId;
setTimeout(() => { this.copiedId = null; }, 2000);
setTimeout(() => {
this.copiedId = null;
}, 2000);
});
}
@@ -102,10 +112,12 @@ export class BotsComponent implements OnInit {
this.botService.delete(botId).subscribe({
next: () => {
this.deletingId = null;
this.bots = this.bots.filter(b => b.id !== botId);
this.bots = this.bots.filter((b) => b.id !== botId);
delete this.revealedTokens[botId];
},
error: () => { this.deletingId = null; }
error: () => {
this.deletingId = null;
},
});
}
}