diff --git a/Makefile b/Makefile index 7746b09..3883141 100644 --- a/Makefile +++ b/Makefile @@ -115,9 +115,17 @@ deploy: site ## Sert l'application sur le réseau local (Ctrl+C pour arrêter) deploy-web: deploy ## Comme make deploy (ancien nom, étape 0b) +# Fichiers de docs/identite/logo/ publiés dans icons/ (manifeste, favicon, +# barre haute). +SITE_ICONS := icone.svg icone-192.png icone-512.png icone-maskable-192.png icone-maskable-512.png \ + apple-touch-icon-180.png favicon.svg favicon-32.png \ + logo-horizontal-couleur.svg logo-horizontal-sombre.svg symbole-couleur.svg symbole-sombre.svg + # Le site tel qu'il sera publié, reconstruit entièrement à chaque appel : -# - web/ à la racine : la page de l'application, son style, le manifeste, les -# icônes, le Web Worker du moteur, et les pages de test sous moteur/ ; +# - web/ à la racine : la page de l'application, son style, sa police, le +# manifeste, le Web Worker du moteur, et les pages de test sous moteur/ ; +# - sous icons/, le logo et les icônes de la charte, copiés depuis +# docs/identite/logo/ (une seule source, ADR 0010) ; # - l'application compilée (build/web/src/) sous app/ ; # - le moteur sous wasm/ ; # - les copies du glossaire et des scénarios sous docs/, lues par l'aide de @@ -128,6 +136,8 @@ site: engine-wasm web ## Assemble le site publié dans build/site/ rm -rf $(BUILD_DIR)/site mkdir -p $(BUILD_DIR)/site/wasm $(BUILD_DIR)/site/docs cp -r web/. $(BUILD_DIR)/site/ + mkdir -p $(BUILD_DIR)/site/icons + cp $(addprefix docs/identite/logo/,$(SITE_ICONS)) $(BUILD_DIR)/site/icons/ cp -r $(BUILD_DIR)/web/src $(BUILD_DIR)/site/app cp $(BUILD_DIR)/wasm/engine.mjs $(BUILD_DIR)/wasm/engine.wasm $(BUILD_DIR)/wasm/engine.data \ $(BUILD_DIR)/wasm/positions.json $(BUILD_DIR)/site/wasm/ diff --git a/scripts/make-icons.py b/scripts/make-icons.py deleted file mode 100644 index bd42c57..0000000 --- a/scripts/make-icons.py +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/env python3 -# Produit les icônes PNG de l'application (web/icons/icon-192.png et -# icon-512.png) à partir des formes de web/icons/icon.svg : un fond de bois -# et deux pions. Icônes provisoires, en attendant l'identité définitive. -# -# Python seul, sans bibliothèque : chaque pixel est échantillonné 4 × 4 fois -# pour adoucir les bords. À relancer seulement si les formes changent, dans -# la chaîne C (Python 3) : -# docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/src" -w /src \ -# ustagammon/build-c:2 python3 scripts/make-icons.py -# -# Copyright (C) 2026 les auteurs d'Ustagammon. -# Licence : GPLv3 ou ultérieure (voir LICENSE). - -import struct -import zlib - -BACKGROUND = (0x6B, 0x44, 0x26) -# Pions dessinés dans l'ordre : (centre x, centre y, rayon, remplissage, -# bord), en unités d'une icône de 512 pixels ; bord de 10 unités. -CHECKERS = [ - (300, 300, 120, (0x2B, 0x22, 0x1C), (0x0D, 0x0A, 0x08)), - (212, 212, 120, (0xFB, 0xF6, 0xEA), (0x7D, 0x6A, 0x52)), -] -EDGE = 10 -SAMPLES = 4 - - -def color_at(x, y): - """Couleur d'un point de l'icône, en unités de 512.""" - color = BACKGROUND - for cx, cy, r, fill, edge in CHECKERS: - d2 = (x - cx) ** 2 + (y - cy) ** 2 - if d2 <= (r + EDGE / 2) ** 2: - color = fill if d2 <= (r - EDGE / 2) ** 2 else edge - return color - - -def render(size): - scale = 512 / size - rows = [] - for py in range(size): - row = bytearray([0]) # filtre PNG « aucun » - for px in range(size): - total = [0, 0, 0] - for sy in range(SAMPLES): - for sx in range(SAMPLES): - x = (px + (sx + 0.5) / SAMPLES) * scale - y = (py + (sy + 0.5) / SAMPLES) * scale - for i, c in enumerate(color_at(x, y)): - total[i] += c - row += bytes(round(t / SAMPLES**2) for t in total) - rows.append(bytes(row)) - return b"".join(rows) - - -def png(size, pixels): - def chunk(kind, data): - body = kind + data - return struct.pack(">I", len(data)) + body + struct.pack(">I", zlib.crc32(body)) - - header = struct.pack(">IIBBBBB", size, size, 8, 2, 0, 0, 0) # RVB, 8 bits - return ( - b"\x89PNG\r\n\x1a\n" - + chunk(b"IHDR", header) - + chunk(b"IDAT", zlib.compress(pixels, 9)) - + chunk(b"IEND", b"") - ) - - -for size in (192, 512): - with open(f"web/icons/icon-{size}.png", "wb") as f: - f.write(png(size, render(size))) diff --git a/tests/test_site.sh b/tests/test_site.sh index e394931..ff3b846 100755 --- a/tests/test_site.sh +++ b/tests/test_site.sh @@ -43,8 +43,28 @@ check "app/main.js" "text/javascript" check "app/ui/app.js" "text/javascript" check "app/domain/game/game.js" "text/javascript" check "manifest.webmanifest" "application/manifest\+json" -check "icons/icon-192.png" "image/png" -check "icons/icon-512.png" "image/png" +# Charte graphique (ADR 0010) : icônes et logo copiés de docs/identite/logo/, +# police Alegreya Sans et sa licence. +check "icons/favicon.svg" "image/svg\+xml" +check "icons/favicon-32.png" "image/png" +check "icons/apple-touch-icon-180.png" "image/png" +check "icons/logo-horizontal-couleur.svg" "image/svg\+xml" +check "icons/logo-horizontal-sombre.svg" "image/svg\+xml" +check "icons/symbole-couleur.svg" "image/svg\+xml" +check "icons/symbole-sombre.svg" "image/svg\+xml" +cmp -s <(curl -s "$base/icons/icone-512.png") docs/identite/logo/icone-512.png \ + || report "/icons/icone-512.png diffère de docs/identite/logo/" +for weight in Regular Italic Medium Bold ExtraBold; do + check "fonts/AlegreyaSans-$weight.woff2" "font/woff2" +done +check "fonts/OFL.txt" "text/plain" +# Chaque fichier désigné par la page (icônes, police, style) est servi. +page="$(curl -s "$base/")" +while read -r ref; do + curl -sf -o /dev/null "$base/$ref" || report "index.html : $ref introuvable" +done < <(grep -oE '(href|src|srcset)="[^"]*"' <<<"$page" | sed -E 's/^[a-z]+="(.*)"$/\1/') +grep -qF "url(\"fonts/AlegreyaSans-Regular.woff2\")" <(curl -s "$base/app.css") \ + || report "app.css : police Alegreya Sans non déclarée" check "sw.js" "text/javascript" check "engine-worker.mjs" "text/javascript" check "wasm/engine.mjs" "text/javascript" @@ -65,6 +85,8 @@ cmp -s <(curl -s "$base/docs/glossaire/videau.md") docs/glossaire/videau.md \ # Le manifeste désigne des icônes servies, et l'application à la racine. manifest="$(curl -s "$base/manifest.webmanifest")" grep -q '"start_url": "./"' <<<"$manifest" || report "manifeste : start_url absent" +grep -q '"theme_color": "#4A2E1F"' <<<"$manifest" || report "manifeste : theme_color autre que le noyer" +grep -q '"purpose": "maskable"' <<<"$manifest" || report "manifeste : pas d'icône maskable" while read -r icon; do curl -sf -o /dev/null "$base/$icon" || report "manifeste : icône $icon introuvable" done < <(grep -o '"src": "[^"]*"' <<<"$manifest" | sed 's/"src": "\(.*\)"/\1/') @@ -73,7 +95,7 @@ done < <(grep -o '"src": "[^"]*"' <<<"$manifest" | sed 's/"src": "\(.*\)"/\1/') sw="$(curl -s "$base/sw.js")" grep -qE '^const VERSION = "[0-9a-f]{16}";' <<<"$sw" || report "sw.js : version absente" for file in ./ index.html app/main.js wasm/engine.data wasm/engine.wasm moteur/ \ - docs/glossaire/videau.md version.json; do + docs/glossaire/videau.md version.json fonts/AlegreyaSans-Bold.woff2 icons/favicon.svg; do grep -qF " \"$file\"," <<<"$sw" || report "sw.js : $file absent de la liste à mettre en cache" done listed="$(grep -c '^ "' <<<"$sw")" diff --git a/web/icons/icon-192.png b/web/icons/icon-192.png deleted file mode 100644 index 82f3f1b..0000000 Binary files a/web/icons/icon-192.png and /dev/null differ diff --git a/web/icons/icon-512.png b/web/icons/icon-512.png deleted file mode 100644 index febd9a4..0000000 Binary files a/web/icons/icon-512.png and /dev/null differ diff --git a/web/icons/icon.svg b/web/icons/icon.svg deleted file mode 100644 index d4902fc..0000000 --- a/web/icons/icon.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - diff --git a/web/manifest.webmanifest b/web/manifest.webmanifest index 18eb96d..95e0ad3 100644 --- a/web/manifest.webmanifest +++ b/web/manifest.webmanifest @@ -7,12 +7,13 @@ "scope": "./", "display": "standalone", "orientation": "any", - "background_color": "#f7f2e8", - "theme_color": "#6b4426", + "background_color": "#F5EFE5", + "theme_color": "#4A2E1F", "icons": [ - { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, - { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" }, - { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }, - { "src": "icons/icon.svg", "sizes": "any", "type": "image/svg+xml" } + { "src": "icons/icone-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" }, + { "src": "icons/icone-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" }, + { "src": "icons/icone-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "maskable" }, + { "src": "icons/icone-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "maskable" }, + { "src": "icons/icone.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" } ] }