Plusieurs gros ajouts :
All checks were successful
Build & Push Images / build (brain) (push) Successful in 1m29s
Build & Push Images / build (core) (push) Successful in 1m51s
Build & Push Images / build-switcher (push) Successful in 16s
Build & Push Images / build (web) (push) Successful in 1m43s

- 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:
2026-06-07 09:52:15 +02:00
parent 76977eda0e
commit 75357983a1
113 changed files with 6347 additions and 662 deletions

View File

@@ -0,0 +1,110 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LucideAngularModule, Dices, BookmarkPlus, Sparkles, ChevronLeft } from 'lucide-angular';
import { catchError, of } from 'rxjs';
import { RandomTableService } from '../../services/random-table.service';
import { RandomTable, RandomTableEntry } from '../../services/random-table.model';
import { DiceUtils, DiceRoll } from '../../shared/dice.utils';
import { DiceRollResult } from '../session-dice-panel/session-dice-panel.component';
/**
* Panneau "Tables aléatoires" du mode jeu : choisir une table de la campagne,
* la JETER, consigner le résultat au journal, et (option) demander à l'IA
* d'improviser un court récit sur le tirage.
*
* Réutilise les sorties existantes du panneau de référence :
* - `rolled` (DiceRollResult) → entrée DICE_ROLL dans le journal ;
* - `aiReplyToJournal` (string) → entrée NOTE (récit IA).
*/
@Component({
selector: 'app-session-random-tables-panel',
standalone: true,
imports: [CommonModule, LucideAngularModule],
templateUrl: './session-random-tables-panel.component.html',
styleUrls: ['./session-random-tables-panel.component.scss']
})
export class SessionRandomTablesPanelComponent implements OnInit {
readonly Dices = Dices;
readonly BookmarkPlus = BookmarkPlus;
readonly Sparkles = Sparkles;
readonly ChevronLeft = ChevronLeft;
@Input() campaignId!: string;
@Input() canAddToJournal = true;
@Output() rolled = new EventEmitter<DiceRollResult>();
@Output() aiReplyToJournal = new EventEmitter<string>();
tables: RandomTable[] = [];
loading = false;
selected: RandomTable | null = null;
lastRoll: DiceRoll | null = null;
matched: RandomTableEntry | null = null;
improvising = false;
constructor(private service: RandomTableService) {}
ngOnInit(): void {
if (!this.campaignId) return;
this.loading = true;
this.service.getByCampaign(this.campaignId).pipe(catchError(() => of([] as RandomTable[])))
.subscribe(list => { this.tables = list; this.loading = false; });
}
select(table: RandomTable): void {
this.selected = table;
this.lastRoll = null;
this.matched = null;
}
backToList(): void {
this.selected = null;
this.lastRoll = null;
this.matched = null;
}
roll(): void {
if (!this.selected) return;
const result = DiceUtils.roll(this.selected.diceFormula);
if (!result) { this.lastRoll = null; this.matched = null; return; }
this.lastRoll = result;
this.matched = this.selected.entries.find(
e => result.total >= e.minRoll && result.total <= e.maxRoll
) ?? null;
}
rangeLabel(entry: RandomTableEntry): string {
return entry.minRoll === entry.maxRoll ? String(entry.minRoll) : `${entry.minRoll}${entry.maxRoll}`;
}
/** Consigne le tirage au journal (entrée DICE_ROLL via la sortie `rolled`). */
addToJournal(): void {
if (!this.canAddToJournal || !this.selected || !this.lastRoll) return;
const label = this.matched?.label ?? 'aucun résultat';
const result: DiceRollResult = {
notation: this.selected.diceFormula,
rolls: this.lastRoll.rolls,
modifier: 0,
total: this.lastRoll.total,
summary: `🎲 ${this.selected.name} : ${this.lastRoll.total}${label}`
};
this.rolled.emit(result);
}
/** Demande à l'IA un court récit sur le résultat, puis l'envoie au journal (NOTE). */
improvise(): void {
if (!this.selected || !this.matched || this.improvising) return;
this.improvising = true;
this.service.improvise(
this.campaignId, this.selected.name, this.matched.label, this.matched.detail ?? ''
).subscribe({
next: (r) => {
this.improvising = false;
if (r.narration?.trim() && this.canAddToJournal) {
this.aiReplyToJournal.emit(r.narration.trim());
}
},
error: () => { this.improvising = false; }
});
}
}