Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ fonctionnement du site pour son propriétaire est dans [README.md](README.md) ;

## Le projet

Portfolio personnel statique (Astro 5, TypeScript strict), une seule page qui défile, en
français (`/`, par défaut) et en anglais (`/en/`). Aucun framework client, aucune
Portfolio personnel statique (Astro 5, TypeScript strict), en français (`/`, par défaut) et
en anglais (`/en/`), en trois rubriques : **Profil** (l'accueil, façon CV, avec les projets à la
une), **Projets** (tous les projets, une fiche par projet) et **Billets** (billets tech, liés ou
non à un projet). Ce qui s'affiche se règle dans `site.json → affichage`, à part pour
`npm run dev` (`local`) et pour le site publié (`deploye`) ; lire `reglages(langue).affichage`. Aucun framework client, aucune
dépendance d'exécution : du HTML/CSS généré au build, plus un seul script navigateur
(`src/scripts/ui.ts`). Déployé sur GitHub Pages.

Expand All @@ -31,18 +34,22 @@ ordinateur **et** à ~390 px de large (pas de défilement horizontal).
|---|---|
| `src/data/*.json` | Tout le contenu et les réglages (profil, expériences, formations, certifications, bénévolat, textes, thèmes). Source de vérité, en français. |
| `src/data/en/*.json` | Traduction anglaise : **uniquement les champs à traduire** (voir « Langues »). |
| `src/content/projets/*.md` | Un projet par fichier (en français seulement). |
| `src/content.config.ts` | Schémas des listes de `src/data/` et des projets. |
| `src/content/projets/*.md` | Un projet par fichier (en français seulement) : sa fiche. |
| `src/content/articles/<nom>/` | Un billet par dossier : `fr.md` (en-tête complet), `en.md` (traduction, facultatif), images. |
| `src/content.config.ts` | Schémas des listes de `src/data/`, des projets et des billets. |
| `src/lib/reglages.ts` | Schémas de `site.json`, `profil.json`, `themes.json` ; `reglages(langue)` → `{ site, profil, t }`. |
| `src/lib/i18n.ts` | Langues, `langueDe(Astro.url)`, pose des traductions sur le français. |
| `src/lib/contenu.ts` | `liste(nom, langue)` : une liste de `src/data/` traduite. |
| `src/lib/dates.ts` | Dates « AAAA-MM », périodes et durées, en fr et en en. |
| `src/lib/dates.ts` | Dates « AAAA-MM » et « AAAA-MM-JJ », périodes et durées, en fr et en en. |
| `src/lib/badge.ts` | Badge « Made by ore_link » : son CSS, son HTML et sa version SVG, par thème, avec ou sans café. |
| `src/lib/tuiles.ts` | Plugin rehype : sur les fiches projet, une tuile de verre par intertitre `##` (les cinq questions, voir README). |
| `src/lib/billets.ts` | `billets(langue)` : billets publiés et traduits ; pages et flux RSS. Vide si la rubrique est coupée. |
| `src/lib/paths.ts` | `url(chemin, langue)` pour les liens internes, `asset(chemin)` pour `public/`. |
| `src/vues/` | Pages communes aux deux langues (accueil, projet). |
| `src/pages/` | Routes seulement : `/`, `/en/`, `/projets/[slug]/`, `/en/projets/[slug]/`, 404. Elles rendent une vue. |
| `src/vues/` | Pages communes aux deux langues (accueil, projets, fiche, billets, billet, made by). |
| `src/pages/` | Routes seulement : `/`, `/projets/[...chemin]/` (page Projets et fiches), `/articles/[...chemin]/` (liste et billets), `/articles/rss.xml`, `/madeby/` (`[outil].astro`, non répertoriée), leurs versions `/en/…`, 404. Elles rendent une vue. |
| `src/components/sections/` | Une section de l'accueil par fichier. |
| `src/layouts/Base.astro` | `<head>`, scripts d'avant-rendu (thème, habillage), haut de page, pied de page. |
| `src/scripts/ui.ts` | Tout le JS navigateur : thème, menu des langues, replis « Voir plus », bulles, liens externes, feux d'artifice du Nouvel An. |
| `src/layouts/Base.astro` | `<head>`, scripts d'avant-rendu (thème, habillage), haut de page (rubriques), pied de page. |
| `src/scripts/ui.ts` | Tout le JS navigateur : thème, menu des langues, replis « Voir plus », bulles, filtre des billets, liens externes, feux d'artifice du Nouvel An. |
| `src/styles/tokens.css` | Jetons de design (couleurs clair/sombre, verre, rayons). |
| `src/styles/global.css` | Mise en page et composants. |
| `src/styles/habillages/`, `src/components/habillages/` | Habillages (saisons, fêtes) : un CSS et un composant chacun, `commun.css`, `Etoiles` et `Flocons` partagés. |
Expand Down Expand Up @@ -83,6 +90,10 @@ ordinateur **et** à ~390 px de large (pas de défilement horizontal).
position, les listes de textes (compétences) sont remplacées en entier. Un champ absent
reste en français ; un champ ou un `id` inconnu **fait échouer le build** (c'est voulu).
- Les pages de projets ne sont pas traduites (seule l'interface autour l'est).
- Les billets le sont : `en.md` à côté de `fr.md`, avec seulement `titre`, `resume`, `sujets`
(facultatif) et le texte. Sans `en.md`, la page anglaise montre le français avec un bandeau.
Un champ inconnu dans `en.md`, un `en.md` sans `fr.md` ou un `projet` inconnu fait échouer le
build. Dates des billets en « AAAA-MM-JJ » ; `brouillon: true` ne sort qu'avec `npm run dev`.

## Thèmes

Expand Down
113 changes: 102 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
# oreLINK · Portfolio

**Portfolio of Aurélien Bertrand, Data Engineer based in Bordeaux, France.**
A single-page static site in French and English, driven entirely by data files.
A static site in French and English, driven entirely by data files: a profile page, a
projects page with one overview per project, and tech notes (optional).

[**orelink.github.io**](https://orelink.github.io) · [English version](https://orelink.github.io/en/)

Expand All @@ -22,6 +23,8 @@ A single-page static site in French and English, driven entirely by data files.
themes all live in JSON files. No content is hard-coded.
- **Validated at build time**: every file is checked against a schema (zod). A malformed
date, an invalid color or a broken translation blocks deployment with a precise error.
- **Projects and notes**: every project has an overview page; notes (*billets*) can be
linked to a project, are written in Markdown in both languages and come with an RSS feed.
- **Bilingual**: French at `/`, English at `/en/`, on the same page. Translation files hold
only the text; everything else comes from the French source.
- **Light and dark**: follows the device setting, with a toggle to switch.
Expand Down Expand Up @@ -72,7 +75,8 @@ Everything on the page comes from these files; the code never needs to change.
| `src/data/formations.json` | Education |
| `src/data/certifications.json` | Certifications |
| `src/data/benevolat.json` | Volunteering |
| `src/content/projets/*.md` | Projects: one file per project (home page tile and detail page) |
| `src/content/projets/*.md` | Projects: one file per project (tile and overview page) |
| `src/content/articles/<name>/` | Notes: one folder per note, `fr.md`, optional `en.md`, images |
| `src/data/en/*.json` | English translation (text only) |
| `src/data/themes.json` | Seasonal and holiday skins, see [THEMES.md](THEMES.md) |

Expand All @@ -82,10 +86,34 @@ computed automatically, from most recent to oldest.
<details>
<summary><strong><code>site.json</code> settings</strong></summary>

- `sections`: array order is page order. `"afficher": false` hides a section; `titre`
changes its heading. An empty section disappears on its own.
- `affichage`: what is shown, set separately for `local` (`npm run dev`) and `deploye`
(`npm run build`, i.e. the live site and `npm run preview`). Each block has the same keys:

```json
"profil": { "experience": true, "formation": true, "certifications": true, "benevolat": true },
"projets": { "aLaUne": true, "page": true, "fiches": "auto" },
"billets": false,
"madeBy": true
```

- `profil`: the sections of the profile page.
- `projets.aLaUne`: the "Featured projects" section of the profile page.
- `projets.page`: the `/projets/` page, its navigation link and every link to it ("All
projects", breadcrumb, "Back to Projects").
- `projets.fiches`: the `/projets/<name>/` overview pages and every link to them
("View details"). `"auto"` (the default) follows `projets.page`. Without overviews, a
project linked from a note points to the Projects page instead, or to nothing.
- `billets`: the notes section (navigation link, pages, RSS feed). Notes are still
validated at build time either way.
- `madeBy`: the unlisted `/madeby/` page (see below).

With only the profile left, the navigation bar disappears.
- `sections`: array order is page order; `titre` changes a section's heading. An empty
section disappears on its own.
- `hauteurSectionRepliee`: above this height (in pixels), a section collapses behind a
"Show all" button.
- `projetsALaUne`: how many project tiles the home page shows (the first ones by `ordre`);
the Projects page shows them all.
- `couleurs.accentClair` / `couleurs.accentSombre`: button and link color in light and dark
themes (`#RRGGBB`), used when no skin is active.
- `textes`: every label on the site ("Contact me", "See more"…).
Expand All @@ -103,6 +131,8 @@ computed automatically, from most recent to oldest.
tooltip. `code` is `fr`, `en` or `es` (flags drawn in `src/components/Drapeau.astro`).
- `liens.cv`: put `public/cv.pdf` in place and set `"cv": "cv.pdf"`. An empty link hides its
button.
- `liens.buymeacoffee`: your Buy Me a Coffee page, used by the "Made by" badge. Empty: the
badge code shows a `TON-PSEUDO` placeholder.

</details>

Expand All @@ -116,8 +146,9 @@ ongoing position or commitment, use `"fin": null`: the page shows "Present" and
**Logos**: optional `logo` field, a path inside `public/` (e.g. `"logos/acme.png"`) or a URL.
Square images work best (400 × 400). Without a logo, initials are shown.

**Projects**: a `src/content/projets/<name>.md` file creates a tile on the home page **and**
a `/projets/<name>/` "How I built it" page.
**Projects**: a `src/content/projets/<name>.md` file creates a tile on the `/projets/` page
(and on the home page if it is among the first `projetsALaUne`) **and** a `/projets/<name>/`
overview page. Keep it to the big picture; the details go in notes.

```yaml
---
Expand All @@ -133,11 +164,60 @@ ordre: 1 # optional: forces the order (lowest first)
couleur: "#E8962E" # optional: badge color
---

## The need
…
## Quelle était la situation au départ ?
## Qu'est-ce que ça fait ?
## Comment je l'ai construit, et pourquoi ?
## Comment ça tourne sans moi ?
## Qu'est-ce que j'en ai tiré ?
```

The body is plain Markdown; `##` headings build the page's table of contents.
The body is plain Markdown, and each `##` heading starts its own tile on the page. Every
overview follows the same five questions, in this order:

1. **Quelle était la situation au départ ?** The context, the problem, for whom, the
constraints (budget, data, privacy…).
2. **Qu'est-ce que ça fait ?** What the product does, ideally its three key features.
3. **Comment je l'ai construit, et pourquoi ?** Architecture and two or three decisions,
with the options that were set aside (`###` subheadings).
4. **Comment ça tourne sans moi ?** Automation, tests, deployment, monitoring, cost.
5. **Qu'est-ce que j'en ai tiré ?** What worked, what I would do differently, what's next.
For a project still in progress: **Où j'en suis ?**

Skip a question rather than leave it empty. Keep a page to a two- or three-minute read:
details belong in a note. Put a non-breaking space before `?`.

</details>

<details>
<summary><strong>Notes (billets)</strong></summary>

One folder per note in `src/content/articles/`; the folder name is the address
(`/articles/<name>/`, `/en/articles/<name>/`). Images go in the folder, next to the text, and
are referenced as `![Description](./chart.png)`.

`fr.md` holds the full header and the French text:

```yaml
---
titre: Forecasting powder…
resume: One or two sentences.
date: 2026-10-21 # publication date (YYYY-MM-DD)
miseAJour: 2026-11-02 # optional: last significant update
projet: bluebird # optional: file name of the related project
sujets: [Data, Météo] # topics: in the RSS feed only, not shown for now
brouillon: true # optional: shown with `npm run dev`, never published
---

Text…
```

`en.md` (optional) holds only the translation: `titre`, `resume`, optionally `sujets`, and
the English text. Without it, the English page shows the French text with a notice. An
unknown project, an unknown field in `en.md` or an `en.md` without `fr.md` fails the build.

The notes section only appears when `affichage → billets` is `true` in `site.json` and at
least one note is published. Drafts count as published with `npm run dev` only: set
`affichage.local.billets` to `true` to preview them.

</details>

Expand All @@ -153,20 +233,31 @@ logos, links…) comes from the French file with the same name.
order; plain text lists (skills) are replaced as a whole.
- A missing field stays in French; an unknown field or `id` fails the build.
- Project pages are written in French only; the surrounding interface is translated.
- Notes are translated with an `en.md` next to `fr.md` (see above).

</details>

## "Made by ore_link" badge

`/madeby/` is an unlisted page (no link leads to it, `noindex`) that hands out the
badge to put on other sites: a preview of every variant (themes `auto`, `clair`, `sombre`,
`accent`, with or without the Buy Me a Coffee button), the matching HTML code, and two copy
buttons: the HTML (no JavaScript needed on the target site) or a standalone SVG image (for
READMEs and anywhere HTML is not allowed). The badge is defined once in `src/lib/badge.ts`;
the coffee logo is the official one, `public/logos/buymeacoffee.svg`.

## Architecture

```
src/
├── data/ # content and settings (JSON); data/en/: translations
├── content/projets/ # one Markdown file per project
├── content/articles/ # one folder per note (fr.md, en.md, images)
├── content.config.ts # data schemas
├── lib/ # settings per language, translations, dates, links, decor
├── layouts/Base.astro # <head>, pre-render scripts, header and footer
├── vues/ # home and project page, shared by both languages
├── pages/ # routes: /, /en/, /projets/…, /en/projets/…, 404
├── vues/ # pages shared by both languages: profile, projects, notes
├── pages/ # routes: /, /projets/…, /articles/… (and /en/…), 404
├── components/
│ ├── sections/ # one home page section per file
│ └── habillages/ # decor for the seasonal and holiday skins
Expand Down
3 changes: 3 additions & 0 deletions astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-check
import { defineConfig } from 'astro/config';
import { tuilesParQuestion } from './src/lib/tuiles.ts';

// Déploiement GitHub Pages (repo orelink.github.io → page « utilisateur », base '/').
// Pour un repo « projet » (ex. mon-portfolio), passer base à '/mon-portfolio'.
Expand All @@ -10,5 +11,7 @@ export default defineConfig({
markdown: {
// Coloration des blocs de code par variables CSS : elle suit les jetons du thème.
shikiConfig: { theme: 'css-variables' },
// Fiches projet : une tuile par question (src/lib/tuiles.ts).
rehypePlugins: [tuilesParQuestion],
},
});
Loading
Loading