- Page pages/pensees-ecologiques.vue → pages/media.vue (titre "ATIS Média")
- Labels onglet/menu "Pensées" → "Média" (app.vue, agences, index, filters)
- auteurs-pensees.json reconciled avec 141 docs LightRAG (était 27)
· 28 auteurs (était 18), 64 livres, slugs corrigés (ex: bookchin-ecologie-liberte)
· 12 écoles: 8 familles FRACAS Bonpote + 4 extensions ATIS
· Labels alignés Bonpote: Écologies libertaires (ex eco-anarchisme),
Écologies anti-industrielles (ex technocritique)
· Familles Bonpote ajoutées: Capitalisme vert + Écofascismes
(corpus_status: non_ingere — fidélité carte, critique éditoriale assumée)
V2 Phase 2.3 — corpus réel reflété, alignement Bonpote initial
398 lines
20 KiB
Vue
398 lines
20 KiB
Vue
<template>
|
||
<div class="flex flex-col h-screen overflow-hidden" :class="{ dark: isDark }" style="background: var(--nav-bg);">
|
||
|
||
<!-- ═══════════════════════════════════════ TOP NAV GLOBAL -->
|
||
<header
|
||
class="flex items-center justify-between px-4 py-2.5 shrink-0 relative z-[9999] shadow-sm gap-3"
|
||
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);"
|
||
>
|
||
<!-- Logo -->
|
||
<a href="/" class="logo-link flex items-center gap-2 hover:opacity-90 transition-opacity shrink-0" title="Architecture d'Écologie Politique">
|
||
<div
|
||
class="h-8 px-2 rounded-lg flex items-center justify-center shrink-0"
|
||
style="background: var(--nav-primary-solid);"
|
||
>
|
||
<span class="font-bold text-xs tracking-tight" style="color: var(--nav-text-on-primary);">AEP</span>
|
||
</div>
|
||
<div class="logo-text flex flex-col leading-tight">
|
||
<span class="logo-line-1 font-bold tracking-tight" style="color: var(--nav-text);">Architecture</span>
|
||
<span class="logo-line-2 font-bold tracking-tight" style="color: var(--nav-text);">d'Écologie Politique</span>
|
||
</div>
|
||
</a>
|
||
|
||
<!-- ── Onglets desktop (≥1024px) — remplace la barre de recherche ── -->
|
||
<nav class="hidden lg:flex flex-1 justify-center items-end gap-0 mx-6" aria-label="Navigation projets">
|
||
<NuxtLink
|
||
to="/"
|
||
class="nav-tab"
|
||
:class="{ 'nav-tab--active': route.path === '/' }"
|
||
>
|
||
Écosystème Entraide Architecture
|
||
</NuxtLink>
|
||
<NuxtLink
|
||
to="/agences"
|
||
class="nav-tab"
|
||
:class="{ 'nav-tab--active': route.path === '/agences' }"
|
||
>
|
||
Réseaux AEP
|
||
</NuxtLink>
|
||
<NuxtLink
|
||
to="/trouver-du-taf"
|
||
class="nav-tab"
|
||
:class="{ 'nav-tab--active': route.path === '/trouver-du-taf' }"
|
||
>
|
||
Jobs
|
||
</NuxtLink>
|
||
<NuxtLink
|
||
to="/codev"
|
||
class="nav-tab"
|
||
:class="{ 'nav-tab--active': route.path.startsWith('/codev') }"
|
||
>
|
||
Codev
|
||
</NuxtLink>
|
||
<NuxtLink
|
||
to="/rag"
|
||
class="nav-tab"
|
||
:class="{ 'nav-tab--active': route.path === '/rag' }"
|
||
>
|
||
RAG
|
||
<span class="nav-tab-badge">en construction</span>
|
||
</NuxtLink>
|
||
</nav>
|
||
|
||
<!-- ── Barre recherche mobile (640px–1024px) — masquée < 640px car accessible dans la sheet -->
|
||
<div class="hidden sm:flex lg:hidden flex-1 mx-2">
|
||
<label class="flex items-center gap-2 w-full px-3 py-1.5 rounded-xl border" style="background: var(--nav-bg); border-color: var(--nav-bg-alt);">
|
||
<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" style="color: var(--nav-text-muted); flex-shrink: 0;">
|
||
<circle cx="11" cy="11" r="8"/>
|
||
<line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||
</svg>
|
||
<input
|
||
v-model="headerSearch"
|
||
type="search"
|
||
placeholder="Rechercher…"
|
||
class="flex-1 bg-transparent border-0 outline-none text-sm"
|
||
style="color: var(--nav-text); font-family: var(--nav-font);"
|
||
autocomplete="off"
|
||
@input="onHeaderSearch"
|
||
@keydown.enter="onHeaderSearch"
|
||
/>
|
||
<button
|
||
v-if="headerSearch"
|
||
type="button"
|
||
@click.stop="clearHeaderSearch"
|
||
class="flex-shrink-0"
|
||
style="color: var(--nav-text-muted);"
|
||
aria-label="Effacer la recherche"
|
||
>
|
||
<svg width="13" height="13" 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>
|
||
</label>
|
||
</div>
|
||
|
||
<!-- Actions droite -->
|
||
<div class="flex items-center gap-2 shrink-0">
|
||
<NuxtLink
|
||
to="/a-propos"
|
||
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all hover:opacity-80 hidden md:inline-flex items-center gap-1"
|
||
style="color: var(--nav-text-muted);"
|
||
>
|
||
À propos
|
||
</NuxtLink>
|
||
<NuxtLink
|
||
to="/signaler"
|
||
class="px-3 py-1.5 rounded-lg text-sm font-medium transition-all hover:opacity-80 hidden lg:inline-flex items-center gap-1"
|
||
style="color: var(--nav-text-muted);"
|
||
>
|
||
Signaler
|
||
</NuxtLink>
|
||
<!-- Proposer — popover 3 choix -->
|
||
<div class="hidden sm:block relative" ref="proposerAnchor" data-proposer-popover>
|
||
<button
|
||
@click="proposerOpen = !proposerOpen"
|
||
class="px-3 py-1.5 rounded-lg text-sm font-semibold transition-all hover:opacity-80 inline-flex items-center gap-1"
|
||
style="background: var(--nav-accent); color: var(--nav-text);"
|
||
aria-label="Proposer une contribution"
|
||
>
|
||
+ Proposer
|
||
</button>
|
||
<div
|
||
v-if="proposerOpen"
|
||
class="absolute right-0 top-full mt-1 rounded-lg shadow-lg min-w-[240px] py-1"
|
||
style="background: var(--nav-surface); border: 1px solid var(--nav-bg-alt); z-index: 9999;"
|
||
>
|
||
<NuxtLink
|
||
to="/contribuer"
|
||
class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70"
|
||
style="color: var(--nav-text);"
|
||
@click="proposerOpen = false"
|
||
>
|
||
<span>Fiche Entraide <span style="color: var(--nav-text-muted); font-weight: 400; font-size: 0.7rem; display: block;">Carte 1 — Écosystème archi</span></span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
<div style="height: 1px; background: var(--nav-bg-alt); margin: 2px 0;"></div>
|
||
<NuxtLink
|
||
to="/contribuer-reseau"
|
||
class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70"
|
||
style="color: var(--nav-text);"
|
||
@click="proposerOpen = false"
|
||
>
|
||
<span>Réseau / collectif <span style="color: var(--nav-text-muted); font-weight: 400; font-size: 0.7rem; display: block;">Carte 2 — Réseaux AEP</span></span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
<div style="height: 1px; background: var(--nav-bg-alt); margin: 2px 0;"></div>
|
||
<NuxtLink
|
||
to="/contribuer-job"
|
||
class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70"
|
||
style="color: var(--nav-text);"
|
||
@click="proposerOpen = false"
|
||
>
|
||
<span>Plateforme jobs <span style="color: var(--nav-text-muted); font-weight: 400; font-size: 0.7rem; display: block;">Carte 3 — Jobs archi</span></span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toggle dark mode -->
|
||
<button
|
||
@click="toggleDark"
|
||
class="p-2 rounded-lg transition-all hover:opacity-80"
|
||
style="background: var(--nav-bg-alt); color: var(--nav-text-muted);"
|
||
:title="isDark ? 'Passer en mode clair' : 'Passer en mode sombre'"
|
||
:aria-label="isDark ? 'Mode clair' : 'Mode sombre'"
|
||
>
|
||
<svg v-if="!isDark" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
||
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
|
||
</svg>
|
||
<svg v-else width="15" height="15" 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="5"/>
|
||
<line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
|
||
<line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
|
||
</svg>
|
||
</button>
|
||
|
||
<!-- Mobile : contribuer icône → popover -->
|
||
<div class="sm:hidden relative" data-proposer-popover>
|
||
<button
|
||
@click="proposerOpen = !proposerOpen"
|
||
class="p-2 rounded-lg"
|
||
style="background: var(--nav-accent); color: var(--nav-text);"
|
||
title="Contribuer"
|
||
aria-label="Contribuer"
|
||
>
|
||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||
</svg>
|
||
</button>
|
||
<div
|
||
v-if="proposerOpen"
|
||
class="absolute right-0 top-full mt-1 rounded-lg shadow-lg min-w-[220px] py-1"
|
||
style="background: var(--nav-surface); border: 1px solid var(--nav-bg-alt); z-index: 9999;"
|
||
>
|
||
<NuxtLink to="/contribuer" class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" style="color: var(--nav-text);" @click="proposerOpen = false">
|
||
<span>Fiche Entraide</span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
<div style="height: 1px; background: var(--nav-bg-alt); margin: 2px 0;"></div>
|
||
<NuxtLink to="/contribuer-reseau" class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" style="color: var(--nav-text);" @click="proposerOpen = false">
|
||
<span>Réseau / collectif</span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
<div style="height: 1px; background: var(--nav-bg-alt); margin: 2px 0;"></div>
|
||
<NuxtLink to="/contribuer-job" class="flex items-center justify-between px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" style="color: var(--nav-text);" @click="proposerOpen = false">
|
||
<span>Plateforme jobs</span>
|
||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0; color: var(--nav-text-muted);"><polyline points="9 18 15 12 9 6"/></svg>
|
||
</NuxtLink>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Hamburger mobile (lg:hidden) — toujours en dernier à droite -->
|
||
<div class="lg:hidden relative">
|
||
<button
|
||
@click="hamburgerOpen = !hamburgerOpen"
|
||
class="p-2 rounded-lg transition-all hover:opacity-80"
|
||
style="background: var(--nav-bg-alt); color: var(--nav-text-muted);"
|
||
:aria-label="hamburgerOpen ? 'Fermer le menu' : 'Menu'"
|
||
:aria-expanded="hamburgerOpen"
|
||
>
|
||
<svg v-if="!hamburgerOpen" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
||
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
|
||
</svg>
|
||
<svg v-else width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" aria-hidden="true">
|
||
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
|
||
</svg>
|
||
</button>
|
||
<div
|
||
v-if="hamburgerOpen"
|
||
class="absolute right-0 top-full mt-1 rounded-lg shadow-lg min-w-[210px] py-1"
|
||
style="background: var(--nav-surface); border: 1px solid var(--nav-bg-alt); z-index: 9999;"
|
||
@click="hamburgerOpen = false"
|
||
>
|
||
<NuxtLink to="/" class="block px-4 py-2.5 text-sm font-medium transition-opacity hover:opacity-70" :style="route.path === '/' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Écosystème Entraide Architecture</NuxtLink>
|
||
<NuxtLink to="/agences" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/agences' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Réseaux AEP</NuxtLink>
|
||
<NuxtLink to="/trouver-du-taf" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/trouver-du-taf' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Jobs</NuxtLink>
|
||
<NuxtLink to="/rag" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text);">RAG</NuxtLink>
|
||
<NuxtLink to="/codev" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path.startsWith('/codev') ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text);'">Codev</NuxtLink>
|
||
<div style="height: 1px; background: var(--nav-bg-alt); margin: 4px 0;"></div>
|
||
<NuxtLink to="/manifeste" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" :style="route.path === '/manifeste' ? 'color: var(--nav-primary-solid); font-weight: 700;' : 'color: var(--nav-text-muted);'">Manifeste</NuxtLink>
|
||
<NuxtLink to="/a-propos" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text-muted);">À propos</NuxtLink>
|
||
<a href="https://liberapay.com/trans-former.fr/donate" target="_blank" rel="noopener noreferrer" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text-muted);">Soutenir →</a>
|
||
<NuxtLink to="/signaler" class="block px-4 py-2.5 text-sm transition-opacity hover:opacity-70" style="color: var(--nav-text-muted);">Signaler</NuxtLink>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Contenu page (flex-1 pour remplir l'espace) -->
|
||
<div class="flex-1" :class="route.path === '/' ? 'overflow-hidden' : 'overflow-y-auto'">
|
||
<NuxtPage />
|
||
</div>
|
||
|
||
<!-- Bandeau bas — transparence IA + soutien + compteurs semaine -->
|
||
<BandeauBas />
|
||
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
|
||
const hamburgerOpen = ref(false)
|
||
watch(() => route.path, () => { hamburgerOpen.value = false })
|
||
|
||
// ── Popover "+ Proposer" ─────────────────────────────────────────────────
|
||
const proposerOpen = ref(false)
|
||
const proposerAnchor = ref<HTMLElement | null>(null)
|
||
|
||
function onClickOutsideProposer(e: MouseEvent) {
|
||
// Ferme si le clic est hors de tout élément portant data-proposer-popover
|
||
const target = e.target as HTMLElement
|
||
if (!target.closest('[data-proposer-popover]')) {
|
||
proposerOpen.value = false
|
||
}
|
||
}
|
||
|
||
watch(proposerOpen, (open) => {
|
||
if (open) {
|
||
// Délai court pour ne pas attraper le clic d'ouverture lui-même
|
||
setTimeout(() => document.addEventListener('click', onClickOutsideProposer, true), 10)
|
||
} else {
|
||
document.removeEventListener('click', onClickOutsideProposer, true)
|
||
}
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
document.removeEventListener('click', onClickOutsideProposer, true)
|
||
})
|
||
|
||
// ── Dark mode ─────────────────────────────────────────────────────────────
|
||
const isDark = ref(false)
|
||
|
||
onMounted(() => {
|
||
const stored = localStorage.getItem('aep_theme')
|
||
if (stored === 'dark') {
|
||
isDark.value = true
|
||
document.documentElement.classList.add('dark')
|
||
}
|
||
})
|
||
|
||
function toggleDark() {
|
||
isDark.value = !isDark.value
|
||
if (isDark.value) {
|
||
document.documentElement.classList.add('dark')
|
||
localStorage.setItem('aep_theme', 'dark')
|
||
} else {
|
||
document.documentElement.classList.remove('dark')
|
||
localStorage.setItem('aep_theme', 'light')
|
||
}
|
||
}
|
||
|
||
// ── Barre de recherche header mobile ─────────────────────────────────────
|
||
const headerSearch = ref((route.query.q as string) ?? '')
|
||
|
||
// Sync depuis URL quand la route change
|
||
watch(() => route.query.q, (v) => {
|
||
headerSearch.value = (v as string) ?? ''
|
||
})
|
||
|
||
function onHeaderSearch() {
|
||
const q = headerSearch.value.trim()
|
||
if (route.path === '/') {
|
||
router.replace({ query: q ? { ...route.query, q } : { ...route.query, q: undefined } })
|
||
} else if (q) {
|
||
router.push({ path: '/', query: { q } })
|
||
}
|
||
}
|
||
|
||
function clearHeaderSearch() {
|
||
headerSearch.value = ''
|
||
if (route.path === '/') {
|
||
const q = { ...route.query }
|
||
delete q.q
|
||
router.replace({ query: Object.keys(q).length ? q : undefined })
|
||
}
|
||
}
|
||
|
||
// ── Fiche aléatoire ───────────────────────────────────────────────────────
|
||
function goRandom() {
|
||
router.push({ path: '/', query: { random: '1' } })
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
/* ── Logo header (texte 2 lignes) ─────────────────────────────────────── */
|
||
.logo-text {
|
||
line-height: 1.05;
|
||
}
|
||
.logo-line-1, .logo-line-2 {
|
||
font-size: 0.7rem;
|
||
letter-spacing: -0.01em;
|
||
}
|
||
@media (min-width: 640px) {
|
||
.logo-line-1, .logo-line-2 { font-size: 0.78rem; }
|
||
}
|
||
@media (min-width: 1024px) {
|
||
.logo-line-1, .logo-line-2 { font-size: 0.85rem; }
|
||
}
|
||
|
||
/* ── Onglets header desktop ───────────────────────────────────────────── */
|
||
.nav-tab {
|
||
position: relative;
|
||
display: inline-flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 6px 16px 8px;
|
||
font-size: 0.8rem;
|
||
font-weight: 500;
|
||
color: var(--nav-text-muted);
|
||
text-decoration: none;
|
||
transition: color 0.15s;
|
||
border-bottom: 2px solid transparent;
|
||
gap: 2px;
|
||
}
|
||
|
||
.nav-tab:hover {
|
||
color: var(--nav-text);
|
||
}
|
||
|
||
.nav-tab--active {
|
||
color: var(--nav-text);
|
||
border-bottom-color: var(--nav-primary-solid);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.nav-tab-badge {
|
||
font-size: 0.6rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.04em;
|
||
color: var(--nav-text-muted);
|
||
opacity: 0.65;
|
||
}
|
||
</style>
|