feat(session-1): socle SvelteKit et encyclopédie /zerbaj/ prérendue

SvelteKit 2 + Svelte 5 runes + TS strict, adapter-static en prérendu
intégral, trailingSlash always, paths.relative false.

Mesures de la porte de sortie
- 56 pages HTML prérendues (13 fiches × 3 langues + pages de service)
- 59 Ko de JS gzip (budget : 170) · build total 1,4 Mo (budget : 2 Mo)
- 0 chargement tiers, vérifié par grep du build
- fiche lisible sans JS : 1694 caractères de texte rendu
- npm run check : 248 fichiers, 0 erreur, 0 warning
- 0 erreur console au chargement
Lighthouse non exécuté : porte encore ouverte, à vérifier avant la S6.

Charte OKI
Tokens partagés, thème sombre par défaut, Archivo et Inter self-hébergées
(124 Ko de woff2), flag-bar à segments francs, bouton à balayage or,
gate prefers-reduced-motion unique. CSP stricte en .htaccess et _headers,
404 statique autonome, PWA avec registerSW à chemin absolu (playbook 2.9).

L'interface kréyòl n'est pas écrite par l'agent
Le catalogue gcf ne contient que les termes venant des documents du
porteur — 10 chaînes sur 132. Le reste vaut null et se replie sur le
français ; check:i18n produit la liste des 122 phrases à traduire, par
écran. <html lang> annonce la langue réellement écrite : fr tant que la
couverture reste sous 60 %, avec les fragments kréyòl marqués un par un.
Des tournures kréyòl que j'avais inventées ont été retirées.

Corrections en cours de build
- vite-plugin-pwa/sveltekit n'existe pas sans @vite-pwa/sveltekit :
  VitePWA en direct, comme le prescrivait déjà le playbook.
- @sveltejs/vite-plugin-svelte v7 n'exporte plus sveltekit : import
  depuis @sveltejs/kit/vite, vérifié sur les exports réels.
- Le sélecteur de langue fabriquait /fr/offline/, page inexistante.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
OKI
2026-07-27 11:44:21 -04:00
co-authored by Claude Opus 5
parent 8cba5adfa5
commit ccad7177b3
58 changed files with 9946 additions and 3 deletions
@@ -0,0 +1,94 @@
<script lang="ts">
import { page } from '$app/state';
import Seo from '$lib/components/Seo.svelte';
import { chemin, t } from '$lib/i18n';
import { entreesJouables, FRUITS } from '$lib/data';
const { data } = $props();
const locale = $derived(data.locale);
const jouables = entreesJouables().length;
/**
* `livre` bascule à vrai quand la route du mode existe réellement.
* Session 1 livre la coquille ; les modes arrivent en session 2 (Svelte pur),
* puis en session 4 pour l'arcade. Annoncer un mode sans le livrer produirait
* un lien mort — et le prerender strict fait échouer le build, ce qui est voulu.
*/
const modes = $derived([
{ id: 'rekonet', cle: 'jeu.rekonet', desc: 'jeu.rekonet.desc', route: 'rekonet', besoinPhotos: true, livre: false },
{ id: 'kache', cle: 'jeu.kache', desc: 'jeu.kache.desc', route: 'kache', besoinPhotos: false, livre: false },
{ id: 'sezon', cle: 'jeu.sezon', desc: 'jeu.sezon.desc', route: 'sezon', besoinPhotos: false, livre: false },
{ id: 'ranmase', cle: 'jeu.ranmase', desc: 'jeu.ranmase.desc', route: 'ranmase', besoinPhotos: false, livre: false },
{ id: 'patouche', cle: 'jeu.patouche', desc: 'jeu.patouche.desc', route: 'patouche', besoinPhotos: false, livre: false }
] as const);
</script>
<Seo {locale} pathname={page.url.pathname} titre={t(locale, 'jeu.titre')} />
<div class="container section">
<h1>{t(locale, 'jeu.titre')}</h1>
<p class="muted">{t(locale, 'jeu.choisir')}</p>
<ul class="grid modes">
{#each modes as mode (mode.id)}
{@const bloque = mode.besoinPhotos && jouables < 4}
<li>
{#if mode.livre && !bloque}
<a class="card" class:card--sur={mode.id === 'patouche'} href={chemin(locale, 'je', mode.route)}>
<h2 lang="gcf">{t(locale, mode.cle)}</h2>
<p>{t(locale, mode.desc)}</p>
</a>
{:else}
<div class="card card--bloque">
<h2 lang="gcf">{t(locale, mode.cle)}</h2>
<p>{t(locale, mode.desc)}</p>
{#if bloque}
<p class="tag tag--danger">{t(locale, 'jeu.indisponible.titre')}</p>
<p class="muted petit">{t(locale, 'jeu.indisponible.texte')}</p>
<p>
<a href={chemin(locale, 'kontribiye')}>{t(locale, 'manques.aider')}</a>
</p>
{:else}
<p class="tag">Bientôt</p>
{/if}
</div>
{/if}
</li>
{/each}
</ul>
<p class="etat muted">
{jouables} espèce{jouables > 1 ? 's' : ''} sur {FRUITS.length} dispose dune photographie validée.
</p>
</div>
<style>
.modes {
list-style: none;
margin: var(--space-3) 0;
padding: 0;
}
.modes h2 {
font-size: 1.3rem;
margin-bottom: 0.35rem;
}
.card--bloque {
border-left-color: var(--line);
opacity: 0.85;
}
.card--sur {
border-left-color: var(--rouge-oki);
}
.petit {
font-size: 0.85rem;
}
.etat {
font-size: 0.85rem;
}
</style>