Plusieurs gros ajouts :
Some checks failed
Build & Push Images / build (brain) (push) Has been cancelled
Build & Push Images / build (core) (push) Has been cancelled
Build & Push Images / build (web) (push) Has been cancelled
Build & Push Images / build-switcher (push) Has been cancelled

- 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 5eb15dc449
commit edc4434298
113 changed files with 6347 additions and 662 deletions

View File

@@ -0,0 +1,66 @@
/**
* Utilitaires de dés partagés (jets aléatoires cryptographiques).
*
* Centralise le parsing de formules ("1d20", "2d6", "d100") et le jet, utilisé
* par les tables aléatoires (et réutilisable ailleurs). Pas de Math.random pour
* le tirage : on privilégie crypto.getRandomValues quand disponible.
*/
export interface ParsedDice {
/** Nombre de dés (ex. 2 pour "2d6"). */
count: number;
/** Nombre de faces (ex. 6 pour "2d6"). */
faces: number;
}
export interface DiceRoll {
/** Formule normalisée utilisée pour le jet. */
formula: string;
/** Jets individuels de chaque dé. */
rolls: number[];
/** Somme des jets (la valeur qui désigne l'entrée d'une table). */
total: number;
}
export class DiceUtils {
/** Parse une formule simple `[N]dM`. Renvoie null si invalide. */
static parse(formula: string | null | undefined): ParsedDice | null {
const m = /^\s*(\d*)\s*[dD]\s*(\d+)\s*$/.exec(formula ?? '');
if (!m) return null;
const count = m[1] ? parseInt(m[1], 10) : 1;
const faces = parseInt(m[2], 10);
if (count < 1 || count > 100 || faces < 2 || faces > 10000) return null;
return { count, faces };
}
/** Entier aléatoire dans [min, max] inclus (crypto si dispo). */
static randomInt(min: number, max: number): number {
const span = max - min + 1;
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
const buf = new Uint32Array(1);
crypto.getRandomValues(buf);
return min + (buf[0] % span);
}
return min + Math.floor(Math.random() * span);
}
/** Lance la formule. Renvoie null si la formule est invalide. */
static roll(formula: string): DiceRoll | null {
const parsed = this.parse(formula);
if (!parsed) return null;
const rolls: number[] = [];
for (let i = 0; i < parsed.count; i++) {
rolls.push(this.randomInt(1, parsed.faces));
}
const total = rolls.reduce((a, b) => a + b, 0);
return { formula, rolls, total };
}
/** Plage de totaux possibles d'une formule (ex. "2d6" → {min:2, max:12}). */
static totalRange(formula: string): { min: number; max: number } | null {
const parsed = this.parse(formula);
if (!parsed) return null;
return { min: parsed.count, max: parsed.count * parsed.faces };
}
}