Plusieurs gros ajouts :
- Possibilité de discuter avec un PDF ; RAG ou analyse approfondie. Enlèvement de l'autre outil PDF de discussion qui analysait d'abord un PDF en proposant directement une intégration sans attendre qu'on pose de question - Mise en place de l'import directement dans les outils dans la sidebar - Mise en place d'un outil pour créer des tables aléatoires avec possibilité d'utiliser pendant la partie - Mise en place d'un outil pour mettre en place des PNJ, scènes, chapitre.... directement à partir de la discussion avec le PDF - Mise en place RAG avec mistal-embeding ou nomic si on utilise ollama - Mise en place mistral, google en fournisseurs alternatifs pour l'IA dans le cloud - version 0.11.0-bêta
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { Router } from '@angular/router';
|
||||
import { LucideAngularModule, Plus, Check, Drama, Clapperboard, BookText, GitBranch, Dices, ExternalLink } from 'lucide-angular';
|
||||
import { CampaignService } from '../../../services/campaign.service';
|
||||
import { NpcService } from '../../../services/npc.service';
|
||||
import { RandomTableService } from '../../../services/random-table.service';
|
||||
import { Arc, Chapter } from '../../../services/campaign.model';
|
||||
import { NotebookAction } from '../../../services/notebook-action.model';
|
||||
|
||||
/**
|
||||
* Carte « Créer dans la campagne » issue d'une proposition de l'IA. Gère la cible
|
||||
* (chapitre pour une scène, arc pour un chapitre) et appelle les services existants.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-notebook-action-card',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule, LucideAngularModule],
|
||||
templateUrl: './notebook-action-card.component.html',
|
||||
styleUrls: ['./notebook-action-card.component.scss']
|
||||
})
|
||||
export class NotebookActionCardComponent implements OnInit {
|
||||
readonly Plus = Plus;
|
||||
readonly Check = Check;
|
||||
readonly ExternalLink = ExternalLink;
|
||||
|
||||
@Input() action!: NotebookAction;
|
||||
@Input() campaignId!: string;
|
||||
@Input() arcs: Arc[] = [];
|
||||
@Input() chaptersByArc: Record<string, Chapter[]> = {};
|
||||
/** Émis après une création réussie → l'atelier rafraîchit la sidebar. */
|
||||
@Output() created = new EventEmitter<void>();
|
||||
|
||||
selectedArcId = '';
|
||||
selectedChapterId = '';
|
||||
|
||||
status: 'idle' | 'creating' | 'created' | 'error' = 'idle';
|
||||
errorMsg = '';
|
||||
createdRoute: string[] | null = null;
|
||||
|
||||
constructor(
|
||||
private campaignService: CampaignService,
|
||||
private npcService: NpcService,
|
||||
private tableService: RandomTableService,
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.arcs.length) this.selectedArcId = this.arcs[0].id!;
|
||||
this.syncChapter();
|
||||
}
|
||||
|
||||
get typeLabel(): string {
|
||||
switch (this.action.type) {
|
||||
case 'npc': return 'PNJ';
|
||||
case 'scene': return 'Scène';
|
||||
case 'chapter': return 'Chapitre';
|
||||
case 'arc': return 'Arc';
|
||||
case 'table': return 'Table aléatoire';
|
||||
default: return this.action.type;
|
||||
}
|
||||
}
|
||||
|
||||
get typeIcon() {
|
||||
switch (this.action.type) {
|
||||
case 'npc': return Drama;
|
||||
case 'scene': return Clapperboard;
|
||||
case 'chapter': return BookText;
|
||||
case 'arc': return GitBranch;
|
||||
case 'table': return Dices;
|
||||
default: return Plus;
|
||||
}
|
||||
}
|
||||
|
||||
get needsArc(): boolean { return this.action.type === 'chapter' || this.action.type === 'scene'; }
|
||||
get needsChapter(): boolean { return this.action.type === 'scene'; }
|
||||
|
||||
get targetChapters(): Chapter[] { return this.chaptersByArc[this.selectedArcId] ?? []; }
|
||||
|
||||
syncChapter(): void {
|
||||
const chs = this.targetChapters;
|
||||
this.selectedChapterId = chs.length ? chs[0].id! : '';
|
||||
}
|
||||
|
||||
get canCreate(): boolean {
|
||||
if (this.status === 'creating' || this.status === 'created') return false;
|
||||
if (this.needsArc && !this.selectedArcId) return false;
|
||||
if (this.needsChapter && !this.selectedChapterId) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
create(): void {
|
||||
if (!this.canCreate) return;
|
||||
this.status = 'creating';
|
||||
this.errorMsg = '';
|
||||
switch (this.action.type) {
|
||||
case 'npc': return this.createNpc();
|
||||
case 'arc': return this.createArc();
|
||||
case 'chapter': return this.createChapter();
|
||||
case 'scene': return this.createScene();
|
||||
case 'table': return this.createTable();
|
||||
}
|
||||
}
|
||||
|
||||
private createNpc(): void {
|
||||
this.npcService.create({
|
||||
name: this.action.name,
|
||||
campaignId: this.campaignId,
|
||||
values: this.action.description ? { Description: this.action.description } : {}
|
||||
}).subscribe({
|
||||
next: (n) => this.done(['/campaigns', this.campaignId, 'npcs', n.id!]),
|
||||
error: (e) => this.fail(e)
|
||||
});
|
||||
}
|
||||
|
||||
private createArc(): void {
|
||||
this.campaignService.createArc({
|
||||
name: this.action.name,
|
||||
description: this.action.description,
|
||||
campaignId: this.campaignId,
|
||||
order: this.arcs.length,
|
||||
type: this.action.arcType === 'HUB' ? 'HUB' : 'LINEAR'
|
||||
}).subscribe({
|
||||
next: (a) => this.done(['/campaigns', this.campaignId, 'arcs', a.id!]),
|
||||
error: (e) => this.fail(e)
|
||||
});
|
||||
}
|
||||
|
||||
private createChapter(): void {
|
||||
const order = (this.chaptersByArc[this.selectedArcId] ?? []).length;
|
||||
this.campaignService.createChapter({
|
||||
name: this.action.name,
|
||||
description: this.action.description,
|
||||
arcId: this.selectedArcId,
|
||||
order
|
||||
}).subscribe({
|
||||
next: (c) => this.done(['/campaigns', this.campaignId, 'arcs', this.selectedArcId, 'chapters', c.id!]),
|
||||
error: (e) => this.fail(e)
|
||||
});
|
||||
}
|
||||
|
||||
private createScene(): void {
|
||||
this.campaignService.createScene({
|
||||
name: this.action.name,
|
||||
description: this.action.description,
|
||||
playerNarration: this.action.content,
|
||||
chapterId: this.selectedChapterId,
|
||||
order: 0
|
||||
}).subscribe({
|
||||
next: (s) => this.done(
|
||||
['/campaigns', this.campaignId, 'arcs', this.selectedArcId, 'chapters', this.selectedChapterId, 'scenes', s.id!]),
|
||||
error: (e) => this.fail(e)
|
||||
});
|
||||
}
|
||||
|
||||
private createTable(): void {
|
||||
this.tableService.create({
|
||||
name: this.action.name,
|
||||
diceFormula: this.action.diceFormula || '1d20',
|
||||
campaignId: this.campaignId,
|
||||
entries: (this.action.entries ?? []).map(e => ({
|
||||
minRoll: e.minRoll, maxRoll: e.maxRoll, label: e.label, detail: e.detail
|
||||
}))
|
||||
}).subscribe({
|
||||
next: (t) => this.done(['/campaigns', this.campaignId, 'random-tables', t.id!]),
|
||||
error: (e) => this.fail(e)
|
||||
});
|
||||
}
|
||||
|
||||
private done(route: string[]): void {
|
||||
this.status = 'created';
|
||||
this.createdRoute = route;
|
||||
this.created.emit();
|
||||
}
|
||||
|
||||
private fail(err: unknown): void {
|
||||
this.status = 'error';
|
||||
this.errorMsg = (err as { error?: { message?: string } })?.error?.message || 'Échec de la création.';
|
||||
}
|
||||
|
||||
openCreated(): void {
|
||||
if (this.createdRoute) this.router.navigate(this.createdRoute);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user