diff --git a/.gitignore b/.gitignore
index c310d0c..e689032 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,11 @@
-site/\n.obsidian/\n__pycache__/
+site/
+__pycache__/
+
+# Assets binaires — générés par scripts/build-assets.py
+# Hébergés sur https://static.alpinux.org/logo/
+docs/assets/*.png
+docs/assets/*.ico
+docs/assets/images/
+
+# Obsidian (local uniquement)
+.obsidian/
diff --git a/docs/assets/alpinux-logo.svg b/docs/assets/alpinux-logo.svg
new file mode 100644
index 0000000..8c1fd3d
--- /dev/null
+++ b/docs/assets/alpinux-logo.svg
@@ -0,0 +1,52 @@
+
diff --git a/home/index.html b/home/index.html
new file mode 100644
index 0000000..0e04d47
--- /dev/null
+++ b/home/index.html
@@ -0,0 +1,297 @@
+
+
+
+
+
+
+
+ Alpinux — Le LUG de Savoie
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Prochains événements
+
+
+
1er & 3e jeudis
+
Réunion mensuelle
+
📍 Dynamo Chambéry — 18h00
+
+
+
Voir le calendrier
+
Install Party & ateliers
+
+
+
+
+ Nos services
+
+
+
+
+
+
+
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 2e8603c..a97be46 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -23,7 +23,7 @@ theme:
- search.suggest
- content.code.copy
- content.code.select
- logo: assets/alpinux-logo.png
+ logo: assets/alpinux-logo.svg
font:
text: Roboto
code: Roboto Mono
@@ -48,6 +48,9 @@ markdown_extensions:
- toc:
permalink: true
- footnotes
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
extra:
social:
diff --git a/overrides/main.html b/overrides/main.html
index ca0e9a4..e1aaa88 100644
--- a/overrides/main.html
+++ b/overrides/main.html
@@ -5,3 +5,10 @@
Alpinux
{% endblock %}
+
+{% block extrahead %}
+
+
+
+
+{% endblock %}
diff --git a/overrides/partials/logo.html b/overrides/partials/logo.html
new file mode 100644
index 0000000..98a1aa8
--- /dev/null
+++ b/overrides/partials/logo.html
@@ -0,0 +1,3 @@
+
diff --git a/scripts/build-assets.py b/scripts/build-assets.py
new file mode 100644
index 0000000..f34e973
--- /dev/null
+++ b/scripts/build-assets.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+"""
+Génère les assets binaires (logo PNG + favicons) depuis la source SVG.
+Dépendances : Pillow, chromium
+
+Usage :
+ python3 scripts/build-assets.py [--out /chemin/de/sortie]
+
+Les fichiers générés sont ensuite uploadés sur static.alpinux.org/logo/
+"""
+
+import argparse
+import subprocess
+import tempfile
+import os
+from pathlib import Path
+
+from PIL import Image, ImageDraw, ImageFont
+
+REPO = Path(__file__).resolve().parent.parent
+SVG = REPO / "docs/assets/alpinux-logo.svg"
+FONT_R = "/usr/share/fonts/truetype/msttcorefonts/arial.ttf"
+FONT_B = "/usr/share/fonts/truetype/msttcorefonts/arialbd.ttf"
+COLOR = (15, 78, 143) # #0f4e8f
+
+
+def render_shapes(svg_path: Path, width: int, height: int) -> Image.Image:
+ """Render SVG shapes via chromium headless (no text)."""
+ html = f"""
+
+
+
+
+"""
+ with tempfile.NamedTemporaryFile(suffix=".html", mode="w", delete=False) as f:
+ f.write(html)
+ tmp_html = f.name
+ out_png = tmp_html.replace(".html", ".png")
+ subprocess.run([
+ "chromium", "--headless", "--disable-gpu", "--no-sandbox",
+ f"--screenshot={out_png}", f"--window-size={width},{height}",
+ f"file://{tmp_html}"
+ ], capture_output=True)
+ img = Image.open(out_png).convert("RGBA")
+ os.unlink(tmp_html)
+ os.unlink(out_png)
+ return img
+
+
+def add_text(canvas: Image.Image) -> Image.Image:
+ """Composite Alpinux text with correct weights onto the canvas."""
+ draw = ImageDraw.Draw(canvas)
+ size = 30
+ f_reg = ImageFont.truetype(FONT_R, size)
+ f_bld = ImageFont.truetype(FONT_B, size)
+ parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
+
+ widths = []
+ for char, bold in parts:
+ f = f_bld if bold else f_reg
+ bb = f.getbbox(char)
+ widths.append(bb[2] - bb[0])
+
+ total_w = sum(widths)
+ x = (canvas.width - total_w) // 2
+ y = 164 + (36 - size) // 2 + 1
+
+ for (char, bold), w in zip(parts, widths):
+ f = f_bld if bold else f_reg
+ bb = f.getbbox(char)
+ draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
+ x += w
+
+ return canvas
+
+
+def build_logo(out_dir: Path):
+ """Build 200×200 logo PNG."""
+ shapes = render_shapes(SVG, 200, 164)
+ canvas = Image.new("RGBA", (200, 200), (255, 255, 255, 255))
+ canvas.paste(shapes, (0, 0))
+ canvas = add_text(canvas)
+ path = out_dir / "alpinux-logo.png"
+ canvas.convert("RGB").save(path)
+ print(f" {path} (200×200)")
+
+ # 512px high-res version
+ shapes512 = render_shapes(SVG, 512, 421)
+ canvas512 = Image.new("RGBA", (512, 512), (255, 255, 255, 255))
+ canvas512.paste(shapes512, (0, 0))
+ # Scale text proportionally
+ draw = ImageDraw.Draw(canvas512)
+ size = 77
+ f_reg = ImageFont.truetype(FONT_R, size)
+ f_bld = ImageFont.truetype(FONT_B, size)
+ parts = [("A", False), ("l", True), ("p", False), ("inux", True)]
+ widths = [f_bld.getbbox(c)[2]-f_bld.getbbox(c)[0] if b else f_reg.getbbox(c)[2]-f_reg.getbbox(c)[0] for c,b in parts]
+ total_w = sum(widths)
+ x = (512 - total_w) // 2
+ y = 421 + (91 - size) // 2 + 2
+ for (char, bold), w in zip(parts, widths):
+ f = f_bld if bold else f_reg
+ bb = f.getbbox(char)
+ draw.text((x - bb[0], y - bb[1]), char, font=f, fill=COLOR)
+ x += w
+ path512 = out_dir / "alpinux-logo-512.png"
+ canvas512.convert("RGB").save(path512)
+ print(f" {path512} (512×512)")
+
+
+def build_favicons(out_dir: Path):
+ """Build favicon PNG set + .ico from the icon portion of the SVG."""
+ icon_src = render_shapes(SVG, 200, 164).crop((0, 0, 164, 164))
+
+ sizes = {
+ "favicon-16.png": 16,
+ "favicon-32.png": 32,
+ "favicon.png": 48,
+ "favicon-96.png": 96,
+ "favicon-192.png": 192,
+ }
+ imgs = {}
+ for name, sz in sizes.items():
+ img = icon_src.resize((sz, sz), Image.LANCZOS)
+ bg = Image.new("RGBA", (sz, sz), (255, 255, 255, 255))
+ bg.paste(img, (0, 0))
+ p = out_dir / name
+ bg.save(p)
+ imgs[sz] = bg
+ print(f" {p} ({sz}×{sz})")
+
+ ico = out_dir / "favicon.ico"
+ imgs[16].save(ico, format="ICO", sizes=[(16,16),(32,32),(48,48)],
+ append_images=[imgs[32], imgs[48]])
+ print(f" {ico} (multi-size: 16+32+48)")
+
+
+def main():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--out", default="/tmp/alpinux-static-assets",
+ help="Répertoire de sortie (défaut: /tmp/alpinux-static-assets)")
+ args = parser.parse_args()
+
+ out_dir = Path(args.out)
+ out_dir.mkdir(parents=True, exist_ok=True)
+
+ print("Génération des assets Alpinux...")
+ build_logo(out_dir)
+ build_favicons(out_dir)
+ print(f"\nFichiers dans : {out_dir}")
+ print("À uploader sur : static.alpinux.org/logo/")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/static.alpinux.org.vhost.conf b/scripts/static.alpinux.org.vhost.conf
new file mode 100644
index 0000000..9f76113
--- /dev/null
+++ b/scripts/static.alpinux.org.vhost.conf
@@ -0,0 +1,37 @@
+# Apache vhost pour static.alpinux.org
+# À créer via ISPConfig : Sites > Ajouter un site web
+# Domaine : static.alpinux.org | DocumentRoot : /var/www/clients/clientX/webY/web
+# Activer SSL Let's Encrypt dans ISPConfig
+#
+# Ou, si créé manuellement, coller ce fichier dans /etc/apache2/sites-enabled/
+
+
+ ServerName static.alpinux.org
+ Redirect permanent / https://static.alpinux.org/
+
+
+
+ ServerName static.alpinux.org
+ DocumentRoot /var/www/clients/client1/web-static/web
+
+ # En-têtes CORS — permet au wiki et à la page d'accueil de charger les assets
+ Header always set Access-Control-Allow-Origin "*"
+ Header always set Cache-Control "public, max-age=31536000, immutable"
+
+ # Pas d'exécution PHP
+ php_admin_flag engine Off
+
+
+ Options -Indexes +FollowSymLinks
+ AllowOverride None
+ Require all granted
+
+
+ # Logs
+ ErrorLog /var/log/apache2/static.alpinux.org-error.log
+ CustomLog /var/log/apache2/static.alpinux.org-access.log combined
+
+ SSLEngine on
+ SSLCertificateFile /etc/letsencrypt/live/static.alpinux.org/fullchain.pem
+ SSLCertificateKeyFile /etc/letsencrypt/live/static.alpinux.org/privkey.pem
+