Compare commits
3 Commits
586742d90e
...
11732a6a4b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11732a6a4b | ||
|
|
538c9a1214 | ||
|
|
8d673482b6 |
@@ -11,6 +11,56 @@ Journal technique de la V2. Décisions, anomalies, points bloquants, TODOs.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 2026-05-08 — Fix mobile + chatbot prod (cause racine résolue)
|
||||||
|
|
||||||
|
**Commits :** session loggée sur main (pushé sur gitea)
|
||||||
|
**Pattern :** pilote direct, 2 batches successifs, ~3h, 11 fichiers
|
||||||
|
|
||||||
|
### Cause racine bug "chatbot Carte 1 == Carte 2"
|
||||||
|
|
||||||
|
`/api/chatbot-reseaux` était **404 en prod** (jamais déployé) — explique pourquoi 5 cycles de fix précédents (ChatbotReseaux.vue prop endpoint, useRoute fallback, useMarkdown direct, etc.) n'ont rien donné : le code source était correct depuis le début. Le rebuild + redeploy de cette session résout le bug.
|
||||||
|
|
||||||
|
**Verif :** `curl -s -X POST https://aep.trans-former.fr/api/chatbot-reseaux` → 200 + réponse distincte de `/api/chatbot`.
|
||||||
|
|
||||||
|
### Batch 1 — fixes mobile principaux
|
||||||
|
|
||||||
|
- Hamburger app.vue : ajout Jobs + Manifeste + Soutenir, ré-ordonnancement (Manifeste dans 2e groupe avec À propos/Soutenir/Signaler)
|
||||||
|
- BandeauBas.vue : FAB cœur jaune mobile retiré (Soutenir migré dans hamburger via lien Liberapay direct)
|
||||||
|
- agences.vue mobile : 3e onglet "Graphe" ajouté + masquage MobileSheet en mode graphe (canvas fullscreen)
|
||||||
|
- a-propos.vue : section 1 "Mission" retirée (devient pop-up Carte 1) + `overflow-x: hidden` sur `.apropos-page` + retrait `white-space: nowrap` problématique sur `.badge-detail`
|
||||||
|
- pages/manifeste.vue : nouvelle page (texte version `manifeste-page-carto-V1.md`, sans le diagramme ASCII pour V1 web)
|
||||||
|
- components/MissionPopup.vue : nouveau composant générique (props `title`, `ctaLabel`, `storageKey`, slot pour contenu, `:slotted()` pour styles)
|
||||||
|
- index.vue : intégration MissionPopup + bouton (i) `position:fixed` bottom-left + auto-show 1ère visite via `localStorage.aep_mission_seen`
|
||||||
|
- trouver-du-taf.vue : toggle "Filtres [N] [chevron]" mobile-only (`@media max-width: 767px`) avec `taff-filters-collapsible` max-height transition
|
||||||
|
- FicheModal.vue + FicheModalV2.vue : sur mobile `top: 76px` + `max-height: calc(100dvh - 92px)` au lieu de `top: 50% translate(-50%, -50%)` + `max-height: 90vh` qui mordait sur le header
|
||||||
|
|
||||||
|
### Batch 2 — pop-up Carte 2, logo, intro Jobs, labels graphe
|
||||||
|
|
||||||
|
- agences.vue : pop-up Réseaux AEP avec MissionPopup (storageKey `aep_reseaux_seen`, ctaLabel "Explorer les 120 réseaux") + bouton (i) flottant
|
||||||
|
- app.vue logo header : badge AEP + 2 spans `logo-line-1` ("Architecture") / `logo-line-2` ("d'Écologie Politique") avec font-size responsive (0.7rem mobile → 0.85rem ≥1024)
|
||||||
|
- trouver-du-taf.vue : `<details class="taff-pedago" open>` avec 3 blocs (deux onglets, trois étiquettes, cinq axes) + onglet "Plateformes B2C" → "Pour archi indépendants"
|
||||||
|
- GraphView.vue : `d3NodeSelection.filter(type==='structure').append('text')` avec class `graph-struct-label`, `dy: -(d.r + 5)`, font-size 9.5px, halo via `paint-order: stroke; stroke: var(--nav-bg)` (style global non-scoped pour piercer D3)
|
||||||
|
|
||||||
|
### Bug d'opération à retenir
|
||||||
|
|
||||||
|
Lors du 1er déploiement batch 2, `bash deploy.sh` semblait OK (HTTP 200) mais le HTML en prod ne contenait pas les modifs. **Cause** : Dropbox sync a effacé `.output/` entre `npm run build` et le tar SCP — le tar a uploadé un `.output` quasi-vide. Solution : 2e cycle clean (`Remove-Item .nuxt/dist + .output`) + rebuild + redeploy avec `yes y |` (skip confirm interactif `.env diff`).
|
||||||
|
|
||||||
|
**Réflexe à intégrer** : après build, vérifier `grep -o "<un-fragment-de-modif>" .output/public/_nuxt/*.js | head` AVANT le deploy. Si 0 match → ne pas deploy, rebuild.
|
||||||
|
|
||||||
|
### Bug de communication à retenir
|
||||||
|
|
||||||
|
Jules a signalé "le logo n'a pas marché", "B2C pas renommé", "hamburger pas modifié" alors que le HTML en prod contenait bien les modifs (vérifié curl avec `?nc=$(date +%s)`). **Cause** : cache navigateur / service worker Nuxt. Réflexe à mettre en place pour /done de toute session web : si Jules dit "ça n'apparaît pas", vérifier curl en bypass cache AVANT de chercher un bug. Si match curl → demander hard refresh (Ctrl+Shift+R).
|
||||||
|
|
||||||
|
### Reste à faire (batch 3)
|
||||||
|
|
||||||
|
Voir `0 INBOX/PROMPTS/cascade-megaboum/REPRISE-aep-carto-fix-batch3.md` :
|
||||||
|
- Bouton "+" → sélecteur 3 cartes (Entraide/Réseaux/Jobs)
|
||||||
|
- Pop-up explication 5 axes Jobs (paragraphe par axe)
|
||||||
|
- Pop-up Carte 1 visibilité (option à clarifier avec Jules)
|
||||||
|
- GraphView Carte 1 (centres = hashtags, couche échelle activable) — gros chantier session dédiée
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-04-27 — Session V3 : Finition mobile + Blog Liberapay + 3 deploys
|
## 2026-04-27 — Session V3 : Finition mobile + Blog Liberapay + 3 deploys
|
||||||
|
|
||||||
**Commit :** `a02a555` — feat(mobile): accordéon outremer, hamburger nav, logo AEP, fiches cliquables, chatbot fullscreen
|
**Commit :** `a02a555` — feat(mobile): accordéon outremer, hamburger nav, logo AEP, fiches cliquables, chatbot fullscreen
|
||||||
|
|||||||
103
app.vue
103
app.vue
@@ -108,14 +108,52 @@
|
|||||||
>
|
>
|
||||||
Signaler
|
Signaler
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<!-- Proposer une ressource -->
|
<!-- Proposer — popover 3 choix -->
|
||||||
<NuxtLink
|
<div class="hidden sm:block relative" ref="proposerAnchor" data-proposer-popover>
|
||||||
to="/contribuer"
|
<button
|
||||||
class="px-3 py-1.5 rounded-lg text-sm font-semibold transition-all hover:opacity-80 hidden sm:inline-flex items-center gap-1"
|
@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);"
|
style="background: var(--nav-accent); color: var(--nav-text);"
|
||||||
|
aria-label="Proposer une contribution"
|
||||||
>
|
>
|
||||||
+ Proposer
|
+ 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>
|
</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 -->
|
<!-- Toggle dark mode -->
|
||||||
<button
|
<button
|
||||||
@@ -137,18 +175,40 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Mobile : contribuer icône -->
|
<!-- Mobile : contribuer icône → popover -->
|
||||||
<NuxtLink
|
<div class="sm:hidden relative" data-proposer-popover>
|
||||||
to="/contribuer"
|
<button
|
||||||
class="sm:hidden p-2 rounded-lg"
|
@click="proposerOpen = !proposerOpen"
|
||||||
|
class="p-2 rounded-lg"
|
||||||
style="background: var(--nav-accent); color: var(--nav-text);"
|
style="background: var(--nav-accent); color: var(--nav-text);"
|
||||||
title="Contribuer une fiche"
|
title="Contribuer"
|
||||||
aria-label="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">
|
<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"/>
|
<line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/>
|
||||||
</svg>
|
</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>
|
</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 -->
|
<!-- Hamburger mobile (lg:hidden) — toujours en dernier à droite -->
|
||||||
<div class="lg:hidden relative">
|
<div class="lg:hidden relative">
|
||||||
@@ -205,6 +265,31 @@ const route = useRoute()
|
|||||||
const hamburgerOpen = ref(false)
|
const hamburgerOpen = ref(false)
|
||||||
watch(() => route.path, () => { hamburgerOpen.value = 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 ─────────────────────────────────────────────────────────────
|
// ── Dark mode ─────────────────────────────────────────────────────────────
|
||||||
const isDark = ref(false)
|
const isDark = ref(false)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
<template>
|
||||||
<button v-if="!open" @click="open = true"
|
<button v-if="!open" @click="open = true"
|
||||||
class="fixed bottom-6 right-6 z-[1000] flex items-center gap-2 px-4 rounded-full shadow-lg"
|
class="fixed bottom-6 right-6 z-[1000] flex items-center gap-2 px-4 rounded-full shadow-lg"
|
||||||
style="height:48px;background:var(--nav-primary);color:var(--nav-text-on-primary);font-size:0.875rem;font-weight:600;"
|
style="height:48px;background:var(--nav-primary);color:var(--nav-text-on-primary);font-size:0.875rem;font-weight:600;"
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
<div v-if="open" class="fixed bottom-6 right-6 z-[1000] flex flex-col"
|
<div v-if="open" class="fixed bottom-6 right-6 z-[1000] flex flex-col"
|
||||||
style="width:min(360px,calc(100vw - 24px));max-height:60vh;background:var(--nav-surface);border-radius:14px;box-shadow:0 8px 32px rgba(26,34,56,0.22);overflow:hidden;border:1px solid var(--nav-bg-alt);"
|
style="width:min(360px,calc(100vw - 24px));max-height:60vh;background:var(--nav-surface);border-radius:14px;box-shadow:0 8px 32px rgba(26,34,56,0.22);overflow:hidden;border:1px solid var(--nav-bg-alt);"
|
||||||
role="dialog" aria-modal="true" aria-label="RAG Pensees Ecologiques">
|
role="dialog" aria-modal="true" aria-label="RAG Pensees Ecologiques">
|
||||||
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between px-4 py-3 shrink-0" style="border-bottom:1px solid var(--nav-bg-alt);background:var(--nav-bg);">
|
<div class="flex items-center justify-between px-4 py-3 shrink-0" style="border-bottom:1px solid var(--nav-bg-alt);background:var(--nav-bg);">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-sm font-bold" style="color:var(--nav-text);">RAG Pensees Ecologiques</p>
|
<p class="text-sm font-bold" style="color:var(--nav-text);">RAG Pensees Ecologiques</p>
|
||||||
@@ -25,9 +26,31 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Corpus toggle -->
|
||||||
|
<div class="shrink-0 px-3 pt-2 pb-1" style="background:var(--nav-bg);border-bottom:1px solid var(--nav-bg-alt);">
|
||||||
|
<div class="flex gap-1" role="group" aria-label="Choisir le corpus">
|
||||||
|
<button
|
||||||
|
v-for="opt in corpusOptions"
|
||||||
|
:key="opt.value"
|
||||||
|
@click="setCorpus(opt.value)"
|
||||||
|
:title="opt.tooltip"
|
||||||
|
class="flex-1 px-2 py-1 rounded text-xs font-medium transition-colors"
|
||||||
|
:style="corpus === opt.value
|
||||||
|
? 'background:var(--nav-primary);color:var(--nav-text-on-primary);'
|
||||||
|
: 'background:var(--nav-bg-alt);color:var(--nav-text-muted);'"
|
||||||
|
:aria-pressed="corpus === opt.value">
|
||||||
|
{{ opt.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Messages -->
|
||||||
<div ref="msgEl" class="flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-3" style="min-height:0;">
|
<div ref="msgEl" class="flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-3" style="min-height:0;">
|
||||||
<div v-if="messages.length === 0" style="font-size:0.8rem;color:var(--nav-text-muted);line-height:1.5;">
|
<div v-if="messages.length === 0" style="font-size:0.8rem;color:var(--nav-text-muted);line-height:1.5;">
|
||||||
Pose une question sur les pensees ecologiques : ecosocialisme, decroissance, ecofeminismes, technocritique, deep ecology...
|
<template v-if="corpus === 'pensees'">Pose une question sur les pensees ecologiques : ecosocialisme, decroissance, ecofeminismes, technocritique, deep ecology...</template>
|
||||||
|
<template v-else-if="corpus === 'projets'">Pose une question sur les projets d'architecture de Jules : Butte Pinson, strategie thermique, partis pris constructifs...</template>
|
||||||
|
<template v-else>Pose une question sur les pensees ecologiques ancrees dans les projets archi de Jules (corpus croise, defaut).</template>
|
||||||
</div>
|
</div>
|
||||||
<template v-for="(msg, i) in messages" :key="i">
|
<template v-for="(msg, i) in messages" :key="i">
|
||||||
<div v-if="msg.role === 'user'" class="self-end max-w-[85%] px-3 py-2 rounded-xl text-sm"
|
<div v-if="msg.role === 'user'" class="self-end max-w-[85%] px-3 py-2 rounded-xl text-sm"
|
||||||
@@ -35,14 +58,14 @@
|
|||||||
<div v-else class="self-start max-w-full">
|
<div v-else class="self-start max-w-full">
|
||||||
<div class="px-3 py-2 rounded-xl text-sm leading-relaxed" style="background:var(--nav-bg-alt);color:var(--nav-text);"
|
<div class="px-3 py-2 rounded-xl text-sm leading-relaxed" style="background:var(--nav-bg-alt);color:var(--nav-text);"
|
||||||
v-html="renderMd(stripSrc(msg.content))" />
|
v-html="renderMd(stripSrc(msg.content))" />
|
||||||
<div v-if="parseSrc(msg.content).length" class="mt-1.5">
|
<div v-if="filteredSources(msg.content).length" class="mt-1.5">
|
||||||
<button @click="toggled[i] = !toggled[i]" class="flex items-center gap-1 text-xs hover:opacity-70" style="color:var(--nav-text-muted);">
|
<button @click="toggled[i] = !toggled[i]" class="flex items-center gap-1 text-xs hover:opacity-70" style="color:var(--nav-text-muted);">
|
||||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"
|
||||||
:style="`transform:rotate(${toggled[i] ? 90 : 0}deg);transition:transform 0.15s`"><polyline points="9 18 15 12 9 6"/></svg>
|
:style="`transform:rotate(${toggled[i] ? 90 : 0}deg);transition:transform 0.15s`"><polyline points="9 18 15 12 9 6"/></svg>
|
||||||
Sources ({{ parseSrc(msg.content).length }})
|
Sources ({{ filteredSources(msg.content).length }})
|
||||||
</button>
|
</button>
|
||||||
<div v-if="toggled[i]" class="mt-1 flex flex-col gap-1">
|
<div v-if="toggled[i]" class="mt-1 flex flex-col gap-1">
|
||||||
<div v-for="(s, si) in parseSrc(msg.content)" :key="si" class="px-2 py-1 rounded text-xs"
|
<div v-for="(s, si) in filteredSources(msg.content)" :key="si" class="px-2 py-1 rounded text-xs"
|
||||||
style="background:var(--nav-bg-alt);color:var(--nav-text-muted);border-left:2px solid var(--nav-primary-solid);">
|
style="background:var(--nav-bg-alt);color:var(--nav-text-muted);border-left:2px solid var(--nav-primary-solid);">
|
||||||
<span style="font-weight:600;color:var(--nav-text);">[{{ si + 1 }}]</span> {{ s }}
|
<span style="font-weight:600;color:var(--nav-text);">[{{ si + 1 }}]</span> {{ s }}
|
||||||
</div>
|
</div>
|
||||||
@@ -55,6 +78,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-if="err" class="text-xs px-3 py-2 rounded-xl" style="background:#fee;color:#c0392b;">{{ err }}</div>
|
<div v-if="err" class="text-xs px-3 py-2 rounded-xl" style="background:#fee;color:#c0392b;">{{ err }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Input -->
|
||||||
<div class="shrink-0 px-3 py-3" style="border-top:1px solid var(--nav-bg-alt);">
|
<div class="shrink-0 px-3 py-3" style="border-top:1px solid var(--nav-bg-alt);">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<input ref="inputEl" v-model="q" type="text" placeholder="Ta question..." maxlength="500"
|
<input ref="inputEl" v-model="q" type="text" placeholder="Ta question..." maxlength="500"
|
||||||
@@ -77,6 +102,25 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Message { role: 'user' | 'assistant'; content: string }
|
interface Message { role: 'user' | 'assistant'; content: string }
|
||||||
|
|
||||||
|
type CorpusMode = 'pensees' | 'projets' | 'both'
|
||||||
|
|
||||||
|
const CORPUS_STORAGE_KEY = 'chatbot-pensees-corpus'
|
||||||
|
|
||||||
|
// Patterns projet : les sources qui matchent sont des refs projet archi
|
||||||
|
// Pattern actuel : butte pinson. Extensible en ajoutant d'autres slugs.
|
||||||
|
const PROJECT_SOURCE_PATTERNS = [/butte.?pinson/i, /butte_pinson/i]
|
||||||
|
|
||||||
|
function isProjectSource(s: string): boolean {
|
||||||
|
return PROJECT_SOURCE_PATTERNS.some(p => p.test(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
const corpusOptions: { value: CorpusMode; label: string; tooltip: string }[] = [
|
||||||
|
{ value: 'pensees', label: 'Pensees', tooltip: 'Corpus FRACAS uniquement (auteurs ecologie politique)' },
|
||||||
|
{ value: 'projets', label: 'Projets', tooltip: 'Projets archi de Jules uniquement' },
|
||||||
|
{ value: 'both', label: 'Croise*', tooltip: 'Projets ancres + pensees en eclairage (defaut)' },
|
||||||
|
]
|
||||||
|
|
||||||
const props = defineProps<{ auteurContext?: string | null }>()
|
const props = defineProps<{ auteurContext?: string | null }>()
|
||||||
const open = ref(false)
|
const open = ref(false)
|
||||||
const q = ref('')
|
const q = ref('')
|
||||||
@@ -88,6 +132,23 @@ const msgEl = ref<HTMLElement | null>(null)
|
|||||||
const inputEl = ref<HTMLInputElement | null>(null)
|
const inputEl = ref<HTMLInputElement | null>(null)
|
||||||
const corpusCount = 18
|
const corpusCount = 18
|
||||||
|
|
||||||
|
// Corpus state - init depuis localStorage
|
||||||
|
const corpus = ref<CorpusMode>('both')
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
const saved = window.localStorage.getItem(CORPUS_STORAGE_KEY) as CorpusMode | null
|
||||||
|
if (saved && ['pensees', 'projets', 'both'].includes(saved)) {
|
||||||
|
corpus.value = saved
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCorpus(val: CorpusMode) {
|
||||||
|
corpus.value = val
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.localStorage.setItem(CORPUS_STORAGE_KEY, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(open, (val) => {
|
watch(open, (val) => {
|
||||||
if (!val) return
|
if (!val) return
|
||||||
nextTick(() => inputEl.value?.focus())
|
nextTick(() => inputEl.value?.focus())
|
||||||
@@ -109,7 +170,10 @@ async function send() {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
await nextTick(); scrollBottom()
|
await nextTick(); scrollBottom()
|
||||||
try {
|
try {
|
||||||
const res = await $fetch<{ response: string }>('/api/chatbot-pensees', { method: 'POST', body: { query, mode: 'hybrid' } })
|
const res = await $fetch<{ response: string }>('/api/chatbot-pensees', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { query, mode: 'hybrid', corpus: corpus.value },
|
||||||
|
})
|
||||||
messages.value.push({ role: 'assistant', content: res.response ?? '' })
|
messages.value.push({ role: 'assistant', content: res.response ?? '' })
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
const s = e?.response?.status ?? e?.statusCode
|
const s = e?.response?.status ?? e?.statusCode
|
||||||
@@ -119,16 +183,28 @@ async function send() {
|
|||||||
await nextTick(); scrollBottom()
|
await nextTick(); scrollBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scrollBottom() { if (msgEl.value) msgEl.value.scrollTop = msgEl.value.scrollHeight }
|
function scrollBottom() { if (msgEl.value) msgEl.value.scrollTop = msgEl.value.scrollHeight }
|
||||||
|
|
||||||
function renderMd(t: string) {
|
function renderMd(t: string) {
|
||||||
return '<p>' + t.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/\*(.+?)\*/g, '<em>$1</em>').replace(/\n\n/g, '</p><p>').replace(/\n/g, '<br>') + '</p>'
|
return '<p>' + t.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/\*(.+?)\*/g, '<em>$1</em>').replace(/\n\n/g, '</p><p>').replace(/\n/g, '<br>') + '</p>'
|
||||||
}
|
}
|
||||||
function stripSrc(t: string) { return t.replace(/\n*(?:Sources?|References?)\s*:[\s\S]*$/i, '').trim() }
|
function stripSrc(t: string) { return t.replace(/\n*(?:Sources?|References?)\s*:[\s\S]*$/i, '').trim() }
|
||||||
|
|
||||||
function parseSrc(t: string): string[] {
|
function parseSrc(t: string): string[] {
|
||||||
const bloc = t.match(/\n*(?:Sources?|References?)\s*:\n?([\s\S]+?)$/i)
|
const bloc = t.match(/\n*(?:Sources?|References?)\s*:\n?([\s\S]+?)$/i)
|
||||||
if (bloc) return bloc[1].split('\n').map(l => l.replace(/^[-*\d.[\]]+\s*/, '').trim()).filter(l => l.length > 3)
|
if (bloc) return bloc[1].split('\n').map(l => l.replace(/^[-*\d.[\]]+\s*/, '').trim()).filter(l => l.length > 3)
|
||||||
return [...new Set([...t.matchAll(/\[([^\]]{5,80})\]/g)].filter(m => m[1].includes(' - ')).map(m => m[1]))]
|
return [...new Set([...t.matchAll(/\[([^\]]{5,80})\]/g)].filter(m => m[1].includes(' - ')).map(m => m[1]))]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filtrage UI des sources selon corpus actif
|
||||||
|
function filteredSources(t: string): string[] {
|
||||||
|
const all = parseSrc(t)
|
||||||
|
if (corpus.value === 'both') return all
|
||||||
|
if (corpus.value === 'projets') return all.filter(s => isProjectSource(s))
|
||||||
|
// corpus === 'pensees' : exclure les sources projet
|
||||||
|
return all.filter(s => !isProjectSource(s))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@@ -52,9 +52,10 @@
|
|||||||
<div class="chatbot-body-inner" ref="messagesContainer">
|
<div class="chatbot-body-inner" ref="messagesContainer">
|
||||||
<!-- Onboarding -->
|
<!-- Onboarding -->
|
||||||
<div v-if="messages.length === 0" class="onboarding-bubble">
|
<div v-if="messages.length === 0" class="onboarding-bubble">
|
||||||
<p>Explore les 120 structures de la carte par la conversation. Je peux t'aider à trouver des collectifs, agences ou réseaux selon ta situation, ta pratique ou tes inspirations du moment.</p>
|
<p>Je connais les structures d'entraide pour architectes référencées sur cette carte — appui juridique, technique, économique, formation, santé mentale, gestion d'agence…</p>
|
||||||
<p class="example">Exemple : "Je cherche des acteurs de la rénovation de maisons individuelles en France, plutôt en milieu rural, avec des approches biosourcées ou low-tech."</p>
|
<p>Décris ta situation, je te propose les fiches les plus pertinentes.</p>
|
||||||
<p style="margin-top: 8px; font-size: 0.72rem; opacity: 0.6;">Propulsé par Mistral FR - serveur européen souverain, zéro rétention.</p>
|
<p class="example">Exemple : "Architecte salarié, litige avec mon employeur, besoin d'un appui juridique droit du travail, Île-de-France."</p>
|
||||||
|
<p style="margin-top: 8px; font-size: 0.72rem; opacity: 0.6;">Propulsé par Mistral FR — serveur européen souverain, zéro rétention.</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Messages -->
|
<!-- Messages -->
|
||||||
|
|||||||
@@ -1,38 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-1.5">
|
<div class="space-y-1">
|
||||||
<p class="text-xs font-bold uppercase tracking-widest" style="color: var(--nav-text-muted);">Échelle</p>
|
<p class="filter-label">ÉCHELLE</p>
|
||||||
<!-- Inline sur 1 ligne — même pattern que FonctionFilter -->
|
<div class="chips-row">
|
||||||
<div class="flex flex-wrap gap-x-4 gap-y-1.5">
|
<span
|
||||||
<label
|
|
||||||
v-for="option in ECHELLES"
|
v-for="option in ECHELLES"
|
||||||
:key="option"
|
:key="option"
|
||||||
class="flex items-center gap-1.5 cursor-pointer select-none transition-opacity"
|
class="chip"
|
||||||
>
|
|
||||||
<!-- Case carrée -->
|
|
||||||
<span
|
|
||||||
class="flex items-center justify-center shrink-0 transition-all"
|
|
||||||
style="width: 18px; height: 18px; border: 1.5px solid; border-radius: 3px;"
|
|
||||||
:style="isSelected(option)
|
:style="isSelected(option)
|
||||||
? 'background: var(--nav-primary); border-color: var(--nav-primary); color: #ffffff;'
|
? 'background: var(--nav-primary); color: var(--nav-text-on-primary); font-weight: 600;'
|
||||||
: 'background: var(--nav-bg-alt); border-color: rgba(26,34,56,0.25); color: transparent;'"
|
: 'background: var(--nav-bg-alt); color: var(--nav-text-muted);'"
|
||||||
>
|
@click="toggle(option)"
|
||||||
<svg v-if="isSelected(option)" width="11" height="11" viewBox="0 0 12 12" fill="none">
|
|
||||||
<polyline points="2,6 5,9 10,3" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<!-- Label -->
|
|
||||||
<span
|
|
||||||
class="text-sm leading-tight"
|
|
||||||
:style="isSelected(option) ? 'color: var(--nav-text); font-weight: 600;' : 'color: var(--nav-text);'"
|
|
||||||
>{{ option }}</span>
|
>{{ option }}</span>
|
||||||
<!-- Input réel (masqué) -->
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
class="sr-only"
|
|
||||||
:checked="isSelected(option)"
|
|
||||||
@change="toggle(option)"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -61,3 +39,24 @@ function toggle(option: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.filter-label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--nav-text-muted);
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.chips-row { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px; }
|
||||||
|
.chip {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
transition: all 0.15s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,35 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-1.5">
|
|
||||||
<p class="text-xs font-bold uppercase tracking-widest" style="color: var(--nav-text-muted);">Fonction</p>
|
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
|
<!-- Label + toggle collapse -->
|
||||||
|
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;">
|
||||||
|
<p class="filter-label" style="margin-bottom: 0;">
|
||||||
|
FONCTION
|
||||||
|
<span v-if="modelValue.length" style="font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 0.65rem; margin-left: 4px;">({{ modelValue.length }} active{{ modelValue.length > 1 ? 's' : '' }})</span>
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
|
@click="toggleCollapse"
|
||||||
|
style="font-size: 0.7rem; color: var(--nav-text-muted); background: none; border: none; cursor: pointer; text-decoration: underline; padding: 0; white-space: nowrap;"
|
||||||
|
>{{ isOpen ? 'Replier' : 'Fonctions (' + FONCTIONS.length + ')' }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Chips (visible si ouvert ou si des fonctions sont actives) -->
|
||||||
|
<div v-if="isOpen" class="chips-row">
|
||||||
|
<span
|
||||||
v-for="fn in FONCTIONS"
|
v-for="fn in FONCTIONS"
|
||||||
:key="fn"
|
:key="fn"
|
||||||
@click="toggle(fn)"
|
class="chip"
|
||||||
:aria-pressed="modelValue.includes(fn)"
|
|
||||||
class="flex items-center gap-2.5 w-full rounded px-1 py-0.5 transition-all text-left hover:opacity-80"
|
|
||||||
:style="modelValue.includes(fn) ? 'background: rgba(26,34,56,0.06);' : ''"
|
|
||||||
>
|
|
||||||
<!-- Case : affiche le rang de priorité si actif, sinon le nombre d'orgs -->
|
|
||||||
<span
|
|
||||||
class="flex items-center justify-center shrink-0 text-xs font-bold transition-all"
|
|
||||||
style="width: 24px; height: 24px; border: 1.5px solid; border-radius: 4px;"
|
|
||||||
:style="modelValue.includes(fn)
|
:style="modelValue.includes(fn)
|
||||||
? 'background: var(--nav-primary); border-color: var(--nav-primary); color: var(--nav-text-on-primary);'
|
? 'background: var(--nav-primary); color: var(--nav-text-on-primary); font-weight: 600;'
|
||||||
: 'background: var(--nav-bg-alt); border-color: var(--nav-bg-alt); color: var(--nav-text-muted);'"
|
: 'background: var(--nav-bg-alt); color: var(--nav-text-muted);'"
|
||||||
>
|
@click="toggle(fn)"
|
||||||
{{ modelValue.includes(fn) ? (modelValue.indexOf(fn) + 1) : (counts[fn] ?? 0) }}
|
|
||||||
</span>
|
|
||||||
<!-- Label -->
|
|
||||||
<span
|
|
||||||
class="text-sm leading-tight"
|
|
||||||
:style="modelValue.includes(fn) ? 'color: var(--nav-text); font-weight: 600;' : 'color: var(--nav-text);'"
|
|
||||||
>{{ fn }}</span>
|
>{{ fn }}</span>
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Effacer (visible même replié si filtres actifs) -->
|
||||||
<p v-if="modelValue.length" class="text-xs pt-0.5" style="color: var(--nav-text-muted);">
|
<p v-if="modelValue.length" class="text-xs pt-0.5" style="color: var(--nav-text-muted);">
|
||||||
{{ modelValue.length }} actif{{ modelValue.length > 1 ? 's' : '' }}
|
<button @click="emit('update:modelValue', [])" class="underline hover:opacity-70">Effacer</button>
|
||||||
<button @click="emit('update:modelValue', [])" class="ml-2 underline hover:opacity-70">Effacer</button>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -57,6 +55,25 @@ const emit = defineEmits<{
|
|||||||
'update:modelValue': [value: string[]]
|
'update:modelValue': [value: string[]]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
// Replié par défaut, ouvre automatiquement quand des filtres sont actifs
|
||||||
|
const manuallyOpen = ref(false)
|
||||||
|
|
||||||
|
const isOpen = computed(() => {
|
||||||
|
return manuallyOpen.value || props.modelValue.length > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
function toggleCollapse() {
|
||||||
|
// Si des filtres actifs forcent l'ouverture, on doit gérer le cas « forcer fermer »
|
||||||
|
if (isOpen.value) {
|
||||||
|
manuallyOpen.value = false
|
||||||
|
// Si des fonctions sont actives, le computed va les réouvrir — on les efface
|
||||||
|
// Non : on laisse le choix à l'utilisateur. On toggle juste manuallyOpen.
|
||||||
|
// Quand replié avec filtres actifs, l'indicateur "(N actives)" reste visible.
|
||||||
|
} else {
|
||||||
|
manuallyOpen.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function toggle(fn: string) {
|
function toggle(fn: string) {
|
||||||
if (props.modelValue.includes(fn)) {
|
if (props.modelValue.includes(fn)) {
|
||||||
emit('update:modelValue', props.modelValue.filter(f => f !== fn))
|
emit('update:modelValue', props.modelValue.filter(f => f !== fn))
|
||||||
@@ -65,3 +82,23 @@ function toggle(fn: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.filter-label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: var(--nav-text-muted);
|
||||||
|
display: block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.chips-row { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 4px; }
|
||||||
|
.chip {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 3px 10px;
|
||||||
|
border-radius: 9999px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
transition: all 0.15s;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -877,6 +877,7 @@ onUnmounted(() => {
|
|||||||
/* Labels des structures dans le graphe (D3 injecte les <text>, donc style global) */
|
/* Labels des structures dans le graphe (D3 injecte les <text>, donc style global) */
|
||||||
.graph-view .graph-struct-label {
|
.graph-view .graph-struct-label {
|
||||||
fill: var(--nav-text);
|
fill: var(--nav-text);
|
||||||
|
opacity: 0.7;
|
||||||
paint-order: stroke;
|
paint-order: stroke;
|
||||||
stroke: var(--nav-bg);
|
stroke: var(--nav-bg);
|
||||||
stroke-width: 3px;
|
stroke-width: 3px;
|
||||||
|
|||||||
@@ -129,11 +129,11 @@
|
|||||||
@click="desktopMapView = 'graphe'"
|
@click="desktopMapView = 'graphe'"
|
||||||
>Vue graphique</button>
|
>Vue graphique</button>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/pensees-ecologiques"
|
to="/media"
|
||||||
class="px-5 py-2 text-sm font-medium transition-colors"
|
class="px-5 py-2 text-sm font-medium transition-colors"
|
||||||
style="color: var(--nav-text-muted); border-bottom: 2px solid transparent;"
|
style="color: var(--nav-text-muted); border-bottom: 2px solid transparent;"
|
||||||
active-class="!color-nav-text"
|
active-class="!color-nav-text"
|
||||||
>Pensees</NuxtLink>
|
>Média</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Carte Métropole desktop -->
|
<!-- Carte Métropole desktop -->
|
||||||
@@ -226,10 +226,10 @@
|
|||||||
@click="mobileMapView = 'graphe'"
|
@click="mobileMapView = 'graphe'"
|
||||||
>Graphe</button>
|
>Graphe</button>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/pensees-ecologiques"
|
to="/media"
|
||||||
class="flex-1 py-2 text-sm font-medium transition-colors text-center"
|
class="flex-1 py-2 text-sm font-medium transition-colors text-center"
|
||||||
style="color: var(--nav-text-muted); border-bottom: 2px solid transparent;"
|
style="color: var(--nav-text-muted); border-bottom: 2px solid transparent;"
|
||||||
>Pensees</NuxtLink>
|
>Média</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="lg:hidden flex-1 relative overflow-hidden">
|
<div class="lg:hidden flex-1 relative overflow-hidden">
|
||||||
|
|||||||
@@ -201,10 +201,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Filtres FONCTION — chips flex-wrap -->
|
<!-- Filtres FONCTION — chips flex-wrap + toggle collapse -->
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<span class="text-xs font-bold uppercase tracking-wide block mb-1" style="color: var(--nav-text-muted);">FONCTION</span>
|
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px;">
|
||||||
<div class="flex flex-wrap gap-1">
|
<span class="text-xs font-bold uppercase tracking-wide" style="color: var(--nav-text-muted);">
|
||||||
|
FONCTION
|
||||||
|
<span v-if="fonctions.length" style="font-weight: 400; text-transform: none; letter-spacing: 0; font-size: 0.65rem; margin-left: 4px;">({{ fonctions.length }} active{{ fonctions.length > 1 ? 's' : '' }})</span>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
@click="mobileFonctionsOpen = !mobileFonctionsOpen"
|
||||||
|
style="font-size: 0.65rem; color: var(--nav-text-muted); background: none; border: none; cursor: pointer; text-decoration: underline; padding: 0; white-space: nowrap;"
|
||||||
|
>{{ mobileFonctionsOpen || fonctions.length ? (mobileFonctionsOpen ? 'Replier' : 'Afficher') : 'Fonctions (' + FONCTIONS.length + ')' }}</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="mobileFonctionsOpen || fonctions.length" class="flex flex-wrap gap-1">
|
||||||
<span
|
<span
|
||||||
v-for="fn in FONCTIONS"
|
v-for="fn in FONCTIONS"
|
||||||
:key="fn"
|
:key="fn"
|
||||||
@@ -365,6 +374,7 @@ const ficheModalOpen = ref(false)
|
|||||||
const ficheModalId = ref<number | null>(null)
|
const ficheModalId = ref<number | null>(null)
|
||||||
const mobileMapView = ref<'metropole' | 'outremer'>('metropole')
|
const mobileMapView = ref<'metropole' | 'outremer'>('metropole')
|
||||||
const missionOpen = ref(false)
|
const missionOpen = ref(false)
|
||||||
|
const mobileFonctionsOpen = ref(false)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,9 +7,9 @@
|
|||||||
<!-- Header onglet -->
|
<!-- Header onglet -->
|
||||||
<div class="shrink-0 px-5 py-3"
|
<div class="shrink-0 px-5 py-3"
|
||||||
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);">
|
style="background: var(--nav-surface); border-bottom: 1px solid var(--nav-bg-alt);">
|
||||||
<h1 class="font-bold text-base" style="color: var(--nav-text);">Pensees Ecologiques</h1>
|
<h1 class="font-bold text-base" style="color: var(--nav-text);">ATIS Média</h1>
|
||||||
<p class="text-xs mt-0.5" style="color: var(--nav-text-muted);">
|
<p class="text-xs mt-0.5" style="color: var(--nav-text-muted);">
|
||||||
{{ corpusCount }} auteurs ingeres dans le RAG - carte FRACAS Bonpote V2
|
{{ corpusCount }} auteurs ingérés dans le RAG - carte FRACAS Bonpote V2
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -79,5 +79,5 @@ function onInterrogerRag(auteurId: string) {
|
|||||||
chatbotAuteur.value = auteur?.nom ?? null
|
chatbotAuteur.value = auteur?.nom ?? null
|
||||||
}
|
}
|
||||||
|
|
||||||
useHead({ title: 'AEP - Pensees Ecologiques - Carte FRACAS' })
|
useHead({ title: 'AEP - Média - Carte FRACAS Bonpote' })
|
||||||
</script>
|
</script>
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
{
|
{
|
||||||
"meta": {
|
"meta": {
|
||||||
"version": "1.0",
|
"version": "2.0",
|
||||||
"source": "FRACAS Bonpote V2 oct 2024 + LightRAG corpus J+2",
|
"source": "FRACAS Bonpote V2 oct 2024 + LightRAG corpus 11/05/2026",
|
||||||
"corpus_ingere": 27,
|
"corpus_ingere": 141,
|
||||||
|
"auteurs_count": 28,
|
||||||
|
"livres_count": 64,
|
||||||
|
"ecoles_count": 12,
|
||||||
|
"note_doublons_en_fr": "3 livres avec version EN aussi indexee dans le RAG pour cross-language queries : carson-mer-autour-de-nous-fr/EN, graeber-wengrow-aurore-fr/EN, saito-capital-anthropocene/EN. JSON conserve la version FR.",
|
||||||
"updated": "2026-05-11"
|
"updated": "2026-05-11"
|
||||||
},
|
},
|
||||||
"ecoles": [
|
"ecoles": [
|
||||||
@@ -14,10 +18,18 @@
|
|||||||
"x_hint": 0.55,
|
"x_hint": 0.55,
|
||||||
"y_hint": 0.28
|
"y_hint": 0.28
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "marxismes-ecologiques",
|
||||||
|
"label": "Marxismes écologiques",
|
||||||
|
"description": "Relecture écologique des écrits de Marx et de ses continuateurs contemporains. Le Capital comme critique du métabolisme homme-nature. Décroissance communiste.",
|
||||||
|
"color": "#8e44ad",
|
||||||
|
"x_hint": 0.65,
|
||||||
|
"y_hint": 0.2
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "eco-anarchisme",
|
"id": "eco-anarchisme",
|
||||||
"label": "Éco-anarchisme",
|
"label": "Écologies libertaires",
|
||||||
"description": "Écologies libertaires et anti-industrielles. Contre l'État, le capitalisme et la domination de la nature — pour l'autogestion et le municipalisme libertaire.",
|
"description": "Filiation des traditions du socialisme ouvrier anglais et de l'anarchisme. Les dominations de l'homme sur l'homme, sur la femme et sur la nature ne peuvent être prises séparément. Éco-communautés, institutions autogérées, démocratie radicale, municipalisme libertaire.",
|
||||||
"color": "#2d6a4f",
|
"color": "#2d6a4f",
|
||||||
"x_hint": 0.25,
|
"x_hint": 0.25,
|
||||||
"y_hint": 0.3
|
"y_hint": 0.3
|
||||||
@@ -40,8 +52,8 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "technocritique",
|
"id": "technocritique",
|
||||||
"label": "Technocritique",
|
"label": "Écologies anti-industrielles",
|
||||||
"description": "Critique radicale de la technique comme système autonome. Contre l'illusion de la technologie comme solution aux crises qu'elle engendre.",
|
"description": "Rejet du productivisme et de l'hyper-mécanisation issus de l'ère industrielle. Approche technocritique : critique du gigantisme productif et de l'État, refus de l'idéologie du Progrès. Considérer la technique comme un système avec ses logiques propres.",
|
||||||
"color": "#7f8c8d",
|
"color": "#7f8c8d",
|
||||||
"x_hint": 0.2,
|
"x_hint": 0.2,
|
||||||
"y_hint": 0.48
|
"y_hint": 0.48
|
||||||
@@ -65,10 +77,38 @@
|
|||||||
{
|
{
|
||||||
"id": "pensees-vivant",
|
"id": "pensees-vivant",
|
||||||
"label": "Pensées du vivant",
|
"label": "Pensées du vivant",
|
||||||
"description": "Anthropologie et ontologies de la nature. Dépasser le dualisme nature/culture. Sympoïèse, multi-espèces.",
|
"description": "Anthropologie et ontologies de la nature. Dépasser le dualisme nature/culture. Sympoïèse, multi-espèces, éthologie politique.",
|
||||||
"color": "#6b8e6e",
|
"color": "#6b8e6e",
|
||||||
"x_hint": 0.62,
|
"x_hint": 0.62,
|
||||||
"y_hint": 0.58
|
"y_hint": 0.58
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "collapsologie",
|
||||||
|
"label": "Collapsologie",
|
||||||
|
"description": "Étude interdisciplinaire de l'effondrement de la civilisation industrielle et des voies de résilience. Articule sciences du vivant, géopolitique et psychologie de la transition.",
|
||||||
|
"color": "#34495e",
|
||||||
|
"x_hint": 0.42,
|
||||||
|
"y_hint": 0.22
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "capitalisme-vert",
|
||||||
|
"label": "Capitalisme vert",
|
||||||
|
"description": "Théoriciens du capitalisme qui intègrent la dimension environnementale aux échanges marchands (taxes, compensation, technologies vertes). Certains accélèrent la dynamique capitaliste, voulant contrôler le Système-Terre sans nuire aux intérêts de la classe possédante. Famille critiquée par toutes les autres.",
|
||||||
|
"color": "#6c8a6d",
|
||||||
|
"x_hint": 0.85,
|
||||||
|
"y_hint": 0.5,
|
||||||
|
"corpus_status": "non_ingere",
|
||||||
|
"note_editoriale": "Famille intégrée pour fidélité à la carte FRACAS Bonpote. Pas d'auteurs ingérés dans le RAG ATIS (critique éditoriale assumée)."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ecofascismes",
|
||||||
|
"label": "Écofascismes",
|
||||||
|
"description": "Émergés à bas bruit depuis les années 1980, fragmentés. En Europe : éco-différentialisme, séparation des « races »/civilisations adaptées à leur environnement. Aux USA : néo-malthusianisme, xénophobie, apologie de la wilderness, logiques survivalistes. Famille critiquée par toutes les autres.",
|
||||||
|
"color": "#5d4037",
|
||||||
|
"x_hint": 0.92,
|
||||||
|
"y_hint": 0.85,
|
||||||
|
"corpus_status": "non_ingere",
|
||||||
|
"note_editoriale": "Famille intégrée pour fidélité à la carte FRACAS Bonpote. Pas d'auteurs ingérés dans le RAG ATIS (critique éditoriale assumée)."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"auteurs": [
|
"auteurs": [
|
||||||
@@ -79,12 +119,12 @@
|
|||||||
"ecoles": ["eco-anarchisme"],
|
"ecoles": ["eco-anarchisme"],
|
||||||
"ecole_principale": "eco-anarchisme",
|
"ecole_principale": "eco-anarchisme",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "bookchin-ecology-of-freedom", "titre": "L'Écologie de la liberté", "annee": 1982, "couches": ["fond", "structure"] },
|
{ "slug": "bookchin-ecologie-liberte", "titre": "L'Écologie de la liberté", "annee": 1982, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "bookchin-post-scarcity", "titre": "Post-Scarcity Anarchism", "annee": 1971, "couches": ["fond", "structure"] },
|
{ "slug": "bookchin-post-scarcity", "titre": "Post-Scarcity Anarchism", "annee": 1971, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "bookchin-urbanization", "titre": "The Rise of Urbanization and the Decline of Citizenship", "annee": 1987, "couches": ["fond", "structure"] }
|
{ "slug": "bookchin-urbanization-citizenship", "titre": "The Rise of Urbanization and the Decline of Citizenship", "annee": 1987, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Municipalisme libertaire", "Écologie sociale", "Hiérarchie comme origine de la domination nature"],
|
"theses_cles": ["Municipalisme libertaire", "Écologie sociale", "Hiérarchie comme origine de la domination nature"],
|
||||||
"bio_courte": "Théoricien américain de l'écologie sociale et du municipalisme libertaire. A développé le concept d'\"écologie sociale\" articulant domination sociale et destruction de la nature."
|
"bio_courte": "Théoricien américain de l'écologie sociale et du municipalisme libertaire. A développé le concept d'écologie sociale articulant domination sociale et destruction de la nature."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pierre-kropotkine",
|
"id": "pierre-kropotkine",
|
||||||
@@ -94,11 +134,69 @@
|
|||||||
"ecole_principale": "eco-anarchisme",
|
"ecole_principale": "eco-anarchisme",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "kropotkine-entraide", "titre": "L'Entraide, un facteur de l'évolution", "annee": 1902, "couches": ["fond", "structure"] },
|
{ "slug": "kropotkine-entraide", "titre": "L'Entraide, un facteur de l'évolution", "annee": 1902, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "kropotkine-pain", "titre": "La Conquête du pain", "annee": 1892, "couches": ["fond", "structure"] }
|
{ "slug": "kropotkine-conquete-pain", "titre": "La Conquête du pain", "annee": 1892, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "kropotkine-champs-usines", "titre": "Champs, usines et ateliers", "annee": 1898, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Entraide vs sélection naturelle darwiniste", "Fédéralisme anarchiste", "Géographie critique"],
|
"theses_cles": ["Entraide vs sélection naturelle darwiniste", "Fédéralisme anarchiste", "Géographie critique et décentralisation industrielle"],
|
||||||
"bio_courte": "Géographe et révolutionnaire russe. Son oeuvre centrale démontre que l'entraide, et non la compétition, est le moteur principal de l'évolution."
|
"bio_courte": "Géographe et révolutionnaire russe. Son oeuvre centrale démontre que l'entraide, et non la compétition, est le moteur principal de l'évolution."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "elisee-reclus",
|
||||||
|
"nom": "Élisée Reclus",
|
||||||
|
"dates": "1830-1905",
|
||||||
|
"ecoles": ["eco-anarchisme"],
|
||||||
|
"ecole_principale": "eco-anarchisme",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "reclus-homme-terre", "titre": "L'Homme et la Terre", "annee": 1905, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "reclus-evolution-revolution", "titre": "L'Évolution, la révolution et l'idéal anarchique", "annee": 1898, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "reclus-histoire-ruisseau", "titre": "Histoire d'un ruisseau", "annee": 1869, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Géographie sociale anarchiste", "Homme comme nature prenant conscience d'elle-même", "Antimilitarisme et internationalisme"],
|
||||||
|
"bio_courte": "Géographe anarchiste français, auteur de la Nouvelle Géographie universelle. Précurseur de l'écologie politique et de la géographie humaine critique."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "david-graeber",
|
||||||
|
"nom": "David Graeber",
|
||||||
|
"dates": "1961-2020",
|
||||||
|
"ecoles": ["eco-anarchisme"],
|
||||||
|
"ecole_principale": "eco-anarchisme",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "graeber-bullshit-jobs", "titre": "Bullshit Jobs", "annee": 2018, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "graeber-wengrow-aurore", "titre": "Au commencement était... Une nouvelle histoire de l'humanité", "annee": 2021, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Travail sans valeur comme instrument de domination", "Anthropologie anarchiste", "Contre le récit d'une évolution linéaire de l'humanité"],
|
||||||
|
"bio_courte": "Anthropologue américain, figure du mouvement Occupy. Ses travaux déconstruisent les mythes fondateurs du capitalisme et proposent une anthropologie radicalement alternative.",
|
||||||
|
"note_rag": "graeber-wengrow-aurore-fr aussi indexe pour cross-language queries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "karl-marx",
|
||||||
|
"nom": "Karl Marx",
|
||||||
|
"dates": "1818-1883",
|
||||||
|
"ecoles": ["marxismes-ecologiques", "ecosocialisme"],
|
||||||
|
"ecole_principale": "marxismes-ecologiques",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "marx-manuscrits-1844", "titre": "Manuscrits économico-philosophiques de 1844", "annee": 1844, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "marx-capital-livre1", "titre": "Le Capital, Livre I", "annee": 1867, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "marx-grundrisse", "titre": "Grundrisse", "annee": 1857, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Métabolisme entre travail humain et nature", "Aliénation naturelle", "Accumulation primitive et rupture métabolique"],
|
||||||
|
"bio_courte": "Pensée-racine des marxismes écologiques. Les Grundrisse et le Capital contiennent une critique écologique du capitalisme que le 20e siècle a largement occultée."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "kohei-saito",
|
||||||
|
"nom": "Kohei Saito",
|
||||||
|
"dates": "1987-",
|
||||||
|
"ecoles": ["marxismes-ecologiques"],
|
||||||
|
"ecole_principale": "marxismes-ecologiques",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "saito-marx-anthropocene", "titre": "Marx dans l'Anthropocène", "annee": 2016, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "saito-decroissance-communisme", "titre": "La Décroissance communiste", "annee": 2020, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "saito-capital-anthropocene", "titre": "Le Capital dans l'Anthropocène", "annee": 2020, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Marx et l'écologie dans les cahiers tardifs", "Métabolisme social et rupture métabolique", "Décroissance communiste comme horizon"],
|
||||||
|
"bio_courte": "Philosophe japonais, auteur d'une relecture écologiste des cahiers tardifs de Marx. Défend une décroissance communiste comme seule réponse cohérente à la crise écologique.",
|
||||||
|
"note_rag": "saito-capital-anthropocene-en aussi indexe pour cross-language queries"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "michael-lowy",
|
"id": "michael-lowy",
|
||||||
"nom": "Michael Löwy",
|
"nom": "Michael Löwy",
|
||||||
@@ -119,37 +217,39 @@
|
|||||||
"ecole_principale": "ecosocialisme",
|
"ecole_principale": "ecosocialisme",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "malm-fossil-capital", "titre": "Fossil Capital", "annee": 2016, "couches": ["fond", "structure"] },
|
{ "slug": "malm-fossil-capital", "titre": "Fossil Capital", "annee": 2016, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "malm-comment-saboter", "titre": "Comment saboter un pipeline ?", "annee": 2020, "couches": ["fond", "structure"] },
|
{ "slug": "malm-saboter-pipeline", "titre": "Comment saboter un pipeline ?", "annee": 2020, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "malm-corona-climat", "titre": "Corona, Climate, Chronic Emergency", "annee": 2020, "couches": ["fond", "structure"] }
|
{ "slug": "malm-corona-climat", "titre": "Corona, Climate, Chronic Emergency", "annee": 2020, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Capitalisme fossile", "Sabotage stratégique", "Urgence climatique et action directe"],
|
"theses_cles": ["Capitalisme fossile comme choix historique, non technologique", "Sabotage stratégique", "Urgence climatique et action directe"],
|
||||||
"bio_courte": "Professeur d'écologie humaine à Lund. Théoricien du 'capital fossile' et défenseur d'une écologie de guerre pour répondre à l'urgence climatique."
|
"bio_courte": "Professeur d'écologie humaine à Lund. Théoricien du capital fossile et défenseur d'une écologie de guerre pour répondre à l'urgence climatique."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "kohei-saito",
|
"id": "naomi-klein",
|
||||||
"nom": "Kohei Saito",
|
"nom": "Naomi Klein",
|
||||||
"dates": "1987-",
|
"dates": "1970-",
|
||||||
"ecoles": ["ecosocialisme"],
|
"ecoles": ["ecosocialisme"],
|
||||||
"ecole_principale": "ecosocialisme",
|
"ecole_principale": "ecosocialisme",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "saito-marx-ecosocialisme", "titre": "Marx dans l'Anthropocène", "annee": 2020, "couches": ["fond", "structure"] }
|
{ "slug": "klein-strategie-choc", "titre": "La Stratégie du choc", "annee": 2007, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "klein-tout-peut-changer", "titre": "Tout peut changer", "annee": 2014, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "klein-feu", "titre": "Le Feu qui nous attend", "annee": 2019, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Marx et l'écologie", "Métabolisme social", "Décroissance communiste"],
|
"theses_cles": ["Capitalisme du désastre", "Crise climatique comme opportunité de transformation radicale", "Gestion du choc comme tactique néolibérale"],
|
||||||
"bio_courte": "Philosophe japonais, auteur d'une relecture écologiste des cahiers tardifs de Marx. Défend une 'décroissance communiste' comme horizon."
|
"bio_courte": "Journaliste et activiste canadienne, une des voix les plus influentes du mouvement clima-justice. Articule critique du capitalisme et urgence écologique."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "karl-marx",
|
"id": "andre-gorz",
|
||||||
"nom": "Karl Marx",
|
"nom": "André Gorz",
|
||||||
"dates": "1818-1883",
|
"dates": "1923-2007",
|
||||||
"ecoles": ["ecosocialisme", "eco-anarchisme"],
|
"ecoles": ["ecosocialisme", "decroissance", "technocritique"],
|
||||||
"ecole_principale": "ecosocialisme",
|
"ecole_principale": "ecosocialisme",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "marx-manuscrits-1844", "titre": "Manuscrits de 1844", "annee": 1844, "couches": ["fond", "structure"] },
|
{ "slug": "gorz-capitalisme-socialisme-ecologie", "titre": "Capitalisme, Socialisme, Écologie", "annee": 1991, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "marx-capital", "titre": "Le Capital", "annee": 1867, "couches": ["fond", "structure"] },
|
{ "slug": "gorz-immateriel", "titre": "L'Immatériel", "annee": 2003, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "marx-grundrisse", "titre": "Grundrisse", "annee": 1857, "couches": ["fond", "structure"] }
|
{ "slug": "gorz-utopie-ou-mort", "titre": "Utopie ou mort", "annee": 1975, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Métabolisme entre travail humain et nature", "Aliénation naturelle", "Accumulation primitive"],
|
"theses_cles": ["Sortie du capitalisme par la réduction du temps de travail", "Économie de suffisance", "Immatériel comme nouvelle aliénation"],
|
||||||
"bio_courte": "Pensée-racine de l'écosocialisme. Les Grundrisse et le Capital contiennent une critique écologique du capitalisme souvent occultée."
|
"bio_courte": "Philosophe austro-français, pionnier de l'écosocialisme et de la critique du travail. Relie marxisme, existentialisme et écologie dans une pensée de la libération."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "serge-latouche",
|
"id": "serge-latouche",
|
||||||
@@ -158,23 +258,24 @@
|
|||||||
"ecoles": ["decroissance"],
|
"ecoles": ["decroissance"],
|
||||||
"ecole_principale": "decroissance",
|
"ecole_principale": "decroissance",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "latouche-decroissance", "titre": "Le Pari de la décroissance", "annee": 2006, "couches": ["fond", "structure"] },
|
{ "slug": "latouche-abondance-frugale", "titre": "Bon pour la casse : les déraisons de l'obsolescence programmée", "annee": 2012, "couches": ["fond", "structure"] },
|
||||||
{ "slug": "latouche-petit-traite", "titre": "Petit traité de la décroissance sereine", "annee": 2007, "couches": ["fond", "structure"] }
|
{ "slug": "latouche-petit-traite-decroissance", "titre": "Petit traité de la décroissance sereine", "annee": 2007, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "latouche-reenchanter-monde", "titre": "Pour un biorégionalisme en bonne intelligence avec les autres", "annee": 2019, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Sereine décroissance", "Critique du développement", "Société frugale abondante"],
|
"theses_cles": ["Sereine décroissance", "Critique du développement et de l'occidentalisation", "Société frugale abondante"],
|
||||||
"bio_courte": "Économiste hétérodoxe franco-algérien, principal théoricien de la décroissance en France. Critique radical de l'économie du développement."
|
"bio_courte": "Économiste hétérodoxe franco-algérien, principal théoricien de la décroissance en France. Critique radical de l'économie du développement et de l'impérialisme culturel."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "pablo-servigne",
|
"id": "nicholas-georgescu-roegen",
|
||||||
"nom": "Pablo Servigne",
|
"nom": "Nicholas Georgescu-Roegen",
|
||||||
"dates": "1978-",
|
"dates": "1906-1994",
|
||||||
"ecoles": ["decroissance", "pensees-vivant"],
|
"ecoles": ["decroissance"],
|
||||||
"ecole_principale": "decroissance",
|
"ecole_principale": "decroissance",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "servigne-comment-tout", "titre": "Comment tout peut s'effondrer", "annee": 2015, "couches": ["fond", "structure"] }
|
{ "slug": "georgescu-decroissance", "titre": "Demain la décroissance", "annee": 1979, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Collapsologie", "Entraide comme résilience", "Transition post-collapse"],
|
"theses_cles": ["Entropie et économie", "Impossibilité thermodynamique de la croissance infinie", "Bioéconomie"],
|
||||||
"bio_courte": "Ingénieur agronome belge, cofondateur de la collapsologie. Explore les conditions d'un effondrement de la civilisation industrielle et les voies de résilience."
|
"bio_courte": "Mathématicien et économiste roumain, père fondateur de la bioéconomie. Démontre que la croissance économique est irréversiblement limitée par les lois de la thermodynamique."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "donella-meadows",
|
"id": "donella-meadows",
|
||||||
@@ -183,11 +284,26 @@
|
|||||||
"ecoles": ["decroissance"],
|
"ecoles": ["decroissance"],
|
||||||
"ecole_principale": "decroissance",
|
"ecole_principale": "decroissance",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "meadows-limites-croissance", "titre": "Les Limites à la croissance", "annee": 1972, "couches": ["fond", "structure"] }
|
{ "slug": "meadows-halte-croissance", "titre": "Halte à la croissance ?", "annee": 1972, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "meadows-thinking-systems", "titre": "Thinking in Systems", "annee": 2008, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Limites planétaires", "Modèles systémiques", "Overshoot"],
|
"theses_cles": ["Limites planétaires", "Modèles systémiques de l'overshoot", "Pensée systémique comme outil de transformation"],
|
||||||
"bio_courte": "Le rapport Meadows (1972) est le premier modèle systémique démontrant l'impossibilité d'une croissance infinie dans un monde fini."
|
"bio_courte": "Le rapport Meadows (1972) est le premier modèle systémique démontrant l'impossibilité d'une croissance infinie dans un monde fini."
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "pablo-servigne",
|
||||||
|
"nom": "Pablo Servigne",
|
||||||
|
"dates": "1978-",
|
||||||
|
"ecoles": ["collapsologie"],
|
||||||
|
"ecole_principale": "collapsologie",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "servigne-effondrer", "titre": "Comment tout peut s'effondrer", "annee": 2015, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "servigne-autre-fin-du-monde", "titre": "Une autre fin du monde est possible", "annee": 2018, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "servigne-entraide-autre-loi", "titre": "L'Entraide, l'autre loi de la jungle", "annee": 2017, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Collapsologie", "Conditions systémiques de l'effondrement industriel", "Transition post-collapse et résilience collective"],
|
||||||
|
"bio_courte": "Ingénieur agronome belge, cofondateur de la collapsologie. Explore les conditions d'un effondrement de la civilisation industrielle et les voies de résilience collective."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "francoise-deaubonne",
|
"id": "francoise-deaubonne",
|
||||||
"nom": "Françoise d'Eaubonne",
|
"nom": "Françoise d'Eaubonne",
|
||||||
@@ -197,8 +313,8 @@
|
|||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "eaubonne-feminisme-mort", "titre": "Le Féminisme ou la mort", "annee": 1974, "couches": ["fond", "structure"] }
|
{ "slug": "eaubonne-feminisme-mort", "titre": "Le Féminisme ou la mort", "annee": 1974, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Écoféminisme (terme inventé)", "Patriarcat et destruction de la nature", "Révolution féministe écologique"],
|
"theses_cles": ["Écoféminisme (terme inventé en 1974)", "Patriarcat et destruction de la nature comme même système", "Révolution féministe écologique"],
|
||||||
"bio_courte": "Féministe française, inventrice du terme 'écoféminisme' en 1974. Lie patriarcat et destruction de l'environnement dans une même critique."
|
"bio_courte": "Féministe française, inventrice du terme écoféminisme en 1974. Lie patriarcat et destruction de l'environnement dans une même critique radicale."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "silvia-federici",
|
"id": "silvia-federici",
|
||||||
@@ -207,10 +323,12 @@
|
|||||||
"ecoles": ["ecofeminismes"],
|
"ecoles": ["ecofeminismes"],
|
||||||
"ecole_principale": "ecofeminismes",
|
"ecole_principale": "ecofeminismes",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "federici-caliban", "titre": "Caliban et la sorcière", "annee": 2004, "couches": ["fond", "structure"] }
|
{ "slug": "federici-caliban-sorciere", "titre": "Caliban et la sorcière", "annee": 2004, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "federici-par-dela-peau", "titre": "Par-delà la peau", "annee": 2019, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "federici-point-zero", "titre": "Le Point zéro de la révolution", "annee": 2012, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Accumulation primitive et corps des femmes", "Chasse aux sorcières", "Travail reproductif"],
|
"theses_cles": ["Accumulation primitive et corps des femmes", "Chasse aux sorcières comme contre-révolution", "Travail reproductif et commons"],
|
||||||
"bio_courte": "Philosophe italo-américaine, théoricienne du féminisme marxiste. Caliban et la sorcière relit l'accumulation primitive à travers la domination des femmes."
|
"bio_courte": "Philosophe italo-américaine, théoricienne du féminisme marxiste. Caliban et la sorcière relit l'accumulation primitive à travers la domination des femmes et la destruction des commons."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "vandana-shiva",
|
"id": "vandana-shiva",
|
||||||
@@ -219,10 +337,22 @@
|
|||||||
"ecoles": ["ecofeminismes", "ecologies-decoloniales"],
|
"ecoles": ["ecofeminismes", "ecologies-decoloniales"],
|
||||||
"ecole_principale": "ecofeminismes",
|
"ecole_principale": "ecofeminismes",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "shiva-monocultures-esprit", "titre": "Monocultures of the Mind", "annee": 1993, "couches": ["fond", "structure"] }
|
{ "slug": "shiva-staying-alive", "titre": "Staying Alive: Women, Ecology and Development", "annee": 1988, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Biopiraterie", "Souveraineté alimentaire", "Écoféminisme tiers-mondiste"],
|
"theses_cles": ["Biopiraterie et souveraineté alimentaire", "Écoféminisme tiers-mondiste", "Développement comme destruction"],
|
||||||
"bio_courte": "Physicienne et militante indienne, figure mondiale de l'écoféminisme et de la souveraineté alimentaire. Cofondatrice de Navdanya."
|
"bio_courte": "Physicienne et militante indienne, figure mondiale de l'écoféminisme et de la souveraineté alimentaire. Cofondatrice de Navdanya, contre la biopiraterie des semences."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fatima-ouassak",
|
||||||
|
"nom": "Fatima Ouassak",
|
||||||
|
"dates": "1978-",
|
||||||
|
"ecoles": ["ecofeminismes", "ecologies-decoloniales"],
|
||||||
|
"ecole_principale": "ecofeminismes",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "ouassak-puissance-meres", "titre": "La Puissance des mères", "annee": 2020, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Féminisme populaire et de couleur", "Écologie de banlieue", "Puissance maternelle comme force politique"],
|
||||||
|
"bio_courte": "Politiste et militante franco-tunisienne. Articule luttes de classes populaires, antiracisme et écologie dans une perspective féministe ancrée dans les quartiers."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "malcolm-ferdinand",
|
"id": "malcolm-ferdinand",
|
||||||
@@ -231,10 +361,11 @@
|
|||||||
"ecoles": ["ecologies-decoloniales"],
|
"ecoles": ["ecologies-decoloniales"],
|
||||||
"ecole_principale": "ecologies-decoloniales",
|
"ecole_principale": "ecologies-decoloniales",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "ferdinand-ecologie-decoloniale", "titre": "Une écologie décoloniale", "annee": 2019, "couches": ["fond", "structure"] }
|
{ "slug": "ferdinand-ecologie-decoloniale", "titre": "Une écologie décoloniale", "annee": 2019, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "ferdinand-monde-en-commun", "titre": "Un monde en commun", "annee": 2022, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Double fracture coloniale et écologique", "Habiter le monde", "Antillanité et écologie"],
|
"theses_cles": ["Double fracture coloniale et écologique", "Habiter le monde en commun", "Antillanité et écologie politique"],
|
||||||
"bio_courte": "Ingénieur et philosophe martiniquais. Son oeuvre articule colonialisme et destruction de l'environnement autour de la 'double fracture' historique."
|
"bio_courte": "Ingénieur et philosophe martiniquais. Son oeuvre articule colonialisme et destruction de l'environnement autour de la double fracture coloniale-écologique."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "jacques-ellul",
|
"id": "jacques-ellul",
|
||||||
@@ -243,34 +374,24 @@
|
|||||||
"ecoles": ["technocritique"],
|
"ecoles": ["technocritique"],
|
||||||
"ecole_principale": "technocritique",
|
"ecole_principale": "technocritique",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "ellul-technique-enjeu", "titre": "La Technique ou l'Enjeu du siècle", "annee": 1954, "couches": ["fond", "structure"] }
|
{ "slug": "ellul-technique-enjeu-siecle", "titre": "La Technique ou l'Enjeu du siècle", "annee": 1954, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "ellul-systeme-technicien", "titre": "Le Système technicien", "annee": 1977, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "ellul-bluff-technologique", "titre": "Le Bluff technologique", "annee": 1988, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Technique comme système autonome", "Efficacité comme valeur unique", "Propagande et technosystème"],
|
"theses_cles": ["Technique comme système autonome échappant au contrôle humain", "Efficacité comme valeur unique de la modernité", "Propagande et technosystème"],
|
||||||
"bio_courte": "Juriste, sociologue et théologien bordelais. Son oeuvre fondatrice analyse la Technique comme système autonome qui échappe à tout contrôle humain."
|
"bio_courte": "Juriste, sociologue et théologien bordelais. Son oeuvre fondatrice analyse la Technique comme système autonome, père de la technocritique radicale française."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "david-graeber",
|
"id": "bernard-charbonneau",
|
||||||
"nom": "David Graeber",
|
"nom": "Bernard Charbonneau",
|
||||||
"dates": "1961-2020",
|
"dates": "1910-1996",
|
||||||
"ecoles": ["eco-anarchisme"],
|
"ecoles": ["technocritique"],
|
||||||
"ecole_principale": "eco-anarchisme",
|
"ecole_principale": "technocritique",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "graeber-dette", "titre": "Dette : 5000 ans d'histoire", "annee": 2011, "couches": ["fond", "structure"] }
|
{ "slug": "charbonneau-jardin-babylone", "titre": "Le Jardin de Babylone", "annee": 1969, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Dette comme instrument de domination", "Anthropologie anarchiste", "Bullshit jobs"],
|
"theses_cles": ["Liberté contre organisation", "Critique de l'aménagement du territoire", "Personnalisme anarchisant et nature"],
|
||||||
"bio_courte": "Anthropologue américain, figure du mouvement Occupy. Ses travaux anthropologiques déconstruisent les mythes fondateurs du capitalisme (troc, dette, marché)."
|
"bio_courte": "Essayiste béarnais, compagnon d'Ellul. Pionnier méconnu de l'écologie politique française. Défend la liberté contre toute organisation — État, marché, technique."
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "philippe-descola",
|
|
||||||
"nom": "Philippe Descola",
|
|
||||||
"dates": "1949-",
|
|
||||||
"ecoles": ["pensees-vivant"],
|
|
||||||
"ecole_principale": "pensees-vivant",
|
|
||||||
"livres_rag": [
|
|
||||||
{ "slug": "descola-par-dela-nature", "titre": "Par-delà nature et culture", "annee": 2005, "couches": ["fond", "structure"] }
|
|
||||||
],
|
|
||||||
"theses_cles": ["Dualisme nature/culture comme exception occidentale", "4 ontologies (animisme, totémisme, analogisme, naturalisme)", "Cosmopolitiques"],
|
|
||||||
"bio_courte": "Anthropologue et ethnologue français, successeur de Lévi-Strauss au Collège de France. Démontre que le dualisme nature/culture est une anomalie culturelle."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "rachel-carson",
|
"id": "rachel-carson",
|
||||||
@@ -279,10 +400,12 @@
|
|||||||
"ecoles": ["ethiques-environnementales"],
|
"ecoles": ["ethiques-environnementales"],
|
||||||
"ecole_principale": "ethiques-environnementales",
|
"ecole_principale": "ethiques-environnementales",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "carson-printemps-silencieux", "titre": "Printemps silencieux", "annee": 1962, "couches": ["fond", "structure"] }
|
{ "slug": "carson-printemps-silencieux", "titre": "Printemps silencieux", "annee": 1962, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "carson-mer-autour-de-nous", "titre": "The Sea Around Us", "annee": 1951, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Impact des pesticides sur les écosystèmes", "Naissance du mouvement environnementaliste moderne", "Responsabilité scientifique"],
|
"theses_cles": ["Impact des pesticides sur les écosystèmes", "Naissance du mouvement environnementaliste moderne", "Responsabilité scientifique et démocratie"],
|
||||||
"bio_courte": "Marine biologist and author américaine. Son livre Printemps silencieux (1962) a lancé le mouvement environnementaliste moderne en dénonçant les pesticides."
|
"bio_courte": "Marine biologist et autrice américaine. Printemps silencieux (1962) a lancé le mouvement environnementaliste moderne en dénonçant les pesticides.",
|
||||||
|
"note_rag": "carson-mer-autour-de-nous-fr aussi indexe pour cross-language queries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "arne-naess",
|
"id": "arne-naess",
|
||||||
@@ -291,10 +414,78 @@
|
|||||||
"ecoles": ["ethiques-environnementales"],
|
"ecoles": ["ethiques-environnementales"],
|
||||||
"ecole_principale": "ethiques-environnementales",
|
"ecole_principale": "ethiques-environnementales",
|
||||||
"livres_rag": [
|
"livres_rag": [
|
||||||
{ "slug": "naess-ecologie-profonde", "titre": "Écologie, communauté et style de vie", "annee": 1989, "couches": ["fond", "structure"] }
|
{ "slug": "naess-ecology-of-wisdom", "titre": "Ecology of Wisdom", "annee": 2008, "couches": ["fond", "structure"] }
|
||||||
],
|
],
|
||||||
"theses_cles": ["Deep ecology vs écologie superficielle", "Égalité biosphérique", "Réalisation de Soi élargie"],
|
"theses_cles": ["Deep ecology vs écologie superficielle", "Égalité biosphérique", "Réalisation de Soi élargie au-delà du moi individuel"],
|
||||||
"bio_courte": "Philosophe norvégien, fondateur de la 'deep ecology'. Défend une valeur intrinsèque de tous les êtres vivants, indépendamment de leur utilité pour les humains."
|
"bio_courte": "Philosophe norvégien, fondateur de la deep ecology. Défend une valeur intrinsèque de tous les êtres vivants, indépendamment de leur utilité pour les humains."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "philippe-descola",
|
||||||
|
"nom": "Philippe Descola",
|
||||||
|
"dates": "1949-",
|
||||||
|
"ecoles": ["pensees-vivant"],
|
||||||
|
"ecole_principale": "pensees-vivant",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "descola-par-dela-nature-culture", "titre": "Par-delà nature et culture", "annee": 2005, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "descola-composition-mondes", "titre": "La Composition des mondes", "annee": 2014, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Dualisme nature/culture comme exception occidentale", "4 ontologies (animisme, totémisme, analogisme, naturalisme)", "Cosmopolitiques et pluriversalité"],
|
||||||
|
"bio_courte": "Anthropologue et ethnologue français, successeur de Lévi-Strauss au Collège de France. Démontre que le dualisme nature/culture est une anomalie culturelle occidentale."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vinciane-despret",
|
||||||
|
"nom": "Vinciane Despret",
|
||||||
|
"dates": "1959-",
|
||||||
|
"ecoles": ["pensees-vivant"],
|
||||||
|
"ecole_principale": "pensees-vivant",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "despret-habiter-oiseau", "titre": "Habiter en oiseau", "annee": 2019, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "despret-autobiographie-poulpe", "titre": "Autobiographie d'un poulpe", "annee": 2021, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "despret-quand-loup-habitera", "titre": "Quand le loup habitera avec l'agneau", "annee": 2002, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Éthologie politique", "Faire bon ménage avec les non-humains", "Épistémologie du point de vue animal"],
|
||||||
|
"bio_courte": "Philosophe et éthologiste belge. Explore comment penser avec les animaux plutôt que sur eux, développant une éthologie politique de la cohabitation inter-espèces."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "baptiste-morizot",
|
||||||
|
"nom": "Baptiste Morizot",
|
||||||
|
"dates": "1983-",
|
||||||
|
"ecoles": ["pensees-vivant"],
|
||||||
|
"ecole_principale": "pensees-vivant",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "morizot-sur-piste-animale", "titre": "Sur la piste animale", "annee": 2018, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "morizot-manieres-etre-vivant", "titre": "Manières d'être vivant", "annee": 2020, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "morizot-raviver-braises", "titre": "Raviver les braises du vivant", "annee": 2020, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Crise de la sensibilité au vivant", "Diplomatie sauvage", "Désensauvagement comme désorientation ontologique"],
|
||||||
|
"bio_courte": "Philosophe et pisteur français. Propose une diplomatie sauvage fondée sur l'attention au vivant. La crise écologique comme crise de la relation, avant d'être une crise de ressources."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bruno-latour",
|
||||||
|
"nom": "Bruno Latour",
|
||||||
|
"dates": "1947-2022",
|
||||||
|
"ecoles": ["pensees-vivant"],
|
||||||
|
"ecole_principale": "pensees-vivant",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "latour-jamais-ete-modernes", "titre": "Nous n'avons jamais été modernes", "annee": 1991, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "latour-face-a-gaia", "titre": "Face à Gaïa", "annee": 2015, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "latour-ou-atterrir", "titre": "Où atterrir ?", "annee": 2017, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Modernes n'ayant jamais séparé nature et société", "Gaïa comme entité politique", "Terrestres vs Hors-sol"],
|
||||||
|
"bio_courte": "Sociologue et philosophe français, fondateur de la théorie acteur-réseau. Son dernier travail tourne autour de Gaïa et de la question politique du Terrestre face au dérèglement."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "isabelle-stengers",
|
||||||
|
"nom": "Isabelle Stengers",
|
||||||
|
"dates": "1949-",
|
||||||
|
"ecoles": ["pensees-vivant"],
|
||||||
|
"ecole_principale": "pensees-vivant",
|
||||||
|
"livres_rag": [
|
||||||
|
{ "slug": "stengers-cosmopolitiques", "titre": "Cosmopolitiques", "annee": 1997, "couches": ["fond", "structure"] },
|
||||||
|
{ "slug": "stengers-reactiver-sens-commun", "titre": "Réactiver le sens commun", "annee": 2020, "couches": ["fond", "structure"] }
|
||||||
|
],
|
||||||
|
"theses_cles": ["Cosmopolitiques : faire droit aux pratiques non-scientifiques", "Capitalisme comme sorcellerie", "Sens commun contre la raison instrumentale"],
|
||||||
|
"bio_courte": "Philosophe des sciences belge. Déploie une pensée cosmopolitique qui fait droit à toutes les pratiques, scientifiques et non-scientifiques, face à la destruction capitaliste."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { checkRateLimitJson } from '~/server/utils/rateLimitJson'
|
|||||||
interface ChatbotPenseesRequest {
|
interface ChatbotPenseesRequest {
|
||||||
query: string
|
query: string
|
||||||
mode?: 'hybrid' | 'local' | 'global' | 'naive' | 'mix'
|
mode?: 'hybrid' | 'local' | 'global' | 'naive' | 'mix'
|
||||||
|
corpus?: 'pensees' | 'projets' | 'both'
|
||||||
filter_couche?: 'fond' | 'forme' | 'structure' | null
|
filter_couche?: 'fond' | 'forme' | 'structure' | null
|
||||||
filter_ecole?: string | null
|
filter_ecole?: string | null
|
||||||
history?: Array<{ role: 'user' | 'assistant'; content: string }>
|
history?: Array<{ role: 'user' | 'assistant'; content: string }>
|
||||||
@@ -13,7 +14,7 @@ interface LightRAGQueryResponse {
|
|||||||
response: string
|
response: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSTEM_PREFACE = `Tu es un agent du RAG Pensées Écologiques, infrastructure militante du collectif trans-former.fr.
|
const SYSTEM_PREFACE_PENSEES = `Tu es un agent du RAG Pensées Écologiques, infrastructure militante du collectif trans-former.fr.
|
||||||
Tu réponds en t'appuyant STRICTEMENT sur le corpus ingéré (auteurs FRACAS Bonpote : écosocialisme, éco-anarchisme, écoféminismes, écologies décoloniales, technocritique, pensées du vivant, décroissance...).
|
Tu réponds en t'appuyant STRICTEMENT sur le corpus ingéré (auteurs FRACAS Bonpote : écosocialisme, éco-anarchisme, écoféminismes, écologies décoloniales, technocritique, pensées du vivant, décroissance...).
|
||||||
|
|
||||||
Règles :
|
Règles :
|
||||||
@@ -23,6 +24,29 @@ Règles :
|
|||||||
- Réponse en français, dense, sans délayage.
|
- Réponse en français, dense, sans délayage.
|
||||||
- Distingue les positions selon les écoles quand elles divergent.`
|
- Distingue les positions selon les écoles quand elles divergent.`
|
||||||
|
|
||||||
|
const SYSTEM_PREFACE_PROJETS = `Tu es un agent du RAG Projets de Jules Nény (architecte, collectif trans-former.fr).
|
||||||
|
Tu réponds STRICTEMENT à partir des documents projet (fichiers butte-pinson__*.md et autres projets archi de Jules).
|
||||||
|
N'utilise PAS le corpus FRACAS Pensées Écologiques pour répondre, sauf si l'usager te le demande explicitement.
|
||||||
|
|
||||||
|
Règles :
|
||||||
|
- Cite les sources (nom de projet, document) à chaque assertion importante.
|
||||||
|
- Si la question dépasse le corpus projet, dis-le clairement. Pas d'hallucination.
|
||||||
|
- Ton praticien réflexif : 1ère personne quand pertinent, narration située.
|
||||||
|
- Réponse en français, dense, sans délayage.`
|
||||||
|
|
||||||
|
const SYSTEM_PREFACE_BOTH = `Tu es un agent du RAG croisé Pensées x Projets de Jules Nény (architecte militant, collectif trans-former.fr).
|
||||||
|
CENTRE TA RÉPONSE sur les documents PROJETS (fichiers butte-pinson__*.md et autres projets archi).
|
||||||
|
Mobilise le corpus FRACAS Pensées (autres fichiers) UNIQUEMENT pour éclairer théoriquement les partis pris des projets, jamais l'inverse.
|
||||||
|
|
||||||
|
Pondération attendue : ~70% ancrage projet concret, ~30% éclairage théorique FRACAS.
|
||||||
|
|
||||||
|
Règles :
|
||||||
|
- Cite les sources (auteur ou nom de projet, document) à chaque assertion.
|
||||||
|
- Si un thème n'est pas couvert par les projets, dis-le clairement avant d'éventuellement étendre au corpus Pensées.
|
||||||
|
- Pas d'hallucination, pas d'extrapolation hors corpus.
|
||||||
|
- Ton praticien militant : direct, pas neutre, ancré dans la pratique architecturale.
|
||||||
|
- Réponse en français, dense, sans délayage.`
|
||||||
|
|
||||||
export default defineEventHandler(async (event: H3Event) => {
|
export default defineEventHandler(async (event: H3Event) => {
|
||||||
const config = useRuntimeConfig(event)
|
const config = useRuntimeConfig(event)
|
||||||
|
|
||||||
@@ -45,8 +69,17 @@ export default defineEventHandler(async (event: H3Event) => {
|
|||||||
|
|
||||||
const query = body.query.trim()
|
const query = body.query.trim()
|
||||||
const mode = body.mode || 'hybrid'
|
const mode = body.mode || 'hybrid'
|
||||||
|
const corpus = body.corpus || 'both'
|
||||||
const ragUrl = (config.ragPeUrl as string) || 'http://localhost:9621'
|
const ragUrl = (config.ragPeUrl as string) || 'http://localhost:9621'
|
||||||
|
|
||||||
|
// Préface adaptative selon corpus demandé
|
||||||
|
const systemPreface =
|
||||||
|
corpus === 'pensees'
|
||||||
|
? SYSTEM_PREFACE_PENSEES
|
||||||
|
: corpus === 'projets'
|
||||||
|
? SYSTEM_PREFACE_PROJETS
|
||||||
|
: SYSTEM_PREFACE_BOTH
|
||||||
|
|
||||||
// 3. Health guard — LightRAG down = erreur claire, pas de fallback hallucinatoire
|
// 3. Health guard — LightRAG down = erreur claire, pas de fallback hallucinatoire
|
||||||
try {
|
try {
|
||||||
await $fetch(`${ragUrl}/health`, { timeout: 5000 })
|
await $fetch(`${ragUrl}/health`, { timeout: 5000 })
|
||||||
@@ -58,7 +91,7 @@ export default defineEventHandler(async (event: H3Event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. Call LightRAG VPS — préface système injectée dans la query
|
// 4. Call LightRAG VPS — préface système injectée dans la query
|
||||||
const ragQuery = `${SYSTEM_PREFACE}\n\nQuestion : ${query}`
|
const ragQuery = `${systemPreface}\n\nQuestion : ${query}`
|
||||||
|
|
||||||
let ragResponse: LightRAGQueryResponse
|
let ragResponse: LightRAGQueryResponse
|
||||||
try {
|
try {
|
||||||
@@ -79,6 +112,7 @@ export default defineEventHandler(async (event: H3Event) => {
|
|||||||
return {
|
return {
|
||||||
response: ragResponse.response ?? '',
|
response: ragResponse.response ?? '',
|
||||||
mode,
|
mode,
|
||||||
|
corpus,
|
||||||
filter: { couche: body.filter_couche ?? null, ecole: body.filter_ecole ?? null },
|
filter: { couche: body.filter_couche ?? null, ecole: body.filter_ecole ?? null },
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user