fix(media): toolbar remise entre carte et chatbot + nav renommée

- fix: layout-toggle-bar à l'intérieur du layout-container (entre carte D3 et chatbot)
- fix: chatbot de nouveau visible en mode split
- feat: nav "Écosystème Entraide Architecture" → "Écosystème Entraide"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jules Neny
2026-05-22 15:50:22 +02:00
parent ea7c8cc91e
commit cd8fe9e258
2 changed files with 204 additions and 212 deletions

View File

@@ -34,7 +34,7 @@
class="nav-tab" class="nav-tab"
:class="{ 'nav-tab--active': route.path === '/' }" :class="{ 'nav-tab--active': route.path === '/' }"
> >
Écosystème Entraide Architecture Écosystème Entraide
</NuxtLink> </NuxtLink>
<NuxtLink <NuxtLink
to="/agences" to="/agences"

View File

@@ -1,114 +1,12 @@
<template> <template>
<div class="media-visuel"> <div class="media-visuel">
<!-- Barre de toggle (toujours visible) --> <!-- Conteneur principal : carte toolbar chatbot (ou bonpote/rag-backend) -->
<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"> <div class="layout-container">
<!-- VUE CARTE : carte D3 + poignee + chatbot --> <!-- SLOT CARTE D3 (mode carte uniquement) -->
<template v-if="contentView === 'carte'">
<!-- Slot carte D3 -->
<div <div
v-if="contentView === 'carte'"
class="carte-slot" class="carte-slot"
:class="[ :class="[
layoutMode === 'split' ? 'carte-split' : '', layoutMode === 'split' ? 'carte-split' : '',
@@ -147,9 +45,109 @@
</div> </div>
</div> </div>
<!-- Poignee draggable (split uniquement) --> <!-- BARRE DE TOGGLE (entre carte et chatbot, toujours visible) -->
<div class="layout-toggle-bar shrink-0">
<!-- Gauche : contrôles layout (seulement 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 (toujours, indépendants du layoutMode) -->
<div style="margin-left: auto; display: flex; align-items: center; gap: 4px; flex-wrap: wrap;">
<!-- Slider opacité PDF -->
<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 -->
<div class="carte-pensees-ctrl">
<input
type="checkbox"
v-model="showFracasPdf"
class="fracas-check"
title="Superposer la carte FRACAS en PDF"
/>
<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>
<!-- POIGNEE DRAGGABLE (split uniquement) -->
<div <div
v-if="layoutMode === 'split'" v-if="contentView === 'carte' && layoutMode === 'split'"
class="split-handle" class="split-handle"
@mousedown.prevent="onHandleMousedown" @mousedown.prevent="onHandleMousedown"
title="Redimensionner" title="Redimensionner"
@@ -157,8 +155,9 @@
<span class="split-handle-grip"></span> <span class="split-handle-grip"></span>
</div> </div>
<!-- Slot chatbot --> <!-- SLOT CHATBOT (mode carte uniquement) -->
<div <div
v-if="contentView === 'carte'"
class="chatbot-slot" class="chatbot-slot"
:class="[ :class="[
layoutMode === 'split' ? 'chatbot-split' : '', layoutMode === 'split' ? 'chatbot-split' : '',
@@ -172,11 +171,9 @@
</ClientOnly> </ClientOnly>
</div> </div>
</template>
<!-- VUE BONPOTE --> <!-- VUE BONPOTE -->
<div <div
v-else-if="contentView === 'bonpote'" v-if="contentView === 'bonpote'"
class="flex-1 overflow-y-auto px-6 py-8" class="flex-1 overflow-y-auto px-6 py-8"
style="max-width: 680px; margin: 0 auto;" style="max-width: 680px; margin: 0 auto;"
> >
@@ -231,7 +228,7 @@
<!-- VUE RAG BACKEND --> <!-- VUE RAG BACKEND -->
<div <div
v-else-if="contentView === 'rag-backend'" v-if="contentView === 'rag-backend'"
style="flex: 1; overflow: hidden; display: flex; flex-direction: column;" style="flex: 1; overflow: hidden; display: flex; flex-direction: column;"
> >
<MediaTabBackend /> <MediaTabBackend />
@@ -351,9 +348,7 @@ function showCarte() {
localStorage.setItem(CONTENT_KEY, 'carte') localStorage.setItem(CONTENT_KEY, 'carte')
localStorage.setItem(LAYOUT_KEY, 'split') localStorage.setItem(LAYOUT_KEY, 'split')
} }
nextTick(() => { nextTick(() => cartePenseesRef.value?.triggerResize())
cartePenseesRef.value?.triggerResize()
})
} }
function onHandleMousedown(e: MouseEvent) { function onHandleMousedown(e: MouseEvent) {
@@ -374,9 +369,7 @@ function onHandleMousemove(e: MouseEvent) {
function onHandleMouseup() { function onHandleMouseup() {
window.removeEventListener('mousemove', onHandleMousemove) window.removeEventListener('mousemove', onHandleMousemove)
window.removeEventListener('mouseup', onHandleMouseup) window.removeEventListener('mouseup', onHandleMouseup)
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') localStorage.setItem(SPLIT_RATIO_KEY, String(splitRatio.value))
localStorage.setItem(SPLIT_RATIO_KEY, String(splitRatio.value))
}
cartePenseesRef.value?.triggerResize() cartePenseesRef.value?.triggerResize()
} }
@@ -459,7 +452,49 @@ function onInterrogerRag(auteurId: string) {
min-height: 0; min-height: 0;
} }
/* --- Barre de toggle --- */ .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;
}
/* --- Barre de toggle (entre carte et chatbot) --- */
.layout-toggle-bar { .layout-toggle-bar {
flex-shrink: 0; flex-shrink: 0;
display: flex; display: flex;
@@ -526,49 +561,6 @@ function onInterrogerRag(auteurId: string) {
accent-color: var(--nav-primary, #3b6ea5); 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 --- */ /* --- Poignee draggable --- */
.split-handle { .split-handle {
flex-shrink: 0; flex-shrink: 0;