Migration Angular 20 -> 21 : fin de la migration securite (0 vulnerabilite npm)

- ng update @angular/core@21 @angular/cli@21 (corrige GHSA-jrmj-c5cx-3cw6, XSS SVG)
- Migration automatique des templates vers le control flow natif (@if/@for, 138 fichiers)
- Correction des 5 `track` invalides generes par la migration (trackBy a 1 argument
  -> track $index) + suppression des fonctions trackBy mortes
- TypeScript 5.9, zone.js 0.15 ; npm audit : 0 vulnerabilite

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 14:55:36 +02:00
parent 6acad41672
commit 23878f1c63
142 changed files with 7055 additions and 6292 deletions

View File

@@ -2,33 +2,43 @@
<div #messagesContainer class="messages-area">
<div class="welcome-hint" *ngIf="messages.length === 0 && !currentAssistantText && !error">
<lucide-icon [img]="Sparkles" [size]="18"></lucide-icon>
<p>Pose une question à l'IA pendant la partie.</p>
<p class="welcome-sub">
Elle connaît ton univers, ta campagne, les règles du système et tout ce qui a été noté dans le journal.
</p>
</div>
@if (messages.length === 0 && !currentAssistantText && !error) {
<div class="welcome-hint">
<lucide-icon [img]="Sparkles" [size]="18"></lucide-icon>
<p>Pose une question à l'IA pendant la partie.</p>
<p class="welcome-sub">
Elle connaît ton univers, ta campagne, les règles du système et tout ce qui a été noté dans le journal.
</p>
</div>
}
<div *ngFor="let m of messages" class="msg" [class.msg--user]="m.role === 'user'" [class.msg--assistant]="m.role === 'assistant'">
<div class="msg-content">{{ m.content }}</div>
<button *ngIf="m.role === 'assistant'"
type="button"
class="msg-action"
[disabled]="!canSaveToJournal"
[title]="canSaveToJournal ? 'Ajouter cette réponse au journal' : 'Session terminée'"
(click)="onSaveToJournal(m.content)">
<lucide-icon [img]="BookmarkPlus" [size]="12"></lucide-icon>
Au journal
</button>
</div>
@for (m of messages; track m) {
<div class="msg" [class.msg--user]="m.role === 'user'" [class.msg--assistant]="m.role === 'assistant'">
<div class="msg-content">{{ m.content }}</div>
@if (m.role === 'assistant') {
<button
type="button"
class="msg-action"
[disabled]="!canSaveToJournal"
[title]="canSaveToJournal ? 'Ajouter cette réponse au journal' : 'Session terminée'"
(click)="onSaveToJournal(m.content)">
<lucide-icon [img]="BookmarkPlus" [size]="12"></lucide-icon>
Au journal
</button>
}
</div>
}
<!-- Stream en cours : on affiche les tokens au fil de l'eau. -->
<div *ngIf="currentAssistantText" class="msg msg--assistant msg--streaming">
<div class="msg-content">{{ currentAssistantText }}<span class="cursor"></span></div>
</div>
@if (currentAssistantText) {
<div class="msg msg--assistant msg--streaming">
<div class="msg-content">{{ currentAssistantText }}<span class="cursor"></span></div>
</div>
}
<p class="error-hint" *ngIf="error">{{ error }}</p>
@if (error) {
<p class="error-hint">{{ error }}</p>
}
</div>
<div class="composer">
@@ -39,33 +49,37 @@
rows="2"
[placeholder]="isStreaming ? 'LIA répond' : 'Demande une idée, un rebondissement, une description'"
[disabled]="isStreaming"
(keydown.control.enter)="send()"></textarea>
(keydown.control.enter)="send()"></textarea>
<div class="composer-actions">
<button type="button"
class="btn-link"
[disabled]="messages.length === 0 && !currentAssistantText"
(click)="clearConversation()"
title="Effacer la conversation">
class="btn-link"
[disabled]="messages.length === 0 && !currentAssistantText"
(click)="clearConversation()"
title="Effacer la conversation">
<lucide-icon [img]="Trash2" [size]="12"></lucide-icon>
</button>
<button *ngIf="!isStreaming"
type="button"
class="btn-primary btn-send"
[disabled]="!input.trim()"
(click)="send()">
<lucide-icon [img]="Send" [size]="14"></lucide-icon>
Envoyer
</button>
@if (!isStreaming) {
<button
type="button"
class="btn-primary btn-send"
[disabled]="!input.trim()"
(click)="send()">
<lucide-icon [img]="Send" [size]="14"></lucide-icon>
Envoyer
</button>
}
<button *ngIf="isStreaming"
type="button"
class="btn-secondary btn-send"
(click)="cancelStream()">
<lucide-icon [img]="Square" [size]="14"></lucide-icon>
Stop
</button>
@if (isStreaming) {
<button
type="button"
class="btn-secondary btn-send"
(click)="cancelStream()">
<lucide-icon [img]="Square" [size]="14"></lucide-icon>
Stop
</button>
}
</div>
</div>

View File

@@ -2,7 +2,7 @@ import {
Component, EventEmitter, Input, OnChanges, OnDestroy, Output, SimpleChanges,
ElementRef, ViewChild
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import {
LucideAngularModule, Send, Sparkles, Trash2, BookmarkPlus, Square
@@ -23,7 +23,7 @@ import { AiChatService, ChatMessage } from '../../services/ai-chat.service';
*/
@Component({
selector: 'app-session-ai-chat-panel',
imports: [CommonModule, FormsModule, LucideAngularModule],
imports: [FormsModule, LucideAngularModule],
templateUrl: './session-ai-chat-panel.component.html',
styleUrls: ['./session-ai-chat-panel.component.scss']
})