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:
@@ -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 ? 'L’IA 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>
|
||||
|
||||
|
||||
@@ -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']
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user