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

@@ -3,10 +3,13 @@ import { switchMap, map } from 'rxjs/operators';
import { CampaignService } from '../services/campaign.service';
import { CharacterService } from '../services/character.service';
import { NpcService } from '../services/npc.service';
import { RandomTableService } from '../services/random-table.service';
import { TreeItem, SecondarySidebarConfig, GlobalItem } from '../services/layout.service';
import { Arc, Chapter, Scene, Campaign } from '../services/campaign.model';
import { Character } from '../services/character.model';
import { Npc } from '../services/npc.model';
import { RandomTable } from '../services/random-table.model';
import { catchError } from 'rxjs/operators';
/**
* Helper — charge l'arborescence complète d'une campagne (arcs -> chapitres -> scènes)
@@ -22,13 +25,17 @@ export interface CampaignTreeData {
scenesByChapter: Record<string, Scene[]>;
characters: Character[];
npcs: Npc[];
randomTables: RandomTable[];
}
export function loadCampaignTreeData(
service: CampaignService,
campaignId: string,
characterService: CharacterService,
npcService: NpcService
npcService: NpcService,
// Optionnel pour ne pas casser les ~15 appelants existants : si fourni, les
// tables aléatoires sont chargées et apparaissent dans la sidebar.
randomTableService?: RandomTableService
): Observable<CampaignTreeData> {
// Note refonte Playthrough : les PJ appartiennent désormais à une Partie,
// pas à la campagne — on ne les charge plus ici (les vues qui les affichent
@@ -36,11 +43,14 @@ export function loadCampaignTreeData(
return forkJoin({
arcs: service.getArcs(campaignId),
characters: of([] as Character[]),
npcs: npcService.getByCampaign(campaignId)
npcs: npcService.getByCampaign(campaignId),
randomTables: randomTableService
? randomTableService.getByCampaign(campaignId).pipe(catchError(() => of([] as RandomTable[])))
: of([] as RandomTable[])
}).pipe(
switchMap(({ arcs, characters, npcs }) => {
switchMap(({ arcs, characters, npcs, randomTables }) => {
if (arcs.length === 0) {
return of({ arcs, chaptersByArc: {}, scenesByChapter: {}, characters, npcs });
return of({ arcs, chaptersByArc: {}, scenesByChapter: {}, characters, npcs, randomTables });
}
const chapterCalls = arcs.map(a =>
service.getChapters(a.id!).pipe(map(chapters => ({ arcId: a.id!, chapters })))
@@ -55,7 +65,7 @@ export function loadCampaignTreeData(
});
if (allChapters.length === 0) {
return of({ arcs, chaptersByArc, scenesByChapter: {}, characters, npcs });
return of({ arcs, chaptersByArc, scenesByChapter: {}, characters, npcs, randomTables });
}
const sceneCalls = allChapters.map(c =>
service.getScenes(c.id!).pipe(map(scenes => ({ chapterId: c.id!, scenes })))
@@ -64,7 +74,7 @@ export function loadCampaignTreeData(
map(sceneResults => {
const scenesByChapter: Record<string, Scene[]> = {};
sceneResults.forEach(r => { scenesByChapter[r.chapterId] = r.scenes; });
return { arcs, chaptersByArc, scenesByChapter, characters, npcs };
return { arcs, chaptersByArc, scenesByChapter, characters, npcs, randomTables };
})
);
})
@@ -157,7 +167,46 @@ export function buildCampaignTree(campaignId: string, data: CampaignTreeData): T
};
});
return [...arcNodes, npcsNode];
const sortedTables = [...(data.randomTables ?? [])].sort(byName);
const tableItems: TreeItem[] = sortedTables.map(t => ({
id: `random-table-${t.id}`,
label: t.name,
iconKey: t.icon ?? 'dice',
route: `/campaigns/${campaignId}/random-tables/${t.id}`
}));
const tablesNode: TreeItem = {
id: 'random-tables-root',
label: 'Tables aléatoires',
iconKey: 'dice',
children: tableItems,
meta: tableItems.length ? String(tableItems.length) : undefined,
sectionHeaderBefore: 'Outils',
createActions: [{
id: 'new-random-table',
label: 'Nouvelle table',
route: `/campaigns/${campaignId}/random-tables/create`,
actionIcon: 'plus'
}]
};
// Lien simple vers les ateliers (la liste se charge sur sa page — pas de fetch ici).
const notebooksNode: TreeItem = {
id: 'notebooks-root',
label: 'Ateliers (IA + PDF)',
iconKey: 'book-open',
route: `/campaigns/${campaignId}/notebooks`
};
// Importer un PDF de campagne → arborescence (outil, comme tables & ateliers).
const importNode: TreeItem = {
id: 'import-pdf-root',
label: 'Importer un PDF',
iconKey: 'file-up',
route: `/campaigns/${campaignId}/import`
};
return [...arcNodes, npcsNode, tablesNode, notebooksNode, importNode];
}
/**