ne pas se reposer sur ces acquis

This commit is contained in:
2026-01-14 00:05:28 +04:00
parent d2607859bd
commit b45c19687c
27 changed files with 942 additions and 335 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

View File

@@ -1,66 +0,0 @@
---
export interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
.link-card {
list-style: none;
display: flex;
padding: 1px;
background-color: #f0f0f0;
background-image: none;
background-size: 400%;
border-radius: 7px;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: calc(1.5rem - 1px);
border-radius: 8px;
color: rgb(49, 49, 49);
opacity: 0.8;
}
h2 {
margin: 0;
font-size: 1.25rem;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.5rem;
margin-bottom: 0;
}
h2 span {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
background-image: var(--accent-gradient);
}
.link-card:is(:hover, :focus-within) h2 {
color: rgb(var(--accent-light));
}
.link-card:is(:hover, :focus-within) h2 span {
transform: translateX(4px);
}
</style>

View File

@@ -0,0 +1,163 @@
---
import type { CollectionEntry } from 'astro:content';
import GoldButton from './ui/GoldButton.astro';
interface Props {
posts: CollectionEntry<'journal' | 'logs'>[];
tags: string[];
basePath: 'journal' | 'logs';
}
const { posts, tags, basePath } = Astro.props;
---
<div id="journal-search-component">
<div class="search-wrapper">
<input type="search" id="search-input" placeholder="Rechercher..." />
</div>
<div id="tag-filters" class="tags-container">
<button class="tag-btn active" data-tag="all">Toutes</button>
{
tags.map((tag) => (
<button class="tag-btn" data-tag={tag}>{tag}</button>
))
}
</div>
<div id="search-results" class="card-grid">
{
posts.map((post) => (
<GoldButton
href={`/${basePath}/${post.slug}/`}
title={post.data.title}
body={`Publié le ${post.data.publishDate.toLocaleDateString('fr-FR')}`}
/>
))
}
</div>
<p id="no-results" class="no-results-message" style="display: none;">
Aucun document ne correspond à votre recherche.
</p>
</div>
<script define:vars={{ posts, tags, basePath }}>
const searchInput = document.getElementById('search-input');
const resultsContainer = document.getElementById('search-results');
const noResultsMessage = document.getElementById('no-results');
const tagFiltersContainer = document.getElementById('tag-filters');
let currentQuery = '';
let activeTag = 'all';
// --- Fonctions Utilitaires ---
// Fonction pour normaliser le texte (enlever les accents, mettre en minuscule)
function normalizeText(text) {
return text
.toLowerCase()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '');
}
// Fonction pour mettre à jour l'affichage
function renderResults() {
let filteredPosts = posts;
// 1. Filtrer par balise active
if (activeTag !== 'all') {
filteredPosts = filteredPosts.filter(post => post.data.tags?.includes(activeTag));
}
// 2. Filtrer par recherche textuelle
if (currentQuery) {
filteredPosts = filteredPosts.filter(post => normalizeText(post.data.title).includes(currentQuery));
}
// 3. Mettre à jour le DOM
noResultsMessage.style.display = filteredPosts.length === 0 ? 'block' : 'none';
resultsContainer.innerHTML = filteredPosts
.map(
(post) =>
`<a href="/${basePath}/${post.slug}/" class="gold-button">
<div class="title">${post.data.title}</div>
<p class="body">Publié le ${new Date(post.data.publishDate).toLocaleDateString('fr-FR')}</p>
</a>`
)
.join('');
}
// --- Écouteurs d'événements ---
searchInput.addEventListener('input', (e) => {
currentQuery = normalizeText(e.target.value);
renderResults();
});
tagFiltersContainer.addEventListener('click', (e) => {
if (!e.target.matches('.tag-btn')) return;
// Mettre à jour le style du bouton actif
tagFiltersContainer.querySelector('.active').classList.remove('active');
e.target.classList.add('active');
activeTag = e.target.dataset.tag;
renderResults();
});
</script>
<style>
/* Styles pour les filtres de balises */
.tags-container {
display: flex;
justify-content: center;
gap: 0.75rem;
flex-wrap: wrap;
margin-bottom: 3rem;
}
.search-wrapper {
margin-bottom: 3rem;
text-align: center;
}
#search-input {
width: 100%;
max-width: 400px;
padding: 0.75rem 1rem;
font-size: 1.1rem;
font-family: 'EB Garamond', serif;
border: 2px solid #dcd0b9;
border-radius: 8px;
background-color: rgba(253, 246, 232, 0.8);
color: #4a4130;
}
#search-input:focus {
outline: none;
border-color: #c89b3c;
box-shadow: 0 0 10px rgba(200, 155, 60, 0.4);
}
.no-results-message {
text-align: center;
font-style: italic;
font-size: 1.2rem;
color: #4a4130;
}
.tag-btn {
background-color: transparent;
color: #b8860b;
padding: 0.4rem 1rem;
border-radius: 15px;
font-size: 0.9rem;
font-family: 'Cinzel', serif;
border: 1px solid rgba(200, 155, 60, 0.5);
cursor: pointer;
transition: all 0.2s ease;
}
.tag-btn:hover {
background-color: rgba(200, 155, 60, 0.2);
border-color: #c89b3c;
}
.tag-btn.active {
background-color: #c89b3c;
color: #2c2a24;
border-color: #c89b3c;
}
</style>

View File

@@ -0,0 +1,10 @@
---
// Ce composant accepte les props SVG standards comme `class`.
const { ...props } = Astro.props;
---
<svg {...props} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 21h12a2 2 0 0 0 2-2v-2H10v2a2 2 0 0 1-2 2Z"></path>
<path d="M4 21a2 2 0 0 1-2-2v-2h10v4H4Z"></path>
<path d="M4 5v12h16V5a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2Z"></path>
</svg>

View File

@@ -0,0 +1,87 @@
---
const { pathname } = Astro.url;
// Dans une application réelle, ces liens pourraient provenir d'un fichier de configuration.
const navItems = [
{ href: '/', label: 'Accueil' },
{ href: '/atelier', label: 'Atelier' },
{ href: '/observatoire', label: 'Observatoire' },
{ href: '/boussole', label: 'Boussole' },
{ href: '/journal', label: 'Journal' },
{ href: '/auteur', label: 'Auteur' },
{ href: '/logs', label: 'Logs' },
];
---
<nav class="game-nav">
<div id="nav-container" class="nav-container">
{
navItems.map((item) => {
// Met en surbrillance si le chemin commence par l'URL du lien (ex: /journal/2 commence par /journal)
const isActive = item.href === '/' ? pathname === '/' : pathname.startsWith(item.href) && item.href.length > 1;
return (
<a href={item.href} class:list={['nav-item', { active: isActive }]} title={item.label}>
{item.label}
</a>
);
})
}
</div>
</nav>
<style>
/* --- Styles de la barre de navigation --- */
.game-nav {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: auto; /* La hauteur s'adapte au contenu */
background: rgba(253, 246, 232, 0.85); /* Fond parchemin semi-transparent */
border-top: 2px solid #c89b3c; /* Bordure dorée */
box-shadow: 0 -5px 20px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(8px);
z-index: 1000;
padding: 0.5rem 1rem; /* Un peu d'espace intérieur */
}
/* --- Styles du conteneur des liens --- */
.nav-container {
display: flex;
flex-wrap: wrap; /* Permet aux éléments de passer à la ligne */
justify-content: center;
align-items: center;
height: inherit;
gap: 0.5rem 1.5rem; /* Espace vertical et horizontal entre les liens */
}
.nav-item {
text-decoration: none;
color: #4a4130; /* Couleur texte sombre pour le contraste */
font-family: 'Cinzel', serif;
font-size: 0.9rem;
transition: all 0.2s ease-in-out;
padding: 0.25rem 0.5rem;
}
.nav-item.active {
color: #b8860b; /* Couleur dorée pour l'élément actif */
transform: scale(1.05);
}
.nav-item:hover {
color: #b8860b; /* Couleur dorée au survol */
transform: scale(1.1);
}
/* --- Styles pour les écrans larges (Desktop) --- */
@media (min-width: 768px) {
.game-nav {
height: 55px; /* Hauteur fixe pour le bureau */
padding: 0 1rem;
}
.nav-container {
flex-wrap: nowrap; /* Empêche le retour à la ligne sur les grands écrans */
}
}
</style>

View File

@@ -0,0 +1,17 @@
---
interface Props {
href: string;
title: string;
body: string;
}
const { href, title, body } = Astro.props;
---
<style>
/* Les styles ont été déplacés dans GameLayout.astro pour être globaux */
</style>
<a href={href} class="gold-button">
<div class="title">{title}</div>
<p class="body">{body}</p>
</a>

View File

@@ -0,0 +1,20 @@
---
// Ce composant est un simple conteneur stylisé.
// Il n'a pas besoin de props, il affiche simplement le contenu passé à l'intérieur.
---
<div class="parchment-card">
<slot />
</div>
<style>
.parchment-card {
background-color: rgba(253, 246, 232, 0.8); /* Fond parchemin légèrement transparent */
border: 1px solid #dcd0b9;
border-radius: 8px;
padding: 2rem;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);
backdrop-filter: blur(4px); /* Effet de verre dépoli pour voir la texture du fond */
margin-bottom: 2rem;
}
</style>

View File

@@ -1,31 +1,27 @@
// 1. Importer les utilitaires de `astro:content` import { defineCollection, z } from 'astro:content';
import { z, defineCollection } from 'astro:content';
// 2. Définir une collection pour le journal d'aventure // Collection pour les articles du journal d'aventure
const journalCollection = defineCollection({ const journalCollection = defineCollection({
type: 'content', // 'content' pour les fichiers .md ou .mdx type: 'content',
schema: z.object({ schema: z.object({
title: z.string(), title: z.string(),
author: z.string(), author: z.string(),
publishDate: z.date(), publishDate: z.date(),
image: z.object({ tags: z.array(z.string()).optional(), // Ajout des balises (optionnel)
src: z.string(), }),
alt: z.string(),
}).optional(),
}),
}); });
// 3. Définir une collection pour les logs de construction // Collection pour les logs de construction
const logsCollection = defineCollection({ const logsCollection = defineCollection({
type: 'content', type: 'content',
schema: z.object({ schema: z.object({
title: z.string(), title: z.string(),
publishDate: z.date(), publishDate: z.date(),
}), tags: z.array(z.string()).optional(),
}),
}); });
// 4. Exporter les collections pour les enregistrer
export const collections = { export const collections = {
'journal': journalCollection, journal: journalCollection,
'logs': logsCollection, logs: logsCollection,
}; };

View File

@@ -2,11 +2,12 @@
title: "L'Éveil du Cul Brillant" title: "L'Éveil du Cul Brillant"
author: "G'Mas" author: "G'Mas"
publishDate: 2026-01-10 publishDate: 2026-01-10
tags: ["Yeuze-sur-Chenarde", "Exploration", "Gurdil", "Bulle", "Social", "Mystère"]
--- ---
### Chroniques de Yeuze-sur-Chenarde ### Chroniques de Yeuze-sur-Chenarde
L'arrivée à **Yeuze-sur-Chenarde** restera gravée dans ma mémoire, non pas par le sang, mais par la glace. Nous sommes arrivés en début d'après-midi, et le froid m'a immédiatement saisi les os, moi qui suis si sensible à ce "souffle de vie" qui anime le monde. Accompagné de la fée Bulle, je ne pensais qu'à une chose : le **Grand Chêne**. Cet arbre colossal, mon but, mon phare dans cette immensité blanche qui se dresse dorénavant devant nous L'arrivée à **Yeuze-sur-Chenarde** restera gravée dans ma mémoire, non pas par le sang, mais par la glace. Nous sommes arrivés en début d'après-midi, et le froid m'a immédiatement saisi les os, moi qui suis si sensible à ce "souffle de vie" qui anime le monde. Accompagné de la fée Bulle, je ne pensais qu'à une chose : le **Grand Chêne**. Cet arbre colossal, mon but, mon phare dans cette immensité blanche qui se dresse dorénavant devant nous.
Soudain, le chaos. Un projectile percute l'arrière de mon crâne. Gurdil le nain, plus chanceux ou plus bas, l'évite de justesse. Nyrae, notre Tabaxi, n'a pas cette chance, bien qu'elle ait aperçu nos assaillants avant l'impact. Ce n'était pas une embuscade de brigands, mais une armée de gamins du village armés de boules de neige ! Frigorifié, incapable de réagir, je me suis laissé "exploser" par leurs tirs. Une défaite tactique cuisante qui nous a forcés à nous réfugier, tels des vaincus, dans la chaleur de l'auberge. Soudain, le chaos. Un projectile percute l'arrière de mon crâne. Gurdil le nain, plus chanceux ou plus bas, l'évite de justesse. Nyrae, notre Tabaxi, n'a pas cette chance, bien qu'elle ait aperçu nos assaillants avant l'impact. Ce n'était pas une embuscade de brigands, mais une armée de gamins du village armés de boules de neige ! Frigorifié, incapable de réagir, je me suis laissé "exploser" par leurs tirs. Une défaite tactique cuisante qui nous a forcés à nous réfugier, tels des vaincus, dans la chaleur de l'auberge.
@@ -76,4 +77,3 @@ Kwel semble être une source d'informations aussi riche qu'incertaine. Voici les
**Paradoxe Temporel :** Il affirme avoir fait partie d'aventuriers il y a un millénaire, tout en disant qu'il a lui-même moins de 1000 ans (alors que le village, lui, dépasse cet âge). **Paradoxe Temporel :** Il affirme avoir fait partie d'aventuriers il y a un millénaire, tout en disant qu'il a lui-même moins de 1000 ans (alors que le village, lui, dépasse cet âge).
**Connaissance des Grungs :** Il a mentionné avoir déjà croisé des membres de votre espèce (Grung) au cours de sa vie. **Connaissance des Grungs :** Il a mentionné avoir déjà croisé des membres de votre espèce (Grung) au cours de sa vie.

View File

@@ -0,0 +1,35 @@
---
title: "Acquis et Paradigmes d'Astro"
publishDate: 2026-01-13
tags: ["Astro", "Architecture", "Paradigmes", "Composants", "Routage", "Contenu"]
---
Ce log est une halte. Un moment pour observer le chemin parcouru, non pas comme une liste de compétences acquises, mais comme la compréhension des fondations sur lesquelles nous nous tenons. Cette version du site est une étape nécessaire : l'apprentissage des langages et des structures du web "plat", pour mieux en déceler les limites et préparer le véritable saut dans l'immersion.
### 1. L'Apprentissage du Langage des Bâtisseurs
Avant de pouvoir façonner le sable en mondes, il fallait apprendre à tailler la pierre. L'architecture actuelle repose sur les paradigmes d'Astro, qui sont ceux des bâtisseurs de la toile moderne.
* **L'Art de la Brique (`Composants Astro`)** : Nous avons appris à créer des "briques" réutilisables (`GoldButton`, `ParchmentCard`). C'est un art de l'efficacité, de la modularité. Chaque brique est autonome, avec ses propres styles. C'est puissant, mais cela nous enferme dans une logique de "boîtes" empilées les unes sur les autres.
* **Le Grimoire Ordonné (`Content Collections`)** : Nous avons appris à cataloguer nos récits et nos logs. Chaque parchemin est validé, chaque métadonnée est à sa place. C'est la sagesse de l'archiviste, qui assure l'ordre et la cohérence. Mais un livre, même magique, reste une succession de pages linéaires.
* **Les Sentiers Tracés (`Routage Dynamique`)** : Nous avons appris à tracer les chemins qui relient nos pages. De l'index du journal à un article, d'un article à sa balise. Ces chemins sont clairs, rapides, générés à l'avance. Mais ce sont des sentiers balisés sur une carte en deux dimensions, pas une exploration libre dans un monde vivant.
### 2. La Conscience des Murs Invisibles
Cette architecture modulaire, si prisée, est aussi une cage dorée. Elle nous a permis de construire vite et bien, mais elle nous a aussi forcés à penser en "pages", en "blocs", en "liens". L'immersion que permet Three.js ne peut se contenter de cela.
Le paradigme actuel est celui du **document**. On navigue d'un document à l'autre. L'ambition est de passer au paradigme de l'**espace**. Un monde unique et persistant dans lequel le contenu n'est pas une page que l'on charge, mais un objet avec lequel on interagit.
C'est ici que des logiques ont été abandonnées, non par erreur, mais par nécessité. Le menu hamburger, par exemple, est une convention du web 2D. Dans un monde 3D, la navigation pourrait être un objet dans la scène, une carte que l'on déplie, un chemin de lumière que l'on suit.
### 3. Le Prochain Pas : Façonner le Sable
Cette fondation n'est pas vaine. Elle est le sol stable sur lequel nous allons maintenant ériger un sanctuaire en trois dimensions. La prochaine grande étape n'est pas d'ajouter une fonctionnalité, mais de changer de regard.
* **Comprendre chaque grain** : Il s'agira de déconstruire. Comment une scène Three.js peut-elle devenir le "layout" principal ? Comment faire en sorte que le routage d'Astro ne recharge pas une page, mais déclenche une animation dans la scène 3D ?
* **Construire avec intention** : Chaque choix devra être conscient. Nous n'appliquerons pas des solutions toutes faites, mais nous chercherons à comprendre les principes fondamentaux de la 3D sur le web pour créer une expérience qui a du sens. Le but n'est pas de "faire" un site 3D, mais de **comprendre** comment le faire naître.
Cette étape est terminée. Le pèlerin a appris les cartes du vieux monde. Il est temps, maintenant, de les brûler pour dessiner la sienne.

View File

@@ -1,6 +1,7 @@
--- ---
title: "Initialisation du projet" title: "Initialisation du projet"
publishDate: 2026-01-09 publishDate: 2026-01-09
tags: ["Setup", "Infrastructure", "Docker", "Raspberry Pi", "Astro"]
--- ---
Le projet a été initialisé avec Astro. La structure de base est en place. Le projet a été initialisé avec Astro. La structure de base est en place.

View File

@@ -1,7 +1,7 @@
--- ---
title: "Ajout de la matière du passé" title: "Ajout de la matière du passé"
publishDate: 2026-01-12 publishDate: 2026-01-12
description: "Création du contenu narratif principal du site à travers la voix de G'Mas." tags: ["Contenu", "Design", "Narratif", "Composants"]
--- ---
Le site a été enrichi avec le contenu principal, donnant vie à l'univers narratif du projet. L'ensemble du site adopte désormais la voix et la perspective de G'Mas, le pèlerin Grung. Le site a été enrichi avec le contenu principal, donnant vie à l'univers narratif du projet. L'ensemble du site adopte désormais la voix et la perspective de G'Mas, le pèlerin Grung.

View File

@@ -0,0 +1,252 @@
---
import { ViewTransitions } from 'astro:transitions';
import GameNav from '../components/ui/GameNav.astro';
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Clone de AFK Journey avec Astro et Three.js" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<ViewTransitions />
</head>
<body>
<main>
<slot />
</main>
<GameNav />
</body>
</html>
<style is:global>
@import url('https://fonts.googleapis.com/css2?family=Cinzel:wght@700&family=EB+Garamond&display=swap');
html {
background-color: #fdf6e8; /* Ton crème/parchemin */
}
body {
/* La couleur de fond est maintenant sur l'élément HTML */
background-color: transparent;
color: #4a4a4a;
font-family: 'EB Garamond', serif;
margin: 0;
/* Ajout d'un padding en bas pour ne pas que la nav masque le contenu */
padding: 2rem 2rem 100px 2rem;
position: relative;
}
/* Applique une texture de grain de papier en arrière-plan */
body::before {
content: '';
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
/* Assurez-vous d'avoir une image de texture dans public/textures/ */
background-image: url('/textures/paper-grain.png');
opacity: 0.5; /* J'augmente l'opacité pour qu'elle soit bien visible */
pointer-events: none;
z-index: -1;
}
/* --- Styles des Titres --- */
h1, h2 {
font-family: 'Cinzel', serif;
text-align: center;
color: #3a352a; /* Une couleur de texte sombre pour un bon contraste */
margin-bottom: 2rem; /* Espace après l'ornement */
}
/* Ajoute un ornement doré sous les titres */
h1::after, h2::after {
content: '';
display: block;
width: 100px; /* Largeur de l'ornement */
height: 2px;
background: linear-gradient(90deg, transparent, #c89b3c, transparent); /* Dégradé doré */
margin: 0.75rem auto 0; /* Espace entre le texte et l'ornement */
opacity: 0.8;
/* Animation pour "dessiner" la ligne */
transform: scaleX(0);
animation: drawLine 1s cubic-bezier(0.22, 1, 0.36, 1) 0.3s forwards;
}
/* On peut différencier légèrement le h2 */
h2 {
font-size: 1.8rem;
color: #4a4130;
}
/* --- Styles pour le contenu Markdown (.prose) --- */
.prose h3 {
font-family: 'Cinzel', serif;
text-align: left;
font-size: 1.5rem;
border-bottom: 1px solid #dcd0b9;
padding-bottom: 0.5rem;
margin-top: 2.5rem;
}
/* On retire l'ornement pour les h3 */
.prose h3::after {
content: none;
}
.prose p {
line-height: 1.7;
font-size: 1.1rem;
}
.parchment-card a {
color: #c89b3c; /* Or vif */
text-decoration: none;
font-weight: bold;
transition: color 0.2s;
}
.parchment-card a:hover {
filter: brightness(1.2);
}
.prose ul {
list-style: none;
padding-left: 1rem;
}
.prose ul li::before {
content: '•';
color: #c89b3c;
font-weight: bold;
display: inline-block;
width: 1em;
margin-left: -1em;
}
.prose hr {
border: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #c89b3c, transparent);
margin: 3rem 0;
}
.prose table {
width: 100%;
border-collapse: collapse;
margin: 2rem 0;
}
.prose th, .prose td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #dcd0b9; /* Ligne de séparation parchemin */
}
.prose th {
font-family: 'Cinzel', serif;
background-color: rgba(253, 246, 232, 0.5); /* Fond de cellule plus clair */
}
/* --- Styles pour GoldButton --- */
.gold-button {
background: linear-gradient(145deg, #fefbf3, #f8f1e4); /* Dégradé parchemin clair */
border: 2px solid #c89b3c;
border-radius: 15px;
color: #4a4130; /* Texte sombre pour le contraste */
padding: 1.5rem;
text-align: center;
text-decoration: none;
transition: all 0.3s ease;
display: block;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1), inset 0 0 5px rgba(200, 155, 60, 0.1);
}
.gold-button:hover {
transform: translateY(-5px);
filter: brightness(1.05);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15), inset 0 0 10px rgba(200, 155, 60, 0.2);
border-color: #f0e6d2;
}
.gold-button .title { /* Ciblage plus spécifique */
font-family: 'Cinzel', serif;
font-size: 1.5rem;
color: #b8860b; /* Titre en couleur or */
margin-bottom: 0.5rem;
}
.gold-button .body { /* Ciblage plus spécifique */
font-family: 'EB Garamond', serif;
font-size: 1rem;
color: #4a4130; /* Texte sombre pour le contraste */
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
/* --- Styles pour les Tags --- */
.tags-container {
display: flex;
justify-content: center;
gap: 0.5rem;
flex-wrap: wrap;
margin-bottom: 2rem;
}
.tag {
background-color: rgba(200, 155, 60, 0.2);
color: #b8860b;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.8rem;
font-family: 'Cinzel', serif;
border: 1px solid rgba(200, 155, 60, 0.3);
text-decoration: none;
transition: all 0.2s ease;
}
.tag:hover {
background-color: #c89b3c;
color: #2c2a24;
}
/* Keyframes pour l'animation de l'ornement */
@keyframes drawLine {
from {
transform: scaleX(0);
}
to {
transform: scaleX(1);
}
}
/* Animation pour la nouvelle page (le "pop-up") */
@keyframes slide-in {
from {
transform: translateY(15%) scale(0.95);
opacity: 0;
}
to {
transform: translateY(0) scale(1);
opacity: 1;
}
}
/* Animation pour l'ancienne page qui disparaît */
@keyframes fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
/* On applique nos animations aux pseudo-éléments de View Transitions */
::view-transition-new(root) {
animation: 0.4s cubic-bezier(0.22, 1, 0.36, 1) 0s 1 normal both running slide-in;
}
::view-transition-old(root) {
animation: 0.2s ease-out 0s 1 normal both running fade-out;
}
</style>

View File

@@ -1,50 +0,0 @@
---
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="description" content="Astro description" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
<style is:global>
table {
width: 100%;
border-collapse: collapse;
margin-block: 1rem;
}
th,
td {
border: 1px solid #ccc;
padding: 0.5rem;
text-align: left;
}
th {
background-color: #f2f2f2;
}
</style>
</head>
<body>
<header>
<nav>
<a href="/">Accueil</a> |
<a href="/journal">Journal</a> |
<a href="/auteur">Auteur</a> |
<a href="/logs">Logs de Construction</a>
</nav>
</header>
<main>
<slot />
</main>
</body>
</html>

View File

@@ -1,45 +1,48 @@
--- ---
import Layout from '../layouts/Layout.astro'; import GameLayout from '../layouts/GameLayout.astro';
import ParchmentCard from '../components/ui/ParchmentCard.astro';
--- ---
<Layout title="Le Sanctuaire des Grains - Sweet Journey"> <GameLayout title="Le Sanctuaire des Grains - Sweet Journey">
<h1>🏜️ Le Sanctuaire des Grains</h1> <ParchmentCard>
<p> <h1>🏜️ Le Sanctuaire des Grains</h1>
Entrez dans mon lieu de méditation. D'où je viens, la création n'est pas une affaire de métal et de feu, mais de patience, de concentration et de sable. Chaque grain est une possibilité, chaque ligne de code un chemin tracé dans les dunes de l'esprit. Cette section dévoile les arts que j'emploie pour donner forme à ce monde. <p>
</p> Entrez dans mon lieu de méditation. D'où je viens, la création n'est pas une affaire de métal et de feu, mais de patience, de concentration et de sable. Chaque grain est une possibilité, chaque ligne de code un chemin tracé dans les dunes de l'esprit. Cette section dévoile les arts que j'emploie pour donner forme à ce monde.
</p>
<h2>Le Façonnage du Sable : Les Arts Techniques</h2> <h2>Le Façonnage du Sable : Les Arts Techniques</h2>
<p> <p>
Au lieu d'une forge, mon art s'exprime par le façonnage. Les idées brutes sont comme du sable sauvage ; il faut le canaliser, le modeler avec discipline pour qu'il devienne une histoire, une expérience. Pour invoquer un monde 3D dans le navigateur, le choix des arts est aussi crucial que le choix du sable. Au lieu d'une forge, mon art s'exprime par le façonnage. Les idées brutes sont comme du sable sauvage ; il faut le canaliser, le modeler avec discipline pour qu'il devienne une histoire, une expérience. Pour invoquer un monde 3D dans le navigateur, le choix des arts est aussi crucial que le choix du sable.
</p> </p>
<h3>Pourquoi Three.js ?</h3> <h3>Pourquoi Three.js ?</h3>
<p><strong>Three.js</strong> est mon art martial numérique. C'est un ensemble de katas (fonctions) en JavaScript qui me permet de maîtriser le flux de la 3D dans le navigateur. Chaque mouvement est précis : je peux dicter la course de la lumière, orchestrer la danse des objets et appliquer les textures comme on applique un pigment. C'est le ki qui donne vie à la matière inerte.</p> <p><strong>Three.js</strong> est mon art martial numérique. C'est un ensemble de katas (fonctions) en JavaScript qui me permet de maîtriser le flux de la 3D dans le navigateur. Chaque mouvement est précis : je peux dicter la course de la lumière, orchestrer la danse des objets et appliquer les textures comme on applique un pigment. C'est le ki qui donne vie à la matière inerte.</p>
<h3>Pourquoi Blender ?</h3> <h3>Pourquoi Blender ?</h3>
<p>Si Three.js est l'art du mouvement, <strong>Blender</strong> est l'art de la méditation. C'est dans ce monde intérieur que je voyage pour façonner les personnages, les paysages et les rêves de mon peuple. C'est un outil open source qui me permet de donner une forme tangible à mes visions, avant de leur insuffler le souffle de vie sur le web.</p> <p>Si Three.js est l'art du mouvement, <strong>Blender</strong> est l'art de la méditation. C'est dans ce monde intérieur que je voyage pour façonner les personnages, les paysages et les rêves de mon peuple. C'est un outil open source qui me permet de donner une forme tangible à mes visions, avant de leur insuffler le souffle de vie sur le web.</p>
<h2>Le Kit du Pèlerin : Outils de Prédilection</h2> <h2>Le Kit du Pèlerin : Outils de Prédilection</h2>
<p>Tout pèlerin transporte avec lui des outils essentiels. Les miens ne sont pas faits de bois ou de métal, mais ils sont tout aussi vitaux pour mon voyage.</p> <p>Tout pèlerin transporte avec lui des outils essentiels. Les miens ne sont pas faits de bois ou de métal, mais ils sont tout aussi vitaux pour mon voyage.</p>
<dl> <dl>
<dt><strong>Visual Studio Code : Mon Grimoire de Rituels</strong></dt> <dt><strong>Visual Studio Code : Mon Grimoire de Rituels</strong></dt>
<dd>C'est ici que je calligraphie les incantations (le code) qui donnent leurs instructions au sable. Chaque fonction est un mantra, chaque variable un grain choisi avec soin pour construire le monde.</dd> <dd>C'est ici que je calligraphie les incantations (le code) qui donnent leurs instructions au sable. Chaque fonction est un mantra, chaque variable un grain choisi avec soin pour construire le monde.</dd>
<dt><strong>Lenovo Yoga 530 (Ubuntu) : Ma Pierre de Méditation</strong></dt> <dt><strong>Lenovo Yoga 530 (Ubuntu) : Ma Pierre de Méditation</strong></dt>
<dd>C'est sur cette surface que je m'installe pour me concentrer. Sa flexibilité me permet de trouver la posture juste, et son cœur (Ubuntu) bat au rythme du partage et de la liberté, des valeurs chères à tout pèlerin.</dd> <dd>C'est sur cette surface que je m'installe pour me concentrer. Sa flexibilité me permet de trouver la posture juste, et son cœur (Ubuntu) bat au rythme du partage et de la liberté, des valeurs chères à tout pèlerin.</dd>
<dt><strong>Wacom One, GIMP & Krita : Ma Toile de Sable et mes Pigments de Songe</strong></dt> <dt><strong>Wacom One, GIMP & Krita : Ma Toile de Sable et mes Pigments de Songe</strong></dt>
<dd>La tablette est une étendue de sable fin sur laquelle je dessine les premières formes de mes visions. GIMP et Krita m'offre les pigments extraits des rêves pour leur donner couleur et vie.</dd> <dd>La tablette est une étendue de sable fin sur laquelle je dessine les premières formes de mes visions. GIMP et Krita m'offre les pigments extraits des rêves pour leur donner couleur et vie.</dd>
<dt><strong>Obsidian & Carnet à dessin : Ma Carte des Étoiles et mon Journal de Piste</strong></dt> <dt><strong>Obsidian & Carnet à dessin : Ma Carte des Étoiles et mon Journal de Piste</strong></dt>
<dd>Le carnet recueille les croquis bruts de mon voyage. Obsidian, lui, est ma carte du ciel intérieure ; il me permet de relier chaque idée, chaque souvenir et chaque rêve en une constellation de savoir qui guide ma quête.</dd> <dd>Le carnet recueille les croquis bruts de mon voyage. Obsidian, lui, est ma carte du ciel intérieure ; il me permet de relier chaque idée, chaque souvenir et chaque rêve en une constellation de savoir qui guide ma quête.</dd>
<dt><strong>Raspberry Pi : Mon Foyer de Pèlerin</strong></dt> <dt><strong>Raspberry Pi : Mon Foyer de Pèlerin</strong></dt>
<dd>Ce petit autel est le cœur de mon campement numérique. C'est un serveur humble mais résilient, d'où je publie mes récits et partage mes créations avec le monde. Il bat au rythme de plusieurs esprits bienveillants :</dd> <dd>Ce petit autel est le cœur de mon campement numérique. C'est un serveur humble mais résilient, d'où je publie mes récits et partage mes créations avec le monde. Il bat au rythme de plusieurs esprits bienveillants :</dd>
<dd> <dd>
<ul> <ul>
<li><strong>Docker :</strong> L'art de créer des bocaux à songes. Chaque service est contenu dans son propre terrarium magique, parfait et isolé, pour s'assurer que les rêves ne se mélangent pas.</li> <li><strong>Docker :</strong> L'art de créer des bocaux à songes. Chaque service est contenu dans son propre terrarium magique, parfait et isolé, pour s'assurer que les rêves ne se mélangent pas.</li>
<li><strong>Nginx Proxy Manager :</strong> Le gardien des carrefours, qui guide les visiteurs égarés vers la bonne histoire, le bon outil.</li> <li><strong>Nginx Proxy Manager :</strong> Le gardien des carrefours, qui guide les visiteurs égarés vers la bonne histoire, le bon outil.</li>
<li><strong>Nextcloud & Wallabag :</strong> Ma besace sans fond et mon filet à murmures. L'un stocke mes trouvailles et mes grains de sable précieux, l'autre capture les histoires du vent pour une lecture future.</li> <li><strong>Nextcloud & Wallabag :</strong> Ma besace sans fond et mon filet à murmures. L'un stocke mes trouvailles et mes grains de sable précieux, l'autre capture les histoires du vent pour une lecture future.</li>
<li><strong>Dolibarr & Superset :</strong> Mon grand livre de marchand et ma lentille d'analyse. Ils m'aident à tenir les comptes de mes échanges de rêves et à scruter la nature profonde de chaque grain de sable (donnée) que je collecte.</li> <li><strong>Dolibarr & Superset :</strong> Mon grand livre de marchand et ma lentille d'analyse. Ils m'aident à tenir les comptes de mes échanges de rêves et à scruter la nature profonde de chaque grain de sable (donnée) que je collecte.</li>
</ul> </ul>
</dd> </dd>
</dl> </dl>
</Layout> </ParchmentCard>
</GameLayout>

View File

@@ -1,42 +1,46 @@
--- ---
import Layout from '../layouts/Layout.astro'; import GameLayout from '../layouts/GameLayout.astro';
import ParchmentCard from '../components/ui/ParchmentCard.astro';
--- ---
<Layout title="L'Artisan des Rêves - Nicolas Latchimy"> <GameLayout title="L'Artisan des Rêves - Nicolas Latchimy">
<h1>🐸 L'Artisan derrière le Pèlerin</h1> <ParchmentCard>
<p> <h1>🐸 L'Artisan derrière le Pèlerin</h1>
Je suis G'Mas, un pèlerin fait de sable et de songes. Mais qui est l'artisan qui me rêve ? Qui est celui dont les mains façonnent mon voyage ? Laissez-moi vous parler de lui, car son histoire est la source de la mienne. <p>
</p> Je suis G'Mas, un pèlerin fait de sable et de songes. Mais qui est l'artisan qui me rêve ? Qui est celui dont les mains façonnent mon voyage ? Laissez-moi vous parler de lui, car son histoire est la source de la mienne.
<p> </p>
Il se nomme <strong>Nicolas Latchimy</strong>. Artiste, développeur, un esprit ubiquiste qui, depuis l'âge de trois ans, utilise le dessin pour donner une forme à ses émotions. Son parcours est un long pèlerinage entre deux mondes : celui de l'Art, étudié à Paris et à La Réunion, et celui du Code, appris plus tard pour construire de nouvelles formes de récits. <p>
</p> Il se nomme <strong>Nicolas Latchimy</strong>. Artiste, développeur, un esprit ubiquiste qui, depuis l'âge de trois ans, utilise le dessin pour donner une forme à ses émotions. Son parcours est un long pèlerinage entre deux mondes : celui de l'Art, étudié à Paris et à La Réunion, et celui du Code, appris plus tard pour construire de nouvelles formes de récits.
</p>
<h2>Hatjan Krâpo, le Cousin du grung</h2> <h2>Hatjan Krâpo, le Cousin du grung</h2>
<p> <p>
Avant que je n'existe, un autre batracien est né de son crayon : <strong>Hatjan Krâpo</strong>. Mon lointain cousin de papier. Si je suis un pèlerin des sables, Hatjan est un symbole du rejet, du dégoût, mais aussi d'une humanité cinglante. À travers lui, Nicolas explore les thèmes qui le hantent : l'exclusion, les discriminations, le harcèlement. C'est un cri silencieux contre les écarts qui fracturent nos sociétés. Avant que je n'existe, un autre batracien est né de son crayon : <strong>Hatjan Krâpo</strong>. Mon lointain cousin de papier. Si je suis un pèlerin des sables, Hatjan est un symbole du rejet, du dégoût, mais aussi d'une humanité cinglante. À travers lui, Nicolas explore les thèmes qui le hantent : l'exclusion, les discriminations, le harcèlement. C'est un cri silencieux contre les écarts qui fracturent nos sociétés.
</p> </p>
<h2>Mosaic Chantilly, l'Armateur d'Art</h2> <h2>Mosaic Chantilly, l'Armateur d'Art</h2>
<p> <p>
Le 14 septembre 2024, un autre chemin s'est ouvert. Sous le nom de <strong>Mosaic Chantilly</strong>, il est devenu un "armateur d'art à corps perdu". Sa mission : déconstruire l'art comme une mosaïque pour en révéler l'essence. C'est une quête de dix ans qui commence, un nouveau voyage dans la matière et la forme. Le 14 septembre 2024, un autre chemin s'est ouvert. Sous le nom de <strong>Mosaic Chantilly</strong>, il est devenu un "armateur d'art à corps perdu". Sa mission : déconstruire l'art comme une mosaïque pour en révéler l'essence. C'est une quête de dix ans qui commence, un nouveau voyage dans la matière et la forme.
</p> </p>
<h2>Du Pinceau au Code</h2> <h2>Du Pinceau au Code</h2>
<p> <p>
Après des années à questionner le monde à travers l'art, il s'est tourné vers le développement web. Non pas pour abandonner le dessin, mais pour lui offrir un nouveau terrain de jeu. Il voit le code comme un autre langage pour raconter des histoires, pour créer des ponts. Ce site en est la preuve : un lieu où la 3D, la narration et la technique se rencontrent. Après des années à questionner le monde à travers l'art, il s'est tourné vers le développement web. Non pas pour abandonner le dessin, mais pour lui offrir un nouveau terrain de jeu. Il voit le code comme un autre langage pour raconter des histoires, pour créer des ponts. Ce site en est la preuve : un lieu où la 3D, la narration et la technique se rencontrent.
</p> </p>
<h2>Retrouver l'Artisan</h2> <h2>Retrouver l'Artisan</h2>
<p>Pour suivre ses autres pérégrinations, qu'elles soient dessinées ou codées :</p> <p>Pour suivre ses autres pérégrinations, qu'elles soient dessinées ou codées :</p>
<ul> <ul>
<li><strong>Portfolio Artistique :</strong> <a href="https://www.pinterest.com/imazmaronrstones/errances-cr%C3%A9atives/" target="_blank" rel="noopener noreferrer">Errances Créatives sur Pinterest</a></li> <li><strong>Portfolio Artistique :</strong> <a href="https://www.pinterest.com/imazmaronrstones/errances-cr%C3%A9atives/" target="_blank" rel="noopener noreferrer">Errances Créatives sur Pinterest</a></li>
<li><strong>Page Auteur :</strong> <a href="https://www.la-reunion-des-livres.re/auteur/latchimy-nicolas/" target="_blank" rel="noopener noreferrer">La Réunion des Livres</a></li> <li><strong>Page Auteur :</strong> <a href="https://www.la-reunion-des-livres.re/auteur/latchimy-nicolas/" target="_blank" rel="noopener noreferrer">La Réunion des Livres</a></li>
<li><strong>Forum Dessiné :</strong> <a href="https://www.forum-dessine.fr/auteurs/krapo" target="_blank" rel="noopener noreferrer">Profil de Krâpo</a></li> <li><strong>Forum Dessiné :</strong> <a href="https://www.forum-dessine.fr/auteurs/krapo" target="_blank" rel="noopener noreferrer">Profil de Krâpo</a></li>
<li><strong>23h de la BD (2019) :</strong> <a href="https://23hbd.com/participants/2019/krapo/" target="_blank" rel="noopener noreferrer">Participation 2019</a></li> <li><strong>23h de la BD (2019) :</strong> <a href="https://23hbd.com/participants/2019/krapo/" target="_blank" rel="noopener noreferrer">Participation 2019</a></li>
<li><strong>23h de la BD (2020) :</strong> <a href="https://23hbd.com/participants/2020/krapo/" target="_blank" rel="noopener noreferrer">Participation 2020</a></li> <li><strong>23h de la BD (2020) :</strong> <a href="https://23hbd.com/participants/2020/krapo/" target="_blank" rel="noopener noreferrer">Participation 2020</a></li>
<li><strong>Webtoon (Liestral) :</strong> <a href="https://www.webtoons.com/fr/canvas/liestral/list?title_no=1049110" target="_blank" rel="noopener noreferrer">Découvrir son webtoon</a></li> <li><strong>Webtoon (Liestral) :</strong> <a href="https://www.webtoons.com/fr/canvas/liestral/list?title_no=1049110" target="_blank" rel="noopener noreferrer">Découvrir son webtoon</a></li>
<li><strong>Instagram :</strong> <a href="https://www.instagram.com/mosaic_chantilly/" target="_blank" rel="noopener noreferrer">@mosaic_chantilly</a></li> <li><strong>Instagram :</strong> <a href="https://www.instagram.com/mosaic_chantilly/" target="_blank" rel="noopener noreferrer">@mosaic_chantilly</a></li>
<li><strong>CV en ligne :</strong> <a href="https://nohay.github.io/" target="_blank" rel="noopener noreferrer">Découvrir son parcours</a></li> <li><strong>CV en ligne :</strong> <a href="https://nohay.github.io/" target="_blank" rel="noopener noreferrer">Découvrir son parcours</a></li>
</ul> </ul>
</ParchmentCard>
</GameLayout>
</Layout> </Layout>

View File

@@ -1,17 +1,19 @@
--- ---
import Layout from '../layouts/Layout.astro'; import GameLayout from '../layouts/GameLayout.astro';
import ParchmentCard from '../components/ui/ParchmentCard.astro';
--- ---
<Layout title="La Boussole Éthique - Sweet Journey"> <GameLayout title="La Boussole Éthique - Sweet Journey">
<h1>⚖️ La Boussole Éthique</h1> <ParchmentCard>
<p> <h1>⚖️ La Boussole Éthique</h1>
Mon voyage a commencé à cause d'un fléau : un sable sombre, dépourvu de vie, qui corrompt mes terres et empoisonne les rêves de mon peuple. Cette quête m'a enseigné à distinguer le sable pur du sable vicié. Il en va de même pour les créations des mortels. Certaines sont conçues pour apporter un repos réparateur, d'autres sont des pièges, aussi subtils que le poison sur ma peau. <p>
</p> Mon voyage a commencé à cause d'un fléau : un sable sombre, dépourvu de vie, qui corrompt mes terres et empoisonne les rêves de mon peuple. Cette quête m'a enseigné à distinguer le sable pur du sable vicié. Il en va de même pour les créations des mortels. Certaines sont conçues pour apporter un repos réparateur, d'autres sont des pièges, aussi subtils que le poison sur ma peau.
</p>
<h2>Les Sables de Vie : Les Chemins à Suivre</h2> <h2>Les Sables de Vie : Les Chemins à Suivre</h2>
<p>Ce sont les modèles basés sur le respect et la valeur. L'**Open Source**, par exemple, est comme une oasis où chaque pèlerin peut puiser de l'eau, mais aussi y ajouter une goutte pour le suivant. C'est un cycle de don où la connaissance n'est pas un trésor gardé, mais une source qui abreuve la communauté. Ma conviction est que l'on peut bâtir de grandes choses avec des matériaux que d'autres jugent obsolètes, en laissant une empreinte légère. L'autonomie est possible sans dévaster le monde ; c'est une question de culture. Se souvenir de la naissance de la grande toile (le web) et voir où elle en est aujourd'hui doit nous inspirer à créer en harmonie avec l'ensemble.</p> <p>Ce sont les modèles basés sur le respect et la valeur. L'**Open Source**, par exemple, est comme une oasis où chaque pèlerin peut puiser de l'eau, mais aussi y ajouter une goutte pour le suivant. C'est un cycle de don où la connaissance n'est pas un trésor gardé, mais une source qui abreuve la communauté. Ma conviction est que l'on peut bâtir de grandes choses avec des matériaux que d'autres jugent obsolètes, en laissant une empreinte légère. L'autonomie est possible sans dévaster le monde ; c'est une question de culture. Se souvenir de la naissance de la grande toile (le web) et voir où elle en est aujourd'hui doit nous inspirer à créer en harmonie avec l'ensemble.</p>
<h2>Les Marécages de Sable Noir : Les Chemins à Éviter</h2> <h2>Les Marécages de Sable Noir : Les Chemins à Éviter</h2>
<p>Ici se trouvent les sables mouvants. Je perçois dans le monde une tendance à la démesure, une soif de pouvoir qui oublie l'équilibre. On y cherche à dominer plutôt qu'à harmoniser, à prendre plutôt qu'à partager. Cette philosophie ressemble au sable noir : elle crée des systèmes où quelques-uns s'enrichissent en asséchant les rêves de la multitude. Mon art se refuse à emprunter cette voie.</p> <p>Ici se trouvent les sables mouvants. Je perçois dans le monde une tendance à la démesure, une soif de pouvoir qui oublie l'équilibre. On y cherche à dominer plutôt qu'à harmoniser, à prendre plutôt qu'à partager. Cette philosophie ressemble au sable noir : elle crée des systèmes où quelques-uns s'enrichissent en asséchant les rêves de la multitude. Mon art se refuse à emprunter cette voie.</p>
</ParchmentCard>
</Layout> </GameLayout>

View File

@@ -1,17 +1,9 @@
--- ---
import Layout from '../layouts/Layout.astro'; import GameLayout from '../layouts/GameLayout.astro';
import Card from '../components/Card.astro'; import GoldButton from '../components/ui/GoldButton.astro';
--- ---
<style>
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 2rem;
padding: 0;
}
</style>
<Layout title="Bienvenue sur JDRSweetJourney"> <GameLayout title="Bienvenue sur JDRSweetJourney">
<h1>Bienvenue sur le projet JDR Sweet Journey !</h1> <h1>Bienvenue sur le projet JDR Sweet Journey !</h1>
<h2>L'Ambition</h2> <h2>L'Ambition</h2>
<p>L'objectif est de créer un clone entièrement web de l'application mobile <strong>AFK Journey</strong>. Ce projet sera une vitrine technologique utilisant :</p> <p>L'objectif est de créer un clone entièrement web de l'application mobile <strong>AFK Journey</strong>. Ce projet sera une vitrine technologique utilisant :</p>
@@ -20,10 +12,10 @@ import Card from '../components/Card.astro';
<li>La retranscription de ces éléments dans le navigateur grâce à <strong>Three.js</strong>.</li> <li>La retranscription de ces éléments dans le navigateur grâce à <strong>Three.js</strong>.</li>
</ul> </ul>
<ul class="card-grid"> <ul class="card-grid">
<Card <GoldButton
href="https://threejs-journey.com" href="https://threejs-journey.com"
title="🎓 Formation : Three.js Journey" title="🎓 Formation : Three.js Journey"
body="Pour garantir une base technique solide, ce projet s'appuie sur les enseignements de Three.js Journey, une formation de référence pour maîtriser Three.js." body="Ce projet s'appuie sur les enseignements de Three.js Journey pour garantir une base technique solide."
/> />
</ul> </ul>
<p>Si en plus, cette plateforme peut servir de point de repère pour nos aventures de jeu de rôle, c'est une pierre deux coups !</p> <p>Si en plus, cette plateforme peut servir de point de repère pour nos aventures de jeu de rôle, c'est une pierre deux coups !</p>
@@ -32,17 +24,17 @@ import Card from '../components/Card.astro';
<p>Ce site est aussi un levier pour maîtriser les paradigmes technologiques de demain. Explorez le <strong>journal d'aventure</strong> pour suivre la quête, ou consultez les <strong>logs de construction</strong> pour voir les coulisses techniques.</p> <p>Ce site est aussi un levier pour maîtriser les paradigmes technologiques de demain. Explorez le <strong>journal d'aventure</strong> pour suivre la quête, ou consultez les <strong>logs de construction</strong> pour voir les coulisses techniques.</p>
<ul class="card-grid"> <ul class="card-grid">
<Card <GoldButton
href="/atelier" href="/atelier"
title="🏜️ Le Sanctuaire des Grains" title="🏜️ Le Sanctuaire des Grains"
body="Découvrez les arts et les outils du pèlerin pour façonner le sable et les rêves." body="Découvrez les arts et les outils du pèlerin pour façonner le sable et les rêves."
/> />
<Card <GoldButton
href="/observatoire" href="/observatoire"
title="🎨 L'Observatoire" title="🎨 L'Observatoire"
body="Explorez les sources d'inspiration : artistes, développeurs, podcasts et jeux qui nourrissent ce projet." body="Explorez les sources d'inspiration : artistes, développeurs, podcasts et jeux qui nourrissent ce projet."
/> />
<Card <GoldButton
href="/boussole" href="/boussole"
title="⚖️ La Boussole Éthique" title="⚖️ La Boussole Éthique"
body="Une réflexion sur les modèles économiques vertueux et ceux à éviter dans le monde du jeu vidéo." body="Une réflexion sur les modèles économiques vertueux et ceux à éviter dans le monde du jeu vidéo."
@@ -50,4 +42,4 @@ import Card from '../components/Card.astro';
</ul> </ul>
<p>Quelle ambition, jeune batracien...</p> <p>Quelle ambition, jeune batracien...</p>
</Layout> </GameLayout>

View File

@@ -0,0 +1,45 @@
---
import { getCollection } from 'astro:content';
import GameLayout from '../../layouts/GameLayout.astro';
import ParchmentCard from '../../components/ui/ParchmentCard.astro';
// 1. Génère une page statique pour chaque article de la collection
export async function getStaticPaths() {
const posts = await getCollection('journal');
return posts.map((post) => ({
params: { slug: post.slug },
props: post,
}));
}
// 2. Récupère les props de l'article correspondant
const post = Astro.props;
const { Content } = await post.render();
---
<GameLayout title={post.data.title}>
<ParchmentCard>
<h1>{post.data.title}</h1>
<div class="meta">
Par {post.data.author} &bull; Le {post.data.publishDate.toLocaleDateString('fr-FR')}
</div>
{post.data.tags && (
<div class="tags-container">
{post.data.tags.map((tag) => (
<a href={`/journal/tags/${tag}`} class="tag">{tag}</a>
))}
</div>
)}
<article class="prose">
<Content />
</article>
</ParchmentCard>
</GameLayout>
<style>
.meta {
text-align: center;
font-style: italic;
margin-bottom: 2rem;
}
</style>

View File

@@ -1,24 +0,0 @@
---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
// 1. Génère une page pour chaque entrée de la collection 'journal'
export async function getStaticPaths() {
const journalEntries = await getCollection('journal');
return journalEntries.map(entry => ({
params: { slug: entry.slug },
props: { entry },
}));
}
// 2. Récupère les props pour la page actuelle
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Layout title={entry.data.title}>
<h1>{entry.data.title}</h1>
<p>Par {entry.data.author}, le {entry.data.publishDate.toLocaleDateString('fr-FR')}</p>
<hr>
<Content />
</Layout>

View File

@@ -1,18 +1,26 @@
--- ---
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro'; import GameLayout from '../../layouts/GameLayout.astro';
import ContentSearch from '../../components/ContentSearch.astro';
// 1. Récupère TOUS les articles et les trie.
const allPosts = await getCollection('journal');
const sortedPosts = allPosts.sort(
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
);
// 2. Extrait toutes les balises uniques
const allTags = [...new Set(allPosts.flatMap(post => post.data.tags || []))];
const allJournalEntries = await getCollection('journal');
--- ---
<Layout title="Journal d'Aventure"> <GameLayout title="Journal d'Aventure">
<h1>Journal d'Aventure</h1> <h1>📖 Journal d'Aventure</h1>
<ul> <p style="text-align: center; margin-bottom: 3rem;">
{allJournalEntries.map(entry => ( Les chroniques de nos voyages. Utilisez la barre de recherche pour trouver un récit.
<li> </p>
<a href={`/journal/${entry.slug}`}>{entry.data.title}</a>
<p>Publié le: {entry.data.publishDate.toLocaleDateString('fr-FR')}</p> <!-- 2. On passe tous les articles au composant client -->
</li> <ContentSearch posts={sortedPosts} tags={allTags} basePath="journal" />
))}
</ul> </GameLayout>
</Layout>

View File

@@ -0,0 +1,39 @@
---
import { getCollection } from 'astro:content';
import GameLayout from '../../../layouts/GameLayout.astro';
import GoldButton from '../../../components/ui/GoldButton.astro';
export async function getStaticPaths() {
const allPosts = await getCollection('journal');
const allTags = [...new Set(allPosts.flatMap((post) => post.data.tags || []))];
return allTags.map((tag) => {
const filteredPosts = allPosts.filter((post) => post.data.tags?.includes(tag));
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}
const { tag } = Astro.params;
const { posts } = Astro.props;
---
<GameLayout title={`Récits: ${tag}`}>
<h1>Récits avec la balise : <span class="tag-title">{tag}</span></h1>
<div class="card-grid">
{
posts.map((post) => (
<GoldButton href={`/journal/${post.slug}/`} title={post.data.title} body={`Publié le ${post.data.publishDate.toLocaleDateString('fr-FR')}`} />
))
}
</div>
</GameLayout>
<style>
.tag-title {
color: #c89b3c;
}
</style>

View File

@@ -0,0 +1,47 @@
---
import { getCollection } from 'astro:content';
import GameLayout from '../../layouts/GameLayout.astro';
import ParchmentCard from '../../components/ui/ParchmentCard.astro';
// 1. Génère une page pour chaque entrée de la collection 'logs'
export async function getStaticPaths() {
const logEntries = await getCollection('logs');
return logEntries.map((entry) => ({
params: { slug: entry.slug },
props: entry,
}));
}
// 2. Récupère les props pour la page actuelle
const post = Astro.props;
const { Content } = await post.render();
---
<GameLayout title={post.data.title}>
<ParchmentCard>
<h1>{post.data.title}</h1>
<div class="meta">
Publié le {post.data.publishDate.toLocaleDateString('fr-FR')}
</div>
{post.data.tags && (
<div class="tags-container">
{post.data.tags.map((tag) => (
<a href={`/logs/tags/${tag}`} class="tag">{tag}</a>
))}
</div>
)}
<article class="prose">
<Content />
</article>
</ParchmentCard>
</GameLayout>
<style>
/* Les styles des tags sont maintenant globaux dans GameLayout.astro */
.meta {
text-align: center;
font-style: italic;
margin-bottom: 2rem;
}
</style>

View File

@@ -1,24 +0,0 @@
---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro';
// 1. Génère une page pour chaque entrée de la collection 'logs'
export async function getStaticPaths() {
const journalEntries = await getCollection('logs');
return journalEntries.map(entry => ({
params: { slug: entry.slug },
props: { entry },
}));
}
// 2. Récupère les props pour la page actuelle
const { entry } = Astro.props;
const { Content } = await entry.render();
---
<Layout title={entry.data.title}>
<h1>{entry.data.title}</h1>
<p>Publié le {entry.data.publishDate.toLocaleDateString('fr-FR')}</p>
<hr>
<Content />
</Layout>

View File

@@ -1,18 +1,25 @@
--- ---
import { getCollection } from 'astro:content'; import { getCollection } from 'astro:content';
import Layout from '../../layouts/Layout.astro'; import GameLayout from '../../layouts/GameLayout.astro';
import ContentSearch from '../../components/ContentSearch.astro';
const allLogEntries = await getCollection('logs'); // 1. Récupère TOUS les logs et les trie.
const allPosts = await getCollection('logs');
const sortedPosts = allPosts.sort(
(a, b) => b.data.publishDate.valueOf() - a.data.publishDate.valueOf()
);
// 2. Extrait toutes les balises uniques
const allTags = [...new Set(allPosts.flatMap(post => post.data.tags || []))];
--- ---
<Layout title="Logs de Construction"> <GameLayout title="Logs de Construction">
<h1>Logs de Construction</h1> <h1>⚙️ Logs de Construction</h1>
<ul> <p style="text-align: center; margin-bottom: 3rem;">
{allLogEntries.map(entry => ( Les coulisses techniques du projet. Utilisez la barre de recherche pour trouver un log.
<li> </p>
<a href={`/logs/${entry.slug}`}>{entry.data.title}</a>
<p>Publié le: {entry.data.publishDate.toLocaleDateString('fr-FR')}</p> <!-- 3. On utilise le composant de recherche pour les logs -->
</li> <ContentSearch posts={sortedPosts} tags={allTags} basePath="logs" />
))}
</ul> </GameLayout>
</Layout>

View File

@@ -0,0 +1,39 @@
---
import { getCollection } from 'astro:content';
import GameLayout from '../../../layouts/GameLayout.astro';
import GoldButton from '../../../components/ui/GoldButton.astro';
export async function getStaticPaths() {
const allPosts = await getCollection('logs');
const allTags = [...new Set(allPosts.flatMap((post) => post.data.tags || []))];
return allTags.map((tag) => {
const filteredPosts = allPosts.filter((post) => post.data.tags?.includes(tag));
return {
params: { tag },
props: { posts: filteredPosts },
};
});
}
const { tag } = Astro.params;
const { posts } = Astro.props;
---
<GameLayout title={`Logs: ${tag}`}>
<h1>Logs avec la balise : <span class="tag-title">{tag}</span></h1>
<div class="card-grid">
{
posts.map((post) => (
<GoldButton href={`/logs/${post.slug}/`} title={post.data.title} body={`Publié le ${post.data.publishDate.toLocaleDateString('fr-FR')}`} />
))
}
</div>
</GameLayout>
<style>
.tag-title {
color: #c89b3c;
}
</style>

View File

@@ -1,28 +1,32 @@
--- ---
import Layout from '../layouts/Layout.astro'; import GameLayout from '../layouts/GameLayout.astro';
import ParchmentCard from '../components/ui/ParchmentCard.astro';
--- ---
<Layout title="L'Observatoire des Songes - Sweet Journey"> <GameLayout title="L'Observatoire des Songes - Sweet Journey">
<h1>🔭 L'Observatoire des Songes</h1> <ParchmentCard>
<p> <h1>🔭 L'Observatoire des Songes</h1>
Un pèlerin, même dans les mers de sable, doit lever les yeux vers les constellations pour ne pas perdre son chemin. Mon voyage n'est pas solitaire ; il est guidé par les échos d'autres mondes, les murmures d'autres créateurs. Cet observatoire est ma carte du ciel, où chaque étoile est une âme dont la lumière m'inspire. <p>
</p> Un pèlerin, même dans les mers de sable, doit lever les yeux vers les constellations pour ne pas perdre son chemin. Mon voyage n'est pas solitaire ; il est guidé par les échos d'autres mondes, les murmures d'autres créateurs. Cet observatoire est ma carte du ciel, où chaque étoile est une âme dont la lumière m'inspire.
</p>
<h2>Les Tisseurs de Rêves : Artistes & Développeurs</h2> <h2>Les Tisseurs de Rêves : Artistes & Développeurs</h2>
<p>Certains artisans, comme le maître Hayao Miyazaki, créent des mondes où le vent, les arbres et les esprits ont une voix. Leurs récits sont des leçons d'harmonie, un rappel que même la plus petite créature a sa place dans le grand cycle. J'étudie leurs œuvres comme un ancien grimoire, y cherchant la maîtrise de la forme et du mouvement.</p> <p>Certains artisans, comme le maître Hayao Miyazaki, créent des mondes où le vent, les arbres et les esprits ont une voix. Leurs récits sont des leçons d'harmonie, un rappel que même la plus petite créature a sa place dans le grand cycle. J'étudie leurs œuvres comme un ancien grimoire, y cherchant la maîtrise de la forme et du mouvement.</p>
<h2>Mes Compagnons de Quête</h2> <h2>Mes Compagnons de Quête</h2>
<p>Mon pèlerinage serait vain sans ceux qui marchent à mes côtés. Chacun est une étoile dans ma nuit, une source de force et de récits inattendus. Leur courage et leurs bizarreries nourrissent mon voyage autant que le sable :</p> <p>Mon pèlerinage serait vain sans ceux qui marchent à mes côtés. Chacun est une étoile dans ma nuit, une source de force et de récits inattendus. Leur courage et leurs bizarreries nourrissent mon voyage autant que le sable :</p>
<ul> <ul>
<li><strong>Orson, le Gardien du Froid :</strong> Notre Maître de Jeu, un être aussi énigmatique et froid que sa patrie gelée. Il tisse les fils de notre destinée tout en suivant la sienne : une quête de vengeance pour sa sœur disparue, marquée au fer rouge par un symbole arcanique. Il est le vent glacial qui nous pousse en avant.</li> <li><strong>Orson, le Gardien du Froid :</strong> Notre Maître de Jeu, un être aussi énigmatique et froid que sa patrie gelée. Il tisse les fils de notre destinée tout en suivant la sienne : une quête de vengeance pour sa sœur disparue, marquée au fer rouge par un symbole arcanique. Il est le vent glacial qui nous pousse en avant.</li>
<li><strong>Gurdill "Cul Brillant" :</strong> Un prince nain en exil, dont le surnom lumineux cache une profonde connaissance de la forge. Banni par les siens, il cherche à prouver sa valeur, son marteau elfique à la main. Sa quête de rédemption est aussi flamboyante que son fameux caleçon.</li> <li><strong>Gurdill "Cul Brillant" :</strong> Un prince nain en exil, dont le surnom lumineux cache une profonde connaissance de la forge. Banni par les siens, il cherche à prouver sa valeur, son marteau elfique à la main. Sa quête de rédemption est aussi flamboyante que son fameux caleçon.</li>
<li><strong>Bulle aux Mains dOr :</strong> Une fée guérisseuse, exilée volontaire de son royaume pour comprendre la souffrance du monde matériel. Sa lumière est un baume pour nos blessures, et sa naïveté un rappel constant de la pureté que nous cherchons à protéger.</li> <li><strong>Bulle aux Mains dOr :</strong> Une fée guérisseuse, exilée volontaire de son royaume pour comprendre la souffrance du monde matériel. Sa lumière est un baume pour nos blessures, et sa naïveté un rappel constant de la pureté que nous cherchons à protéger.</li>
<li><strong>Jinn le Mélodiste :</strong> Un barde Genasi au sang de feu, dont la musique est aussi ardente que son tempérament. Sa quête pour retrouver l'épée de ses ancêtres est un chant d'honneur qui rythme nos pas et enflamme nos cœurs.</li> <li><strong>Jinn le Mélodiste :</strong> Un barde Genasi au sang de feu, dont la musique est aussi ardente que son tempérament. Sa quête pour retrouver l'épée de ses ancêtres est un chant d'honneur qui rythme nos pas et enflamme nos cœurs.</li>
<li><strong>Nyrae la Guérisseuse :</strong> Une Tabaxi dont les pas silencieux la guident loin de sa forêt natale. Son instinct et sa connaissance des onguents sont un réconfort précieux, une présence sauvage et apaisante dans notre groupe hétéroclite.</li> <li><strong>Nyrae la Guérisseuse :</strong> Une Tabaxi dont les pas silencieux la guident loin de sa forêt natale. Son instinct et sa connaissance des onguents sont un réconfort précieux, une présence sauvage et apaisante dans notre groupe hétéroclite.</li>
</ul> </ul>
<h2>Les Murmures du Vent</h2> <h2>Les Murmures du Vent</h2>
<h2>Les Chroniques d'Autres Mondes</h2> <h2>Les Chroniques d'Autres Mondes</h2>
</ParchmentCard>
</GameLayout>
</Layout> </Layout>