Refactoring complet : contenu livre, config unique, routes, admin et light mode
- Source unique : supprime app/data/librodrome.config.yml, renomme site/ en bookplayer.config.yml - Morceaux : renommés avec slugs lisibles, fichiers audio renommés, inversion ch2↔ch3 corrigée - Chapitres : 11 fichiers .md réécrits avec le vrai contenu du livre (synthèse fidèle du PDF) - Routes : /lire → /modele-eco, /ecouter → /en-musique, redirections 301 - Admin chapitres : champs structurés (titre, description, temps lecture), compteur mots - Éditeur markdown : mode split, plein écran, support Tab, meilleur rendu aperçu - Admin morceaux : drag & drop, ajout/suppression, gestion playlist - Light mode : palettes printemps/été plus saturées et contrastées, teintes primary - Raccourcis clavier player : espace, flèches gauche/droite - Paroles : toggle supprimé, toujours visibles et scrollables - Nouvelles pages : autonomie, evenement Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,8 @@ export default defineAppConfig({
|
|||||||
height: '4rem',
|
height: '4rem',
|
||||||
nav: [
|
nav: [
|
||||||
{ label: 'Autonomie', to: '/autonomie' },
|
{ label: 'Autonomie', to: '/autonomie' },
|
||||||
{ label: 'Modèle éco', to: '/lire' },
|
{ label: 'Modèle éco', to: '/modele-eco' },
|
||||||
{ label: 'En musique', to: '/ecouter' },
|
{ label: 'En musique', to: '/en-musique' },
|
||||||
{ label: 'Évènement', to: '/evenement' },
|
{ label: 'Évènement', to: '/evenement' },
|
||||||
{ label: 'À propos', to: '/a-propos' },
|
{ label: 'À propos', to: '/a-propos' },
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ useHead({
|
|||||||
return title ? `${title} — Le Librodrome` : 'Le librodrome'
|
return title ? `${title} — Le Librodrome` : 'Le librodrome'
|
||||||
},
|
},
|
||||||
meta: [
|
meta: [
|
||||||
{ name: 'description', content: 'Une économie du don — enfin concevable. Un livre et 9 chansons, lecture guidée et écoute libre.' },
|
{ name: 'description', content: 'Une économie du don — enfin concevable. Un livre et des chansons, lecture guidée et écoute libre.' },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -97,24 +97,24 @@ a {
|
|||||||
color: hsl(var(--color-text)) !important;
|
color: hsl(var(--color-text)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* white with opacity → dark text with same opacity */
|
/* white with opacity → dark text with boosted opacity for punch */
|
||||||
.palette-light .text-white\/20 { color: hsl(var(--color-text) / 0.2) !important; }
|
.palette-light .text-white\/20 { color: hsl(var(--color-text) / 0.28) !important; }
|
||||||
.palette-light .text-white\/30 { color: hsl(var(--color-text) / 0.3) !important; }
|
.palette-light .text-white\/30 { color: hsl(var(--color-text) / 0.38) !important; }
|
||||||
.palette-light .text-white\/40 { color: hsl(var(--color-text) / 0.4) !important; }
|
.palette-light .text-white\/40 { color: hsl(var(--color-text) / 0.48) !important; }
|
||||||
.palette-light .text-white\/45 { color: hsl(var(--color-text) / 0.45) !important; }
|
.palette-light .text-white\/45 { color: hsl(var(--color-text) / 0.52) !important; }
|
||||||
.palette-light .text-white\/50 { color: hsl(var(--color-text) / 0.5) !important; }
|
.palette-light .text-white\/50 { color: hsl(var(--color-text) / 0.58) !important; }
|
||||||
.palette-light .text-white\/60 { color: hsl(var(--color-text) / 0.6) !important; }
|
.palette-light .text-white\/60 { color: hsl(var(--color-text) / 0.68) !important; }
|
||||||
.palette-light .text-white\/70 { color: hsl(var(--color-text) / 0.7) !important; }
|
.palette-light .text-white\/70 { color: hsl(var(--color-text) / 0.78) !important; }
|
||||||
.palette-light .text-white\/80 { color: hsl(var(--color-text) / 0.8) !important; }
|
.palette-light .text-white\/80 { color: hsl(var(--color-text) / 0.88) !important; }
|
||||||
.palette-light .text-white\/85 { color: hsl(var(--color-text) / 0.85) !important; }
|
.palette-light .text-white\/85 { color: hsl(var(--color-text) / 0.92) !important; }
|
||||||
|
|
||||||
/* white backgrounds → surface tones */
|
/* white backgrounds → surface tones with more contrast */
|
||||||
.palette-light .bg-white\/5 { background-color: hsl(var(--color-text) / 0.04) !important; }
|
.palette-light .bg-white\/5 { background-color: hsl(var(--color-primary) / 0.05) !important; }
|
||||||
.palette-light .bg-white\/8 { background-color: hsl(var(--color-text) / 0.06) !important; }
|
.palette-light .bg-white\/8 { background-color: hsl(var(--color-primary) / 0.07) !important; }
|
||||||
.palette-light .bg-white\/10 { background-color: hsl(var(--color-text) / 0.07) !important; }
|
.palette-light .bg-white\/10 { background-color: hsl(var(--color-primary) / 0.09) !important; }
|
||||||
|
|
||||||
/* borders */
|
/* borders with primary tint */
|
||||||
.palette-light .border-white\/8 { border-color: hsl(var(--color-text) / 0.1) !important; }
|
.palette-light .border-white\/8 { border-color: hsl(var(--color-primary) / 0.15) !important; }
|
||||||
|
|
||||||
/* hover overrides */
|
/* hover overrides */
|
||||||
.palette-light .hover\:text-white:hover,
|
.palette-light .hover\:text-white:hover,
|
||||||
@@ -123,42 +123,43 @@ a {
|
|||||||
color: hsl(var(--color-text)) !important;
|
color: hsl(var(--color-text)) !important;
|
||||||
}
|
}
|
||||||
.palette-light .hover\:text-white\/60:hover {
|
.palette-light .hover\:text-white\/60:hover {
|
||||||
color: hsl(var(--color-text) / 0.6) !important;
|
color: hsl(var(--color-text) / 0.7) !important;
|
||||||
}
|
}
|
||||||
.palette-light .hover\:bg-white\/5:hover {
|
.palette-light .hover\:bg-white\/5:hover {
|
||||||
background-color: hsl(var(--color-text) / 0.04) !important;
|
background-color: hsl(var(--color-primary) / 0.08) !important;
|
||||||
}
|
}
|
||||||
.palette-light .hover\:bg-white\/10:hover {
|
.palette-light .hover\:bg-white\/10:hover {
|
||||||
background-color: hsl(var(--color-text) / 0.07) !important;
|
background-color: hsl(var(--color-primary) / 0.12) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* group-hover overrides */
|
/* group-hover overrides */
|
||||||
.palette-light .group:hover .group-hover\:text-primary\/60 {
|
.palette-light .group:hover .group-hover\:text-primary\/60 {
|
||||||
color: hsl(var(--color-primary) / 0.6) !important;
|
color: hsl(var(--color-primary) / 0.7) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* placeholder overrides */
|
/* placeholder overrides */
|
||||||
.palette-light .placeholder\:text-white\/30::placeholder {
|
.palette-light .placeholder\:text-white\/30::placeholder {
|
||||||
color: hsl(var(--color-text) / 0.3) !important;
|
color: hsl(var(--color-text) / 0.35) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prose/content in light mode */
|
/* Prose/content in light mode */
|
||||||
.palette-light .prose { color: hsl(var(--color-text)); }
|
.palette-light .prose { color: hsl(var(--color-text)); }
|
||||||
.palette-light .prose :where(h1,h2,h3,h4,h5,h6) { color: hsl(var(--color-text)); }
|
.palette-light .prose :where(h1,h2,h3,h4,h5,h6) { color: hsl(var(--color-text)); }
|
||||||
|
|
||||||
/* text-gradient in light mode — needs transparent fill override */
|
/* text-gradient in light mode — vivid gradient */
|
||||||
.palette-light .text-gradient {
|
.palette-light .text-gradient {
|
||||||
background-image: linear-gradient(to right, hsl(var(--color-primary)), hsl(var(--color-accent)));
|
background-image: linear-gradient(135deg, hsl(var(--color-primary)), hsl(var(--color-accent)));
|
||||||
-webkit-background-clip: text;
|
-webkit-background-clip: text;
|
||||||
background-clip: text;
|
background-clip: text;
|
||||||
-webkit-text-fill-color: transparent;
|
-webkit-text-fill-color: transparent;
|
||||||
color: transparent !important;
|
color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* card surfaces */
|
/* card surfaces — subtle shadow for depth */
|
||||||
.palette-light .card-surface {
|
.palette-light .card-surface {
|
||||||
background: hsl(var(--color-surface)) !important;
|
background: hsl(var(--color-surface)) !important;
|
||||||
border-color: hsl(var(--color-text) / 0.1) !important;
|
border-color: hsl(var(--color-primary) / 0.12) !important;
|
||||||
|
box-shadow: 0 1px 3px hsl(var(--color-text) / 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* btn-primary text stays white on colored bg */
|
/* btn-primary text stays white on colored bg */
|
||||||
@@ -166,17 +167,36 @@ a {
|
|||||||
color: white !important;
|
color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* input fields */
|
/* input fields — cleaner contrast */
|
||||||
.palette-light input,
|
.palette-light input,
|
||||||
.palette-light textarea {
|
.palette-light textarea {
|
||||||
color: hsl(var(--color-text));
|
color: hsl(var(--color-text));
|
||||||
background-color: hsl(var(--color-surface));
|
background-color: white;
|
||||||
border-color: hsl(var(--color-text) / 0.12);
|
border-color: hsl(var(--color-text) / 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
.palette-light input:focus,
|
||||||
|
.palette-light textarea:focus {
|
||||||
|
border-color: hsl(var(--color-primary) / 0.5);
|
||||||
|
box-shadow: 0 0 0 3px hsl(var(--color-primary) / 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ecouter view toggle buttons */
|
/* Ecouter view toggle buttons */
|
||||||
.palette-light .bg-white\/10 {
|
.palette-light .bg-white\/10 {
|
||||||
background-color: hsl(var(--color-text) / 0.1) !important;
|
background-color: hsl(var(--color-primary) / 0.1) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light mode scrollbar — tinted with primary */
|
||||||
|
.palette-light ::-webkit-scrollbar-thumb {
|
||||||
|
background: hsl(var(--color-primary) / 0.2);
|
||||||
|
}
|
||||||
|
.palette-light ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: hsl(var(--color-primary) / 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light mode selection — vivid */
|
||||||
|
.palette-light ::selection {
|
||||||
|
background-color: hsl(var(--color-accent) / 0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Page transitions */
|
/* Page transitions */
|
||||||
|
|||||||
@@ -1,35 +1,58 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="md-editor">
|
<div class="md-editor">
|
||||||
|
<div class="md-toolbar">
|
||||||
<div class="md-tabs">
|
<div class="md-tabs">
|
||||||
<button
|
<button
|
||||||
class="md-tab"
|
class="md-tab"
|
||||||
:class="{ 'md-tab--active': tab === 'edit' }"
|
:class="{ 'md-tab--active': tab === 'edit' }"
|
||||||
@click="tab = 'edit'"
|
@click="tab = 'edit'"
|
||||||
>
|
>
|
||||||
|
<div class="i-lucide-pencil h-3.5 w-3.5" />
|
||||||
Édition
|
Édition
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
class="md-tab"
|
||||||
|
:class="{ 'md-tab--active': tab === 'split' }"
|
||||||
|
@click="tab = 'split'"
|
||||||
|
>
|
||||||
|
<div class="i-lucide-columns-2 h-3.5 w-3.5" />
|
||||||
|
Split
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
class="md-tab"
|
class="md-tab"
|
||||||
:class="{ 'md-tab--active': tab === 'preview' }"
|
:class="{ 'md-tab--active': tab === 'preview' }"
|
||||||
@click="tab = 'preview'"
|
@click="tab = 'preview'"
|
||||||
>
|
>
|
||||||
|
<div class="i-lucide-eye h-3.5 w-3.5" />
|
||||||
Aperçu
|
Aperçu
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
class="md-fullscreen"
|
||||||
|
:class="{ 'md-fullscreen--active': fullscreen }"
|
||||||
|
@click="fullscreen = !fullscreen"
|
||||||
|
>
|
||||||
|
<div :class="fullscreen ? 'i-lucide-minimize-2' : 'i-lucide-maximize-2'" class="h-3.5 w-3.5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="md-body" :class="{ 'md-body--split': tab === 'split', 'md-body--fullscreen': fullscreen }">
|
||||||
<textarea
|
<textarea
|
||||||
v-if="tab === 'edit'"
|
v-if="tab === 'edit' || tab === 'split'"
|
||||||
|
ref="textareaRef"
|
||||||
:value="modelValue"
|
:value="modelValue"
|
||||||
class="md-textarea"
|
class="md-textarea"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
@input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
|
@input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
|
||||||
|
@keydown.tab.prevent="insertTab"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-else
|
v-if="tab === 'preview' || tab === 'split'"
|
||||||
class="md-preview prose"
|
class="md-preview prose"
|
||||||
v-html="renderedHtml"
|
v-html="renderedHtml"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -38,22 +61,38 @@ const props = defineProps<{
|
|||||||
rows?: number
|
rows?: number
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:modelValue': [value: string]
|
'update:modelValue': [value: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const tab = ref<'edit' | 'preview'>('edit')
|
const tab = ref<'edit' | 'preview' | 'split'>('edit')
|
||||||
|
const fullscreen = ref(false)
|
||||||
|
const textareaRef = ref<HTMLTextAreaElement>()
|
||||||
|
|
||||||
|
function insertTab(e: KeyboardEvent) {
|
||||||
|
const ta = e.target as HTMLTextAreaElement
|
||||||
|
const start = ta.selectionStart
|
||||||
|
const end = ta.selectionEnd
|
||||||
|
const val = ta.value
|
||||||
|
const newVal = val.substring(0, start) + ' ' + val.substring(end)
|
||||||
|
emit('update:modelValue', newVal)
|
||||||
|
nextTick(() => {
|
||||||
|
ta.selectionStart = ta.selectionEnd = start + 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const renderedHtml = computed(() => {
|
const renderedHtml = computed(() => {
|
||||||
// Simple markdown rendering for preview
|
|
||||||
return props.modelValue
|
return props.modelValue
|
||||||
|
.replace(/^> (.+)$/gm, '<blockquote>$1</blockquote>')
|
||||||
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
||||||
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
||||||
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
||||||
|
.replace(/^- (.+)$/gm, '<li>$1</li>')
|
||||||
|
.replace(/(<li>.*<\/li>\n?)+/g, '<ul>$&</ul>')
|
||||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||||
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||||
.replace(/\n\n/g, '</p><p>')
|
.replace(/\n\n/g, '</p><p>')
|
||||||
.replace(/^(?!<[hp])(.+)/gm, '<p>$1</p>')
|
.replace(/^(?!<[hpulob])(.+)/gm, '<p>$1</p>')
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -64,14 +103,23 @@ const renderedHtml = computed(() => {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-tabs {
|
.md-toolbar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
background: hsl(20 8% 6%);
|
background: hsl(20 8% 6%);
|
||||||
border-bottom: 1px solid hsl(20 8% 14%);
|
border-bottom: 1px solid hsl(20 8% 14%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md-tabs {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.md-tab {
|
.md-tab {
|
||||||
padding: 0.5rem 1rem;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.375rem;
|
||||||
|
padding: 0.5rem 0.875rem;
|
||||||
border: none;
|
border: none;
|
||||||
background: none;
|
background: none;
|
||||||
color: hsl(20 8% 50%);
|
color: hsl(20 8% 50%);
|
||||||
@@ -85,6 +133,39 @@ const renderedHtml = computed(() => {
|
|||||||
background: hsl(20 8% 10%);
|
background: hsl(20 8% 10%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.md-fullscreen {
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
color: hsl(20 8% 40%);
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
.md-fullscreen:hover,
|
||||||
|
.md-fullscreen--active { color: white; }
|
||||||
|
|
||||||
|
.md-body {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-body--split .md-textarea,
|
||||||
|
.md-body--split .md-preview {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-body--split .md-preview {
|
||||||
|
border-left: 1px solid hsl(20 8% 14%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-body--fullscreen {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 50;
|
||||||
|
background: hsl(20 8% 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.md-body--fullscreen .md-textarea,
|
||||||
|
.md-body--fullscreen .md-preview {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
.md-textarea {
|
.md-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -95,7 +176,8 @@ const renderedHtml = computed(() => {
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 20rem;
|
min-height: 24rem;
|
||||||
|
tab-size: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-textarea:focus {
|
.md-textarea:focus {
|
||||||
@@ -104,7 +186,9 @@ const renderedHtml = computed(() => {
|
|||||||
|
|
||||||
.md-preview {
|
.md-preview {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
min-height: 20rem;
|
min-height: 24rem;
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
background: hsl(20 8% 4%);
|
background: hsl(20 8% 4%);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -27,13 +27,13 @@
|
|||||||
<div class="i-lucide-home h-4 w-4" />
|
<div class="i-lucide-home h-4 w-4" />
|
||||||
Accueil
|
Accueil
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink to="/admin/pages/lire" class="sidebar-link" active-class="sidebar-link--active">
|
<NuxtLink to="/admin/pages/modele-eco" class="sidebar-link" active-class="sidebar-link--active">
|
||||||
<div class="i-lucide-book-open h-4 w-4" />
|
<div class="i-lucide-book-open h-4 w-4" />
|
||||||
Lire
|
Modèle éco
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink to="/admin/pages/ecouter" class="sidebar-link" active-class="sidebar-link--active">
|
<NuxtLink to="/admin/pages/en-musique" class="sidebar-link" active-class="sidebar-link--active">
|
||||||
<div class="i-lucide-headphones h-4 w-4" />
|
<div class="i-lucide-headphones h-4 w-4" />
|
||||||
Écouter
|
En musique
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink to="/admin/pages/gratewizard" class="sidebar-link" active-class="sidebar-link--active">
|
<NuxtLink to="/admin/pages/gratewizard" class="sidebar-link" active-class="sidebar-link--active">
|
||||||
<div class="i-lucide-sparkles h-4 w-4" />
|
<div class="i-lucide-sparkles h-4 w-4" />
|
||||||
|
|||||||
@@ -217,17 +217,17 @@ const activeChapter = computed(() => {
|
|||||||
|
|
||||||
// ── Chapter metadata ──
|
// ── Chapter metadata ──
|
||||||
const chapters = [
|
const chapters = [
|
||||||
{ slug: 'introduction', title: 'Introduction' },
|
{ slug: '01-introduction', title: 'Introduction' },
|
||||||
{ slug: 'de-quel-don-parlons-nous', title: 'De quel don parlons-nous ?' },
|
{ slug: '02-don', title: 'De quel don parlons-nous ?' },
|
||||||
{ slug: 'la-mesure-du-don', title: 'La mesure du don' },
|
{ slug: '03-mesure', title: 'La mesure du don' },
|
||||||
{ slug: 'raison-d-etre-d-une-monnaie', title: 'Raison d\'être d\'une monnaie' },
|
{ slug: '04-monnaie', title: 'Raison d\'être d\'une monnaie' },
|
||||||
{ slug: 'la-trm', title: 'La TRM' },
|
{ slug: '05-trm', title: 'La TRM' },
|
||||||
{ slug: 'creer-une-economie', title: 'Créer une économie ?' },
|
{ slug: '06-economie', title: 'Créer une économie ?' },
|
||||||
{ slug: 'echanger', title: 'Échanger' },
|
{ slug: '07-echange', title: 'Échanger' },
|
||||||
{ slug: 'relation-institutionnelle', title: 'Relation institutionnelle' },
|
{ slug: '08-institution', title: 'Relation institutionnelle' },
|
||||||
{ slug: 'autres-greffes', title: 'Autres greffes' },
|
{ slug: '09-greffes', title: 'Autres greffes' },
|
||||||
{ slug: 'et-maintenant', title: 'Et maintenant ?… action ?' },
|
{ slug: '10-maintenant', title: 'Et maintenant ?… action ?' },
|
||||||
{ slug: 'annexes', title: 'Chapitres annexes' },
|
{ slug: '11-annexes', title: 'Chapitres annexes' },
|
||||||
]
|
]
|
||||||
|
|
||||||
// ── Per-chapter color hues ──
|
// ── Per-chapter color hues ──
|
||||||
@@ -424,7 +424,7 @@ watch(isOpen, async (open) => {
|
|||||||
// Load playlist & play first song
|
// Load playlist & play first song
|
||||||
const playlist = getPlaylistOrder()
|
const playlist = getPlaylistOrder()
|
||||||
if (playlist.length) playerStore.setPlaylist(playlist)
|
if (playlist.length) playerStore.setPlaylist(playlist)
|
||||||
const first = getSongs().find(s => s.id === 'chanson-01')
|
const first = getSongs().find(s => s.id === 'ce-livre-est-une-facon')
|
||||||
if (first) {
|
if (first) {
|
||||||
_skipSongWatch = true
|
_skipSongWatch = true
|
||||||
audioPlayer.loadAndPlay(first)
|
audioPlayer.loadAndPlay(first)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<ul class="flex flex-col gap-1">
|
<ul class="flex flex-col gap-1">
|
||||||
<li v-for="chapter in chapters" :key="chapter.path">
|
<li v-for="chapter in chapters" :key="chapter.path">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="`/lire/${chapter.stem}`"
|
:to="`/modele-eco/${chapter.stem}`"
|
||||||
class="flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors hover:bg-white/5"
|
class="flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors hover:bg-white/5"
|
||||||
active-class="bg-primary/10 text-primary font-medium"
|
active-class="bg-primary/10 text-primary font-medium"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -29,6 +29,28 @@
|
|||||||
<circle cx="185" cy="42" r="12" fill="currentColor" opacity="0.2"/>
|
<circle cx="185" cy="42" r="12" fill="currentColor" opacity="0.2"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok menuisier: character with plane and plank -->
|
||||||
|
<svg class="shadok-menuisier" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<ellipse cx="120" cy="155" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
|
||||||
|
<circle cx="120" cy="92" r="25" fill="currentColor" opacity="0.8"/>
|
||||||
|
<path d="M98 82 Q120 68 142 82" fill="currentColor" opacity="0.35"/>
|
||||||
|
<rect x="100" y="80" width="40" height="5" rx="1" fill="currentColor" opacity="0.3"/>
|
||||||
|
<circle cx="112" cy="90" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="130" cy="90" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="113" cy="89" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="131" cy="89" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<line x1="114" y1="103" x2="126" y2="103" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" opacity="0.3"/>
|
||||||
|
<line x1="160" y1="145" x2="195" y2="160" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<rect x="190" y="155" width="30" height="12" rx="2" fill="currentColor" opacity="0.4"/>
|
||||||
|
<rect x="200" y="150" width="8" height="8" rx="1" fill="currentColor" opacity="0.35"/>
|
||||||
|
<line x1="80" y1="148" x2="50" y2="168" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="105" y1="200" x2="95" y2="258" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="135" y1="200" x2="145" y2="258" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<rect x="35" y="170" width="80" height="8" rx="1" fill="currentColor" opacity="0.3"/>
|
||||||
|
<path d="M60 168 Q55 162 62 160" stroke="currentColor" stroke-width="1" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
<path d="M80 166 Q76 158 83 157" stroke="currentColor" stroke-width="1" stroke-linecap="round" fill="none" opacity="0.18"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<div class="container-content">
|
<div class="container-content">
|
||||||
<div class="grid items-center gap-12 md:grid-cols-2">
|
<div class="grid items-center gap-12 md:grid-cols-2">
|
||||||
<!-- Book cover -->
|
<!-- Book cover -->
|
||||||
@@ -135,7 +157,24 @@ const { data: content } = await usePageContent('home')
|
|||||||
50% { transform: translateY(-8px); }
|
50% { transform: translateY(-8px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadok-menuisier {
|
||||||
|
position: absolute;
|
||||||
|
left: 2%;
|
||||||
|
top: 5%;
|
||||||
|
width: clamp(100px, 13vw, 190px);
|
||||||
|
opacity: 0.25;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-float-menuisier 10s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-menuisier {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-9px); }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.shadok-thinker { display: none; }
|
.shadok-thinker { display: none; }
|
||||||
|
.shadok-menuisier { display: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,6 +23,25 @@
|
|||||||
<path d="M48 105 Q25 102 12 100" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.3" fill="none"/>
|
<path d="M48 105 Q25 102 12 100" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.3" fill="none"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok boulanger: character with oven and bread -->
|
||||||
|
<svg class="shadok-boulanger" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<ellipse cx="120" cy="155" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
|
||||||
|
<circle cx="120" cy="92" r="25" fill="currentColor" opacity="0.8"/>
|
||||||
|
<ellipse cx="120" cy="68" rx="18" ry="22" fill="currentColor" opacity="0.35"/>
|
||||||
|
<rect x="105" y="78" width="30" height="5" rx="1" fill="currentColor" opacity="0.4"/>
|
||||||
|
<path d="M110 88 Q114 84 118 88" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<path d="M124 88 Q128 84 132 88" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<path d="M112 102 Q120 108 128 102" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.35"/>
|
||||||
|
<line x1="160" y1="145" x2="190" y2="135" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<rect x="185" y="125" width="40" height="10" rx="5" fill="currentColor" opacity="0.4" transform="rotate(-15 205 130)"/>
|
||||||
|
<line x1="80" y1="148" x2="55" y2="175" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="105" y1="200" x2="95" y2="258" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="135" y1="200" x2="145" y2="258" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<rect x="30" y="180" width="50" height="40" rx="4" fill="currentColor" opacity="0.25"/>
|
||||||
|
<rect x="35" y="185" width="40" height="20" rx="2" fill="currentColor" opacity="0.15"/>
|
||||||
|
<ellipse cx="55" cy="195" rx="12" ry="6" fill="currentColor" opacity="0.12"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="container-content relative z-10 px-4">
|
<div class="container-content relative z-10 px-4">
|
||||||
<div class="mx-auto max-w-3xl text-center">
|
<div class="mx-auto max-w-3xl text-center">
|
||||||
@@ -91,7 +110,25 @@ const { data: content } = await usePageContent('home')
|
|||||||
50% { transform: translateY(-12px); }
|
50% { transform: translateY(-12px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadok-boulanger {
|
||||||
|
position: absolute;
|
||||||
|
left: 3%;
|
||||||
|
bottom: 8%;
|
||||||
|
width: clamp(100px, 13vw, 190px);
|
||||||
|
opacity: 0.25;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-accent));
|
||||||
|
animation: shadok-float-boulanger 9s ease-in-out infinite;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-boulanger {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-8px); }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.shadok-bird { display: none; }
|
.shadok-bird { display: none; }
|
||||||
|
.shadok-boulanger { display: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ const store = usePlayerStore()
|
|||||||
const { setVolume, togglePlayPause, playNext } = useAudioPlayer()
|
const { setVolume, togglePlayPause, playNext } = useAudioPlayer()
|
||||||
|
|
||||||
useMediaSession()
|
useMediaSession()
|
||||||
|
useKeyboardShortcuts()
|
||||||
|
|
||||||
const widgetRef = ref<HTMLElement>()
|
const widgetRef = ref<HTMLElement>()
|
||||||
const isExpanded = ref(false)
|
const isExpanded = ref(false)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<div class="song-item-wrapper">
|
||||||
<div
|
<div
|
||||||
class="card-surface flex cursor-pointer items-center gap-4"
|
class="card-surface flex cursor-pointer items-center gap-4"
|
||||||
:class="{ 'border-primary/40! shadow-primary/10!': isCurrent }"
|
:class="{ 'border-primary/40! shadow-primary/10!': isCurrent }"
|
||||||
@@ -34,6 +35,12 @@
|
|||||||
{{ formatDuration(song.duration) }}
|
{{ formatDuration(song.duration) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Lyrics panel (always visible) -->
|
||||||
|
<div v-if="song.lyrics" class="lyrics-panel">
|
||||||
|
<pre class="lyrics-text">{{ song.lyrics }}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -63,3 +70,23 @@ function formatDuration(seconds: number): string {
|
|||||||
return `${mins}:${secs.toString().padStart(2, '0')}`
|
return `${mins}:${secs.toString().padStart(2, '0')}`
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.lyrics-panel {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
background: hsl(var(--color-surface));
|
||||||
|
border: 1px solid hsl(var(--color-text) / 0.08);
|
||||||
|
max-height: 24rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lyrics-text {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: hsl(var(--color-text) / 0.6);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="song.lyrics" class="rounded-xl bg-surface p-6">
|
<div v-if="song.lyrics" class="rounded-xl bg-surface p-6">
|
||||||
<button
|
|
||||||
class="flex w-full items-center justify-between text-left"
|
|
||||||
@click="isOpen = !isOpen"
|
|
||||||
>
|
|
||||||
<span class="font-display text-sm font-semibold text-white/70">Paroles</span>
|
<span class="font-display text-sm font-semibold text-white/70">Paroles</span>
|
||||||
<div
|
<div class="mt-4 max-h-96 overflow-y-auto">
|
||||||
:class="isOpen ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
|
|
||||||
class="h-4 w-4 text-white/40 transition-transform"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition name="lyrics-expand">
|
|
||||||
<div v-if="isOpen" class="mt-4">
|
|
||||||
<pre class="whitespace-pre-wrap font-sans text-sm leading-relaxed text-white/60">{{ song.lyrics }}</pre>
|
<pre class="whitespace-pre-wrap font-sans text-sm leading-relaxed text-white/60">{{ song.lyrics }}</pre>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -25,26 +13,4 @@ import type { Song } from '~/types/song'
|
|||||||
defineProps<{
|
defineProps<{
|
||||||
song: Song
|
song: Song
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const isOpen = ref(false)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.lyrics-expand-enter-active,
|
|
||||||
.lyrics-expand-leave-active {
|
|
||||||
transition: all 0.3s var(--ease-out-expo);
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lyrics-expand-enter-from,
|
|
||||||
.lyrics-expand-leave-to {
|
|
||||||
max-height: 0;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lyrics-expand-enter-to,
|
|
||||||
.lyrics-expand-leave-from {
|
|
||||||
max-height: 500px;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import yaml from 'yaml'
|
|
||||||
import type { Song } from '~/types/song'
|
import type { Song } from '~/types/song'
|
||||||
import type { ChapterSongLink, BookConfig } from '~/types/book'
|
import type { ChapterSongLink, BookConfig } from '~/types/book'
|
||||||
|
|
||||||
@@ -7,8 +6,7 @@ let _configCache: BookConfig | null = null
|
|||||||
async function loadConfig(): Promise<BookConfig> {
|
async function loadConfig(): Promise<BookConfig> {
|
||||||
if (_configCache) return _configCache
|
if (_configCache) return _configCache
|
||||||
|
|
||||||
const raw = await import('~/data/librodrome.config.yml?raw').then(m => m.default)
|
const parsed = await $fetch<any>('/api/content/config')
|
||||||
const parsed = yaml.parse(raw)
|
|
||||||
|
|
||||||
_configCache = {
|
_configCache = {
|
||||||
title: parsed.book.title,
|
title: parsed.book.title,
|
||||||
|
|||||||
41
app/composables/useKeyboardShortcuts.ts
Normal file
41
app/composables/useKeyboardShortcuts.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
export function useKeyboardShortcuts() {
|
||||||
|
const store = usePlayerStore()
|
||||||
|
const { togglePlayPause, playNext, playPrev } = useAudioPlayer()
|
||||||
|
|
||||||
|
function isInputFocused(): boolean {
|
||||||
|
const el = document.activeElement
|
||||||
|
if (!el) return false
|
||||||
|
const tag = el.tagName.toLowerCase()
|
||||||
|
if (tag === 'input' || tag === 'textarea' || tag === 'select') return true
|
||||||
|
if ((el as HTMLElement).isContentEditable) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onKeyDown(e: KeyboardEvent) {
|
||||||
|
if (isInputFocused()) return
|
||||||
|
if (!store.currentSong) return
|
||||||
|
|
||||||
|
switch (e.code) {
|
||||||
|
case 'Space':
|
||||||
|
e.preventDefault()
|
||||||
|
togglePlayPause()
|
||||||
|
break
|
||||||
|
case 'ArrowRight':
|
||||||
|
e.preventDefault()
|
||||||
|
playNext()
|
||||||
|
break
|
||||||
|
case 'ArrowLeft':
|
||||||
|
e.preventDefault()
|
||||||
|
playPrev()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('keydown', onKeyDown)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('keydown', onKeyDown)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,180 +0,0 @@
|
|||||||
book:
|
|
||||||
title: "Une économie du don — enfin concevable"
|
|
||||||
author: "Yvv"
|
|
||||||
description: "Un livre et 9 chansons pour explorer ensemble les fondements d'une économie fondée sur le don."
|
|
||||||
coverImage: "/images/book-cover.jpg"
|
|
||||||
license: "CC-BY-NC"
|
|
||||||
isbn: "979-1-042-45206-3"
|
|
||||||
|
|
||||||
songs:
|
|
||||||
- id: chanson-01
|
|
||||||
title: "1. Ce livre est une façon"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-01.mp3
|
|
||||||
duration: 718
|
|
||||||
lyrics: ""
|
|
||||||
tags: [introduction, livre, don]
|
|
||||||
|
|
||||||
- id: chanson-02
|
|
||||||
title: "2. Un don qui se mesure"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-02.mp3
|
|
||||||
duration: 589
|
|
||||||
lyrics: ""
|
|
||||||
tags: [don, mesure, valeur]
|
|
||||||
|
|
||||||
- id: chanson-03
|
|
||||||
title: "3. Les asymétries"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-03.mp3
|
|
||||||
duration: 727
|
|
||||||
lyrics: ""
|
|
||||||
tags: [asymétrie, communauté, philosophie]
|
|
||||||
|
|
||||||
- id: chanson-04
|
|
||||||
title: "4. Inverser les flux"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-04.mp3
|
|
||||||
duration: 610
|
|
||||||
lyrics: ""
|
|
||||||
tags: [flux, économie, production]
|
|
||||||
|
|
||||||
- id: chanson-05
|
|
||||||
title: "5. Ainsi soit-il"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-05.mp3
|
|
||||||
duration: 545
|
|
||||||
lyrics: ""
|
|
||||||
tags: [action, engagement, avenir]
|
|
||||||
|
|
||||||
- id: chanson-06
|
|
||||||
title: "6. La croissance, une option ?"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-06.mp3
|
|
||||||
duration: 510
|
|
||||||
lyrics: ""
|
|
||||||
tags: [croissance, monnaie, questionnement]
|
|
||||||
|
|
||||||
- id: chanson-07
|
|
||||||
title: "7. Monnaie libre essence"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-07.mp3
|
|
||||||
duration: 475
|
|
||||||
lyrics: ""
|
|
||||||
tags: [monnaie libre, TRM, June]
|
|
||||||
|
|
||||||
- id: chanson-08
|
|
||||||
title: "8. Des cercles qui se croisent"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-08.mp3
|
|
||||||
duration: 496
|
|
||||||
lyrics: ""
|
|
||||||
tags: [échange, réseau, cercles]
|
|
||||||
|
|
||||||
- id: chanson-09
|
|
||||||
title: "9. Coder la liberté"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-09.mp3
|
|
||||||
duration: 376
|
|
||||||
lyrics: ""
|
|
||||||
tags: [logiciel libre, code, liberté]
|
|
||||||
|
|
||||||
chapterSongs:
|
|
||||||
# Chapitre 1 — Introduction
|
|
||||||
- chapterSlug: introduction
|
|
||||||
songId: chanson-01
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: introduction
|
|
||||||
songId: chanson-02
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 2 — De quel don parlons-nous ?
|
|
||||||
- chapterSlug: de-quel-don-parlons-nous
|
|
||||||
songId: chanson-03
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: de-quel-don-parlons-nous
|
|
||||||
songId: chanson-01
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 3 — La mesure du don
|
|
||||||
- chapterSlug: la-mesure-du-don
|
|
||||||
songId: chanson-02
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: la-mesure-du-don
|
|
||||||
songId: chanson-03
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 4 — Raison d'être d'une monnaie
|
|
||||||
- chapterSlug: raison-d-etre-d-une-monnaie
|
|
||||||
songId: chanson-06
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: raison-d-etre-d-une-monnaie
|
|
||||||
songId: chanson-07
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 5 — La TRM
|
|
||||||
- chapterSlug: la-trm
|
|
||||||
songId: chanson-07
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: la-trm
|
|
||||||
songId: chanson-06
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 6 — Créer une économie ?
|
|
||||||
- chapterSlug: creer-une-economie
|
|
||||||
songId: chanson-04
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: creer-une-economie
|
|
||||||
songId: chanson-07
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 7 — Échanger
|
|
||||||
- chapterSlug: echanger
|
|
||||||
songId: chanson-08
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: echanger
|
|
||||||
songId: chanson-04
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 8 — Relation institutionnelle
|
|
||||||
- chapterSlug: relation-institutionnelle
|
|
||||||
songId: chanson-05
|
|
||||||
primary: false
|
|
||||||
- chapterSlug: relation-institutionnelle
|
|
||||||
songId: chanson-08
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 9 — Autres greffes
|
|
||||||
- chapterSlug: autres-greffes
|
|
||||||
songId: chanson-04
|
|
||||||
primary: false
|
|
||||||
- chapterSlug: autres-greffes
|
|
||||||
songId: chanson-08
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 10 — Et maintenant ?
|
|
||||||
- chapterSlug: et-maintenant
|
|
||||||
songId: chanson-05
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: et-maintenant
|
|
||||||
songId: chanson-09
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
# Chapitre 11 — Annexes
|
|
||||||
- chapterSlug: annexes
|
|
||||||
songId: chanson-09
|
|
||||||
primary: true
|
|
||||||
- chapterSlug: annexes
|
|
||||||
songId: chanson-07
|
|
||||||
primary: false
|
|
||||||
|
|
||||||
defaultPlaylistOrder:
|
|
||||||
- chanson-01
|
|
||||||
- chanson-02
|
|
||||||
- chanson-03
|
|
||||||
- chanson-04
|
|
||||||
- chanson-05
|
|
||||||
- chanson-06
|
|
||||||
- chanson-07
|
|
||||||
- chanson-08
|
|
||||||
- chanson-09
|
|
||||||
@@ -6,24 +6,36 @@
|
|||||||
← Chapitres
|
← Chapitres
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<h1 class="font-display text-2xl font-bold text-white mt-1">
|
<h1 class="font-display text-2xl font-bold text-white mt-1">
|
||||||
{{ chapter?.slug }}
|
{{ chapterTitle || slug }}
|
||||||
</h1>
|
</h1>
|
||||||
|
<span class="text-xs text-white/30 font-mono">{{ slug }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<span v-if="wordCount" class="text-xs text-white/30">{{ wordCount }} mots</span>
|
||||||
<AdminSaveButton :saving="saving" :saved="saved" @save="save" />
|
<AdminSaveButton :saving="saving" :saved="saved" @save="save" />
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template v-if="chapter">
|
<template v-if="chapter">
|
||||||
<AdminFormSection title="Frontmatter" open>
|
<AdminFormSection title="Métadonnées">
|
||||||
<textarea
|
<div class="grid gap-3 sm:grid-cols-2">
|
||||||
v-model="frontmatter"
|
<div>
|
||||||
class="fm-textarea"
|
<label class="field-label">Titre</label>
|
||||||
rows="6"
|
<input v-model="title" class="field-input" placeholder="Titre du chapitre" />
|
||||||
spellcheck="false"
|
</div>
|
||||||
/>
|
<div>
|
||||||
|
<label class="field-label">Temps de lecture</label>
|
||||||
|
<input v-model="readingTime" class="field-input" placeholder="15 min" />
|
||||||
|
</div>
|
||||||
|
<div class="sm:col-span-2">
|
||||||
|
<label class="field-label">Description</label>
|
||||||
|
<input v-model="description" class="field-input" placeholder="Description courte pour le SEO" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</AdminFormSection>
|
</AdminFormSection>
|
||||||
|
|
||||||
<AdminFormSection title="Contenu Markdown" open>
|
<AdminFormSection title="Contenu" open>
|
||||||
<AdminMarkdownEditor v-model="body" :rows="30" />
|
<AdminMarkdownEditor v-model="body" :rows="35" />
|
||||||
</AdminFormSection>
|
</AdminFormSection>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,16 +52,33 @@ const slug = computed(() => route.params.slug as string)
|
|||||||
|
|
||||||
const { data: chapter } = await useFetch(() => `/api/admin/chapters/${slug.value}`)
|
const { data: chapter } = await useFetch(() => `/api/admin/chapters/${slug.value}`)
|
||||||
|
|
||||||
const frontmatter = ref('')
|
const title = ref('')
|
||||||
|
const description = ref('')
|
||||||
|
const readingTime = ref('')
|
||||||
const body = ref('')
|
const body = ref('')
|
||||||
|
|
||||||
|
const chapterTitle = computed(() => title.value)
|
||||||
|
const wordCount = computed(() => {
|
||||||
|
if (!body.value) return 0
|
||||||
|
return body.value.trim().split(/\s+/).filter(Boolean).length
|
||||||
|
})
|
||||||
|
|
||||||
watch(chapter, (val) => {
|
watch(chapter, (val) => {
|
||||||
if (val) {
|
if (val) {
|
||||||
frontmatter.value = val.frontmatter ?? ''
|
// Parse frontmatter fields
|
||||||
|
const fm = val.frontmatter ?? ''
|
||||||
|
title.value = extractFmField(fm, 'title')
|
||||||
|
description.value = extractFmField(fm, 'description')
|
||||||
|
readingTime.value = extractFmField(fm, 'readingTime')
|
||||||
body.value = val.body ?? ''
|
body.value = val.body ?? ''
|
||||||
}
|
}
|
||||||
}, { immediate: true })
|
}, { immediate: true })
|
||||||
|
|
||||||
|
function extractFmField(fm: string, field: string): string {
|
||||||
|
const match = fm.match(new RegExp(`^${field}:\\s*"?([^"\\n]*)"?`, 'm'))
|
||||||
|
return match ? match[1].trim() : ''
|
||||||
|
}
|
||||||
|
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const saved = ref(false)
|
const saved = ref(false)
|
||||||
|
|
||||||
@@ -57,12 +86,17 @@ async function save() {
|
|||||||
saving.value = true
|
saving.value = true
|
||||||
saved.value = false
|
saved.value = false
|
||||||
try {
|
try {
|
||||||
|
const order = chapter.value?.frontmatter?.match(/order:\s*(\d+)/)?.[1] ?? '1'
|
||||||
|
const frontmatter = [
|
||||||
|
`title: "${title.value}"`,
|
||||||
|
`description: "${description.value}"`,
|
||||||
|
`order: ${order}`,
|
||||||
|
`readingTime: "${readingTime.value}"`,
|
||||||
|
].join('\n')
|
||||||
|
|
||||||
await $fetch(`/api/admin/chapters/${slug.value}`, {
|
await $fetch(`/api/admin/chapters/${slug.value}`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: {
|
body: { frontmatter, body: body.value },
|
||||||
frontmatter: frontmatter.value,
|
|
||||||
body: body.value,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
saved.value = true
|
saved.value = true
|
||||||
setTimeout(() => { saved.value = false }, 2000)
|
setTimeout(() => { saved.value = false }, 2000)
|
||||||
@@ -74,20 +108,24 @@ async function save() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.fm-textarea {
|
.field-label {
|
||||||
width: 100%;
|
display: block;
|
||||||
padding: 0.75rem;
|
font-size: 0.75rem;
|
||||||
border: 1px solid hsl(20 8% 18%);
|
color: hsl(20 8% 50%);
|
||||||
border-radius: 0.5rem;
|
margin-bottom: 0.25rem;
|
||||||
background: hsl(20 8% 4%);
|
|
||||||
color: hsl(36 80% 76%);
|
|
||||||
font-family: var(--font-mono, monospace);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
line-height: 1.7;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fm-textarea:focus {
|
.field-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem 0.625rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
border: 1px solid hsl(20 8% 18%);
|
||||||
|
background: hsl(20 8% 6%);
|
||||||
|
color: white;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: hsl(12 76% 48% / 0.5);
|
border-color: hsl(12 76% 48% / 0.5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 class="font-display text-2xl font-bold text-white mb-6">Chapitres</h1>
|
<div class="flex items-center justify-between mb-6">
|
||||||
|
<h1 class="font-display text-2xl font-bold text-white">Chapitres</h1>
|
||||||
|
<AdminSaveButton :saving="saving" :saved="saved" @save="saveOrder" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<NuxtLink
|
<div
|
||||||
v-for="chapter in chapters"
|
v-for="(chapter, i) in chapters"
|
||||||
:key="chapter.slug"
|
:key="chapter.slug"
|
||||||
:to="`/admin/book/${chapter.slug}`"
|
|
||||||
class="chapter-item"
|
class="chapter-item"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart(i, $event)"
|
||||||
|
@dragover.prevent="onDragOver(i)"
|
||||||
|
@dragend="onDragEnd"
|
||||||
|
:class="{ 'chapter-item--dragging': dragIdx === i, 'chapter-item--over': dropIdx === i && dropIdx !== dragIdx }"
|
||||||
>
|
>
|
||||||
<span class="chapter-order">{{ String(chapter.order ?? 0).padStart(2, '0') }}</span>
|
<div class="drag-handle" aria-label="Réordonner">
|
||||||
<span class="chapter-title">{{ chapter.title }}</span>
|
<div class="i-lucide-grip-vertical h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<span class="chapter-order">{{ String(i + 1).padStart(2, '0') }}</span>
|
||||||
|
<NuxtLink
|
||||||
|
:to="`/admin/book/${chapter.slug}`"
|
||||||
|
class="chapter-title"
|
||||||
|
>
|
||||||
|
{{ chapter.title }}
|
||||||
|
</NuxtLink>
|
||||||
|
<button
|
||||||
|
class="delete-btn"
|
||||||
|
@click="removeChapter(chapter.slug)"
|
||||||
|
aria-label="Supprimer"
|
||||||
|
>
|
||||||
|
<div class="i-lucide-trash-2 h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
<NuxtLink :to="`/admin/book/${chapter.slug}`">
|
||||||
<div class="i-lucide-chevron-right h-4 w-4 text-white/20" />
|
<div class="i-lucide-chevron-right h-4 w-4 text-white/20" />
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add chapter -->
|
||||||
|
<div class="mt-6 flex items-end gap-3">
|
||||||
|
<div class="flex-1">
|
||||||
|
<label class="block text-xs text-white/40 mb-1">Titre</label>
|
||||||
|
<input v-model="newTitle" class="admin-input w-full" placeholder="Nouveau chapitre" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs text-white/40 mb-1">Slug</label>
|
||||||
|
<input v-model="newSlug" class="admin-input w-full font-mono text-xs" placeholder="12-slug" />
|
||||||
|
</div>
|
||||||
|
<button class="add-btn" @click="addChapter" :disabled="!newTitle || !newSlug">
|
||||||
|
<div class="i-lucide-plus h-4 w-4" />
|
||||||
|
Ajouter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -23,7 +63,74 @@ definePageMeta({
|
|||||||
middleware: 'admin',
|
middleware: 'admin',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: chapters } = await useFetch('/api/admin/chapters')
|
const { data: chapters, refresh } = await useFetch<any[]>('/api/admin/chapters')
|
||||||
|
const saving = ref(false)
|
||||||
|
const saved = ref(false)
|
||||||
|
const newTitle = ref('')
|
||||||
|
const newSlug = ref('')
|
||||||
|
|
||||||
|
// Drag & drop state
|
||||||
|
const dragIdx = ref<number | null>(null)
|
||||||
|
const dropIdx = ref<number | null>(null)
|
||||||
|
|
||||||
|
function onDragStart(i: number, e: DragEvent) {
|
||||||
|
dragIdx.value = i
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragOver(i: number) {
|
||||||
|
dropIdx.value = i
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragEnd() {
|
||||||
|
if (dragIdx.value !== null && dropIdx.value !== null && dragIdx.value !== dropIdx.value && chapters.value) {
|
||||||
|
const [moved] = chapters.value.splice(dragIdx.value, 1)
|
||||||
|
chapters.value.splice(dropIdx.value, 0, moved)
|
||||||
|
}
|
||||||
|
dragIdx.value = null
|
||||||
|
dropIdx.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveOrder() {
|
||||||
|
if (!chapters.value) return
|
||||||
|
saving.value = true
|
||||||
|
saved.value = false
|
||||||
|
try {
|
||||||
|
const orderedChapters = chapters.value.map((ch: any, i: number) => ({
|
||||||
|
slug: ch.slug,
|
||||||
|
order: i + 1,
|
||||||
|
}))
|
||||||
|
await $fetch('/api/admin/chapters', {
|
||||||
|
method: 'PUT',
|
||||||
|
body: { chapters: orderedChapters },
|
||||||
|
})
|
||||||
|
saved.value = true
|
||||||
|
setTimeout(() => { saved.value = false }, 2000)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
saving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addChapter() {
|
||||||
|
if (!newTitle.value || !newSlug.value) return
|
||||||
|
const order = (chapters.value?.length ?? 0) + 1
|
||||||
|
await $fetch('/api/admin/chapters', {
|
||||||
|
method: 'POST',
|
||||||
|
body: { slug: newSlug.value, title: newTitle.value, order },
|
||||||
|
})
|
||||||
|
newTitle.value = ''
|
||||||
|
newSlug.value = ''
|
||||||
|
await refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function removeChapter(slug: string) {
|
||||||
|
if (!confirm(`Supprimer le chapitre "${slug}" ?`)) return
|
||||||
|
await $fetch(`/api/admin/chapters/${slug}`, { method: 'DELETE' })
|
||||||
|
await refresh()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -34,7 +141,6 @@ const { data: chapters } = await useFetch('/api/admin/chapters')
|
|||||||
padding: 0.75rem 1rem;
|
padding: 0.75rem 1rem;
|
||||||
border: 1px solid hsl(20 8% 14%);
|
border: 1px solid hsl(20 8% 14%);
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
text-decoration: none;
|
|
||||||
transition: all 0.2s;
|
transition: all 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +149,25 @@ const { data: chapters } = await useFetch('/api/admin/chapters')
|
|||||||
background: hsl(20 8% 6%);
|
background: hsl(20 8% 6%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chapter-item--dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapter-item--over {
|
||||||
|
border-top: 2px solid hsl(12 76% 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: grab;
|
||||||
|
padding: 0.25rem;
|
||||||
|
color: hsl(20 8% 35%);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
.chapter-order {
|
.chapter-order {
|
||||||
font-family: var(--font-mono, monospace);
|
font-family: var(--font-mono, monospace);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
@@ -55,5 +180,64 @@ const { data: chapters } = await useFetch('/api/admin/chapters')
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
color: white;
|
color: white;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chapter-title:hover {
|
||||||
|
color: hsl(12 76% 68%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.375rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
color: hsl(0 60% 50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn:hover {
|
||||||
|
background: hsl(0 60% 50% / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-input {
|
||||||
|
padding: 0.375rem 0.5rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
border: 1px solid hsl(20 8% 18%);
|
||||||
|
background: hsl(20 8% 6%);
|
||||||
|
color: white;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: hsl(12 76% 48% / 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid hsl(20 8% 25%);
|
||||||
|
background: none;
|
||||||
|
color: hsl(20 8% 55%);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn:hover:not(:disabled) {
|
||||||
|
border-color: hsl(12 76% 48% / 0.5);
|
||||||
|
color: hsl(12 76% 68%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn:disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -6,14 +6,22 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="config">
|
<template v-if="config">
|
||||||
<AdminFormSection title="Métadonnées des chansons" open>
|
<AdminFormSection title="Morceaux" open>
|
||||||
<div
|
<div
|
||||||
v-for="(song, i) in config.songs"
|
v-for="(song, i) in config.songs"
|
||||||
:key="i"
|
:key="song.id"
|
||||||
class="song-row"
|
class="song-row"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragStart(i, $event)"
|
||||||
|
@dragover.prevent="onDragOver(i)"
|
||||||
|
@dragend="onDragEnd"
|
||||||
|
:class="{ 'song-row--dragging': dragIdx === i, 'song-row--over': dropIdx === i && dropIdx !== dragIdx }"
|
||||||
>
|
>
|
||||||
|
<div class="drag-handle" aria-label="Réordonner">
|
||||||
|
<div class="i-lucide-grip-vertical h-4 w-4" />
|
||||||
|
</div>
|
||||||
<span class="song-num">{{ i + 1 }}</span>
|
<span class="song-num">{{ i + 1 }}</span>
|
||||||
<div class="flex-1 grid gap-2 sm:grid-cols-2">
|
<div class="flex-1 grid gap-2 sm:grid-cols-3">
|
||||||
<input
|
<input
|
||||||
v-model="song.title"
|
v-model="song.title"
|
||||||
class="admin-input"
|
class="admin-input"
|
||||||
@@ -24,8 +32,33 @@
|
|||||||
class="admin-input"
|
class="admin-input"
|
||||||
placeholder="/audio/fichier.mp3"
|
placeholder="/audio/fichier.mp3"
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
v-model="song.id"
|
||||||
|
class="admin-input font-mono text-xs"
|
||||||
|
placeholder="identifiant-slug"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex-1">
|
||||||
|
<textarea
|
||||||
|
v-model="song.lyrics"
|
||||||
|
class="admin-input lyrics-textarea"
|
||||||
|
placeholder="Paroles..."
|
||||||
|
rows="2"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
class="delete-btn"
|
||||||
|
@click="removeSong(i)"
|
||||||
|
aria-label="Supprimer"
|
||||||
|
>
|
||||||
|
<div class="i-lucide-trash-2 h-4 w-4" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="add-btn" @click="addSong">
|
||||||
|
<div class="i-lucide-plus h-4 w-4" />
|
||||||
|
Ajouter un morceau
|
||||||
|
</button>
|
||||||
</AdminFormSection>
|
</AdminFormSection>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,14 +70,80 @@ definePageMeta({
|
|||||||
middleware: 'admin',
|
middleware: 'admin',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: config } = await useFetch('/api/content/config')
|
const { data: config } = await useFetch<any>('/api/content/config')
|
||||||
const saving = ref(false)
|
const saving = ref(false)
|
||||||
const saved = ref(false)
|
const saved = ref(false)
|
||||||
|
|
||||||
|
// Drag & drop state
|
||||||
|
const dragIdx = ref<number | null>(null)
|
||||||
|
const dropIdx = ref<number | null>(null)
|
||||||
|
|
||||||
|
function onDragStart(i: number, e: DragEvent) {
|
||||||
|
dragIdx.value = i
|
||||||
|
if (e.dataTransfer) {
|
||||||
|
e.dataTransfer.effectAllowed = 'move'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragOver(i: number) {
|
||||||
|
dropIdx.value = i
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDragEnd() {
|
||||||
|
if (dragIdx.value !== null && dropIdx.value !== null && dragIdx.value !== dropIdx.value && config.value) {
|
||||||
|
const songs = config.value.songs
|
||||||
|
const [moved] = songs.splice(dragIdx.value, 1)
|
||||||
|
songs.splice(dropIdx.value, 0, moved)
|
||||||
|
// Sync defaultPlaylistOrder
|
||||||
|
config.value.defaultPlaylistOrder = songs.map((s: any) => s.id)
|
||||||
|
}
|
||||||
|
dragIdx.value = null
|
||||||
|
dropIdx.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSong() {
|
||||||
|
if (!config.value) return
|
||||||
|
const newSong = {
|
||||||
|
id: `nouveau-morceau-${Date.now()}`,
|
||||||
|
title: '',
|
||||||
|
artist: 'Yvv',
|
||||||
|
file: '/audio/',
|
||||||
|
duration: 0,
|
||||||
|
lyrics: '',
|
||||||
|
tags: [],
|
||||||
|
}
|
||||||
|
config.value.songs.push(newSong)
|
||||||
|
config.value.defaultPlaylistOrder.push(newSong.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeSong(i: number) {
|
||||||
|
if (!config.value) return
|
||||||
|
const songId = config.value.songs[i].id
|
||||||
|
config.value.songs.splice(i, 1)
|
||||||
|
// Remove from defaultPlaylistOrder
|
||||||
|
const orderIdx = config.value.defaultPlaylistOrder.indexOf(songId)
|
||||||
|
if (orderIdx !== -1) config.value.defaultPlaylistOrder.splice(orderIdx, 1)
|
||||||
|
// Clean chapterSongs
|
||||||
|
config.value.chapterSongs = config.value.chapterSongs.filter((cs: any) => cs.songId !== songId)
|
||||||
|
}
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
saving.value = true
|
saving.value = true
|
||||||
saved.value = false
|
saved.value = false
|
||||||
try {
|
try {
|
||||||
|
// Regenerate IDs from titles for new songs
|
||||||
|
for (const song of config.value!.songs) {
|
||||||
|
if (song.id.startsWith('nouveau-morceau-')) {
|
||||||
|
song.id = song.title
|
||||||
|
.toLowerCase()
|
||||||
|
.normalize('NFD').replace(/[\u0300-\u036f]/g, '')
|
||||||
|
.replace(/[^a-z0-9]+/g, '-')
|
||||||
|
.replace(/^-|-$/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sync playlist order
|
||||||
|
config.value!.defaultPlaylistOrder = config.value!.songs.map((s: any) => s.id)
|
||||||
|
|
||||||
await $fetch('/api/admin/content/config', {
|
await $fetch('/api/admin/content/config', {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: config.value,
|
body: config.value,
|
||||||
@@ -61,10 +160,31 @@ async function save() {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.song-row {
|
.song-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: flex-start;
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
padding: 0.5rem;
|
padding: 0.75rem 0.5rem;
|
||||||
border-bottom: 1px solid hsl(20 8% 10%);
|
border-bottom: 1px solid hsl(20 8% 10%);
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.song-row--dragging {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.song-row--over {
|
||||||
|
border-top: 2px solid hsl(12 76% 48%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle {
|
||||||
|
cursor: grab;
|
||||||
|
padding: 0.25rem;
|
||||||
|
color: hsl(20 8% 35%);
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drag-handle:active {
|
||||||
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
.song-num {
|
.song-num {
|
||||||
@@ -73,6 +193,8 @@ async function save() {
|
|||||||
color: hsl(20 8% 40%);
|
color: hsl(20 8% 40%);
|
||||||
width: 1.25rem;
|
width: 1.25rem;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 0.375rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.admin-input {
|
.admin-input {
|
||||||
@@ -89,4 +211,45 @@ async function save() {
|
|||||||
outline: none;
|
outline: none;
|
||||||
border-color: hsl(12 76% 48% / 0.5);
|
border-color: hsl(12 76% 48% / 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lyrics-textarea {
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.375rem;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
color: hsl(0 60% 50%);
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn:hover {
|
||||||
|
background: hsl(0 60% 50% / 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px dashed hsl(20 8% 25%);
|
||||||
|
background: none;
|
||||||
|
color: hsl(20 8% 55%);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn:hover {
|
||||||
|
border-color: hsl(12 76% 48% / 0.5);
|
||||||
|
color: hsl(12 76% 68%);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
168
app/pages/autonomie.vue
Normal file
168
app/pages/autonomie.vue
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative overflow-hidden section-padding">
|
||||||
|
<!-- Shadok jardinier: character with watering can and plant -->
|
||||||
|
<svg class="shadok-jardinier" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body -->
|
||||||
|
<ellipse cx="110" cy="160" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head -->
|
||||||
|
<circle cx="110" cy="96" r="25" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Straw hat -->
|
||||||
|
<ellipse cx="110" cy="78" rx="35" ry="8" fill="currentColor" opacity="0.4"/>
|
||||||
|
<path d="M85 78 Q110 60 135 78" fill="currentColor" opacity="0.35"/>
|
||||||
|
<!-- Eyes (focused, looking down at plant) -->
|
||||||
|
<circle cx="102" cy="94" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="120" cy="94" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="103" cy="95" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="121" cy="95" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<!-- Smile -->
|
||||||
|
<path d="M103 106 Q110 111 118 106" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Arm holding watering can -->
|
||||||
|
<line x1="70" y1="150" x2="40" y2="170" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Watering can -->
|
||||||
|
<rect x="20" y="165" width="30" height="20" rx="3" fill="currentColor" opacity="0.4"/>
|
||||||
|
<line x1="20" y1="168" x2="10" y2="160" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.4"/>
|
||||||
|
<!-- Water drops -->
|
||||||
|
<circle cx="12" cy="165" r="1.5" fill="currentColor" opacity="0.25"/>
|
||||||
|
<circle cx="8" cy="170" r="1.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="15" cy="172" r="1.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<!-- Other arm -->
|
||||||
|
<line x1="150" y1="150" x2="170" y2="180" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Legs -->
|
||||||
|
<line x1="95" y1="205" x2="85" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="125" y1="205" x2="135" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Plant -->
|
||||||
|
<line x1="180" y1="220" x2="180" y2="180" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.4"/>
|
||||||
|
<path d="M180 195 Q195 185 190 175" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35"/>
|
||||||
|
<path d="M180 205 Q165 195 168 185" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35"/>
|
||||||
|
<path d="M180 185 Q192 172 188 165" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Pot -->
|
||||||
|
<path d="M170 220 L175 240 L185 240 L190 220 Z" fill="currentColor" opacity="0.35"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok bâtisseur: character with trowel building a wall -->
|
||||||
|
<svg class="shadok-batisseur" viewBox="0 0 260 300" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body -->
|
||||||
|
<ellipse cx="130" cy="150" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head -->
|
||||||
|
<circle cx="130" cy="86" r="25" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Hard hat -->
|
||||||
|
<ellipse cx="130" cy="68" rx="28" ry="6" fill="currentColor" opacity="0.4"/>
|
||||||
|
<rect x="108" y="60" width="44" height="10" rx="3" fill="currentColor" opacity="0.35"/>
|
||||||
|
<!-- Eyes (determined) -->
|
||||||
|
<circle cx="122" cy="84" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="140" cy="84" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="123" cy="83" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="141" cy="83" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<!-- Grin -->
|
||||||
|
<path d="M123 96 Q130 101 138 96" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Arm with trowel -->
|
||||||
|
<line x1="170" y1="140" x2="210" y2="120" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Trowel -->
|
||||||
|
<polygon points="210,115 230,110 225,120 210,122" fill="currentColor" opacity="0.45"/>
|
||||||
|
<line x1="210" y1="118" x2="200" y2="125" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.4"/>
|
||||||
|
<!-- Other arm -->
|
||||||
|
<line x1="90" y1="145" x2="65" y2="170" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Legs -->
|
||||||
|
<line x1="115" y1="195" x2="105" y2="255" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="145" y1="195" x2="155" y2="255" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Wall (bricks) -->
|
||||||
|
<rect x="40" y="200" width="50" height="16" rx="1" fill="currentColor" opacity="0.3"/>
|
||||||
|
<rect x="45" y="183" width="40" height="16" rx="1" fill="currentColor" opacity="0.28"/>
|
||||||
|
<rect x="50" y="166" width="30" height="16" rx="1" fill="currentColor" opacity="0.25"/>
|
||||||
|
<!-- Brick lines -->
|
||||||
|
<line x1="65" y1="200" x2="65" y2="216" stroke="currentColor" stroke-width="1" opacity="0.15"/>
|
||||||
|
<line x1="55" y1="183" x2="55" y2="199" stroke="currentColor" stroke-width="1" opacity="0.15"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="container-content">
|
||||||
|
<header class="mb-12 text-center">
|
||||||
|
<p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p>
|
||||||
|
<h1 class="page-title font-display font-bold tracking-tight text-white">
|
||||||
|
{{ content?.title }}
|
||||||
|
</h1>
|
||||||
|
<p class="mt-4 mx-auto max-w-2xl text-white/60">
|
||||||
|
{{ content?.description }}
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="mx-auto max-w-3xl flex flex-col gap-6">
|
||||||
|
<div
|
||||||
|
v-for="(extract, i) in content?.extracts"
|
||||||
|
:key="i"
|
||||||
|
class="card-surface"
|
||||||
|
>
|
||||||
|
<p class="mb-2 font-mono text-xs tracking-widest text-accent uppercase">
|
||||||
|
{{ extract.chapter }}
|
||||||
|
</p>
|
||||||
|
<blockquote class="border-l-2 border-primary/30 pl-4 text-white/70 italic leading-relaxed whitespace-pre-line">
|
||||||
|
{{ extract.text }}
|
||||||
|
</blockquote>
|
||||||
|
<div class="mt-4">
|
||||||
|
<NuxtLink
|
||||||
|
:to="`/modele-eco/${extract.chapterSlug}`"
|
||||||
|
class="inline-flex items-center gap-1 text-sm text-primary hover:text-primary/80 transition-colors"
|
||||||
|
>
|
||||||
|
Lire le chapitre
|
||||||
|
<div class="i-lucide-arrow-right h-3.5 w-3.5" />
|
||||||
|
</NuxtLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
layout: 'default',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: content } = await usePageContent('autonomie')
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: content.value?.meta?.title ?? 'Autonomie',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-title {
|
||||||
|
font-size: clamp(2rem, 5vw, 2.75rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-jardinier {
|
||||||
|
position: absolute;
|
||||||
|
left: 2%;
|
||||||
|
top: 5%;
|
||||||
|
width: clamp(100px, 14vw, 200px);
|
||||||
|
opacity: 0.28;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-float-jardinier 10s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-batisseur {
|
||||||
|
position: absolute;
|
||||||
|
right: 2%;
|
||||||
|
bottom: 5%;
|
||||||
|
width: clamp(110px, 15vw, 210px);
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-accent));
|
||||||
|
animation: shadok-float-batisseur 11s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-jardinier {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-batisseur {
|
||||||
|
0%, 100% { transform: translateY(0) rotate(0deg); }
|
||||||
|
50% { transform: translateY(-8px) rotate(1deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.shadok-jardinier { display: none; }
|
||||||
|
.shadok-batisseur { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative overflow-hidden section-padding">
|
<div class="relative overflow-hidden section-padding">
|
||||||
|
<!-- Shadok danseur: character dancing with music notes -->
|
||||||
|
<svg class="shadok-danseur" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body (dynamic pose, leaning) -->
|
||||||
|
<ellipse cx="120" cy="155" rx="38" ry="46" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head (tilted with joy) -->
|
||||||
|
<ellipse cx="125" cy="92" rx="24" ry="23" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Eyes (happy, squinted) -->
|
||||||
|
<path d="M114 88 Q118 84 122 88" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<path d="M130 88 Q134 84 138 88" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<!-- Big smile -->
|
||||||
|
<path d="M116 100 Q125 108 134 100" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35"/>
|
||||||
|
<!-- Arms thrown up (dancing) -->
|
||||||
|
<line x1="85" y1="140" x2="50" y2="100" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="155" y1="140" x2="190" y2="105" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Hands -->
|
||||||
|
<circle cx="50" cy="98" r="4" fill="currentColor" opacity="0.4"/>
|
||||||
|
<circle cx="190" cy="103" r="4" fill="currentColor" opacity="0.4"/>
|
||||||
|
<!-- Legs (one kicked up) -->
|
||||||
|
<line x1="105" y1="198" x2="80" y2="255" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="135" y1="198" x2="170" y2="240" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Feet -->
|
||||||
|
<path d="M80 255 L68 258 M80 255 L75 261" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
|
<path d="M170 240 L180 238 M170 240 L175 246" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
|
<!-- Music notes floating around -->
|
||||||
|
<text x="42" y="82" fill="currentColor" opacity="0.3" font-size="18">♪</text>
|
||||||
|
<text x="195" y="88" fill="currentColor" opacity="0.25" font-size="16">♫</text>
|
||||||
|
<text x="60" y="65" fill="currentColor" opacity="0.2" font-size="14">♩</text>
|
||||||
|
<text x="180" y="72" fill="currentColor" opacity="0.2" font-size="20">♪</text>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<!-- Shadok DJ: character with headphones behind a turntable -->
|
<!-- Shadok DJ: character with headphones behind a turntable -->
|
||||||
<svg class="shadok-dj" viewBox="0 0 260 300" fill="none" aria-hidden="true">
|
<svg class="shadok-dj" viewBox="0 0 260 300" fill="none" aria-hidden="true">
|
||||||
<!-- Body -->
|
<!-- Body -->
|
||||||
@@ -23,7 +53,7 @@
|
|||||||
<line x1="170" y1="150" x2="205" y2="195" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
<line x1="170" y1="150" x2="205" y2="195" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
<!-- Turntable body -->
|
<!-- Turntable body -->
|
||||||
<rect x="30" y="200" width="200" height="18" rx="4" fill="currentColor" opacity="0.4"/>
|
<rect x="30" y="200" width="200" height="18" rx="4" fill="currentColor" opacity="0.4"/>
|
||||||
<!-- Turntable platter (ellipse for perspective) -->
|
<!-- Turntable platter -->
|
||||||
<ellipse cx="130" cy="200" rx="55" ry="15" fill="currentColor" opacity="0.25"/>
|
<ellipse cx="130" cy="200" rx="55" ry="15" fill="currentColor" opacity="0.25"/>
|
||||||
<ellipse cx="130" cy="200" rx="55" ry="15" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.35"/>
|
<ellipse cx="130" cy="200" rx="55" ry="15" stroke="currentColor" stroke-width="1.5" fill="none" opacity="0.35"/>
|
||||||
<!-- Record center -->
|
<!-- Record center -->
|
||||||
@@ -37,15 +67,38 @@
|
|||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div class="container-content">
|
<div class="container-content">
|
||||||
<header class="mb-12 text-center">
|
<!-- Hero section with book cover -->
|
||||||
|
<div class="mb-12 grid items-center gap-8 md:grid-cols-2">
|
||||||
|
<div class="book-cover-wrapper">
|
||||||
|
<div class="book-cover-3d">
|
||||||
|
<img
|
||||||
|
:src="homeContent?.book.coverImage"
|
||||||
|
:alt="homeContent?.book.coverAlt"
|
||||||
|
class="book-cover-img"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center md:text-left">
|
||||||
<p class="mb-2 font-mono text-sm tracking-widest text-accent uppercase">{{ content?.kicker }}</p>
|
<p class="mb-2 font-mono text-sm tracking-widest text-accent uppercase">{{ content?.kicker }}</p>
|
||||||
<h1 class="page-title font-display font-bold tracking-tight text-white">
|
<h1 class="page-title font-display font-bold tracking-tight text-white">
|
||||||
{{ content?.title }}
|
{{ content?.title }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="mt-4 mx-auto max-w-2xl text-white/60">
|
<p class="mt-4 text-white/60">
|
||||||
{{ content?.description }}
|
{{ content?.description }}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
<div class="mt-6 flex flex-col gap-3 sm:flex-row sm:gap-4 justify-center md:justify-start">
|
||||||
|
<UiBaseButton @click="showBookPlayer = true">
|
||||||
|
<div class="i-lucide-play mr-2 h-5 w-5" />
|
||||||
|
Présentation musicale
|
||||||
|
</UiBaseButton>
|
||||||
|
<UiBaseButton variant="accent" @click="showPdfReader = true">
|
||||||
|
<div class="i-lucide-book-open mr-2 h-5 w-5" />
|
||||||
|
Lire le livre
|
||||||
|
</UiBaseButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Search + view toggle -->
|
<!-- Search + view toggle -->
|
||||||
<div class="mb-6 flex items-center justify-between gap-4">
|
<div class="mb-6 flex items-center justify-between gap-4">
|
||||||
@@ -99,6 +152,9 @@
|
|||||||
{{ content?.noResults }}
|
{{ content?.noResults }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<BookPlayer v-model="showBookPlayer" />
|
||||||
|
<BookPdfReader v-model="showPdfReader" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -107,10 +163,11 @@ definePageMeta({
|
|||||||
layout: 'default',
|
layout: 'default',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: content } = await usePageContent('ecouter')
|
const { data: content } = await usePageContent('en-musique')
|
||||||
|
const { data: homeContent } = await usePageContent('home')
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: content.value?.meta?.title ?? 'Écouter',
|
title: content.value?.meta?.title ?? 'En musique',
|
||||||
})
|
})
|
||||||
|
|
||||||
const store = usePlayerStore()
|
const store = usePlayerStore()
|
||||||
@@ -125,6 +182,8 @@ await loadFullPlaylist()
|
|||||||
|
|
||||||
const search = ref('')
|
const search = ref('')
|
||||||
const viewMode = ref<'list' | 'grid'>('list')
|
const viewMode = ref<'list' | 'grid'>('list')
|
||||||
|
const showBookPlayer = ref(false)
|
||||||
|
const showPdfReader = ref(false)
|
||||||
|
|
||||||
const filteredSongs = computed(() => {
|
const filteredSongs = computed(() => {
|
||||||
const songs = bookData.getSongs()
|
const songs = bookData.getSongs()
|
||||||
@@ -144,6 +203,50 @@ const filteredSongs = computed(() => {
|
|||||||
font-size: clamp(2rem, 5vw, 2.75rem);
|
font-size: clamp(2rem, 5vw, 2.75rem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.book-cover-wrapper {
|
||||||
|
perspective: 800px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-cover-3d {
|
||||||
|
aspect-ratio: 3 / 4;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
border: 1px solid hsl(var(--color-text) / 0.1);
|
||||||
|
box-shadow:
|
||||||
|
0 12px 40px hsl(var(--color-text) / 0.15),
|
||||||
|
0 0 0 1px hsl(var(--color-text) / 0.08);
|
||||||
|
transition: transform 0.5s cubic-bezier(0.645, 0.045, 0.355, 1),
|
||||||
|
box-shadow 0.5s ease;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-cover-3d:hover {
|
||||||
|
transform: rotateY(-8deg) rotateX(3deg) scale(1.02);
|
||||||
|
box-shadow:
|
||||||
|
12px 16px 48px hsl(var(--color-text) / 0.2),
|
||||||
|
0 0 0 1px hsl(var(--color-primary) / 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.book-cover-img {
|
||||||
|
width: 200%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-danseur {
|
||||||
|
position: absolute;
|
||||||
|
left: 2%;
|
||||||
|
top: 3%;
|
||||||
|
width: clamp(100px, 14vw, 200px);
|
||||||
|
opacity: 0.28;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-float-danseur 7s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.shadok-dj {
|
.shadok-dj {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 2%;
|
right: 2%;
|
||||||
@@ -155,12 +258,19 @@ const filteredSongs = computed(() => {
|
|||||||
animation: shadok-float-dj 8s ease-in-out infinite;
|
animation: shadok-float-dj 8s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-danseur {
|
||||||
|
0%, 100% { transform: translateY(0) rotate(0deg); }
|
||||||
|
25% { transform: translateY(-8px) rotate(2deg); }
|
||||||
|
75% { transform: translateY(-4px) rotate(-2deg); }
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes shadok-float-dj {
|
@keyframes shadok-float-dj {
|
||||||
0%, 100% { transform: translateY(0); }
|
0%, 100% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-10px); }
|
50% { transform: translateY(-10px); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
.shadok-danseur { display: none; }
|
||||||
.shadok-dj { display: none; }
|
.shadok-dj { display: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
267
app/pages/evenement.vue
Normal file
267
app/pages/evenement.vue
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative overflow-hidden section-padding min-h-[70vh] flex items-center justify-center">
|
||||||
|
<!-- Shadok jongleur: juggling coins (top-left) -->
|
||||||
|
<svg class="shadok-juggler" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body -->
|
||||||
|
<ellipse cx="120" cy="160" rx="38" ry="46" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head -->
|
||||||
|
<circle cx="120" cy="98" r="24" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Eyes (looking up at coins) -->
|
||||||
|
<circle cx="112" cy="92" r="3.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="130" cy="92" r="3.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="113" cy="91" r="1.5" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="131" cy="91" r="1.5" fill="currentColor" opacity="0.5"/>
|
||||||
|
<!-- Smile -->
|
||||||
|
<path d="M112 108 Q120 114 128 108" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.35"/>
|
||||||
|
<!-- Arms up (juggling) -->
|
||||||
|
<line x1="85" y1="145" x2="55" y2="105" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="155" y1="145" x2="185" y2="105" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Hands -->
|
||||||
|
<circle cx="55" cy="103" r="4" fill="currentColor" opacity="0.4"/>
|
||||||
|
<circle cx="185" cy="103" r="4" fill="currentColor" opacity="0.4"/>
|
||||||
|
<!-- Juggling coins -->
|
||||||
|
<circle cx="90" cy="55" r="8" fill="currentColor" opacity="0.35"/>
|
||||||
|
<text x="86" y="59" fill="currentColor" opacity="0.5" font-size="10" font-weight="bold">$</text>
|
||||||
|
<circle cx="120" cy="40" r="8" fill="currentColor" opacity="0.3"/>
|
||||||
|
<text x="116" y="44" fill="currentColor" opacity="0.45" font-size="10" font-weight="bold">$</text>
|
||||||
|
<circle cx="150" cy="50" r="8" fill="currentColor" opacity="0.32"/>
|
||||||
|
<text x="146" y="54" fill="currentColor" opacity="0.48" font-size="10" font-weight="bold">$</text>
|
||||||
|
<!-- Legs -->
|
||||||
|
<line x1="105" y1="203" x2="95" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="135" y1="203" x2="145" y2="260" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok échelle: on a wobbly ladder (top-right) -->
|
||||||
|
<svg class="shadok-ladder" viewBox="0 0 220 320" fill="none" aria-hidden="true">
|
||||||
|
<!-- Ladder (tilting) -->
|
||||||
|
<line x1="80" y1="50" x2="70" y2="300" stroke="currentColor" stroke-width="3" opacity="0.35"/>
|
||||||
|
<line x1="150" y1="50" x2="140" y2="300" stroke="currentColor" stroke-width="3" opacity="0.35"/>
|
||||||
|
<!-- Rungs -->
|
||||||
|
<line x1="82" y1="80" x2="148" y2="80" stroke="currentColor" stroke-width="2.5" opacity="0.3"/>
|
||||||
|
<line x1="83" y1="120" x2="147" y2="120" stroke="currentColor" stroke-width="2.5" opacity="0.3"/>
|
||||||
|
<line x1="84" y1="160" x2="146" y2="160" stroke="currentColor" stroke-width="2.5" opacity="0.3"/>
|
||||||
|
<line x1="85" y1="200" x2="145" y2="200" stroke="currentColor" stroke-width="2.5" opacity="0.3"/>
|
||||||
|
<line x1="86" y1="240" x2="144" y2="240" stroke="currentColor" stroke-width="2.5" opacity="0.3"/>
|
||||||
|
<!-- Shadok on top (arms out for balance) -->
|
||||||
|
<ellipse cx="115" cy="68" rx="18" ry="14" fill="currentColor" opacity="0.85"/>
|
||||||
|
<circle cx="115" cy="46" r="14" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Eyes (worried) -->
|
||||||
|
<circle cx="110" cy="43" r="3" fill="currentColor" opacity="0.25"/>
|
||||||
|
<circle cx="122" cy="43" r="3" fill="currentColor" opacity="0.25"/>
|
||||||
|
<circle cx="110" cy="44" r="1.2" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="122" cy="44" r="1.2" fill="currentColor" opacity="0.5"/>
|
||||||
|
<!-- Worried mouth -->
|
||||||
|
<path d="M108 52 Q115 49 122 52" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Arms out (balancing) -->
|
||||||
|
<line x1="97" y1="62" x2="60" y2="55" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="133" y1="62" x2="170" y2="55" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok acrobate: doing a cartwheel (center) -->
|
||||||
|
<svg class="shadok-acrobat" viewBox="0 0 260 240" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body (sideways, mid-cartwheel) -->
|
||||||
|
<ellipse cx="130" cy="120" rx="30" ry="38" fill="currentColor" opacity="0.85" transform="rotate(45 130 120)"/>
|
||||||
|
<!-- Head -->
|
||||||
|
<circle cx="155" cy="82" r="20" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Eyes (dizzy/happy) -->
|
||||||
|
<path d="M148 78 Q152 74 156 78" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<path d="M160 78 Q164 74 168 78" stroke="currentColor" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<!-- Smile -->
|
||||||
|
<path d="M150 90 Q158 95 165 90" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Arms (one touching ground, one up) -->
|
||||||
|
<line x1="110" y1="100" x2="80" y2="130" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="150" y1="105" x2="185" y2="70" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Hand on ground -->
|
||||||
|
<circle cx="78" cy="132" r="4" fill="currentColor" opacity="0.4"/>
|
||||||
|
<!-- Legs (splayed in cartwheel) -->
|
||||||
|
<line x1="125" y1="155" x2="100" y2="195" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="140" y1="150" x2="175" y2="175" stroke="currentColor" stroke-width="3" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Motion lines -->
|
||||||
|
<path d="M70 110 Q60 105 55 115" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
<path d="M190 60 Q200 55 205 65" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok dormeur: sleeping on a cloud (bottom-left) -->
|
||||||
|
<svg class="shadok-sleeper" viewBox="0 0 260 220" fill="none" aria-hidden="true">
|
||||||
|
<!-- Cloud -->
|
||||||
|
<ellipse cx="130" cy="150" rx="80" ry="25" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="80" cy="140" r="25" fill="currentColor" opacity="0.18"/>
|
||||||
|
<circle cx="120" cy="130" r="30" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="165" cy="135" r="22" fill="currentColor" opacity="0.18"/>
|
||||||
|
<circle cx="190" cy="142" r="18" fill="currentColor" opacity="0.15"/>
|
||||||
|
<!-- Shadok body (lying down) -->
|
||||||
|
<ellipse cx="130" cy="125" rx="35" ry="18" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head (on cloud, sideways) -->
|
||||||
|
<ellipse cx="85" cy="118" rx="18" ry="16" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Closed eyes (sleeping) -->
|
||||||
|
<path d="M76 115 Q80 112 84 115" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<path d="M88 115 Q92 112 96 115" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.4"/>
|
||||||
|
<!-- Snooze bubbles -->
|
||||||
|
<text x="70" y="100" fill="currentColor" opacity="0.3" font-size="12" font-weight="bold">z</text>
|
||||||
|
<text x="60" y="85" fill="currentColor" opacity="0.25" font-size="16" font-weight="bold">z</text>
|
||||||
|
<text x="48" y="68" fill="currentColor" opacity="0.2" font-size="20" font-weight="bold">z</text>
|
||||||
|
<!-- Legs (curled) -->
|
||||||
|
<path d="M165 125 Q180 130 175 140" stroke="currentColor" stroke-width="3" stroke-linecap="round" fill="none" opacity="0.5"/>
|
||||||
|
<path d="M160 130 Q172 138 168 148" stroke="currentColor" stroke-width="3" stroke-linecap="round" fill="none" opacity="0.5"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok cuisinier: cooking in a cauldron (bottom-right) -->
|
||||||
|
<svg class="shadok-cook" viewBox="0 0 240 300" fill="none" aria-hidden="true">
|
||||||
|
<!-- Body -->
|
||||||
|
<ellipse cx="120" cy="145" rx="38" ry="45" fill="currentColor" opacity="0.85"/>
|
||||||
|
<!-- Head -->
|
||||||
|
<circle cx="120" cy="85" r="24" fill="currentColor" opacity="0.8"/>
|
||||||
|
<!-- Chef hat -->
|
||||||
|
<ellipse cx="120" cy="62" rx="22" ry="18" fill="currentColor" opacity="0.35"/>
|
||||||
|
<rect x="105" y="68" width="30" height="6" rx="1" fill="currentColor" opacity="0.4"/>
|
||||||
|
<!-- Eyes (focused on cooking) -->
|
||||||
|
<circle cx="112" cy="82" r="3.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="130" cy="82" r="3.5" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="113" cy="83" r="1.5" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="131" cy="83" r="1.5" fill="currentColor" opacity="0.5"/>
|
||||||
|
<!-- Tongue out (concentrating) -->
|
||||||
|
<path d="M115 96 Q120 100 125 96" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.3"/>
|
||||||
|
<!-- Arm with ladle -->
|
||||||
|
<line x1="155" y1="135" x2="185" y2="175" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Ladle -->
|
||||||
|
<line x1="185" y1="175" x2="175" y2="200" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
|
||||||
|
<ellipse cx="175" cy="205" rx="8" ry="5" fill="currentColor" opacity="0.35"/>
|
||||||
|
<!-- Other arm -->
|
||||||
|
<line x1="85" y1="140" x2="60" y2="175" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Legs -->
|
||||||
|
<line x1="105" y1="188" x2="95" y2="250" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="135" y1="188" x2="145" y2="250" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<!-- Cauldron -->
|
||||||
|
<path d="M55 220 Q55 260 120 260 Q185 260 185 220" fill="currentColor" opacity="0.3"/>
|
||||||
|
<ellipse cx="120" cy="220" rx="65" ry="12" fill="currentColor" opacity="0.25"/>
|
||||||
|
<ellipse cx="120" cy="220" rx="65" ry="12" stroke="currentColor" stroke-width="2" fill="none" opacity="0.35"/>
|
||||||
|
<!-- Steam -->
|
||||||
|
<path d="M95 210 Q90 195 95 185" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
<path d="M120 208 Q118 190 122 180" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
<path d="M145 210 Q148 195 143 185" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" fill="none" opacity="0.2"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<div class="container-content relative z-10 text-center">
|
||||||
|
<p class="mb-3 font-mono text-sm tracking-widest text-accent uppercase">{{ content?.kicker }}</p>
|
||||||
|
<h1 class="page-title font-display font-extrabold tracking-tight text-white">
|
||||||
|
{{ content?.title }}
|
||||||
|
</h1>
|
||||||
|
<p class="mt-4 text-lg text-white/50">
|
||||||
|
{{ content?.description }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
definePageMeta({
|
||||||
|
layout: 'default',
|
||||||
|
})
|
||||||
|
|
||||||
|
const { data: content } = await usePageContent('evenement')
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
title: content.value?.meta?.title ?? 'Évènement',
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.page-title {
|
||||||
|
font-size: clamp(2.5rem, 6vw, 3.5rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-juggler {
|
||||||
|
position: absolute;
|
||||||
|
left: 4%;
|
||||||
|
top: 5%;
|
||||||
|
width: clamp(100px, 14vw, 190px);
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-bounce-juggler 4s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-ladder {
|
||||||
|
position: absolute;
|
||||||
|
right: 4%;
|
||||||
|
top: 3%;
|
||||||
|
width: clamp(90px, 12vw, 170px);
|
||||||
|
opacity: 0.28;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-accent));
|
||||||
|
animation: shadok-wobble-ladder 5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-acrobat {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 55%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: clamp(100px, 13vw, 180px);
|
||||||
|
opacity: 0.2;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-spin-acrobat 6s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-sleeper {
|
||||||
|
position: absolute;
|
||||||
|
left: 3%;
|
||||||
|
bottom: 5%;
|
||||||
|
width: clamp(110px, 15vw, 210px);
|
||||||
|
opacity: 0.25;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-accent));
|
||||||
|
animation: shadok-float-sleeper 8s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadok-cook {
|
||||||
|
position: absolute;
|
||||||
|
right: 3%;
|
||||||
|
bottom: 4%;
|
||||||
|
width: clamp(100px, 14vw, 200px);
|
||||||
|
opacity: 0.3;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-bounce-cook 5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-bounce-juggler {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
30% { transform: translateY(-12px); }
|
||||||
|
60% { transform: translateY(-6px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-wobble-ladder {
|
||||||
|
0%, 100% { transform: rotate(0deg); }
|
||||||
|
25% { transform: rotate(3deg); }
|
||||||
|
75% { transform: rotate(-3deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-spin-acrobat {
|
||||||
|
0% { transform: translateX(-50%) rotate(0deg); }
|
||||||
|
25% { transform: translateX(-50%) rotate(15deg); }
|
||||||
|
50% { transform: translateX(-50%) rotate(0deg); }
|
||||||
|
75% { transform: translateX(-50%) rotate(-15deg); }
|
||||||
|
100% { transform: translateX(-50%) rotate(0deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-sleeper {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-6px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-bounce-cook {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
40% { transform: translateY(-10px); }
|
||||||
|
70% { transform: translateY(-4px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.shadok-juggler { display: none; }
|
||||||
|
.shadok-ladder { display: none; }
|
||||||
|
.shadok-acrobat { display: none; }
|
||||||
|
.shadok-sleeper { display: none; }
|
||||||
|
.shadok-cook { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
<nav class="mt-16 flex items-center justify-between border-t border-white/8 pt-8">
|
<nav class="mt-16 flex items-center justify-between border-t border-white/8 pt-8">
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-if="prevChapter"
|
v-if="prevChapter"
|
||||||
:to="`/lire/${prevChapter.stem}`"
|
:to="`/modele-eco/${prevChapter.stem}`"
|
||||||
class="btn-ghost gap-2"
|
class="btn-ghost gap-2"
|
||||||
>
|
>
|
||||||
<div class="i-lucide-arrow-left h-4 w-4" />
|
<div class="i-lucide-arrow-left h-4 w-4" />
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
v-if="nextChapter"
|
v-if="nextChapter"
|
||||||
:to="`/lire/${nextChapter.stem}`"
|
:to="`/modele-eco/${nextChapter.stem}`"
|
||||||
class="btn-ghost gap-2"
|
class="btn-ghost gap-2"
|
||||||
>
|
>
|
||||||
<span class="text-sm">{{ nextChapter.title }}</span>
|
<span class="text-sm">{{ nextChapter.title }}</span>
|
||||||
@@ -49,6 +49,30 @@
|
|||||||
<circle cx="87" cy="29" r="1.5" fill="currentColor" opacity="0.3"/>
|
<circle cx="87" cy="29" r="1.5" fill="currentColor" opacity="0.3"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
|
<!-- Shadok scribe: character with quill and inkwell -->
|
||||||
|
<svg class="shadok-scribe" viewBox="0 0 240 280" fill="none" aria-hidden="true">
|
||||||
|
<ellipse cx="120" cy="155" rx="40" ry="48" fill="currentColor" opacity="0.85"/>
|
||||||
|
<circle cx="120" cy="92" r="25" fill="currentColor" opacity="0.8"/>
|
||||||
|
<ellipse cx="120" cy="72" rx="20" ry="8" fill="currentColor" opacity="0.35"/>
|
||||||
|
<circle cx="112" cy="90" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="130" cy="90" r="4" fill="currentColor" opacity="0.2"/>
|
||||||
|
<circle cx="113" cy="91" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<circle cx="131" cy="91" r="1.8" fill="currentColor" opacity="0.5"/>
|
||||||
|
<path d="M118 104 Q120 108 122 104" fill="currentColor" opacity="0.3"/>
|
||||||
|
<line x1="160" y1="140" x2="190" y2="170" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<line x1="190" y1="170" x2="210" y2="145" stroke="currentColor" stroke-width="2" stroke-linecap="round" opacity="0.45"/>
|
||||||
|
<path d="M210 145 Q215 140 212 135 Q208 138 210 145" fill="currentColor" opacity="0.3"/>
|
||||||
|
<line x1="80" y1="148" x2="55" y2="180" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" opacity="0.6"/>
|
||||||
|
<path d="M100 200 Q90 230 95 250" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
|
||||||
|
<path d="M140 200 Q150 230 145 250" stroke="currentColor" stroke-width="3.5" stroke-linecap="round" fill="none" opacity="0.6"/>
|
||||||
|
<rect x="45" y="185" width="20" height="18" rx="3" fill="currentColor" opacity="0.35"/>
|
||||||
|
<ellipse cx="55" cy="185" rx="12" ry="4" fill="currentColor" opacity="0.3"/>
|
||||||
|
<rect x="170" y="175" width="40" height="50" rx="2" fill="currentColor" opacity="0.2"/>
|
||||||
|
<line x1="178" y1="188" x2="202" y2="188" stroke="currentColor" stroke-width="1" opacity="0.15"/>
|
||||||
|
<line x1="178" y1="195" x2="200" y2="195" stroke="currentColor" stroke-width="1" opacity="0.15"/>
|
||||||
|
<line x1="178" y1="202" x2="198" y2="202" stroke="currentColor" stroke-width="1" opacity="0.15"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
<div class="container-content">
|
<div class="container-content">
|
||||||
<header class="mb-12 text-center">
|
<header class="mb-12 text-center">
|
||||||
<p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p>
|
<p class="mb-2 font-mono text-sm tracking-widest text-primary uppercase">{{ content?.kicker }}</p>
|
||||||
@@ -67,7 +91,7 @@
|
|||||||
:key="chapter.path"
|
:key="chapter.path"
|
||||||
>
|
>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="`/lire/${chapter.stem}`"
|
:to="`/modele-eco/${chapter.stem}`"
|
||||||
class="card-surface flex items-start gap-4 group"
|
class="card-surface flex items-start gap-4 group"
|
||||||
>
|
>
|
||||||
<span class="font-mono text-2xl font-bold text-primary/30 leading-none mt-1 w-10 text-right flex-shrink-0">
|
<span class="font-mono text-2xl font-bold text-primary/30 leading-none mt-1 w-10 text-right flex-shrink-0">
|
||||||
@@ -102,7 +126,7 @@ definePageMeta({
|
|||||||
layout: 'default',
|
layout: 'default',
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: content } = await usePageContent('lire')
|
const { data: content } = await usePageContent('modele-eco')
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
title: content.value?.meta?.title ?? 'Table des matières',
|
title: content.value?.meta?.title ?? 'Table des matières',
|
||||||
@@ -150,8 +174,26 @@ const { data: chapters } = await useAsyncData('book-toc', () =>
|
|||||||
50% { transform: translateY(-8px) rotate(-2deg); }
|
50% { transform: translateY(-8px) rotate(-2deg); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadok-scribe {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
bottom: 3%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: clamp(100px, 13vw, 180px);
|
||||||
|
opacity: 0.25;
|
||||||
|
pointer-events: none;
|
||||||
|
color: hsl(var(--color-primary));
|
||||||
|
animation: shadok-float-scribe 10s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadok-float-scribe {
|
||||||
|
0%, 100% { transform: translateX(-50%) translateY(0); }
|
||||||
|
50% { transform: translateX(-50%) translateY(-8px); }
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.shadok-reader { display: none; }
|
.shadok-reader { display: none; }
|
||||||
.shadok-stack { display: none; }
|
.shadok-stack { display: none; }
|
||||||
|
.shadok-scribe { display: none; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -46,29 +46,29 @@ const palettes: Record<PaletteName, PaletteColors> = {
|
|||||||
|
|
||||||
// ══════ LIGHT THEMES ══════
|
// ══════ LIGHT THEMES ══════
|
||||||
|
|
||||||
// Printemps : vert tendre, rose cerisier, lumière fraîche
|
// Printemps : vert vif, rose cerisier punchy, lumière fraîche
|
||||||
printemps: {
|
printemps: {
|
||||||
primary: '145 50% 38%', // vert bourgeon
|
primary: '152 65% 36%', // vert émeraude vif
|
||||||
accent: '340 65% 55%', // rose cerisier
|
accent: '340 78% 52%', // rose cerisier punchy
|
||||||
surface: '120 12% 93%', // rosée du matin
|
surface: '130 18% 90%', // rosée du matin
|
||||||
bg: '100 15% 96%', // clarté verte
|
bg: '110 20% 94%', // clarté verte
|
||||||
surfaceLight: '120 8% 87%', // feuille pâle
|
surfaceLight: '125 14% 84%', // feuille vive
|
||||||
text: '150 15% 12%', // vert profond
|
text: '155 30% 10%', // vert profond saturé
|
||||||
textMuted: '140 8% 42%', // mousse
|
textMuted: '145 14% 38%', // mousse riche
|
||||||
isLight: true,
|
isLight: true,
|
||||||
label: 'Printemps',
|
label: 'Printemps',
|
||||||
icon: 'i-lucide-flower-2',
|
icon: 'i-lucide-flower-2',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Été : doré solaire, turquoise mer, lumineux chaleureux
|
// Été : orange solaire, turquoise pop, lumineux chaleureux
|
||||||
ete: {
|
ete: {
|
||||||
primary: '25 85% 52%', // soleil couchant
|
primary: '22 92% 48%', // soleil éclatant
|
||||||
accent: '175 55% 42%', // turquoise marin
|
accent: '175 72% 38%', // turquoise pop
|
||||||
surface: '40 25% 92%', // sable clair
|
surface: '38 35% 88%', // sable doré
|
||||||
bg: '42 30% 96%', // lumière dorée
|
bg: '40 38% 93%', // lumière dorée chaude
|
||||||
surfaceLight: '38 18% 86%', // dune
|
surfaceLight: '35 28% 82%', // dune chaude
|
||||||
text: '30 20% 12%', // terre chaude
|
text: '28 35% 8%', // brun intense
|
||||||
textMuted: '30 10% 40%', // ombre estivale
|
textMuted: '28 16% 35%', // ombre chaude
|
||||||
isLight: true,
|
isLight: true,
|
||||||
label: 'Été',
|
label: 'Été',
|
||||||
icon: 'i-lucide-sun',
|
icon: 'i-lucide-sun',
|
||||||
|
|||||||
37
content/book/01-introduction.md
Normal file
37
content/book/01-introduction.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "Introduction"
|
||||||
|
description: "Un livre et des chansons pour réaliser une économie du don, portée par les monnaie-libristes."
|
||||||
|
order: 1
|
||||||
|
readingTime: "15 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ce livre est une façon
|
||||||
|
|
||||||
|
Ce livre est un essai. Une proposition. Une intention. Une invitation. Une façon.
|
||||||
|
|
||||||
|
Il ne s'agit pas d'une théorie universelle, mais d'une **expérimentation concrète, économique, civile et artistique**. Pas un guide, pas un kit : plutôt un *git* — un dépôt de réflexions ouvertes, une cartographie et quelques boussoles dans une jungle à défricher.
|
||||||
|
|
||||||
|
## À qui s'adresse ce livre ?
|
||||||
|
|
||||||
|
Ce livre s'adresse aux quelque **8 500 monnaie-libristes** (au moment de l'écriture) qui utilisent la June (Ğ1), monnaie libre fondée sur la Théorie Relative de la Monnaie. L'ambition : proposer un **modèle économique** concret pour passer de l'expérimentation monétaire à une véritable économie du don.
|
||||||
|
|
||||||
|
## Pourquoi une économie du don ?
|
||||||
|
|
||||||
|
Nous avons déjà une économie. Elle couvre nos besoins vitaux, de facto. Alors pourquoi en créer une autre ?
|
||||||
|
|
||||||
|
Pour **l'autonomie**. Et c'est tout sauf un repli. Réfuter l'autonomie, c'est nous bannir en tant qu'adultes bien vivants. L'autonomie, ici, c'est la capacité d'un groupe humain à couvrir ses besoins essentiels par ses propres moyens — sans pour autant se couper du reste du monde.
|
||||||
|
|
||||||
|
## Ce que ce livre n'est pas
|
||||||
|
|
||||||
|
- Ce n'est pas une baguette magique
|
||||||
|
- Ce n'est pas un programme politique
|
||||||
|
- Ça ne dit pas quoi faire lundi
|
||||||
|
- Ce n'est pas du prêt-à-porter : à nous de tailler
|
||||||
|
|
||||||
|
## Ce que ce livre est
|
||||||
|
|
||||||
|
Un **os à ronger**. Une cartographie avec quelques boussoles. Un livre *et* des chansons, parce que la musique porte les idées autrement.
|
||||||
|
|
||||||
|
Chaque chapitre est accompagné d'un morceau qui en prolonge le propos. Le livre se lit, s'écoute, se vit.
|
||||||
|
|
||||||
|
> *On tourne une page pour voir ?*
|
||||||
42
content/book/02-don.md
Normal file
42
content/book/02-don.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "De quel don parlons-nous ?"
|
||||||
|
description: "Du don de Marcel Mauss aux asymétries du quotidien : comprendre le triple geste donner-recevoir-rendre."
|
||||||
|
order: 2
|
||||||
|
readingTime: "20 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## L'oxymore apparent
|
||||||
|
|
||||||
|
« Économie du don » — le mariage fait peur. Un oxymore pour l'esprit. Pour éviter tout quiproquo : il ne s'agit ni de spirituel, ni de karma, ni du « centuple divin ». Ici, le don est un **geste opératoire**, une base, une fondation pour une autre forme de construction.
|
||||||
|
|
||||||
|
## Marcel Mauss et le triple geste
|
||||||
|
|
||||||
|
Ce n'est pas l'image d'Épinal du don gratuit. **Marcel Mauss**, dans son *Essai sur le don* (1925), a montré que le don est un cycle vital en trois temps :
|
||||||
|
|
||||||
|
1. **Donner** — l'initiative, le geste premier
|
||||||
|
2. **Recevoir** — accepter crée un lien, une obligation
|
||||||
|
3. **Rendre** — boucler le cycle, entretenir la relation
|
||||||
|
|
||||||
|
C'est un pacte, une tension, parfois même un combat. Si tu enfreins le protocole... ça ne pardonne pas.
|
||||||
|
|
||||||
|
## La monnaie-dette n'est pas la monnaie
|
||||||
|
|
||||||
|
On entend souvent : « Brûlons la monnaie ! Le troc, la gratuité, le grand projet... » La monnaie serait le vice, la source de tout le mal. **Erreur de cible.** Ce n'est pas la monnaie le problème, c'est la *dette*. La monnaie-dette, celle qui nous tient, celle qui nous guette. Mais si la monnaie est libre ? Elle permet les équilibres.
|
||||||
|
|
||||||
|
## Les asymétries
|
||||||
|
|
||||||
|
Rien n'est symétrique. Ce n'est pas magique. Une pomme aujourd'hui n'est pas la même demain. Une cagette, ce n'est pas cinq palettes. Six heures assis à parler bien à l'aise... six heures à genoux sur un toit — est-ce le même geste ?
|
||||||
|
|
||||||
|
Les asymétries sont partout. On ne peut pas les supprimer, mais on peut les **mesurer**. Et c'est précisément ce que fait une monnaie bien conçue.
|
||||||
|
|
||||||
|
## Communautés et protocoles
|
||||||
|
|
||||||
|
Les collectifs qui prônent le don sans outil de mesure finissent souvent par l'usure, le ressentiment, l'abandon. Légiférer, prescrire un comportement ? C'est tentant, mais bancal.
|
||||||
|
|
||||||
|
Mieux vaut un **protocole** — une façon de traiter les asymétries, d'éviter de juger, de préfigurer. Sans trancher les sorts à l'insu des participants.
|
||||||
|
|
||||||
|
## Eco si nuestra
|
||||||
|
|
||||||
|
L'expérience « Made in Ğ1 » et le jeu **321DU** montrent qu'il est possible de simuler et de vivre une micro-économie du don. Ces prototypages en communauté pseudo-isolée révèlent les mécanismes, les biais, les opportunités.
|
||||||
|
|
||||||
|
> *Pour se rétablir, retomber sur nos pieds, il existe un outil qui s'appelle... « la monnaie ».*
|
||||||
34
content/book/03-mesure.md
Normal file
34
content/book/03-mesure.md
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
---
|
||||||
|
title: "La mesure du don"
|
||||||
|
description: "Le DU comme unité de mesure du don : un retournement sémantique qui change tout."
|
||||||
|
order: 3
|
||||||
|
readingTime: "8 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Une mesure, pas un prix
|
||||||
|
|
||||||
|
Le mot « mesure » a une portée symbolique décisive. Mesurer le don, ce n'est pas le dénaturer ni le marchandiser. C'est lui donner un **référentiel commun** pour que chacun puisse s'y retrouver.
|
||||||
|
|
||||||
|
Le **Dividende Universel (DU)** — la quantité de monnaie libre créée chaque jour pour chaque membre — devient l'unité de mesure naturelle. Un DU, c'est la part quotidienne de création monétaire d'un être humain. Ni plus, ni moins.
|
||||||
|
|
||||||
|
## Le retournement sémantique
|
||||||
|
|
||||||
|
Voici le basculement fondamental que propose l'économie du don en monnaie libre :
|
||||||
|
|
||||||
|
- **« Je ne vends plus, je donne »** — mon geste premier est un don, pas une transaction marchande
|
||||||
|
- **« Je n'achète plus, je reçois »** — recevoir engage, crée un lien
|
||||||
|
- **« Je ne paye plus, je mesure »** — la monnaie libre mesure la gratitude, pas le prix
|
||||||
|
|
||||||
|
Ce retournement n'est pas qu'un jeu de mots. Il transforme la posture intérieure de chaque participant à l'échange.
|
||||||
|
|
||||||
|
## Trans·action
|
||||||
|
|
||||||
|
Le mot « transaction » retrouve son sens premier : **trans** (à travers) + **action**. C'est une action qui traverse, qui relie. Pas un échange froid entre deux intérêts, mais un geste qui crée du lien.
|
||||||
|
|
||||||
|
En monnaie libre, chaque transaction est une **mesure de gratitude**. Combien de DU vaut ce que tu m'as donné ? C'est toi qui le dis. C'est moi qui accepte. Et la monnaie, symétrique dans sa création, garantit que personne n'est structurellement avantagé.
|
||||||
|
|
||||||
|
## Mesurer sans juger
|
||||||
|
|
||||||
|
Le protocole de mesure remplace le besoin de légiférer. Pas besoin d'écrire des règles pour chaque situation : la mesure en DU est un **langage commun** qui permet aux asymétries de s'exprimer et de se résorber naturellement.
|
||||||
|
|
||||||
|
> *Il suffit d'une mesure. Sans morsure. Qui fait sens pour toi et moi, pour tout le monde.*
|
||||||
36
content/book/04-monnaie.md
Normal file
36
content/book/04-monnaie.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "Raison d'être d'une monnaie"
|
||||||
|
description: "Pourquoi la monnaie est la clé de voûte de toute économie — et pourquoi les monnaies-dette sont génétiquement viciées."
|
||||||
|
order: 4
|
||||||
|
readingTime: "25 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## La clé de voûte
|
||||||
|
|
||||||
|
La monnaie est l'outil le plus puissant et le plus sous-estimé de toute économie. C'est elle qui, en permanence, résout les asymétries. Même sans qu'on y pense. Chaque monnaie **programme sa propre engeance** — son pouvoir est immense.
|
||||||
|
|
||||||
|
## L'équation de Fischer
|
||||||
|
|
||||||
|
**E = Q × V** — l'équation de Fischer pose les bases : la masse monétaire en circulation (E) est le produit de la quantité de monnaie (Q) par sa vitesse de circulation (V). Cette relation simple révèle pourquoi le contrôle de la création monétaire est un enjeu de pouvoir fondamental.
|
||||||
|
|
||||||
|
## Trois leçons sur les monnaies-dette
|
||||||
|
|
||||||
|
### 1. La cavalerie
|
||||||
|
|
||||||
|
La monnaie-dette fonctionne comme une cavalerie : pour rembourser la dette d'hier, il faut créer de la dette aujourd'hui. Le système ne tient que par la croissance perpétuelle de l'endettement. Quand la musique s'arrête, tout s'effondre.
|
||||||
|
|
||||||
|
### 2. La machine à faillites
|
||||||
|
|
||||||
|
Dans un système où la monnaie est créée par le crédit, les intérêts ne sont jamais créés. Il y a donc **structurellement moins de monnaie en circulation que de dettes à rembourser**. Le système fabrique mécaniquement des perdants.
|
||||||
|
|
||||||
|
### 3. La pyramide arbitraire
|
||||||
|
|
||||||
|
Qui décide de la création monétaire ? Les banques. Et pour qui ? Pour ceux qui offrent des garanties. Le système monétaire conventionnel est une **pyramide d'accès arbitraire** à la monnaie : ceux qui sont proches de la source en bénéficient, les autres subissent la dilution.
|
||||||
|
|
||||||
|
## Une économie mal codée
|
||||||
|
|
||||||
|
L'analogie informatique est éclairante : notre économie est un programme dont le **code source est vicié**. Les monnaies-dette contiennent des bugs génétiques — cavalerie, faillites programmées, pyramide — qui ne sont pas des accidents mais des propriétés structurelles.
|
||||||
|
|
||||||
|
La monnaie libre propose de **recoder** la base : une monnaie dont la création est symétrique entre tous les membres, dans l'espace et dans le temps.
|
||||||
|
|
||||||
|
> *Chaque monnaie programme sa propre engeance. Ne t'y méprends pas, son pouvoir est immense.*
|
||||||
51
content/book/05-trm.md
Normal file
51
content/book/05-trm.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
---
|
||||||
|
title: "La TRM"
|
||||||
|
description: "Invitation à découvrir la Théorie Relative de la Monnaie de Stéphane Laborde : symétrie, DU et relativité."
|
||||||
|
order: 5
|
||||||
|
readingTime: "30 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Invitation à la TRM
|
||||||
|
|
||||||
|
Ce chapitre est une **invitation à lire la Théorie Relative de la Monnaie** (TRM) de Stéphane Laborde. Pas un résumé — la TRM se mérite, se travaille, se digère. Mais quelques clés pour en saisir la portée.
|
||||||
|
|
||||||
|
## Le flux monétaire
|
||||||
|
|
||||||
|
En monnaie libre, le flux monétaire est **corrélé à l'existence physique** de chaque être humain. Tant que tu vis, tu co-crées de la monnaie. Le Dividende Universel (DU) est cette part de création, identique pour chaque membre, chaque jour.
|
||||||
|
|
||||||
|
## La symétrie spatio-temporelle
|
||||||
|
|
||||||
|
Le principe fondamental de la TRM tient en deux symétries :
|
||||||
|
|
||||||
|
- **Symétrie spatiale** : aucun individu vivant n'est privilégié par rapport à un autre dans la création monétaire
|
||||||
|
- **Symétrie temporelle** : les individus présents ne sont pas privilégiés par rapport aux individus futurs (ni l'inverse)
|
||||||
|
|
||||||
|
Ces deux symétries éliminent les biais génétiques des monnaies-dette : plus de pyramide, plus de privilège d'ancienneté.
|
||||||
|
|
||||||
|
## La formule du DU
|
||||||
|
|
||||||
|
Le DU se calcule relativement à la masse monétaire existante et au nombre de membres. La formule garantit que, quels que soient le moment d'entrée et la durée de participation, chaque membre converge vers la **même part relative** de la masse monétaire.
|
||||||
|
|
||||||
|
## Relativité : penser en M/N
|
||||||
|
|
||||||
|
En monnaie libre, les montants absolus n'ont pas de sens. Ce qui compte, c'est la **part relative** : combien de DU ? Quel pourcentage de la masse monétaire moyenne par membre (M/N) ?
|
||||||
|
|
||||||
|
Cette relativité est contre-intuitive au début, mais libératrice : elle élimine le biais de genèse (les premiers arrivés ne sont pas structurellement plus riches) et le biais d'ancienneté.
|
||||||
|
|
||||||
|
## GrateWizard
|
||||||
|
|
||||||
|
L'outil **GrateWizard** permet de simuler et visualiser les flux monétaires en monnaie libre. Il donne à voir la convergence, les échanges, les équilibres — et aide à développer l'intuition de la relativité.
|
||||||
|
|
||||||
|
## Volume des offres et diversité
|
||||||
|
|
||||||
|
Une monnaie ne vaut que par ce qu'on peut obtenir avec. Le **volume des offres** et leur **diversité** sont les indicateurs clés de la santé d'une économie en monnaie libre. Plus il y a de producteurs, de services, de biens disponibles, plus la monnaie fait sens.
|
||||||
|
|
||||||
|
## Échelles de valeurs et convergence
|
||||||
|
|
||||||
|
Chacun a ses propres échelles de valeurs. La monnaie libre ne les uniformise pas : elle fournit un **référentiel commun** (le DU) qui permet à chacun d'exprimer ses valeurs et de les échanger. Avec le temps, les prix relatifs convergent vers une moyenne, signe que l'économie trouve ses équilibres.
|
||||||
|
|
||||||
|
## Le commun monétaire
|
||||||
|
|
||||||
|
La monnaie libre est un **commun** : elle n'appartient à personne et à tout le monde. Comme l'air ou l'eau, elle est un bien partagé dont chacun est co-producteur. Ce statut de commun est garanti par la symétrie de sa création.
|
||||||
|
|
||||||
|
> *Le DU est la part de chacun. Ni plus, ni moins.*
|
||||||
42
content/book/06-economie.md
Normal file
42
content/book/06-economie.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Créer une économie ?"
|
||||||
|
description: "Passer de la théorie à la pratique : produire, greffer une économie du don sur le tissu local, inverser les flux."
|
||||||
|
order: 6
|
||||||
|
readingTime: "25 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Définition
|
||||||
|
|
||||||
|
Couvrir les besoins, pour vivre, nourrir les plaisirs de vivre. Avant tout : comment **produire**, comment **distribuer**, sans nuire. On en a déjà une économie. Alors pourquoi en créer une autre ? Pas « créer » au sens de partir de zéro — plutôt **greffer** une économie du don sur l'économie existante.
|
||||||
|
|
||||||
|
## Produire avant tout
|
||||||
|
|
||||||
|
Le message aux pionniers est clair : faire tourner la monnaie en rond, ce n'est pas créer. Se faire des virements autour d'une table, c'est juste du vent. L'équation de Fischer est formelle : si tu multiplies zéro production par mille transactions, ça fait toujours zéro. L'économie, c'est d'abord **produire et transformer**.
|
||||||
|
|
||||||
|
## Passer la seconde
|
||||||
|
|
||||||
|
L'analogie du régime moteur : la monnaie libre en est au « point mort ». Pour avancer, il faut **passer la seconde** — produire de vrais biens et services. Cinq vitesses de développement se dessinent, de la simple circulation monétaire à l'économie productive complète.
|
||||||
|
|
||||||
|
## L'économie de greffe
|
||||||
|
|
||||||
|
Le principe fondamental : **deux économies cohabitent**, l'économie classique (euros) et l'économie du don (monnaie libre). Il ne faut surtout pas les mélanger ni les confondre. On ne « quitte » pas l'euro — on développe une autonomie parallèle, progressive. La greffe prend quand l'économie locale du don couvre suffisamment de besoins réels.
|
||||||
|
|
||||||
|
## Connaître son bassin de vie
|
||||||
|
|
||||||
|
Avant de se lancer, il faut cartographier son territoire. Deux indicateurs clés :
|
||||||
|
- Les **mobiz** : les « monnaie-libristes biz » — ceux qui produisent et échangent activement
|
||||||
|
- Les **sherpiz** : les « sherpas biz » — ceux qui guident, forment, accompagnent
|
||||||
|
|
||||||
|
Le ratio mobiz/sherpiz sur un territoire donne une idée réaliste du potentiel de développement.
|
||||||
|
|
||||||
|
## La gestion à l'anglaise
|
||||||
|
|
||||||
|
Un concept emprunté à la mécanique : la **roue libre**. En monnaie libre, la « roue libre » est l'indicateur de rentabilité. Quand une activité tourne sans effort, en roue libre, c'est qu'elle est rentable en DU. Ce n'est pas la marge financière qui compte, c'est la **soutenabilité** du geste productif.
|
||||||
|
|
||||||
|
## L'économie de flux inversés
|
||||||
|
|
||||||
|
Le retournement fondamental : **je donne avant de recevoir**. En économie classique, le flux est conditionnel — je te donne si tu me payes. En économie du don, je donne d'abord, et la mesure (en DU) vient après, comme une gratitude.
|
||||||
|
|
||||||
|
Ce n'est pas de la naïveté : c'est un changement de posture qui transforme la relation économique. Le don est en amont. L'équipe offre le choix, le lieu, le son. Les références sont posées. Tu prends ou pas, tu gratifies selon ton estimation.
|
||||||
|
|
||||||
|
> *L'économie, c'est de l'énergie, de la chaleur c'est sûr. Je grave ma gratitude dans la chaîne.*
|
||||||
37
content/book/07-echange.md
Normal file
37
content/book/07-echange.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "Échanger"
|
||||||
|
description: "Organiser les échanges : filières et boucles, distribution décentralisée, connexion avec l'existant et marchés Ğ1."
|
||||||
|
order: 7
|
||||||
|
readingTime: "18 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Le PIB comme baromètre
|
||||||
|
|
||||||
|
L'échange est le **symptôme de la production**. Le PIB, malgré ses limites, est un baromètre : il mesure le volume des échanges. En monnaie libre, l'équivalent serait le volume de transactions réelles — pas les virements entre amis, mais les échanges qui témoignent d'une production effective.
|
||||||
|
|
||||||
|
## Filières et boucles
|
||||||
|
|
||||||
|
L'économie du don s'organise en **filières** — de la matière première au produit fini — et en **boucles** — des circuits qui relient producteurs, transformateurs et consommateurs.
|
||||||
|
|
||||||
|
Trois exemples concrets :
|
||||||
|
- **Le pain** : du blé au boulanger, une filière courte qui peut fonctionner entièrement en monnaie libre
|
||||||
|
- **Le bois** : de la forêt au menuisier, une filière qui demande de la coordination
|
||||||
|
- **Le photovoltaïque** : une filière plus complexe, qui montre les limites et les possibilités
|
||||||
|
|
||||||
|
L'enjeu : les boucles doivent **boucler** — chaque maillon doit trouver ce dont il a besoin dans le circuit — **et se croiser** — les filières doivent s'interconnecter pour créer de la résilience.
|
||||||
|
|
||||||
|
## Distribuer
|
||||||
|
|
||||||
|
La distribution doit être **décentralisée**. Pas de plateforme unique, pas de point de passage obligé. Chacun est un nœud du réseau, libre de ses connexions. Les outils numériques (Ğ1, applications mobiles) facilitent les échanges mais ne doivent pas devenir des points de contrôle.
|
||||||
|
|
||||||
|
## Connecter avec l'existant
|
||||||
|
|
||||||
|
L'économie du don ne vit pas en vase clos. Elle doit se **connecter avec l'existant** : les SEL (Systèmes d'Échange Local), l'ESS (Économie Sociale et Solidaire), les associations solidaires, les réseaux de producteurs. Ces ponts élargissent le bassin d'offres et renforcent la légitimité.
|
||||||
|
|
||||||
|
## Ğ(marchés)
|
||||||
|
|
||||||
|
Les **Ğ(marchés)** — marchés en monnaie libre — sont le lieu de rencontre physique entre producteurs et consommateurs. Différents formats existent, de la simple table de troc au marché organisé avec producteurs professionnels.
|
||||||
|
|
||||||
|
L'expérience montre qu'un marché en monnaie libre réussit quand il propose des **produits de qualité** portés par de **vrais producteurs**, pas uniquement des objets de seconde main. La professionnalisation de l'offre est un signe de maturité de l'économie locale.
|
||||||
|
|
||||||
|
> *Des cercles qui se croisent, sans les regards qui toisent.*
|
||||||
36
content/book/08-institution.md
Normal file
36
content/book/08-institution.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "Relation institutionnelle"
|
||||||
|
description: "Naviguer dans le cadre légal et fiscal : impôts, TVA, cotisations sociales et financement de l'écosystème."
|
||||||
|
order: 8
|
||||||
|
readingTime: "20 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Une relation délicate
|
||||||
|
|
||||||
|
La relation avec les institutions est **délicate**. L'institution est jalouse de son pouvoir, de son monopole monétaire. Il faut distinguer les **personnes** (souvent bienveillantes, curieuses) des **rôles institutionnels** (contraints par les cadres légaux).
|
||||||
|
|
||||||
|
## Impôts et taxes
|
||||||
|
|
||||||
|
Premier point, fondamental : **pas d'échappatoire fiscale**. La monnaie libre ne dispense pas des impôts. Toute activité économique, quel que soit le moyen de paiement, reste soumise aux obligations fiscales. On n'a pas besoin de la monnaie libre pour ça — et prétendre le contraire serait nuire au projet.
|
||||||
|
|
||||||
|
## Environnement légal
|
||||||
|
|
||||||
|
Le cadre évolue. Le **règlement PACTE**, **MiCA** (Markets in Crypto-Assets) et la directive **DAC8** dessinent un environnement légal pour les crypto-actifs en Europe. La June (Ğ1) doit se positionner dans ce paysage — ni en fuite, ni en confrontation, mais en **conformité intelligente**.
|
||||||
|
|
||||||
|
## TVA
|
||||||
|
|
||||||
|
La question de la TVA est **ambiguë**. Pour les transactions entre particuliers sous les seuils de franchise, la TVA ne s'applique pas. Pour les professionnels, les règles classiques s'appliquent, même si le paiement est en monnaie libre. Le flou actuel invite à la prudence et à la transparence.
|
||||||
|
|
||||||
|
## Bénévolat et cotisations sociales
|
||||||
|
|
||||||
|
Le bénévolat a un cadre légal précis. Recevoir une contrepartie en monnaie libre pour une activité bénévole peut requalifier cette activité en travail dissimulé. Il faut être vigilant sur la **frontière entre don et rémunération**, et sur les implications en termes de cotisations sociales.
|
||||||
|
|
||||||
|
## Financement de l'écosystème
|
||||||
|
|
||||||
|
L'écosystème technique (développement de Duniter, applications, infrastructure) a besoin de financement. Des dispositifs existent : **Ademe**, subventions innovation, mécénat. **Axiom Team**, qui développe le protocole Duniter, utilise un système de bounties pour financer le développement — un modèle hybride entre euros et monnaie libre.
|
||||||
|
|
||||||
|
## Symboles et sémantique
|
||||||
|
|
||||||
|
La relation institutionnelle passe aussi par les mots. **Inverser le flux sémantique** : ne pas dire « je paye en Ğ1 » mais « j'estime ma gratitude en DU ». Ce glissement change la perception : on n'est plus dans un système monétaire alternatif suspect, mais dans une pratique de mesure de la gratitude entre citoyens.
|
||||||
|
|
||||||
|
> *Ce n'est plus « Que la dette soit ». C'est « Que l'équilibre soit ».*
|
||||||
36
content/book/09-greffes.md
Normal file
36
content/book/09-greffes.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
---
|
||||||
|
title: "Autres greffes"
|
||||||
|
description: "Greffer l'économie du don dans divers secteurs : emploi, ESS, agriculture, artisanat, éducation."
|
||||||
|
order: 9
|
||||||
|
readingTime: "15 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Décloisonnement et synergie
|
||||||
|
|
||||||
|
L'économie du don ne peut pas rester cantonnée à un cercle d'initiés. Elle doit **se greffer** sur les structures existantes, créer des ponts, des synergies. Chaque secteur est une opportunité de greffe — avec ses spécificités et ses précautions.
|
||||||
|
|
||||||
|
## Pôle Emploi et missions locales
|
||||||
|
|
||||||
|
Les demandeurs d'emploi et les jeunes accompagnés par les missions locales sont des publics naturels pour l'économie du don. La monnaie libre offre une **dignité économique** à ceux que le système conventionnel marginalise. Mais attention : il ne s'agit pas de remplacer les allocations par de la Ğ1, mais de **compléter** les parcours d'insertion avec une activité productive en monnaie libre.
|
||||||
|
|
||||||
|
## ESS — Économie Sociale et Solidaire
|
||||||
|
|
||||||
|
L'ESS partage des valeurs avec la monnaie libre : solidarité, utilité sociale, gouvernance démocratique. Mais vigilance sur l'**institutionnalisation** : l'ESS est parfois plus une étiquette qu'une pratique, et les financements publics peuvent créer des dépendances qui contredisent l'autonomie recherchée.
|
||||||
|
|
||||||
|
## Associations populaires et caritatives
|
||||||
|
|
||||||
|
Les secours populaires, diaconats et associations caritatives sont en première ligne du besoin. La monnaie libre peut leur offrir un outil complémentaire pour **valoriser les contributions bénévoles** et **fluidifier les échanges** au sein de leurs réseaux.
|
||||||
|
|
||||||
|
## Agriculture et maraîchage
|
||||||
|
|
||||||
|
Les producteurs agricoles et maraîchers sont des **cibles prioritaires** pour l'économie du don. Ils produisent des biens essentiels, travaillent localement, et sont souvent mal rémunérés par le système conventionnel. Une filière alimentaire en monnaie libre est le socle de toute économie du don qui se respecte.
|
||||||
|
|
||||||
|
## Artisanat, commerce, entreprise
|
||||||
|
|
||||||
|
La **roue libre** s'applique ici : quand un artisan ou un commerçant peut fonctionner en monnaie libre sans effort excessif, c'est que la greffe prend. Les boucles organiques — artisan → client → fournisseur → artisan — doivent se construire naturellement, pas par contrainte.
|
||||||
|
|
||||||
|
## Lycées et écoles
|
||||||
|
|
||||||
|
La tranche **15-25 ans** est une destination stratégique. Les jeunes adoptent plus facilement les nouveaux outils, questionnent plus naturellement les systèmes établis. Dans 10 ans, la monnaie libre fêtera sa majorité — et la génération qui aura grandi avec sera celle qui la portera.
|
||||||
|
|
||||||
|
> *Chaque greffe est une invitation. Chaque secteur est un terrain d'expérimentation.*
|
||||||
37
content/book/10-maintenant.md
Normal file
37
content/book/10-maintenant.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
---
|
||||||
|
title: "Et maintenant ?... action ?"
|
||||||
|
description: "L'appel à l'action : événementiel, coopérations ponctuelles et questions pratiques pour passer à l'acte."
|
||||||
|
order: 10
|
||||||
|
readingTime: "8 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Événementiel
|
||||||
|
|
||||||
|
L'événementiel est un **outil de régénération**. Marchés, rencontres, ateliers, festivals — chaque événement en monnaie libre est une occasion de montrer que ça marche, de recruter de nouveaux producteurs, de créer du lien. L'événement n'est pas un but en soi : c'est un **catalyseur**.
|
||||||
|
|
||||||
|
## Coopérations ponctuelles plutôt que fédération
|
||||||
|
|
||||||
|
Le mouvement de la monnaie libre résiste à la structuration verticale — et c'est une force. Plutôt qu'une fédération avec statuts, président et congrès annuel, l'approche privilégie les **coopérations ponctuelles** : des collaborations sur des projets concrets, avec des acteurs qui se choisissent librement.
|
||||||
|
|
||||||
|
Cette horizontalité est cohérente avec les principes de la monnaie libre : pas de structure pyramidale, pas de point de contrôle central.
|
||||||
|
|
||||||
|
## Raison d'être
|
||||||
|
|
||||||
|
La raison d'être de tout cet effort tient en deux mots : **passer la seconde**. Sortir du stade expérimental pour construire une véritable **autonomie collective** — un bassin de vie qui couvre une part significative de ses besoins en monnaie libre.
|
||||||
|
|
||||||
|
## Questions pratiques
|
||||||
|
|
||||||
|
Trois questions à se poser avant de se lancer :
|
||||||
|
|
||||||
|
### Quelles ressources ?
|
||||||
|
Quels producteurs, quels savoir-faire, quelles infrastructures sont disponibles sur le territoire ? La cartographie du bassin de vie est le point de départ.
|
||||||
|
|
||||||
|
### Quel chemin ?
|
||||||
|
Pas de plan quinquennal. Le chemin se dessine en marchant, par essai et erreur, par greffe successive. L'important est de commencer petit et d'apprendre vite.
|
||||||
|
|
||||||
|
### Quel rythme ?
|
||||||
|
Le rythme cardiaque du mouvement — ni trop rapide (burnout), ni trop lent (essoufflement). Les protocoles de décision doivent rester légers pour maintenir l'élan.
|
||||||
|
|
||||||
|
## À vous de danser
|
||||||
|
|
||||||
|
> *Ce livre a seulement tenté de décrire quelques pas de danse possibles. À vous d'inventer les vôtres.*
|
||||||
48
content/book/11-annexes.md
Normal file
48
content/book/11-annexes.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
title: "Chapitres annexes"
|
||||||
|
description: "Approfondissements : cryptomonnaies, blockchain, logiciel libre, Duniter et questions techniques."
|
||||||
|
order: 11
|
||||||
|
readingTime: "25 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
## Les cryptos
|
||||||
|
|
||||||
|
### La June est une crypto
|
||||||
|
|
||||||
|
Oui, la June (Ğ1) est une **cryptomonnaie** au sens technique : elle repose sur un réseau décentralisé, des algorithmes cryptographiques et une blockchain. Mais elle est fondamentalement différente des cryptos spéculatives par sa **co-création symétrique** (le DU) et l'absence de minage compétitif.
|
||||||
|
|
||||||
|
### DeX vs CeX
|
||||||
|
|
||||||
|
Deux modèles d'échange coexistent dans l'univers crypto :
|
||||||
|
- **CeX** (Centralized Exchange) : plateformes centralisées type Binance, Coinbase — rapides mais points de contrôle
|
||||||
|
- **DeX** (Decentralized Exchange) : échanges décentralisés, pair-à-pair — plus lents mais souverains
|
||||||
|
|
||||||
|
La June fonctionne nativement en **DeX** : chaque nœud du réseau est un point d'échange, sans intermédiaire centralisé.
|
||||||
|
|
||||||
|
### La question du bankrun
|
||||||
|
|
||||||
|
Un scénario souvent discuté : que se passerait-il si les monnaie-libristes convertissaient massivement leurs Ğ1 en euros ? L'analyse montre qu'avec une stratégie progressive (type **martingale**), une communauté de quelques milliers de membres pourrait théoriquement exercer une pression significative sur le marché des changes — mais ce n'est ni le but ni l'intérêt. L'objectif est l'**autonomie**, pas la conversion.
|
||||||
|
|
||||||
|
### Réseau monétaire
|
||||||
|
|
||||||
|
La June possède son **propre réseau monétaire**, contrairement aux tokens qui vivent sur des blockchains tierces. C'est un réseau complet : protocole, consensus, certification d'identité, création monétaire — tout est intégré. Cette souveraineté technique est un atout majeur, mais aussi une responsabilité.
|
||||||
|
|
||||||
|
## Le logiciel libre
|
||||||
|
|
||||||
|
### Linux, le modèle
|
||||||
|
|
||||||
|
Linux est la démonstration vivante que le **logiciel libre** peut produire des systèmes de qualité supérieure. Le noyau Linux fait tourner la majorité des serveurs mondiaux, Android, les supercalculateurs. La preuve que la collaboration ouverte surpasse la concurrence fermée.
|
||||||
|
|
||||||
|
### Duniter
|
||||||
|
|
||||||
|
**Duniter** est le logiciel libre qui fait tourner la June. Écrit initialement en TypeScript puis migré, il implémente la TRM dans un protocole blockchain. Sous licence **AGPL** (la plus libre des licences libres), il garantit que le code reste ouvert et modifiable par tous.
|
||||||
|
|
||||||
|
### Axiom Team
|
||||||
|
|
||||||
|
**Axiom Team** est l'équipe de développeurs qui maintient et fait évoluer Duniter. Financée par des bounties et des dons (en euros et en Ğ1), elle incarne le modèle hybride du développement en monnaie libre. La migration vers une nouvelle architecture blockchain en **Rust** (framework Substrate) est en cours — un chantier majeur pour la pérennité du réseau.
|
||||||
|
|
||||||
|
### CC-BY-NC
|
||||||
|
|
||||||
|
Le livre lui-même est publié sous licence **Creative Commons BY-NC** : libre de diffusion et d'adaptation, à condition de citer l'auteur et de ne pas en faire un usage commercial. Cohérent avec l'esprit du don.
|
||||||
|
|
||||||
|
> *Codeurs de rêve, vous décentralisez, vous open sourcez — un monde moins obscur.*
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Chapitres annexes — sujets connexes"
|
|
||||||
description: "Approfondissements : cryptomonnaies, June et blockchain, logiciel libre, réseau monétique et questions techniques."
|
|
||||||
order: 11
|
|
||||||
readingTime: "25 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Coder un rêve
|
|
||||||
|
|
||||||
Dans l'ombre des géants big tek
|
|
||||||
Des lignes poussent discrètes
|
|
||||||
Tourné vers la grande ourse
|
|
||||||
Je coule open source
|
|
||||||
|
|
||||||
Balance ton Json
|
|
||||||
mon shell résonne
|
|
||||||
Coup de dés Python Runtime Upgrade, ninja blade
|
|
||||||
Ça build, Docker compose. Devant l'écran je pose. Ça build.
|
|
||||||
shhhhhhh... it compiles ...llss,
|
|
||||||
tapis dans une typo sans serif, je check les certif
|
|
||||||
merde ça lag,
|
|
||||||
mate mes log,
|
|
||||||
j'ai la langue qui bog.
|
|
||||||
Mon café est tout froid
|
|
||||||
Je ne perds pas la foi.
|
|
||||||
|
|
||||||
Codeurs de rêve
|
|
||||||
Rime pour les dèvs
|
|
||||||
dans les réseaux, où que j'aille
|
|
||||||
vous êtes mes sudo,
|
|
||||||
mes samouraï
|
|
||||||
Hackers, Admin,
|
|
||||||
Devop, develop
|
|
||||||
Vous décentralisez
|
|
||||||
Vous open sourcez
|
|
||||||
Un monde moins obscur
|
|
||||||
Duniterre bien sûr.
|
|
||||||
Notre toil fiduciaire
|
|
||||||
Nous pouvez être fiers
|
|
||||||
|
|
||||||
Appel aux ressources
|
|
||||||
Donner vie au code source
|
|
||||||
Léger besoin de finance
|
|
||||||
Qui fait la différence
|
|
||||||
Axiom fournit
|
|
||||||
La tuyauterie
|
|
||||||
Manipule des bounty
|
|
||||||
Du fuel pour les applis
|
|
||||||
tous vos dons ... les mettent à l'abri
|
|
||||||
Merci
|
|
||||||
|
|
||||||
Crash à minuit
|
|
||||||
Je reste éveillé
|
|
||||||
Le bug fatal
|
|
||||||
Je l'ai anticipé
|
|
||||||
Avec un terminal
|
|
||||||
Je vais le basher
|
|
||||||
Le café est-il prêt ?
|
|
||||||
C'est bientôt aujourd'hui
|
|
||||||
|
|
||||||
Codeurs de rêve
|
|
||||||
Rime pour les dèvs
|
|
||||||
dans les réseaux, où que j'aille
|
|
||||||
vous êtes mes sudo,
|
|
||||||
mes samouraï
|
|
||||||
Hackers, Admin,
|
|
||||||
Devop, develop
|
|
||||||
Vous décentralisez
|
|
||||||
Vous open sourcez
|
|
||||||
Un monde moins obscur
|
|
||||||
Duniterre bien sûr.
|
|
||||||
Notre toil fiduciaire
|
|
||||||
Nous en sommes fiers
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Autres greffes"
|
|
||||||
description: "Applications concrètes de l'économie du don dans divers secteurs : emploi, ESS, agriculture, artisanat, éducation."
|
|
||||||
order: 9
|
|
||||||
readingTime: "15 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Inverser les flux
|
|
||||||
|
|
||||||
Tu choisis ... ta monnaie,
|
|
||||||
son économie,
|
|
||||||
Tu passes de l'une à l'autre
|
|
||||||
sans souci
|
|
||||||
|
|
||||||
Ici, tu peux l'appeler la monnaie du don.
|
|
||||||
Tu peux aussi ne plus l'appeler monnaie du tout.
|
|
||||||
Changer de lunettes, (mais pas les rose)
|
|
||||||
c'est un ruban-mètre, posé sur la planète.
|
|
||||||
|
|
||||||
Le D.U, c'est une mesure.
|
|
||||||
(Choir: La mesure...)
|
|
||||||
Pour ne plus obliger. Pour ne plus devoir.
|
|
||||||
Je donne à mon économie, j'alimente le réservoir.
|
|
||||||
Si je t'aime, j'y mets mon affect.
|
|
||||||
Si je ne t'aime pas, du moins je te respecte.
|
|
||||||
Je compte sur les autres, sur mon économie,
|
|
||||||
Pour y trouver ma pleine mesure.
|
|
||||||
La solidarité organique, pas de panique
|
|
||||||
Elle franchit les limites.
|
|
||||||
(Choir: Organique...)
|
|
||||||
|
|
||||||
"Je ne veux rien en retour", c'est ce que tu dis.
|
|
||||||
Mais tu m'obliges.
|
|
||||||
Tu crées une dette non dite,
|
|
||||||
De quoi rester perplexe.
|
|
||||||
La solidarité mécanique est complexe
|
|
||||||
Elle a ses limites.
|
|
||||||
(Whisper: C'est horrible pour les mites)
|
|
||||||
|
|
||||||
Alors j'opère un retournement.
|
|
||||||
Je choisis mes mots, c'est un glissement.
|
|
||||||
(...)
|
|
||||||
Je ne vends plus.
|
|
||||||
(Choir: Non...)
|
|
||||||
Je donne. (...) C'est mon offre.
|
|
||||||
Le mot retrouve du sens, pleinement.
|
|
||||||
(...)
|
|
||||||
Je n'achète plus.
|
|
||||||
(Choir: Non...)
|
|
||||||
Je reçois.
|
|
||||||
(...)
|
|
||||||
Je reçois la valeur. Je l'évalue. Avec le D.U.
|
|
||||||
La "dépossession monétaire" n'a plus lieu d'être.
|
|
||||||
Ce n'est plus mortifère, ni délétère.
|
|
||||||
(...)
|
|
||||||
Je rentre du marché, nouveau vocabulaire :
|
|
||||||
(Male) - "Hey - j'ai reçu une semaine de cours."
|
|
||||||
(Female) - "Wow, t'as mis gratitude max à la source ?"
|
|
||||||
|
|
||||||
(Male) Je ne paye plus.
|
|
||||||
(Female) Je mesure. J'estime...
|
|
||||||
(Male) Je négocie détendu. (c'est un virage)
|
|
||||||
(Female) J'ajuste ma balance intime...
|
|
||||||
(Male) Je donne du poids.
|
|
||||||
(Female) De la masse.
|
|
||||||
(Both) Ou une température.
|
|
||||||
L'économie, c'est de l'énergie, de la chaleur c'est sûr.
|
|
||||||
Je grave ma gratitude dans la chaîne.
|
|
||||||
C'est une trans-action. Au sens noble du terme.
|
|
||||||
|
|
||||||
Le D.U, c'est une mesure.
|
|
||||||
(Choir: La mesure...)
|
|
||||||
Pour ne plus obliger. Pour ne plus devoir.
|
|
||||||
Je donne à mon économie, j'alimente le réservoir.
|
|
||||||
Si je t'aime, j'y mets mon affect.
|
|
||||||
Si je ne t'aime pas, du moins je te respecte.
|
|
||||||
Je compte sur les autres, sur mon économie,
|
|
||||||
Pour y trouver ma pleine mesure.
|
|
||||||
La solidarité organique, pas de panique
|
|
||||||
Elle franchit les limites.
|
|
||||||
(Choir: Organique...)
|
|
||||||
|
|
||||||
Dis,... et quand c'est la course ? Si c'est une buvette ?
|
|
||||||
Pas le temps des discours, philosopher sur la canette ?
|
|
||||||
(Female : Faut qu'ça dépote !)
|
|
||||||
(...)
|
|
||||||
Alors le don est en amont.
|
|
||||||
L'équipe offre le choix, le lieu, le son.
|
|
||||||
Ils ont posé leurs références.
|
|
||||||
Tu prends ou pas, tu vois si c'est bon,
|
|
||||||
tu entres dans la danse.
|
|
||||||
Tu peux gratifier plus, si le cœur t'en dit.
|
|
||||||
Plaider un coeff. relatif aussi...
|
|
||||||
Mais la mesure est là, autour d'un bel invariant, on sait où on va.
|
|
||||||
(...)
|
|
||||||
Au delà d'un simple théorème
|
|
||||||
C'est le cadeau de la T.R.M.
|
|
||||||
|
|
||||||
On frotte nos échelles.
|
|
||||||
On construit avec les autres.
|
|
||||||
Construction culturelle.
|
|
||||||
J'évalue mon degré de gratitude
|
|
||||||
Pour que ça devienne une habitude.
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Créer une économie ?"
|
|
||||||
description: "Passer de la théorie à la pratique : produire, greffer une économie du don sur le tissu local, inverser les flux."
|
|
||||||
order: 6
|
|
||||||
readingTime: "25 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Inverser les flux
|
|
||||||
|
|
||||||
Tu choisis ... ta monnaie,
|
|
||||||
son économie,
|
|
||||||
Tu passes de l'une à l'autre
|
|
||||||
sans souci
|
|
||||||
|
|
||||||
Ici, tu peux l'appeler la monnaie du don.
|
|
||||||
Tu peux aussi ne plus l'appeler monnaie du tout.
|
|
||||||
Changer de lunettes, (mais pas les rose)
|
|
||||||
c'est un ruban-mètre, posé sur la planète.
|
|
||||||
|
|
||||||
Le D.U, c'est une mesure.
|
|
||||||
(Choir: La mesure...)
|
|
||||||
Pour ne plus obliger. Pour ne plus devoir.
|
|
||||||
Je donne à mon économie, j'alimente le réservoir.
|
|
||||||
Si je t'aime, j'y mets mon affect.
|
|
||||||
Si je ne t'aime pas, du moins je te respecte.
|
|
||||||
Je compte sur les autres, sur mon économie,
|
|
||||||
Pour y trouver ma pleine mesure.
|
|
||||||
La solidarité organique, pas de panique
|
|
||||||
Elle franchit les limites.
|
|
||||||
(Choir: Organique...)
|
|
||||||
|
|
||||||
"Je ne veux rien en retour", c'est ce que tu dis.
|
|
||||||
Mais tu m'obliges.
|
|
||||||
Tu crées une dette non dite,
|
|
||||||
De quoi rester perplexe.
|
|
||||||
La solidarité mécanique est complexe
|
|
||||||
Elle a ses limites.
|
|
||||||
(Whisper: C'est horrible pour les mites)
|
|
||||||
|
|
||||||
Alors j'opère un retournement.
|
|
||||||
Je choisis mes mots, c'est un glissement.
|
|
||||||
(...)
|
|
||||||
Je ne vends plus.
|
|
||||||
(Choir: Non...)
|
|
||||||
Je donne. (...) C'est mon offre.
|
|
||||||
Le mot retrouve du sens, pleinement.
|
|
||||||
(...)
|
|
||||||
Je n'achète plus.
|
|
||||||
(Choir: Non...)
|
|
||||||
Je reçois.
|
|
||||||
(...)
|
|
||||||
Je reçois la valeur. Je l'évalue. Avec le D.U.
|
|
||||||
La "dépossession monétaire" n'a plus lieu d'être.
|
|
||||||
Ce n'est plus mortifère, ni délétère.
|
|
||||||
(...)
|
|
||||||
Je rentre du marché, nouveau vocabulaire :
|
|
||||||
(Male) - "Hey - j'ai reçu une semaine de cours."
|
|
||||||
(Female) - "Wow, t'as mis gratitude max à la source ?"
|
|
||||||
|
|
||||||
(Male) Je ne paye plus.
|
|
||||||
(Female) Je mesure. J'estime...
|
|
||||||
(Male) Je négocie détendu. (c'est un virage)
|
|
||||||
(Female) J'ajuste ma balance intime...
|
|
||||||
(Male) Je donne du poids.
|
|
||||||
(Female) De la masse.
|
|
||||||
(Both) Ou une température.
|
|
||||||
L'économie, c'est de l'énergie, de la chaleur c'est sûr.
|
|
||||||
Je grave ma gratitude dans la chaîne.
|
|
||||||
C'est une trans-action. Au sens noble du terme.
|
|
||||||
|
|
||||||
Le D.U, c'est une mesure.
|
|
||||||
(Choir: La mesure...)
|
|
||||||
Pour ne plus obliger. Pour ne plus devoir.
|
|
||||||
Je donne à mon économie, j'alimente le réservoir.
|
|
||||||
Si je t'aime, j'y mets mon affect.
|
|
||||||
Si je ne t'aime pas, du moins je te respecte.
|
|
||||||
Je compte sur les autres, sur mon économie,
|
|
||||||
Pour y trouver ma pleine mesure.
|
|
||||||
La solidarité organique, pas de panique
|
|
||||||
Elle franchit les limites.
|
|
||||||
(Choir: Organique...)
|
|
||||||
|
|
||||||
Dis,... et quand c'est la course ? Si c'est une buvette ?
|
|
||||||
Pas le temps des discours, philosopher sur la canette ?
|
|
||||||
(Female : Faut qu'ça dépote !)
|
|
||||||
(...)
|
|
||||||
Alors le don est en amont.
|
|
||||||
L'équipe offre le choix, le lieu, le son.
|
|
||||||
Ils ont posé leurs références.
|
|
||||||
Tu prends ou pas, tu vois si c'est bon,
|
|
||||||
tu entres dans la danse.
|
|
||||||
Tu peux gratifier plus, si le cœur t'en dit.
|
|
||||||
Plaider un coeff. relatif aussi...
|
|
||||||
Mais la mesure est là, autour d'un bel invariant, on sait où on va.
|
|
||||||
(...)
|
|
||||||
Au delà d'un simple théorème
|
|
||||||
C'est le cadeau de la T.R.M.
|
|
||||||
|
|
||||||
On frotte nos échelles.
|
|
||||||
On construit avec les autres.
|
|
||||||
Construction culturelle.
|
|
||||||
J'évalue mon degré de gratitude
|
|
||||||
Pour que ça devienne une habitude.
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
---
|
|
||||||
title: "De quel don parlons-nous ?"
|
|
||||||
description: "Exploration philosophique et sociologique du don, des asymétries communautaires aux expériences concrètes."
|
|
||||||
order: 2
|
|
||||||
readingTime: "20 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Les asymétries
|
|
||||||
|
|
||||||
(Male: Voix très posée, grave, proche)
|
|
||||||
Entre soi... Connivence fait loi.
|
|
||||||
On est entre nous On se rassure.
|
|
||||||
On discute assidus...hey, on assure !
|
|
||||||
(suspension)
|
|
||||||
Nous sommes trop convaincus.
|
|
||||||
Cela nous endort. Ce que je crois peut-être à tord ... tue.
|
|
||||||
(Females: Pseudo-isolés... Pseudo-isolés...)
|
|
||||||
|
|
||||||
J'ai vu des collectifs, plus ou moins disruptifs.
|
|
||||||
Parfois prônant le don, par exemple Solaris.
|
|
||||||
J'y ai vu de l'usure, le sentiment d'abus.
|
|
||||||
Des abandons moribonds, ...
|
|
||||||
Ha bon ?
|
|
||||||
|
|
||||||
Malgré quelques notifs, et les esprits attentifs
|
|
||||||
Parfois nous le savons, dans les violons on pisse
|
|
||||||
J'ai vu aussi bien sûr, quelques trous du cul
|
|
||||||
Mais ! Est-ce là une bonne raison ?
|
|
||||||
|
|
||||||
Rien n'est symétrique, ce n'est pas magique.
|
|
||||||
(Females: ) Rien.
|
|
||||||
Une pomme aujourd'hui, n'est pas la même demain.
|
|
||||||
Et s'il y a une cagette, ce n'est pas cinq palettes.
|
|
||||||
(Male: Tone hardens)
|
|
||||||
Six heures assis à parler bien à l'aise...
|
|
||||||
Six heures à genoux sur un toit...ho ! balaise
|
|
||||||
(Females:) Est-ce le même geste, ou une ascèse ? mmmhh.
|
|
||||||
|
|
||||||
Alors que faire ? On désespère, on baisse les bras ?
|
|
||||||
on légifère ? On écrit des lois ?
|
|
||||||
On réglemente, on décide de l'issue ?
|
|
||||||
(suspension)
|
|
||||||
Mieux vaut peut-être un protocole avec quelques bémol.
|
|
||||||
Une façon de traiter, d'éviter de juger, préfigurer.
|
|
||||||
(suspension) Ne pas tranchez les sorts, à leur insu.
|
|
||||||
|
|
||||||
Pour limiter le ressentiment,
|
|
||||||
la lassitude, l'envenime-ment.
|
|
||||||
Il suffit d'une mesure.
|
|
||||||
Sans morsure,
|
|
||||||
qui fait bonne figure,
|
|
||||||
qui fait sens pour toi et moi,
|
|
||||||
pour tout le monde.
|
|
||||||
Qui mesure la pénibilité ? - Pourquoi pas.
|
|
||||||
Qui célèbre le mérite ? - Dans l'ombre du monde ;
|
|
||||||
Pas celui des héritages ? - Ce s'rait possible ça ?
|
|
||||||
Qui réellement, récompense et compense ? sans dépense ni dispense ?
|
|
||||||
|
|
||||||
Une communauté ne peut pas tout écrire.
|
|
||||||
La loi, ne peut pas tout régler.
|
|
||||||
même si c'est toi qui la fait
|
|
||||||
Prescrire un comportement ? - c'est tentant,
|
|
||||||
Une belle et grande morale ? - c'est bancal :
|
|
||||||
Il est utile de prévenir, se souvenir, ...
|
|
||||||
C'est le pire.
|
|
||||||
|
|
||||||
Alors que faire ? On désespère, on baisse les bras ?
|
|
||||||
on légifère ? On écrit des lois ?
|
|
||||||
On réglemente, on décide de l'issue ?
|
|
||||||
(suspension)
|
|
||||||
Mieux vaut peut-être un protocole, avec quelques bémol.
|
|
||||||
Une façon de traiter, d'éviter de juger, préfigurer.
|
|
||||||
(suspension) Ne pas tranchez les sorts, à leur insu.
|
|
||||||
(suspension) Ne tranchez pas mon sort, à mon insu.
|
|
||||||
|
|
||||||
Rien n'est symétrique. Ce n'est pas magique.
|
|
||||||
(Females:) Rien.
|
|
||||||
Pour se rétablir, retomber sur nos pieds
|
|
||||||
Il existe un outil qui s'appelle, ... "la monnaie".
|
|
||||||
|
|
||||||
C'est elle en permanence qui résout le "schmilblick".
|
|
||||||
Même sans qu'on y pense, faut avouer, c'est pratique
|
|
||||||
C'est elle qui compense, récompense ou dispense
|
|
||||||
Mais attention, délit de pleine flagrance !
|
|
||||||
Chaque monnaie programme sa propre engence.
|
|
||||||
Ne t'y méprends pas, son pouvoir est immense.
|
|
||||||
|
|
||||||
Résoudre le problème des asymétries.
|
|
||||||
Réduire le besoin de légiférer.
|
|
||||||
Pour une simple coloc... ou pour un monde entier.
|
|
||||||
Les asymétries sont là.
|
|
||||||
Ne les néglige pas.
|
|
||||||
Tu peux les mesurer,
|
|
||||||
et choisir ... ta monnaie.
|
|
||||||
\[...\]
|
|
||||||
Choisir ta monnaie
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Échanger"
|
|
||||||
description: "Organiser les échanges dans une économie du don : filières, boucles, distribution et marchés Ğ1."
|
|
||||||
order: 7
|
|
||||||
readingTime: "18 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Désir des arts
|
|
||||||
|
|
||||||
La matière est peu docile
|
|
||||||
L'esprit est agile
|
|
||||||
Il désire l'art
|
|
||||||
Les mains sont habiles
|
|
||||||
précieuses et volubiles
|
|
||||||
Elles façonnent des amarres
|
|
||||||
|
|
||||||
C'est reparti pour un tour
|
|
||||||
Pour le désir des arts
|
|
||||||
Nous voilà de retour
|
|
||||||
Vous nous avez manqué
|
|
||||||
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
Les structures sont solides
|
|
||||||
Les intentions fluides
|
|
||||||
Notre équipe est unie
|
|
||||||
Prête pour l'inédit
|
|
||||||
Nous serons à vos côtés
|
|
||||||
En ce lieu de toute beauté
|
|
||||||
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
Qu'ils soient cent
|
|
||||||
Qu'ils soient mille
|
|
||||||
Curieux ou passionnés
|
|
||||||
Chauds ou égarés
|
|
||||||
Occupés ou distraits
|
|
||||||
Partout est l'attrait
|
|
||||||
Touchez sans les gants
|
|
||||||
Ici le désir vrille
|
|
||||||
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
L'Art est de retour
|
|
||||||
Il célèbre l'amour
|
|
||||||
Demain un autre jour
|
|
||||||
Désir des arts toujours
|
|
||||||
Désir des arts toujours
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Et maintenant ?… action ?"
|
|
||||||
description: "L'appel à l'action : comment rejoindre le mouvement, participer et construire ensemble une économie du don."
|
|
||||||
order: 10
|
|
||||||
readingTime: "8 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Ainsi soit-il
|
|
||||||
|
|
||||||
Fiat...
|
|
||||||
Fiat Lux… Fiat Euro.
|
|
||||||
|
|
||||||
Fiat. Ce n'est pas une marque.
|
|
||||||
C'est du latin.
|
|
||||||
Ça veut dire : "Que cela soit".
|
|
||||||
Une parole magique. Performative.
|
|
||||||
Fiat Lux : Que la lumière soit.
|
|
||||||
Fiat Euro : Que la dette soit.
|
|
||||||
Un monopole déclaré.
|
|
||||||
Une clé de voûte qui tient tout l'édifice.
|
|
||||||
Si la clé casse... tout s'écroule.
|
|
||||||
|
|
||||||
Mais la monnaie n'est pas la richesse.
|
|
||||||
C'est juste le mètre... pas le tissu.
|
|
||||||
C'est le baromètre... pas le climat.
|
|
||||||
Ne confondons pas la carte et le territoire.
|
|
||||||
|
|
||||||
Message aux pionniers :
|
|
||||||
Faire tourner la monnaie en rond, ce n'est pas créer.
|
|
||||||
Se faire des virements autour d'une table...
|
|
||||||
C'est juste du vent.
|
|
||||||
L'équation de Fischer est claire.
|
|
||||||
Si tu multiplies zéro production par mille transactions...
|
|
||||||
Ça fait toujours zéro.
|
|
||||||
L'économie, c'est "passer la seconde".
|
|
||||||
C'est produire. Transformer.
|
|
||||||
Le reste ? C'est de la comptabilité.
|
|
||||||
|
|
||||||
Notre monnaie-dette a un code génétique.
|
|
||||||
Elle programme le manque. Elle programme la course.
|
|
||||||
Mais le DU...
|
|
||||||
Le DU change le code source.
|
|
||||||
|
|
||||||
Même accès pour tous.
|
|
||||||
Même pouvoir de création.
|
|
||||||
Ce n'est plus "Que la dette soit".
|
|
||||||
C'est "Que l'équilibre soit".
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Introduction"
|
|
||||||
description: "Un livre et des chansons pour rendre concevable une économie du don, portée par la communauté monnaie-libriste."
|
|
||||||
order: 1
|
|
||||||
readingTime: "15 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Ce livre est un essai.
|
|
||||||
Une proposition.
|
|
||||||
Une intention.
|
|
||||||
Une invitation.
|
|
||||||
(...)
|
|
||||||
Une façon.
|
|
||||||
|
|
||||||
Deux mille vingt-quatre j'écris tout l'été, ...
|
|
||||||
Deux mille vingt-cinq je mûris toute l'année, ...
|
|
||||||
Deux mille vingt-six la sortie... mmmmhh,
|
|
||||||
Le temps pass
|
|
||||||
Est-ce une menace ?
|
|
||||||
|
|
||||||
Pour éviter tout quiproquo :
|
|
||||||
Pour mieux aimer le propos
|
|
||||||
Ce n'est pas une théorie.
|
|
||||||
Pas d'u-niversalité.
|
|
||||||
Loin s'en faut.
|
|
||||||
|
|
||||||
Une expérimentation.
|
|
||||||
Sans prétention
|
|
||||||
Concrète, économique
|
|
||||||
Civile et artistique
|
|
||||||
Mais si ça reste à ton échelle... c'est symbolique.
|
|
||||||
Viser mon bassin de vie ? ça se complique.
|
|
||||||
|
|
||||||
Ce livre n'est pas un guide,
|
|
||||||
davantage un guit'.
|
|
||||||
Ce n'est pas un kit.
|
|
||||||
Ça ne dit pas quoi faire lundi.
|
|
||||||
|
|
||||||
Tu veux une baguette magique ?
|
|
||||||
Supprimer la pression, l'oppression ? - Ce s'rait pas con...
|
|
||||||
Si je ne résous pas mes problèmes d'aujourd'hui... à quoi bon ?
|
|
||||||
Naviguer dans le ciel des idées...
|
|
||||||
C'est fini !
|
|
||||||
|
|
||||||
Créer une économie ?
|
|
||||||
On en a déjà une. Tu veux décrocher la lune ?
|
|
||||||
Elle couvre mes besoins vitaux. De facto.
|
|
||||||
Alors pourquoi ?
|
|
||||||
|
|
||||||
Ben. Pour l'autonomie.
|
|
||||||
...C'est tout - sauf un repli.
|
|
||||||
Balkanisation ? que nenni !
|
|
||||||
Réfuter l'autonomie... c'est fallacieux,
|
|
||||||
c'est nous bannir en tant qu'adultes, bien vivants,
|
|
||||||
restez là sans mot dire,
|
|
||||||
"restez des enfants !"...
|
|
||||||
mmh, suspect et sans avenir.
|
|
||||||
|
|
||||||
Deux mille vingt-six. L'année des défis.
|
|
||||||
Ne plus subir les agendas. Créer les nôtr'.
|
|
||||||
On manque de repères ?... Entre autres, ...
|
|
||||||
Faut les trouver,...
|
|
||||||
En produisant, les inventer.
|
|
||||||
|
|
||||||
Ce livre n'est pas un guide,
|
|
||||||
davantage un guit'.
|
|
||||||
Ce n'est pas un kit.
|
|
||||||
Ça ne dit pas quoi faire lundi.
|
|
||||||
|
|
||||||
C'est un os à ronger.
|
|
||||||
Une cartographie. Quelques boussoles.
|
|
||||||
Dans une jungle à défricher.
|
|
||||||
Ce n'est pas encore l'heure...
|
|
||||||
Du prêt-à-porter.
|
|
||||||
A nous de tailler.
|
|
||||||
|
|
||||||
On tourne une page pour voir ?
|
|
||||||
Décline le rôle de la bonne poire...
|
|
||||||
Je n'ai pas que l'espoir
|
|
||||||
J'ai un pouvoir
|
|
||||||
Tu as bien mieux que l'espoir,
|
|
||||||
Nous avons un grand pouvoir.
|
|
||||||
|
|
||||||
(whisper :) hey, on tourne une page ?
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
---
|
|
||||||
title: "La mesure du don"
|
|
||||||
description: "Comment mesurer le don sans le dénaturer ? Le retournement sémantique qui rend la mesure possible."
|
|
||||||
order: 3
|
|
||||||
readingTime: "8 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Une économie du don ?
|
|
||||||
Mais de quel don nous parlons ?
|
|
||||||
Ce mariage fait peur. Il claque.
|
|
||||||
Un oxymore, on l'apprend juste après l'bac.
|
|
||||||
Une contradiction pour l'esprit.
|
|
||||||
|
|
||||||
On évacue tout de suite le spirituel.
|
|
||||||
Désolé pour le karma, désolé pour le ciel.
|
|
||||||
Je ne parle pas du "centuple divin".
|
|
||||||
Je ne parle pas de ce que l'univers te rendra demain.
|
|
||||||
Ce n'est pas religieux. C'est opératoire.
|
|
||||||
Ici, c'est un geste. Juste un geste.
|
|
||||||
Qui sert de base, qui sert de fondation,
|
|
||||||
À une autre forme de construction.
|
|
||||||
|
|
||||||
Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal.
|
|
||||||
Marcel Mauss nous l'a dit, dans son essai radical.
|
|
||||||
Ce n'est pas un cadeau, c'est un cycle vital.
|
|
||||||
(Females: Donner... Recevoir... Rendre...)
|
|
||||||
|
|
||||||
C'est un pacte, une tension, parfois même un combat.
|
|
||||||
Si tu ne redonnes pas, si tu enfreins le protocole...
|
|
||||||
Ça ne pardonne pas.
|
|
||||||
|
|
||||||
J'entends les rêves de brûler la monnaie.
|
|
||||||
"Le troc", "la gratuité", "Mocica", le grand projet.
|
|
||||||
La monnaie serait le vice, la corruption mentale.
|
|
||||||
Elle serait la source de tout le mal.
|
|
||||||
Erreur de cible.
|
|
||||||
Ce n'est pas la monnaie le problème, c'est la dette !
|
|
||||||
La monnaie-dette, celle qui nous tient, celle qui nous guette.
|
|
||||||
Mais si la monnaie est libre ? Elle permet les équilibres.
|
|
||||||
Si elle devient notre outil ? Elle change le récit.
|
|
||||||
|
|
||||||
Le don qui se mesure, donne la mesure.
|
|
||||||
C'est quand tu donnes ton temps, ton énergie, ta sueur,
|
|
||||||
Que tu crées ton propre étalon de valeur.
|
|
||||||
|
|
||||||
Puis silence. Le geste pose un nouveau décor.
|
|
||||||
Le don — c'est pas une perte.
|
|
||||||
Le don — c'est le début d'un accord.
|
|
||||||
|
|
||||||
Ce n'est pas l'image d'Épinal, le don gratuit, le don idéal.
|
|
||||||
Marcel Mauss nous l'a dit, dans son essai radical.
|
|
||||||
Ce n'est pas un cadeau, c'est un cycle vital.
|
|
||||||
|
|
||||||
Le don qui se mesure, donne la mesure.
|
|
||||||
Le don qui se mesure, donne la mesure.
|
|
||||||
|
|
||||||
De quel don nous parlons ?
|
|
||||||
...Celui qui construit.
|
|
||||||
Celui qui nous relie.
|
|
||||||
Celui qui t'investit,
|
|
||||||
Dans l'économie de ta vie.
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
---
|
|
||||||
title: "La TRM — Théorie Relative de la Monnaie"
|
|
||||||
description: "Les principes fondamentaux de la Théorie Relative de la Monnaie de Stéphane Laborde : symétrie, relativité et Dividende Universel."
|
|
||||||
order: 5
|
|
||||||
readingTime: "35 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Hymne à la monnaie libre
|
|
||||||
|
|
||||||
Des cercles qui se croisent
|
|
||||||
Sans les regards qui toisent
|
|
||||||
Des poings qui se détendent
|
|
||||||
Des mains qui se tendent
|
|
||||||
Une graine plantée
|
|
||||||
Que lui faut-il pour germer ?
|
|
||||||
Un futur qui s'écrit
|
|
||||||
Un souffle tout petit
|
|
||||||
Pas de chaînes pour les pensées
|
|
||||||
|
|
||||||
Construire des vies
|
|
||||||
Nos cœurs qui grossissent
|
|
||||||
Couvrir nos besoins
|
|
||||||
Attention ça glisse
|
|
||||||
Juste prendre soin
|
|
||||||
Nourrir nos plaisirs
|
|
||||||
En arrêtant de nuire
|
|
||||||
Sans laissés-pour-compte
|
|
||||||
Sans place pour la honte
|
|
||||||
Un monde à construire
|
|
||||||
Y consacrer nos vies
|
|
||||||
|
|
||||||
Si tu en as la fibre
|
|
||||||
C'est l'hymne à la monnaie libre
|
|
||||||
|
|
||||||
Les jours se lèvent sur des rêves partagés
|
|
||||||
Quelques champs pour les possibles
|
|
||||||
Des ponts à imaginer
|
|
||||||
Pas de roi
|
|
||||||
Pas de trône
|
|
||||||
Juste l'humanité
|
|
||||||
Est-elle si pénible ?
|
|
||||||
|
|
||||||
Ni maîtres ni esclaves
|
|
||||||
Juste un écho
|
|
||||||
Des esprits qui dansent
|
|
||||||
Dans des corps qui pensent
|
|
||||||
Un chant nouveau
|
|
||||||
Surmontent les entraves
|
|
||||||
|
|
||||||
Construire des vies
|
|
||||||
Nos cœurs qui grossissent
|
|
||||||
Couvrir nos besoins
|
|
||||||
Attention ça glisse
|
|
||||||
Juste prendre soin
|
|
||||||
Y consacrer nos vies
|
|
||||||
Nourrir nos plaisirs
|
|
||||||
Un monde à construire
|
|
||||||
En arrêtant de nuire
|
|
||||||
Sans place pour la honte
|
|
||||||
Sans laissés-pour-compte
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Raison d'être d'une monnaie"
|
|
||||||
description: "Au-delà des trois fonctions classiques, explorer pourquoi nous avons besoin d'une monnaie — et pourquoi la monnaie actuelle est mal codée."
|
|
||||||
order: 4
|
|
||||||
readingTime: "30 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Hymne à la monnaie libre
|
|
||||||
|
|
||||||
Des cercles qui se croisent
|
|
||||||
Sans les regards qui toisent
|
|
||||||
Des poings qui se détendent
|
|
||||||
Des mains qui se tendent
|
|
||||||
Une graine plantée
|
|
||||||
Que lui faut-il pour germer ?
|
|
||||||
Un futur qui s'écrit
|
|
||||||
Un souffle tout petit
|
|
||||||
Pas de chaînes pour les pensées
|
|
||||||
|
|
||||||
Construire des vies
|
|
||||||
Nos cœurs qui grossissent
|
|
||||||
Couvrir nos besoins
|
|
||||||
Attention ça glisse
|
|
||||||
Juste prendre soin
|
|
||||||
Nourrir nos plaisirs
|
|
||||||
En arrêtant de nuire
|
|
||||||
Sans laissés-pour-compte
|
|
||||||
Sans place pour la honte
|
|
||||||
Un monde à construire
|
|
||||||
Y consacrer nos vies
|
|
||||||
|
|
||||||
Si tu en as la fibre
|
|
||||||
C'est l'hymne à la monnaie libre
|
|
||||||
|
|
||||||
Les jours se lèvent sur des rêves partagés
|
|
||||||
Quelques champs pour les possibles
|
|
||||||
Des ponts à imaginer
|
|
||||||
Pas de roi
|
|
||||||
Pas de trône
|
|
||||||
Juste l'humanité
|
|
||||||
Est-elle si pénible ?
|
|
||||||
|
|
||||||
Ni maîtres ni esclaves
|
|
||||||
Juste un écho
|
|
||||||
Des esprits qui dansent
|
|
||||||
Dans des corps qui pensent
|
|
||||||
Un chant nouveau
|
|
||||||
Surmontent les entraves
|
|
||||||
|
|
||||||
Construire des vies
|
|
||||||
Nos cœurs qui grossissent
|
|
||||||
Couvrir nos besoins
|
|
||||||
Attention ça glisse
|
|
||||||
Juste prendre soin
|
|
||||||
Y consacrer nos vies
|
|
||||||
Nourrir nos plaisirs
|
|
||||||
Un monde à construire
|
|
||||||
En arrêtant de nuire
|
|
||||||
Sans place pour la honte
|
|
||||||
Sans laissés-pour-compte
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Relation institutionnelle"
|
|
||||||
description: "Naviguer dans le cadre légal et fiscal : impôts, TVA, cotisations sociales et financement de l'écosystème."
|
|
||||||
order: 8
|
|
||||||
readingTime: "20 min"
|
|
||||||
---
|
|
||||||
|
|
||||||
Ainsi soit-il
|
|
||||||
|
|
||||||
Fiat...
|
|
||||||
Fiat Lux… Fiat Euro.
|
|
||||||
|
|
||||||
Fiat. Ce n'est pas une marque.
|
|
||||||
C'est du latin.
|
|
||||||
Ça veut dire : "Que cela soit".
|
|
||||||
Une parole magique. Performative.
|
|
||||||
Fiat Lux : Que la lumière soit.
|
|
||||||
Fiat Euro : Que la dette soit.
|
|
||||||
Un monopole déclaré.
|
|
||||||
Une clé de voûte qui tient tout l'édifice.
|
|
||||||
Si la clé casse... tout s'écroule.
|
|
||||||
|
|
||||||
Mais la monnaie n'est pas la richesse.
|
|
||||||
C'est juste le mètre... pas le tissu.
|
|
||||||
C'est le baromètre... pas le climat.
|
|
||||||
Ne confondons pas la carte et le territoire.
|
|
||||||
|
|
||||||
Message aux pionniers :
|
|
||||||
Faire tourner la monnaie en rond, ce n'est pas créer.
|
|
||||||
Se faire des virements autour d'une table...
|
|
||||||
C'est juste du vent.
|
|
||||||
L'équation de Fischer est claire.
|
|
||||||
Si tu multiplies zéro production par mille transactions...
|
|
||||||
Ça fait toujours zéro.
|
|
||||||
L'économie, c'est "passer la seconde".
|
|
||||||
C'est produire. Transformer.
|
|
||||||
Le reste ? C'est de la comptabilité.
|
|
||||||
|
|
||||||
Notre monnaie-dette a un code génétique.
|
|
||||||
Elle programme le manque. Elle programme la course.
|
|
||||||
Mais le DU...
|
|
||||||
Le DU change le code source.
|
|
||||||
|
|
||||||
Même accès pour tous.
|
|
||||||
Même pouvoir de création.
|
|
||||||
Ce n'est plus "Que la dette soit".
|
|
||||||
C'est "Que l'équilibre soit".
|
|
||||||
@@ -11,13 +11,15 @@ WORKDIR /src
|
|||||||
# Build
|
# Build
|
||||||
FROM base AS build
|
FROM base AS build
|
||||||
|
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml ./
|
||||||
RUN npm install
|
RUN pnpm install --frozen-lockfile
|
||||||
RUN npm rebuild sharp
|
RUN pnpm rebuild sharp
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npm run build
|
RUN pnpm run build
|
||||||
|
|
||||||
# Production
|
# Production
|
||||||
FROM base AS production
|
FROM base AS production
|
||||||
@@ -40,4 +42,5 @@ CMD [ "node", ".output/server/index.mjs" ]
|
|||||||
FROM base AS development
|
FROM base AS development
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENTRYPOINT [ "npm", "run", "dev" ]
|
RUN corepack enable
|
||||||
|
ENTRYPOINT [ "pnpm", "run", "dev" ]
|
||||||
|
|||||||
16
server/api/admin/chapters/[slug].delete.ts
Normal file
16
server/api/admin/chapters/[slug].delete.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { unlink } from 'node:fs/promises'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const slug = getRouterParam(event, 'slug')
|
||||||
|
|
||||||
|
if (!slug || !/^[a-z0-9-]+$/.test(slug)) {
|
||||||
|
throw createError({ statusCode: 400, statusMessage: 'Invalid slug' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = join(process.cwd(), 'content', 'book', `${slug}.md`)
|
||||||
|
|
||||||
|
await unlink(filePath)
|
||||||
|
|
||||||
|
return { ok: true }
|
||||||
|
})
|
||||||
29
server/api/admin/chapters/index.post.ts
Normal file
29
server/api/admin/chapters/index.post.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { writeFile } from 'node:fs/promises'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody<{ slug: string; title: string; order: number }>(event)
|
||||||
|
|
||||||
|
if (!body?.slug || !body?.title) {
|
||||||
|
throw createError({ statusCode: 400, statusMessage: 'Missing slug or title' })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[a-z0-9-]+$/.test(body.slug)) {
|
||||||
|
throw createError({ statusCode: 400, statusMessage: 'Invalid slug format' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = join(process.cwd(), 'content', 'book', `${body.slug}.md`)
|
||||||
|
|
||||||
|
const content = `---
|
||||||
|
title: "${body.title}"
|
||||||
|
description: ""
|
||||||
|
order: ${body.order}
|
||||||
|
readingTime: "5 min"
|
||||||
|
---
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
await writeFile(filePath, content, 'utf-8')
|
||||||
|
|
||||||
|
return { ok: true, slug: body.slug }
|
||||||
|
})
|
||||||
29
server/api/admin/chapters/index.put.ts
Normal file
29
server/api/admin/chapters/index.put.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { readFile, writeFile } from 'node:fs/promises'
|
||||||
|
import { join } from 'node:path'
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody<{ chapters: { slug: string; order: number }[] }>(event)
|
||||||
|
|
||||||
|
if (!body?.chapters || !Array.isArray(body.chapters)) {
|
||||||
|
throw createError({ statusCode: 400, statusMessage: 'Missing chapters array' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookDir = join(process.cwd(), 'content', 'book')
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
body.chapters.map(async ({ slug, order }) => {
|
||||||
|
const filePath = join(bookDir, `${slug}.md`)
|
||||||
|
const raw = await readFile(filePath, 'utf-8')
|
||||||
|
|
||||||
|
// Replace order in frontmatter
|
||||||
|
const updated = raw.replace(
|
||||||
|
/^(---\n[\s\S]*?)order:\s*\d+/,
|
||||||
|
`$1order: ${order}`,
|
||||||
|
)
|
||||||
|
|
||||||
|
await writeFile(filePath, updated, 'utf-8')
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
return { ok: true }
|
||||||
|
})
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const body = await readBody(event)
|
const body = await readBody(event)
|
||||||
await writeYaml('librodrome.config.yml', body)
|
await writeYaml('bookplayer.config.yml', body)
|
||||||
return { ok: true }
|
return { ok: true }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export default defineEventHandler(async () => {
|
export default defineEventHandler(async () => {
|
||||||
return await readYaml('librodrome.config.yml')
|
return await readYaml('bookplayer.config.yml')
|
||||||
})
|
})
|
||||||
|
|||||||
13
server/middleware/redirects.ts
Normal file
13
server/middleware/redirects.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export default defineEventHandler((event) => {
|
||||||
|
const path = getRequestURL(event).pathname
|
||||||
|
|
||||||
|
if (path.startsWith('/lire')) {
|
||||||
|
const rest = path.slice(5) // remove '/lire'
|
||||||
|
return sendRedirect(event, `/modele-eco${rest || '/'}`, 301)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.startsWith('/ecouter')) {
|
||||||
|
const rest = path.slice(8) // remove '/ecouter'
|
||||||
|
return sendRedirect(event, `/en-musique${rest || '/'}`, 301)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -7,10 +7,10 @@ book:
|
|||||||
isbn: "979-1-042-45206-3"
|
isbn: "979-1-042-45206-3"
|
||||||
|
|
||||||
songs:
|
songs:
|
||||||
- id: chanson-01
|
- id: ce-livre-est-une-facon
|
||||||
title: "1. Ce livre est une façon"
|
title: "Ce livre est une façon"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-01.mp3
|
file: /audio/ce-livre-est-une-facon.mp3
|
||||||
duration: 718
|
duration: 718
|
||||||
lyrics: |
|
lyrics: |
|
||||||
[Intro]
|
[Intro]
|
||||||
@@ -102,10 +102,10 @@ songs:
|
|||||||
(whisper :) hey, on tourne une page ?
|
(whisper :) hey, on tourne une page ?
|
||||||
tags: [introduction, livre, don]
|
tags: [introduction, livre, don]
|
||||||
|
|
||||||
- id: chanson-02
|
- id: un-don-qui-se-mesure
|
||||||
title: "2. De quel don nous parlons ?"
|
title: "Un don qui se mesure"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-02.mp3
|
file: /audio/un-don-qui-se-mesure.mp3
|
||||||
duration: 589
|
duration: 589
|
||||||
lyrics: |
|
lyrics: |
|
||||||
[Intro]
|
[Intro]
|
||||||
@@ -175,10 +175,10 @@ songs:
|
|||||||
Dans l'économie de ta vie.
|
Dans l'économie de ta vie.
|
||||||
tags: [don, mesure, valeur]
|
tags: [don, mesure, valeur]
|
||||||
|
|
||||||
- id: chanson-03
|
- id: les-asymetries
|
||||||
title: "3. Les asymétries"
|
title: "Les asymétries"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-03.mp3
|
file: /audio/les-asymetries.mp3
|
||||||
duration: 727
|
duration: 727
|
||||||
lyrics: |
|
lyrics: |
|
||||||
Les asymétries
|
Les asymétries
|
||||||
@@ -292,10 +292,10 @@ songs:
|
|||||||
Choisir ta monnaie
|
Choisir ta monnaie
|
||||||
tags: [asymétrie, communauté, philosophie]
|
tags: [asymétrie, communauté, philosophie]
|
||||||
|
|
||||||
- id: chanson-04
|
- id: inverser-les-flux
|
||||||
title: "4. Inverser les flux"
|
title: "Inverser les flux"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-04.mp3
|
file: /audio/inverser-les-flux.mp3
|
||||||
duration: 610
|
duration: 610
|
||||||
lyrics: |
|
lyrics: |
|
||||||
Inverser les flux
|
Inverser les flux
|
||||||
@@ -420,10 +420,10 @@ songs:
|
|||||||
Pour que ça devienne une habitude.
|
Pour que ça devienne une habitude.
|
||||||
tags: [flux, économie, production]
|
tags: [flux, économie, production]
|
||||||
|
|
||||||
- id: chanson-05
|
- id: ainsi-soit-il
|
||||||
title: "5. Ainsi soit-il"
|
title: "Ainsi soit-il"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-05.mp3
|
file: /audio/ainsi-soit-il.mp3
|
||||||
duration: 545
|
duration: 545
|
||||||
lyrics: |
|
lyrics: |
|
||||||
Ainsi soit-il
|
Ainsi soit-il
|
||||||
@@ -484,11 +484,27 @@ songs:
|
|||||||
[Silence]
|
[Silence]
|
||||||
tags: [action, engagement, avenir]
|
tags: [action, engagement, avenir]
|
||||||
|
|
||||||
- id: chanson-07
|
- id: la-croissance-une-option
|
||||||
title: "6. Hymne à la monnaie libre"
|
title: "La croissance, une option ?"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-07.mp3
|
file: /audio/la-croissance-une-option.mp3
|
||||||
|
duration: 510
|
||||||
|
lyrics: ""
|
||||||
|
tags: [croissance, monnaie, questionnement]
|
||||||
|
|
||||||
|
- id: monnaie-libre-essence
|
||||||
|
title: "Monnaie libre essence"
|
||||||
|
artist: Yvv
|
||||||
|
file: /audio/monnaie-libre-essence.mp3
|
||||||
duration: 475
|
duration: 475
|
||||||
|
lyrics: ""
|
||||||
|
tags: [monnaie libre, TRM, June]
|
||||||
|
|
||||||
|
- id: des-cercles-qui-se-croisent
|
||||||
|
title: "Des cercles qui se croisent"
|
||||||
|
artist: Yvv
|
||||||
|
file: /audio/des-cercles-qui-se-croisent.mp3
|
||||||
|
duration: 496
|
||||||
lyrics: |
|
lyrics: |
|
||||||
Hymne à la monnaie libre
|
Hymne à la monnaie libre
|
||||||
|
|
||||||
@@ -548,12 +564,12 @@ songs:
|
|||||||
En arrêtant de nuire
|
En arrêtant de nuire
|
||||||
Sans place pour la honte
|
Sans place pour la honte
|
||||||
Sans laissés-pour-compte
|
Sans laissés-pour-compte
|
||||||
tags: [monnaie libre, TRM, June]
|
tags: [échange, réseau, cercles]
|
||||||
|
|
||||||
- id: chanson-09
|
- id: coder-la-liberte
|
||||||
title: "7. Coder un rêve"
|
title: "Coder la liberté"
|
||||||
artist: Yvv
|
artist: Yvv
|
||||||
file: /audio/chanson-09.mp3
|
file: /audio/coder-la-liberte.mp3
|
||||||
duration: 376
|
duration: 376
|
||||||
lyrics: |
|
lyrics: |
|
||||||
Coder un rêve
|
Coder un rêve
|
||||||
@@ -630,176 +646,102 @@ songs:
|
|||||||
Nous en sommes fiers
|
Nous en sommes fiers
|
||||||
tags: [logiciel libre, code, liberté]
|
tags: [logiciel libre, code, liberté]
|
||||||
|
|
||||||
- id: chanson-08
|
|
||||||
title: "8. Désir des arts"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-08.mp3
|
|
||||||
duration: 496
|
|
||||||
lyrics: |
|
|
||||||
Désir des arts
|
|
||||||
|
|
||||||
[Intro]
|
|
||||||
La matière est peu docile
|
|
||||||
L'esprit est agile
|
|
||||||
Il désire l'art
|
|
||||||
Les mains sont habiles
|
|
||||||
précieuses et volubiles
|
|
||||||
Elles façonnent des amarres
|
|
||||||
|
|
||||||
[Pont]
|
|
||||||
C'est reparti pour un tour
|
|
||||||
Pour le désir des arts
|
|
||||||
Nous voilà de retour
|
|
||||||
Vous nous avez manqué
|
|
||||||
|
|
||||||
[Refrain]
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
[Couplet 1]
|
|
||||||
Les structures sont solides
|
|
||||||
Les intentions fluides
|
|
||||||
Notre équipe est unie
|
|
||||||
Prête pour l'inédit
|
|
||||||
Nous serons à vos côtés
|
|
||||||
En ce lieu de toute beauté
|
|
||||||
|
|
||||||
[Refrain]
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
[Couplet 2]
|
|
||||||
Qu'ils soient cent
|
|
||||||
Qu'ils soient mille
|
|
||||||
Curieux ou passionnés
|
|
||||||
Chauds ou égarés
|
|
||||||
Occupés ou distraits
|
|
||||||
Partout est l'attrait
|
|
||||||
Touchez sans les gants
|
|
||||||
Ici le désir vrille
|
|
||||||
|
|
||||||
[Refrain]
|
|
||||||
Les mains habiles
|
|
||||||
L'esprit agile
|
|
||||||
La matière plus docile
|
|
||||||
Sous le geste de l'émotion
|
|
||||||
|
|
||||||
[Outro]
|
|
||||||
L'Art est de retour
|
|
||||||
Il célèbre l'amour
|
|
||||||
Demain un autre jour
|
|
||||||
Désir des arts toujours
|
|
||||||
Désir des arts toujours
|
|
||||||
tags: [échange, réseau, cercles]
|
|
||||||
|
|
||||||
- id: chanson-06
|
|
||||||
title: "9. La croissance, une option ?"
|
|
||||||
artist: Yvv
|
|
||||||
file: /audio/chanson-06.mp3
|
|
||||||
duration: 510
|
|
||||||
lyrics: ""
|
|
||||||
tags: [croissance, monnaie, questionnement]
|
|
||||||
|
|
||||||
chapterSongs:
|
chapterSongs:
|
||||||
# Chapitre 1 — Introduction
|
# Chapitre 1 — Introduction
|
||||||
- chapterSlug: introduction
|
- chapterSlug: 01-introduction
|
||||||
songId: chanson-01
|
songId: ce-livre-est-une-facon
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: introduction
|
- chapterSlug: 01-introduction
|
||||||
songId: chanson-02
|
songId: un-don-qui-se-mesure
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 2 — De quel don parlons-nous ?
|
# Chapitre 2 — De quel don parlons-nous ?
|
||||||
- chapterSlug: de-quel-don-parlons-nous
|
- chapterSlug: 02-don
|
||||||
songId: chanson-03
|
songId: un-don-qui-se-mesure
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: de-quel-don-parlons-nous
|
- chapterSlug: 02-don
|
||||||
songId: chanson-01
|
songId: ce-livre-est-une-facon
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 3 — La mesure du don
|
# Chapitre 3 — La mesure du don
|
||||||
- chapterSlug: la-mesure-du-don
|
- chapterSlug: 03-mesure
|
||||||
songId: chanson-02
|
songId: les-asymetries
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: la-mesure-du-don
|
- chapterSlug: 03-mesure
|
||||||
songId: chanson-03
|
songId: un-don-qui-se-mesure
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 4 — Raison d'être d'une monnaie
|
# Chapitre 4 — Raison d'être d'une monnaie
|
||||||
- chapterSlug: raison-d-etre-d-une-monnaie
|
- chapterSlug: 04-monnaie
|
||||||
songId: chanson-06
|
songId: la-croissance-une-option
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: raison-d-etre-d-une-monnaie
|
- chapterSlug: 04-monnaie
|
||||||
songId: chanson-07
|
songId: monnaie-libre-essence
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 5 — La TRM
|
# Chapitre 5 — La TRM
|
||||||
- chapterSlug: la-trm
|
- chapterSlug: 05-trm
|
||||||
songId: chanson-07
|
songId: monnaie-libre-essence
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: la-trm
|
- chapterSlug: 05-trm
|
||||||
songId: chanson-06
|
songId: la-croissance-une-option
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 6 — Créer une économie ?
|
# Chapitre 6 — Créer une économie ?
|
||||||
- chapterSlug: creer-une-economie
|
- chapterSlug: 06-economie
|
||||||
songId: chanson-04
|
songId: inverser-les-flux
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: creer-une-economie
|
- chapterSlug: 06-economie
|
||||||
songId: chanson-07
|
songId: monnaie-libre-essence
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 7 — Échanger
|
# Chapitre 7 — Échanger
|
||||||
- chapterSlug: echanger
|
- chapterSlug: 07-echange
|
||||||
songId: chanson-08
|
songId: des-cercles-qui-se-croisent
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: echanger
|
- chapterSlug: 07-echange
|
||||||
songId: chanson-04
|
songId: inverser-les-flux
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 8 — Relation institutionnelle
|
# Chapitre 8 — Relation institutionnelle
|
||||||
- chapterSlug: relation-institutionnelle
|
- chapterSlug: 08-institution
|
||||||
songId: chanson-05
|
songId: ainsi-soit-il
|
||||||
primary: false
|
primary: false
|
||||||
- chapterSlug: relation-institutionnelle
|
- chapterSlug: 08-institution
|
||||||
songId: chanson-08
|
songId: des-cercles-qui-se-croisent
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 9 — Autres greffes
|
# Chapitre 9 — Autres greffes
|
||||||
- chapterSlug: autres-greffes
|
- chapterSlug: 09-greffes
|
||||||
songId: chanson-04
|
songId: inverser-les-flux
|
||||||
primary: false
|
primary: false
|
||||||
- chapterSlug: autres-greffes
|
- chapterSlug: 09-greffes
|
||||||
songId: chanson-08
|
songId: des-cercles-qui-se-croisent
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 10 — Et maintenant ?
|
# Chapitre 10 — Et maintenant ?
|
||||||
- chapterSlug: et-maintenant
|
- chapterSlug: 10-maintenant
|
||||||
songId: chanson-05
|
songId: ainsi-soit-il
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: et-maintenant
|
- chapterSlug: 10-maintenant
|
||||||
songId: chanson-09
|
songId: coder-la-liberte
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
# Chapitre 11 — Annexes
|
# Chapitre 11 — Annexes
|
||||||
- chapterSlug: annexes
|
- chapterSlug: 11-annexes
|
||||||
songId: chanson-09
|
songId: coder-la-liberte
|
||||||
primary: true
|
primary: true
|
||||||
- chapterSlug: annexes
|
- chapterSlug: 11-annexes
|
||||||
songId: chanson-07
|
songId: monnaie-libre-essence
|
||||||
primary: false
|
primary: false
|
||||||
|
|
||||||
defaultPlaylistOrder:
|
defaultPlaylistOrder:
|
||||||
- chanson-01
|
- ce-livre-est-une-facon
|
||||||
- chanson-02
|
- un-don-qui-se-mesure
|
||||||
- chanson-03
|
- les-asymetries
|
||||||
- chanson-04
|
- inverser-les-flux
|
||||||
- chanson-05
|
- ainsi-soit-il
|
||||||
- chanson-07
|
- la-croissance-une-option
|
||||||
- chanson-09
|
- monnaie-libre-essence
|
||||||
- chanson-08
|
- des-cercles-qui-se-croisent
|
||||||
- chanson-06
|
- coder-la-liberte
|
||||||
46
site/pages/autonomie.yml
Normal file
46
site/pages/autonomie.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
kicker: Pourquoi l'autonomie
|
||||||
|
title: Autonomie
|
||||||
|
description: Des passages du livre qui éclairent la démarche d'autonomie collective — le fil rouge du projet.
|
||||||
|
meta:
|
||||||
|
title: Autonomie
|
||||||
|
extracts:
|
||||||
|
- chapter: Introduction
|
||||||
|
chapterSlug: 01-introduction
|
||||||
|
text: >
|
||||||
|
Ben. Pour l'autonomie.
|
||||||
|
...C'est tout — sauf un repli.
|
||||||
|
Balkanisation ? que nenni !
|
||||||
|
Réfuter l'autonomie... c'est fallacieux,
|
||||||
|
c'est nous bannir en tant qu'adultes, bien vivants,
|
||||||
|
restez là sans mot dire,
|
||||||
|
"restez des enfants !"...
|
||||||
|
mmh, suspect et sans avenir.
|
||||||
|
- chapter: Introduction
|
||||||
|
chapterSlug: 01-introduction
|
||||||
|
text: >
|
||||||
|
Ne plus subir les agendas. Créer les nôtr'.
|
||||||
|
On manque de repères ?... Entre autres, ...
|
||||||
|
Faut les trouver,...
|
||||||
|
En produisant, les inventer.
|
||||||
|
- chapter: "Raison d'être d'une monnaie"
|
||||||
|
chapterSlug: 04-monnaie
|
||||||
|
text: >
|
||||||
|
Même accès pour tous.
|
||||||
|
Même pouvoir de création.
|
||||||
|
Ce n'est plus "Que la dette soit".
|
||||||
|
C'est "Que l'équilibre soit".
|
||||||
|
- chapter: "Créer une économie ?"
|
||||||
|
chapterSlug: 06-economie
|
||||||
|
text: >
|
||||||
|
Le D.U, c'est une mesure.
|
||||||
|
Pour ne plus obliger. Pour ne plus devoir.
|
||||||
|
Je donne à mon économie, j'alimente le réservoir.
|
||||||
|
Je compte sur les autres, sur mon économie,
|
||||||
|
Pour y trouver ma pleine mesure.
|
||||||
|
- chapter: "Et maintenant ?… action ?"
|
||||||
|
chapterSlug: 10-maintenant
|
||||||
|
text: >
|
||||||
|
Mais la monnaie n'est pas la richesse.
|
||||||
|
C'est juste le mètre... pas le tissu.
|
||||||
|
C'est le baromètre... pas le climat.
|
||||||
|
Ne confondons pas la carte et le territoire.
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
kicker: Écoute libre
|
|
||||||
title: Les 9 chansons
|
|
||||||
description: Ecoutez librement les chansons qui racontent le livre.
|
|
||||||
searchPlaceholder: Rechercher une chanson...
|
|
||||||
noResults: Aucune chanson ne correspond à votre recherche.
|
|
||||||
meta:
|
|
||||||
title: Écouter
|
|
||||||
7
site/pages/en-musique.yml
Normal file
7
site/pages/en-musique.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
kicker: Le livre en musique
|
||||||
|
title: Des chansons qui racontent le livre
|
||||||
|
description: Chaque chanson est un prolongement musical d'un ou deux chapitres.
|
||||||
|
searchPlaceholder: Rechercher une chanson...
|
||||||
|
noResults: Aucune chanson ne correspond à votre recherche.
|
||||||
|
meta:
|
||||||
|
title: En musique
|
||||||
5
site/pages/evenement.yml
Normal file
5
site/pages/evenement.yml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
kicker: Bientôt
|
||||||
|
title: En gestation
|
||||||
|
description: Cette rubrique est en cours de préparation.
|
||||||
|
meta:
|
||||||
|
title: Évènement
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
hero:
|
hero:
|
||||||
kicker: Autonomie collective des bassins de vie.
|
kicker: Autonomie collective des bassins de vie
|
||||||
title: Le librodrome
|
title: Le librodrome
|
||||||
subtitle: Couvrir nos besoins pour vivre et nourrir nos plaisirs vivre. Le premier pas vers une plateforme de
|
subtitle: Créer une économie ? Couvrir nos besoins pour vivre et nourrir nos plaisirs vivre. Ouverture d'une plateforme
|
||||||
productions collectives, où les habitants pourront créer des équipes afin de préparer et réaliser ces productions.
|
de productions collectives, pour facilier la création d'équipes, la préparation et la réalisation de ces
|
||||||
|
productions.
|
||||||
footnote: Ce projet est ouvert. Chaque personne qui souhaite se mobiliser pour participer à une production est invitée à
|
footnote: Ce projet est ouvert. Chaque personne qui souhaite se mobiliser pour participer à une production est invitée à
|
||||||
laisser un message. Pour poser des questions ou laisser un mail.
|
laisser un message. Pour poser des questions ou laisser un mail.
|
||||||
cta:
|
cta:
|
||||||
@@ -23,26 +24,29 @@ bookPresentation:
|
|||||||
title: Une économie du don — enfin concevable
|
title: Une économie du don — enfin concevable
|
||||||
description:
|
description:
|
||||||
- Ce livre explore les fondements d'une économie fondée sur le don.
|
- Ce livre explore les fondements d'une économie fondée sur le don.
|
||||||
- Chaque chapitre est accompagné d'une chanson qui en prolonge le propos, créant une invitation inédite et
|
- Les chansons prolongent le propos en suivant le cours des chapitres, pour le rendre plus accessible. Elles créent
|
||||||
immersive à la lecture.
|
une invitation inédite et immersive à la lecture.
|
||||||
cta:
|
cta:
|
||||||
label: Sommaire
|
label: Sommaire
|
||||||
to: /lire
|
to: /modele-eco
|
||||||
songs:
|
songs:
|
||||||
kicker: Les chansons
|
kicker: Les chansons
|
||||||
title: 9 chansons qui racontent le livre
|
title: Des chansons qui racontent le livre, du moins une partie
|
||||||
description: Chaque chanson est un prolongement musical d'un ou deux chapitres.
|
description: Chaque chanson est un prolongement musical d'un ou deux chapitres. Naturellement, les chansons ne
|
||||||
|
restituent pas l'intégralité du livre.
|
||||||
cta:
|
cta:
|
||||||
label: Voir toutes les chansons
|
label: Voir toutes les chansons
|
||||||
to: /ecouter
|
to: /en-musique
|
||||||
cooperative:
|
cooperative:
|
||||||
icon: users
|
icon: users
|
||||||
kicker: Vision
|
kicker: Vision
|
||||||
title: Une plateforme coopérative
|
title: Une plateforme coopérative
|
||||||
description:
|
description:
|
||||||
- Le Librodrome est le premier pas vers une plateforme de productions collectives. Un espace où les créateurs,
|
- L'ouverture de cette page librodrome est le premier pas vers une plateforme de productions collectives. Un espace
|
||||||
producteurs et personnes mobilisées, contribuent ensemble à faire émerger des projets de production.
|
où les créateurs, producteurs et toute personne mobilisée, contribuent ensemble à faire émerger des projets de
|
||||||
- Ce projet est ouvert. Chaque contribution enrichit l'ensemble. Rejoignez-nous pour construire une autonomie collective à l'échelle des bassins de vie.
|
productions. La plateforme sera utile pour leur réalisation effective, le suivi et le retour d'expérience.
|
||||||
|
- Ce projet est ouvert. Chaque contribution enrichit l'ensemble. Rejoignez-nous pour construire une autonomie
|
||||||
|
collective à l'échelle des bassins de vie.
|
||||||
cta:
|
cta:
|
||||||
label: En savoir plus
|
label: En savoir plus
|
||||||
to: /a-propos
|
to: /a-propos
|
||||||
@@ -50,7 +54,7 @@ grateWizardTeaser:
|
|||||||
kicker: Estimer les valeurs en DU - Les coefficients relatifs
|
kicker: Estimer les valeurs en DU - Les coefficients relatifs
|
||||||
title: grateWizard
|
title: grateWizard
|
||||||
description: Une webapp pour calculer des coefficients relatifs et estimer les valeurs dans une économie du don.
|
description: Une webapp pour calculer des coefficients relatifs et estimer les valeurs dans une économie du don.
|
||||||
Relatif à la moyenne, à l'ancienneté, au solde net, au volume.
|
Relatifs à la moyenne, à l'ancienneté, au solde net, au volume disponible.
|
||||||
cta:
|
cta:
|
||||||
launch: Lancer l'appli
|
launch: Lancer l'appli
|
||||||
more:
|
more:
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
kicker: Le livre
|
kicker: Le livre
|
||||||
title: Table des matières
|
title: Un modèle économique
|
||||||
description: Quelques chapitres pour explorer les fondements d'une économie du don, accompagnés de chansons qui le
|
description: Quelques chapitres pour explorer les fondements d'une économie du don, accompagnés de chansons qui le
|
||||||
racontent autrement.
|
racontent autrement.
|
||||||
meta:
|
meta:
|
||||||
title: Table des matières
|
title: Un modèle économique
|
||||||
@@ -1,23 +1,26 @@
|
|||||||
identity:
|
identity:
|
||||||
name: Le Librodrome
|
name: Le librodrome
|
||||||
description: Une économie du don — enfin concevable. Un livre et 9 chansons, lecture accompagnée et écoute libre.
|
description: Une économie du don — enfin concevable. Un modèle éco, un livre qui le présente. Des chansons qui le
|
||||||
|
racontent, autrement.
|
||||||
url: https://librodrome.org
|
url: https://librodrome.org
|
||||||
navigation:
|
navigation:
|
||||||
- label: Accueil
|
- label: Autonomie
|
||||||
to: /
|
to: /autonomie
|
||||||
- label: Lire
|
- label: Modèle éco
|
||||||
to: /lire
|
to: /modele-eco
|
||||||
- label: Écouter
|
- label: En musique
|
||||||
to: /ecouter
|
to: /en-musique
|
||||||
|
- label: Évènement
|
||||||
|
to: /evenement
|
||||||
- label: À propos
|
- label: À propos
|
||||||
to: /a-propos
|
to: /a-propos
|
||||||
footer:
|
footer:
|
||||||
credits: © 2026 Le Librodrome — Production collective
|
credits: © 2026 Le librodrome — Productions collectives
|
||||||
links:
|
links:
|
||||||
- label: Mentions légales
|
- label: Mentions légales
|
||||||
to: /mentions-legales
|
to: /mentions-legales
|
||||||
gratewizard:
|
gratewizard:
|
||||||
url: https://gratewizard.ml
|
url: https://gratewizard.axiom-team.fr
|
||||||
popup:
|
popup:
|
||||||
width: 420
|
width: 420
|
||||||
height: 720
|
height: 720
|
||||||
|
|||||||
Reference in New Issue
Block a user