- fix: server route /data/auteurs-pensees.json (contournement bug manifest Nitro) - fix: contentView indépendant du layoutMode — boutons CARTE PRINCIPALE / bonpote / RAG backend ne modifient pas l'état carte-full/chatbot-full - feat: bouton CARTE PRINCIPALE → restaure la vue D3 + chatbot split - fix: /rag redirige vers /media (301) - feat: nav "RAG en construction" → "recherche-média" lien /media Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
659 lines
24 KiB
Vue
659 lines
24 KiB
Vue
<template>
|
|
<div class="media-visuel">
|
|
|
|
<!-- Barre de toggle (toujours visible) -->
|
|
<div class="layout-toggle-bar shrink-0">
|
|
|
|
<!-- Gauche : contrôles layout (uniquement en mode carte) -->
|
|
<template v-if="contentView === 'carte'">
|
|
<button
|
|
@click="layoutMode = 'carte-full'"
|
|
:class="{ active: layoutMode === 'carte-full' }"
|
|
class="toggle-btn"
|
|
title="Carte en plein ecran"
|
|
>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/>
|
|
<line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/>
|
|
</svg>
|
|
Carte plein ecran
|
|
</button>
|
|
<button
|
|
v-if="layoutMode !== 'split'"
|
|
@click="layoutMode = 'split'"
|
|
class="toggle-btn"
|
|
title="Vue partagee"
|
|
>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<rect x="3" y="3" width="18" height="18" rx="2"/><line x1="3" y1="12" x2="21" y2="12"/>
|
|
</svg>
|
|
Vue partagee
|
|
</button>
|
|
<button
|
|
@click="layoutMode = 'chatbot-full'"
|
|
:class="{ active: layoutMode === 'chatbot-full' }"
|
|
class="toggle-btn"
|
|
title="Chatbot plein ecran"
|
|
>
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
|
|
</svg>
|
|
Chatbot plein ecran
|
|
</button>
|
|
</template>
|
|
|
|
<!-- Droite : contrôles contenu (indépendants du layout) -->
|
|
<div style="margin-left: auto; display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
|
|
<!-- Slider opacité PDF (quand activé et en mode carte) -->
|
|
<input
|
|
v-if="showFracasPdf && contentView === 'carte'"
|
|
type="range"
|
|
min="0"
|
|
max="100"
|
|
v-model.number="fracasOpacity"
|
|
class="opacity-slider"
|
|
:title="`Opacité ${fracasOpacity}%`"
|
|
/>
|
|
|
|
<!-- CARTE PRINCIPALE -->
|
|
<button
|
|
@click="showCarte"
|
|
:class="{ active: contentView === 'carte' }"
|
|
class="toggle-btn"
|
|
title="Vue principale : carte D3 + chatbot"
|
|
>
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="2"/><path d="M12 2a10 10 0 0 0-7.07 17.07M12 2a10 10 0 0 1 7.07 17.07M3.34 7h17.32M3.34 17h17.32"/>
|
|
</svg>
|
|
CARTE PRINCIPALE
|
|
</button>
|
|
|
|
<!-- Tickbox PDF + carte des pensées écologiques -->
|
|
<div class="carte-pensees-ctrl">
|
|
<input
|
|
type="checkbox"
|
|
v-model="showFracasPdf"
|
|
class="fracas-check"
|
|
title="Superposer la carte FRACAS en PDF (mode carte)"
|
|
/>
|
|
<button
|
|
@click="contentView = 'bonpote'"
|
|
:class="{ active: contentView === 'bonpote' }"
|
|
class="toggle-btn carte-pensees-btn"
|
|
title="Carte des pensées écologiques — référence FRACAS Bonpote V2"
|
|
>
|
|
📗 carte des pensées écologiques
|
|
</button>
|
|
</div>
|
|
|
|
<!-- RAG backend -->
|
|
<button
|
|
@click="contentView = 'rag-backend'"
|
|
:class="{ active: contentView === 'rag-backend' }"
|
|
class="toggle-btn"
|
|
title="Interface LightRAG backend"
|
|
>
|
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="3"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/>
|
|
</svg>
|
|
RAG backend
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Conteneur principal -->
|
|
<div class="layout-container">
|
|
|
|
<!-- VUE CARTE : carte D3 + poignee + chatbot -->
|
|
<template v-if="contentView === 'carte'">
|
|
|
|
<!-- Slot carte D3 -->
|
|
<div
|
|
class="carte-slot"
|
|
:class="[
|
|
layoutMode === 'split' ? 'carte-split' : '',
|
|
layoutMode === 'carte-full' ? 'carte-full' : '',
|
|
layoutMode === 'chatbot-full' ? 'carte-hidden' : '',
|
|
]"
|
|
:style="layoutMode === 'split' ? { flexBasis: carteFlexBasis } : {}"
|
|
style="position: relative;"
|
|
>
|
|
<ClientOnly>
|
|
<CartePensees
|
|
ref="cartePenseesRef"
|
|
:data="penseesData"
|
|
:active="true"
|
|
@select-auteur="onSelectAuteur"
|
|
@select-ecole="onSelectEcole"
|
|
/>
|
|
<template #fallback>
|
|
<div class="w-full h-full flex items-center justify-center" style="color: var(--nav-text-muted);">
|
|
Chargement de la carte...
|
|
</div>
|
|
</template>
|
|
</ClientOnly>
|
|
|
|
<!-- Overlay PDF FRACAS -->
|
|
<div
|
|
v-if="showFracasPdf"
|
|
class="fracas-overlay"
|
|
:style="{ opacity: fracasOpacity / 100 }"
|
|
>
|
|
<embed
|
|
src="/cartes/carte-fracas-bonpote-v2.pdf"
|
|
type="application/pdf"
|
|
style="width: 100%; height: 100%;"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Poignee draggable (split uniquement) -->
|
|
<div
|
|
v-if="layoutMode === 'split'"
|
|
class="split-handle"
|
|
@mousedown.prevent="onHandleMousedown"
|
|
title="Redimensionner"
|
|
>
|
|
<span class="split-handle-grip"></span>
|
|
</div>
|
|
|
|
<!-- Slot chatbot -->
|
|
<div
|
|
class="chatbot-slot"
|
|
:class="[
|
|
layoutMode === 'split' ? 'chatbot-split' : '',
|
|
layoutMode === 'chatbot-full' ? 'chatbot-full-mode' : '',
|
|
layoutMode === 'carte-full' ? 'chatbot-hidden' : '',
|
|
]"
|
|
:style="layoutMode === 'split' ? { flexBasis: chatbotFlexBasis } : {}"
|
|
>
|
|
<ClientOnly>
|
|
<ChatbotPensees :auteurContext="chatbotAuteur" :inline="true" />
|
|
</ClientOnly>
|
|
</div>
|
|
|
|
</template>
|
|
|
|
<!-- VUE BONPOTE -->
|
|
<div
|
|
v-else-if="contentView === 'bonpote'"
|
|
class="flex-1 overflow-y-auto px-6 py-8"
|
|
style="max-width: 680px; margin: 0 auto;"
|
|
>
|
|
<div class="mb-6">
|
|
<p class="text-xs font-bold uppercase tracking-widest mb-2" style="color: var(--nav-text-muted);">Reference editoriale</p>
|
|
<h2 class="text-xl font-bold mb-3" style="color: var(--nav-text);">Carte FRACAS des pensees ecologiques</h2>
|
|
<p class="text-sm leading-relaxed mb-4" style="color: var(--nav-text);">
|
|
FRACAS (Familles, Racines et Arpentages des Courants et Alternatives Solidaires) est une carte des ecoles de pensee ecologique publiee par Bonpote en octobre 2024. Elle reference ~140 auteurs et autrices reparti-es en 10 ecoles de pensee, depuis l'ecosocialisme jusqu'a l'ethique environnementale.
|
|
</p>
|
|
<p class="text-sm leading-relaxed mb-6" style="color: var(--nav-text);">
|
|
Le RAG ATIS est construit sur cette reference : chaque auteur ingere dans la bibliotheque correspond a une entree de la carte FRACAS. Les ecoles de pensee, les positions et les couleurs de notre carte sont transposees 1:1 depuis Bonpote V2.
|
|
</p>
|
|
<div class="flex flex-col gap-3">
|
|
<a href="https://bonpote.com/la-carte-des-pensees-ecologiques/"
|
|
target="_blank" rel="noopener"
|
|
class="flex items-center gap-3 px-4 py-3 rounded-lg hover:opacity-80 transition-opacity"
|
|
style="background: var(--nav-primary, #3b6ea5); color: white; font-size: 0.875rem; font-weight: 600; text-decoration: none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
|
Lire l'article Bonpote + carte interactive
|
|
</a>
|
|
<a href="https://bonpote.com/wp-content/uploads/2024/10/FRACAS_BONPOTE_CARTE_VERSO_V2-OCT2024.pdf"
|
|
target="_blank" rel="noopener"
|
|
class="flex items-center gap-3 px-4 py-3 rounded-lg hover:opacity-80 transition-opacity"
|
|
style="background: var(--nav-bg-alt); color: var(--nav-text); font-size: 0.875rem; font-weight: 500; text-decoration: none;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
Telecharger le poster PDF (recto/verso)
|
|
</a>
|
|
<button
|
|
@click="showCarte"
|
|
class="flex items-center gap-3 px-4 py-3 rounded-lg hover:opacity-80 transition-opacity text-left"
|
|
style="background: var(--nav-bg-alt); color: var(--nav-text); font-size: 0.875rem; font-weight: 500; border: none; cursor: pointer;">
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
|
|
Interroger le RAG ATIS sur ces pensees
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-bold uppercase tracking-widest mb-3" style="color: var(--nav-text-muted);">Les 10 ecoles de pensee (FRACAS V2)</p>
|
|
<div class="flex flex-col gap-2">
|
|
<div v-for="ecole in (penseesData?.ecoles ?? [])" :key="ecole.id"
|
|
class="flex items-start gap-3 px-3 py-2 rounded-lg"
|
|
style="background: var(--nav-bg-alt);">
|
|
<span class="w-3 h-3 rounded-full shrink-0 mt-1" :style="`background:${ecole.color};`"></span>
|
|
<div>
|
|
<p class="text-sm font-semibold" style="color: var(--nav-text);">{{ ecole.label }}</p>
|
|
<p class="text-xs mt-0.5 leading-relaxed" style="color: var(--nav-text-muted);">{{ ecole.description }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- VUE RAG BACKEND -->
|
|
<div
|
|
v-else-if="contentView === 'rag-backend'"
|
|
style="flex: 1; overflow: hidden; display: flex; flex-direction: column;"
|
|
>
|
|
<MediaTabBackend />
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<!-- Fiche auteur modal -->
|
|
<FicheAuteur
|
|
:open="ficheOpen"
|
|
:auteurId="ficheAuteurId"
|
|
:data="penseesData"
|
|
@close="ficheOpen = false"
|
|
@interroger-rag="onInterrogerRag"
|
|
/>
|
|
|
|
<!-- Fiche ecole modal -->
|
|
<FicheEcole
|
|
:open="ficheEcoleOpen"
|
|
:ecoleId="ficheEcoleId"
|
|
:data="penseesData"
|
|
@close="ficheEcoleOpen = false"
|
|
@select-auteur="onSelectAuteurFromEcole"
|
|
@interroger-ecole="onInterrogerEcole"
|
|
/>
|
|
|
|
<!-- Modal info RAG -->
|
|
<Teleport to="body">
|
|
<Transition name="backdrop">
|
|
<div v-if="ragInfoOpen" class="fixed inset-0 z-[2000]" style="background:rgba(26,34,56,0.55);" @click="ragInfoOpen = false" aria-hidden="true" />
|
|
</Transition>
|
|
<Transition name="modal">
|
|
<div v-if="ragInfoOpen" class="fixed z-[2001] left-1/2 flex flex-col"
|
|
style="top:50%;transform:translate(-50%,-50%);width:min(580px,94vw);max-height:85vh;background:var(--nav-bg);border-radius:14px;box-shadow:0 16px 64px rgba(26,34,56,0.28);overflow:hidden;"
|
|
role="dialog" aria-modal="true" aria-label="A propos du RAG FRACAS">
|
|
<div class="flex items-center justify-between px-5 py-4 shrink-0"
|
|
style="border-bottom:2px solid var(--nav-bg-alt);background:var(--nav-surface);">
|
|
<h2 class="font-bold text-base" style="color:var(--nav-text);">FRACAS - Bibliotheque des pensees ecologiques</h2>
|
|
<button @click="ragInfoOpen = false" class="ml-3 shrink-0 flex items-center justify-center w-8 h-8 rounded-full hover:opacity-70"
|
|
style="background:var(--nav-bg-alt);color:var(--nav-text-muted);" aria-label="Fermer">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round">
|
|
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
<div class="flex-1 overflow-y-auto px-5 py-4" style="color:var(--nav-text);font-size:0.875rem;line-height:1.6;">
|
|
<p class="mb-3">Une bibliotheque parlante politisee - des pensees ecologiques de gauche, organisees pour aider a creer une pensee complexe et nuancee, critiquer le recit dominant et soutenir des alternatives concretes et des projets collectifs.</p>
|
|
<p class="mb-4" style="color:var(--nav-text-muted);font-size:0.8rem;">Projet open source, ouvert a toutes et a tous - <a href="https://bonpote.com/la-carte-des-pensees-ecologiques/" target="_blank" rel="noopener" style="text-decoration:underline;">article + carte FRACAS Bonpote V2</a>.</p>
|
|
<div class="flex flex-col gap-3">
|
|
<div class="p-3 rounded-lg" style="background:var(--nav-bg-alt);">
|
|
<p class="font-semibold mb-1" style="font-size:0.8rem;color:var(--nav-text-muted);text-transform:uppercase;letter-spacing:0.05em;">Ce qu'est un RAG</p>
|
|
<p>Les textes sont vectorises dans un espace de 662 dimensions - chaque livre devient un nuage de points semantiques. La proximite entre les points capture la proximite entre les idees, pas les mots.</p>
|
|
</div>
|
|
<div class="p-3 rounded-lg" style="background:var(--nav-bg-alt);">
|
|
<p class="font-semibold mb-1" style="font-size:0.8rem;color:var(--nav-text-muted);text-transform:uppercase;letter-spacing:0.05em;">Chunking intelligent</p>
|
|
<p>Lors de l'ingestion, nous selectionnons les entites cles (concepts, auteurs, relations entre idees) plutot que de decouper mecaniquement les textes.</p>
|
|
</div>
|
|
<div class="p-3 rounded-lg" style="background:var(--nav-bg-alt);">
|
|
<p class="font-semibold mb-2" style="font-size:0.8rem;color:var(--nav-text-muted);text-transform:uppercase;letter-spacing:0.05em;">Trois couches d'analyse</p>
|
|
<div class="flex flex-col gap-1.5">
|
|
<div class="flex gap-2"><span class="font-semibold" style="min-width:70px;">Fond</span><span>Les idees, les theses, les arguments - ce qu'on interroge directement.</span></div>
|
|
<div class="flex gap-2"><span class="font-semibold" style="min-width:70px;">Forme</span><span>Les modeles narratifs, la rhetorique, la construction argumentative.</span></div>
|
|
<div class="flex gap-2"><span class="font-semibold" style="min-width:70px;">Structure</span><span>L'architecture des livres - comment les auteurs construisent leur pensee.</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Transition>
|
|
</Teleport>
|
|
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
interface EcoleData { id: string; label: string; description: string; color: string; x_hint: number; y_hint: number }
|
|
interface LivreRag { slug: string; titre: string; annee: number; couches: string[] }
|
|
interface AuteurData { id: string; nom: string; dates: string; ecoles: string[]; ecole_principale: string; livres_rag: LivreRag[]; theses_cles: string[]; bio_courte: string }
|
|
interface PenseesData { meta: any; ecoles: EcoleData[]; auteurs: AuteurData[] }
|
|
|
|
type LayoutMode = 'split' | 'carte-full' | 'chatbot-full'
|
|
type ContentView = 'carte' | 'bonpote' | 'rag-backend'
|
|
|
|
const LAYOUT_KEY = 'media-layout-mode'
|
|
const CONTENT_KEY = 'media-content-view'
|
|
const SPLIT_RATIO_KEY = 'media-split-ratio'
|
|
const DEFAULT_SPLIT_RATIO = 0.66
|
|
|
|
const ficheOpen = ref(false)
|
|
const ficheAuteurId = ref<string | null>(null)
|
|
const ficheEcoleOpen = ref(false)
|
|
const ficheEcoleId = ref<string | null>(null)
|
|
const ragInfoOpen = ref(false)
|
|
const chatbotAuteur = ref<string | null>(null)
|
|
|
|
const layoutMode = ref<LayoutMode>('split')
|
|
const contentView = ref<ContentView>('carte')
|
|
const cartePenseesRef = ref<{ triggerResize: () => void } | null>(null)
|
|
|
|
const showFracasPdf = ref(false)
|
|
const fracasOpacity = ref(60)
|
|
|
|
const penseesData = ref<PenseesData | null>(null)
|
|
|
|
const splitRatio = ref(DEFAULT_SPLIT_RATIO)
|
|
const carteFlexBasis = computed(() => `${splitRatio.value * 100}%`)
|
|
const chatbotFlexBasis = computed(() => `${(1 - splitRatio.value) * 100}%`)
|
|
|
|
let dragStartY = 0
|
|
let dragStartRatio = DEFAULT_SPLIT_RATIO
|
|
let containerHeight = 0
|
|
|
|
function showCarte() {
|
|
contentView.value = 'carte'
|
|
layoutMode.value = 'split'
|
|
if (typeof window !== 'undefined') {
|
|
localStorage.setItem(CONTENT_KEY, 'carte')
|
|
localStorage.setItem(LAYOUT_KEY, 'split')
|
|
}
|
|
nextTick(() => {
|
|
cartePenseesRef.value?.triggerResize()
|
|
})
|
|
}
|
|
|
|
function onHandleMousedown(e: MouseEvent) {
|
|
dragStartY = e.clientY
|
|
dragStartRatio = splitRatio.value
|
|
const container = (e.target as HTMLElement)?.closest('.layout-container') as HTMLElement | null
|
|
containerHeight = container ? container.clientHeight : window.innerHeight
|
|
window.addEventListener('mousemove', onHandleMousemove)
|
|
window.addEventListener('mouseup', onHandleMouseup)
|
|
}
|
|
|
|
function onHandleMousemove(e: MouseEvent) {
|
|
const delta = e.clientY - dragStartY
|
|
const newRatio = dragStartRatio + delta / containerHeight
|
|
splitRatio.value = Math.min(0.80, Math.max(0.20, newRatio))
|
|
}
|
|
|
|
function onHandleMouseup() {
|
|
window.removeEventListener('mousemove', onHandleMousemove)
|
|
window.removeEventListener('mouseup', onHandleMouseup)
|
|
if (typeof window !== 'undefined') {
|
|
localStorage.setItem(SPLIT_RATIO_KEY, String(splitRatio.value))
|
|
}
|
|
cartePenseesRef.value?.triggerResize()
|
|
}
|
|
|
|
onMounted(async () => {
|
|
if (typeof window !== 'undefined') {
|
|
const savedLayout = localStorage.getItem(LAYOUT_KEY) as LayoutMode | null
|
|
if (savedLayout && (['split', 'carte-full', 'chatbot-full'] as string[]).includes(savedLayout)) {
|
|
layoutMode.value = savedLayout
|
|
}
|
|
const savedContent = localStorage.getItem(CONTENT_KEY) as ContentView | null
|
|
if (savedContent && (['carte', 'bonpote', 'rag-backend'] as string[]).includes(savedContent)) {
|
|
contentView.value = savedContent
|
|
}
|
|
const savedRatio = parseFloat(localStorage.getItem(SPLIT_RATIO_KEY) ?? '')
|
|
if (!isNaN(savedRatio) && savedRatio >= 0.20 && savedRatio <= 0.80) {
|
|
splitRatio.value = savedRatio
|
|
}
|
|
if (!localStorage.getItem('rag-fracas-info-seen')) {
|
|
ragInfoOpen.value = true
|
|
localStorage.setItem('rag-fracas-info-seen', '1')
|
|
}
|
|
}
|
|
try {
|
|
penseesData.value = await $fetch<PenseesData>('/data/auteurs-pensees.json')
|
|
} catch (e) {
|
|
console.error('Erreur chargement auteurs-pensees.json', e)
|
|
}
|
|
})
|
|
|
|
watch(layoutMode, (v) => {
|
|
if (typeof window !== 'undefined') localStorage.setItem(LAYOUT_KEY, v)
|
|
if (v === 'split' || v === 'carte-full') {
|
|
setTimeout(() => cartePenseesRef.value?.triggerResize(), 350)
|
|
}
|
|
})
|
|
|
|
watch(contentView, (v) => {
|
|
if (typeof window !== 'undefined') localStorage.setItem(CONTENT_KEY, v)
|
|
})
|
|
|
|
function onSelectAuteur(id: string) {
|
|
ficheAuteurId.value = id
|
|
ficheOpen.value = true
|
|
chatbotAuteur.value = null
|
|
}
|
|
|
|
function onSelectEcole(id: string) {
|
|
ficheEcoleId.value = id
|
|
ficheEcoleOpen.value = true
|
|
}
|
|
|
|
function onSelectAuteurFromEcole(auteurId: string) {
|
|
ficheEcoleOpen.value = false
|
|
onSelectAuteur(auteurId)
|
|
}
|
|
|
|
function onInterrogerEcole(ecoleId: string) {
|
|
ficheEcoleOpen.value = false
|
|
const ecole = penseesData.value?.ecoles.find(e => e.id === ecoleId)
|
|
chatbotAuteur.value = ecole?.label ?? null
|
|
if (contentView.value !== 'carte') showCarte()
|
|
else if (layoutMode.value === 'carte-full') layoutMode.value = 'split'
|
|
}
|
|
|
|
function onInterrogerRag(auteurId: string) {
|
|
ficheOpen.value = false
|
|
const auteur = penseesData.value?.auteurs.find(a => a.id === auteurId)
|
|
chatbotAuteur.value = auteur?.nom ?? null
|
|
if (contentView.value !== 'carte') showCarte()
|
|
else if (layoutMode.value === 'carte-full') layoutMode.value = 'split'
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.media-visuel {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* --- Barre de toggle --- */
|
|
.layout-toggle-bar {
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 4px 12px;
|
|
background: var(--nav-bg);
|
|
border-top: 1px solid rgba(180, 170, 160, 0.22);
|
|
border-bottom: 1px solid rgba(180, 170, 160, 0.22);
|
|
min-height: 38px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.toggle-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 5px;
|
|
padding: 4px 10px;
|
|
border-radius: 6px;
|
|
font-size: 0.75rem;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
background: var(--nav-bg-alt);
|
|
color: var(--nav-text-muted);
|
|
border: 1px solid transparent;
|
|
transition: background 0.15s, color 0.15s, border-color 0.15s;
|
|
}
|
|
|
|
.toggle-btn:hover {
|
|
background: var(--nav-surface);
|
|
color: var(--nav-text);
|
|
}
|
|
|
|
.toggle-btn.active {
|
|
background: var(--nav-primary);
|
|
color: var(--nav-text-on-primary);
|
|
border-color: var(--nav-primary);
|
|
}
|
|
|
|
/* --- Contrôle fusionné carte des pensées --- */
|
|
.carte-pensees-ctrl {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
border: 1px solid rgba(180, 170, 160, 0.3);
|
|
}
|
|
|
|
.fracas-check {
|
|
margin: 0 2px 0 7px;
|
|
cursor: pointer;
|
|
accent-color: var(--nav-primary, #3b6ea5);
|
|
}
|
|
|
|
.carte-pensees-btn {
|
|
border-radius: 0;
|
|
border: none;
|
|
}
|
|
|
|
.opacity-slider {
|
|
width: 80px;
|
|
cursor: pointer;
|
|
accent-color: var(--nav-primary, #3b6ea5);
|
|
}
|
|
|
|
/* --- Conteneur principal --- */
|
|
.layout-container {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
min-height: 0;
|
|
}
|
|
|
|
/* --- Slot carte --- */
|
|
.carte-slot {
|
|
overflow: hidden;
|
|
position: relative;
|
|
transition: opacity 0.2s ease;
|
|
}
|
|
|
|
.carte-split {
|
|
flex: 0 0 66%;
|
|
min-height: 0;
|
|
opacity: 1;
|
|
}
|
|
|
|
.carte-full {
|
|
flex: 1 1 100%;
|
|
min-height: 0;
|
|
opacity: 1;
|
|
}
|
|
|
|
.carte-hidden {
|
|
flex: 0 0 0;
|
|
height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* --- Overlay PDF FRACAS --- */
|
|
.fracas-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 50;
|
|
pointer-events: none;
|
|
}
|
|
|
|
/* --- Poignee draggable --- */
|
|
.split-handle {
|
|
flex-shrink: 0;
|
|
height: 8px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
cursor: row-resize;
|
|
background: transparent;
|
|
position: relative;
|
|
z-index: 10;
|
|
user-select: none;
|
|
}
|
|
|
|
.split-handle:hover {
|
|
background: rgba(180, 170, 160, 0.18);
|
|
}
|
|
|
|
.split-handle-grip {
|
|
display: block;
|
|
width: 32px;
|
|
height: 4px;
|
|
border-radius: 2px;
|
|
background: repeating-linear-gradient(
|
|
to bottom,
|
|
rgba(160, 150, 140, 0.55) 0px,
|
|
rgba(160, 150, 140, 0.55) 1px,
|
|
transparent 1px,
|
|
transparent 3px
|
|
);
|
|
}
|
|
|
|
@media (max-width: 767px) {
|
|
.split-handle { display: none; }
|
|
}
|
|
|
|
/* --- Slot chatbot --- */
|
|
.chatbot-slot {
|
|
overflow: hidden;
|
|
position: relative;
|
|
transition: opacity 0.2s ease;
|
|
border-top: 1px solid rgba(180, 170, 160, 0.28);
|
|
}
|
|
|
|
.chatbot-split {
|
|
flex: 0 0 34%;
|
|
min-height: 0;
|
|
opacity: 1;
|
|
}
|
|
|
|
.chatbot-full-mode {
|
|
flex: 1 1 100%;
|
|
min-height: 0;
|
|
opacity: 1;
|
|
}
|
|
|
|
.chatbot-hidden {
|
|
flex: 0 0 0;
|
|
height: 0;
|
|
opacity: 0;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* --- Transitions modal RAG info --- */
|
|
.backdrop-enter-active,.backdrop-leave-active { transition: opacity 0.2s; }
|
|
.backdrop-enter-from,.backdrop-leave-to { opacity: 0; }
|
|
.modal-enter-active { transition: opacity 0.2s, transform 0.22s cubic-bezier(0.34,1.56,0.64,1); }
|
|
.modal-leave-active { transition: opacity 0.18s, transform 0.18s ease-in; }
|
|
.modal-enter-from { opacity: 0; transform: translate(-50%,-48%) scale(0.94); }
|
|
.modal-leave-to { opacity: 0; transform: translate(-50%,-48%) scale(0.96); }
|
|
|
|
/* --- Mobile --- */
|
|
@media (max-width: 767px) {
|
|
.carte-split {
|
|
flex: 0 0 60vh;
|
|
height: 60vh;
|
|
}
|
|
.chatbot-split {
|
|
flex: 0 0 calc(40vh - 38px);
|
|
height: calc(40vh - 38px);
|
|
}
|
|
.toggle-btn {
|
|
font-size: 0.7rem;
|
|
padding: 3px 7px;
|
|
}
|
|
}
|
|
</style>
|