Repository navigation
docs: la documentation Typst finie — sections 6 à 8, 16 captures réelles, hébergement permanent (#217) - #219
Merged
Merged
Conversation
Premier lot de `Docs/documentation.typ` : le thème, la page de garde, la table des matières, et les sections 1 à 5 rédigées. Les sections 6 à 8 sont posées en squelette — leurs titres fixent le plan et résolvent les renvois croisés des sections précédentes, leur corps arrive au lot suivant. Le thème importe sa palette de `design/composants.typ` et n'écrit aucun hex : une retouche de `frontend/src/style.css` remonte jusqu'au PDF. Corps inversé pour l'impression, `corail` réservé aux filets et aux étiquettes. Contient 4 des 11 diagrammes (trois axes d'une Race, machine d'état, pipeline d'assets, chaîne d'intégration — plus le handshake d'identité en séquence), les 18 placeholders de capture avec leur légende, et les 12 PNG de `design/out/` embarqués. Trois écarts avec l'énoncé de l'issue, tranchés en faveur du code : le protocole porte 29 messages et non 25 (20 ClientEvent + 9 ServerEvent), une Race a cinq états terminaux et non quatre (`race-state.ts`), et les décomptes de lignes de l'issue étaient surestimés. La compilation exige `--root .` : le document importe hors de `Docs/`. `logo()` de `design/composants.typ` ciblait la seule famille « JetBrainsMono NF » et retombait sur la sérif par défaut là où elle n'est pas installée — le wordmark cessait alors d'être en chasse fixe. Passé en pile de repli, celle que `--font-mono` nomme déjà dans le CSS. `Docs/onboarding.*` n'est pas encore supprimé et le README pas encore modifié : c'est le lot final.
…t 1 (#217) Second et dernier lot. Les sections 6 à 8 sont rédigées, les six diagrammes qui manquaient sont dessinés, `Docs/onboarding.*` est supprimé et le README renvoie au nouveau document. 37 pages, 11 diagrammes, 18 placeholders de capture. Le thème choisissait « Segoe UI » et « Consolas » — des polices Windows, absentes de la machine de compilation. Typst ne le signale pas : il retombe en silence sur sa sérif par défaut, et tout le document sortait dans le style Typst d'origine, celui-là même que ce travail remplace. Passé à `Fira Sans` et `JetBrainsMono NF`, choisies parmi celles que `typst fonts` liste réellement. Le wordmark de `design/composants.typ` avait le même défaut de repli ; l'export des PNG reste identique au bit près. Le PDF committé n'était pas une fonction de sa source. La page de garde le datait avec `datetime.today()`, et Typst horodate le fichier lui-même : deux compilations d'une source inchangée donnaient deux binaires différents. Date écrite, et compilation sous `SOURCE_DATE_EPOCH` — la commande de l'en-tête le porte, et le résultat est vérifiable par `cmp`. Les tableaux passent au supplément « Tableau » : sous « Figure », leur compteur séparé produisait deux « Figure 3 » dans le même document. Les diagrammes de séquence sont composés en corps réduit. chronos dimensionne ses colonnes sur la largeur mesurée des commentaires, et la soumission d'un Run débordait des deux marges — sans erreur de compilation. Trois corrections de fond : une parenthèse orpheline, un « à la section la section 6.3 », et « sept Réglages de salon » alors que `RoomSetting` en compte six génériques plus quatre propres aux Modes de jeu. Le décompte des cinq états terminaux est désormais réconcilié avec `CONTEXT.md`, qui n'en nomme que quatre — le glossaire exclut « fini », le code ne l'exclut pas, et le document dit maintenant laquelle des deux lectures il illustre. Le README annonçait 28 et 13 tests ; il y en a 382 et 159.
…ux, schéma réseau, blocs de code (#217) Le document était juste : il était uniforme. Une seule grille pour toute donnée, des boîtes fletcher pour tout schéma, et dix-huit cadres en attente à la place des captures. Cette passe lui donne un registre. TROIS FAMILLES DE TABLEAUX, choisies sur ce que le lecteur a à FAIRE de la donnée, pas sur le goût du moment. `chiffres()` — sans filet ni colonne, pour douze nombres qui ne se comparent pas entre eux. `cartes()` — un catalogue qu'on parcourt à la recherche d'un seul élément, d'où les 29 messages du protocole. `zebre()` — un contrat qu'on lit ligne par ligne, d'où les endpoints, les migrations et les ADR. SCHÉMA RÉSEAU en cetz, la bibliothèque qui dessine déjà la voiture du jeu. Fletcher pose des boîtes reliées par des flèches, et huit joueurs dans huit boîtes ressemblent à huit modules. Ici les clients sont des silhouettes, l'hôte porte une couronne — c'est une différence de DROITS, elle doit se voir — et les trois portées de diffusion sont dessinées plutôt que décrites. DEUX BLOCS DE CODE, fond `nuit` et filet corail, avec le vrai code du dépôt : le dispatch du fil côté Rust, la réception et la transition pure côté TypeScript. La coloration vient de `nuit.tmTheme`, écrit pour ce document — le thème par défaut de Typst est fait pour du papier blanc et rendait du bleu sombre sur du bleu sombre. Le piège que ce bloc a révélé mérite d'être noté : Typst ne colore QUE les jetons couverts par une règle du thème. Tout le reste — parenthèses, accolades, identifiants — garde la couleur de texte ambiante, ici `encre`, c'est-à-dire le fond. La moitié du code était peinte en invisible, et la compilation restait verte. D'où le `fill: texte` explicite dans `code-reseau()`. ARBORESCENCE en vrai arbre : les glyphes de branche sont calculés depuis la profondeur, pas écrits à la main — `└─` plutôt que `├─` dépend de savoir si l'entrée est la dernière de sa fratrie, et un arbre écrit à la main se trompe là-dessus dès la première insertion au milieu. CAPTURES : les dix-huit `[PLACEHOLDER-*.png]` deviennent de vraies intégrations `image()`. Les fichiers pas encore capturés portent un gabarit aux bonnes dimensions, produit par `Docs/captures/build.sh` et marqué comme tel en toutes lettres. La mise en page est donc déjà celle de la version finale, et prendre une capture consiste à écraser un PNG — aucune ligne de Typst ne bouge ce jour-là. ENCARTS SOMBRES pour ce qui appartient à l'écran et non au papier : la pile du projet en ouverture, l'invariant de la frontière de confiance, et un bandeau de titre `nuit` sur chaque section. Le bandeau plutôt que des pages alternées sombres : le document est imprimable, et une page sur deux en aplat coûterait une cartouche pour rendre les captures illisibles en photocopie. Deux corrections attrapées au passage. Les blocs de code recevaient le panneau clair du document par-dessus le leur : la règle passe de `it => block(…)` à `show … : set block(…)`, seule forme qu'une règle interne puisse écraser. Et les grilles de cartes étaient posées `height: 100%` dans une grille à rangées automatiques — ce 100 % se résolvait sur la hauteur restante de la PAGE, et chaque carte occupait une colonne entière. 43 pages, 12 diagrammes, 18 captures intégrées. PDF toujours reproductible au bit près sous SOURCE_DATE_EPOCH.
Une variable décide, tout le reste en dérive — même patron qu'une poignée de
variables CSS : rien dans le corps du document ne connaît sa propre couleur.
./Docs/build.sh
→ Docs/documentation.pdf thème sombre, pour l'écran
→ Docs/documentation-clair.pdf thème clair, pour l'impression
Une seule source, et c'est le point. Deux `.typ` tenus en parallèle divergent au
premier paragraphe corrigé d'un seul côté, et une documentation fausse à moitié
est une documentation fausse.
Six noms portent toute la bascule : `papier`, `encre`, `gris`, `filet`,
`papier-doux`, `accent-encre`. Deux points ne sont pas de simples inversions.
`corail` pur ne passe pas 3:1 sur blanc et doit être assombri là ; sur `nuit` il
passe largement, et c'est la version pure qu'on veut — inverser mécaniquement
aurait donné un accent délavé. Et les blocs « écran » (encarts, code, bandeaux
de titre) ont pour fond `nuit` en thème clair : en thème sombre `nuit` EST le
fond de page, ils seraient devenus invisibles, d'où `surface` qui remonte d'un
cran sur `panel`.
Les deux PDF restent reproductibles au bit près : `build.sh` exporte
SOURCE_DATE_EPOCH, sans quoi les fichiers committés changent à chaque
compilation même quand pas une ligne n'a bougé.
Le README renvoie aux deux et dit lequel sert à quoi.
Les gabarits sont remplacés par de vraies captures d'écran, prises en lançant l'app en local (backend en mode dev + Vite) et en jouant : trois onglets, trois joueurs — alice, bob, carol. Ce qui est capturé pour de bon : le Menu · la barre de configuration solo dépliée · un Practice en cours, avec une faute réelle en rouge · l'écran de résultats et son graphe SVG (26 wpm, 98,5 %, recomputé par le serveur) · les cent leçons · les trois portes d'entrée · le salon en trois colonnes à trois présents · une course à trois voitures à des positions différentes · Floor is lava · le podium avec ses Gap · Play of the Game · les Paramètres · le bandeau d'erreurs. Deux captures ont demandé plus qu'un clic. Le Play of the Game n'existe que si deux joueurs finissent à moins de deux secondes l'un de l'autre. Un pilotage séquentiel ne peut pas : chaque aller-retour coûte une trentaine de secondes, et les arrivées se retrouvaient à +34 s. Il a fallu programmer les trois arrivées sur un même horodatage absolu, chaque page tenant sa dernière frappe en attente. Résultat : +0,7 s, photo-finish, duel rejoué sur horloge partagée. Le bandeau d'erreurs ne s'allume que sur une erreur NON RATTRAPÉE. Le 502 du proxy de citations n'en est pas une — l'application le traite proprement et affiche « Impossible de charger la citation », ce qui est le bon comportement. La capture vient donc d'une vraie panne : backend arrêté, `CreateRoom`, et la promesse d'ouverture du WebSocket qui part en rejet — « ⚠ WS: ouverture échouée ». 2560 × 1440, densité 2 : les captures restent nettes à l'impression. Cinq gardent leur gabarit. `spam` demande une remise à plat du salon. `ci-jobs` et `release` vivent sur GitHub. `rich-presence` et `activity-salon-vocal` exigent un vrai client Discord avec l'Activity publiée — aucune automatisation locale ne peut les produire, et une image fabriquée serait un mensonge sur le seul point du document qui prouve que le câblage fonctionne.
… par les faits (#217) `floor-is-lava` et `spam` remplacent leurs gabarits, et `ci-jobs` / `release` viennent de GitHub. Restent deux gabarits, ceux qui exigent un vrai client Discord. Floor is lava a demandé cinq joueurs et non trois. Avec trois, la course s'arrête au deuxième battement — vingt secondes de fenêtre, moins que le temps d'aller-retour du pilotage. À cinq (alice, bob, carol, dave, erin), les battements s'étalent sur quatre-vingts secondes. La capture montre ce que le mode fait vraiment : dave brûlé à 40 s, erin à 20 s, tous deux grisés, et trois joueurs encore en course à des positions différentes. Le texte cible n'est pas lisible pendant une course sous ce mode : le badge « 🔥 20 » s'insère en tête de la zone de frappe et masque le premier mot. D'où un `RoomState` intercepté à la source, sur le WebSocket, avant que le rendu ne s'en mêle. Deux légendes ne disaient pas la vérité, et c'est la capture qui l'a montré. « Les trois travaux verts sur une poussée » : aucune exécution du dépôt n'en montre trois. Le travail `release` (#117) est bien déclaré et dépend bien des deux autres, mais il n'a jamais tourné — les trois versions publiées sont antérieures à son ajout. La poussée sur la branche principale affiche `frontend`, `backend`, et un champ « Artifacts » vide. « Une version publiée avec son archive » : `v0.0.3` n'a aucune archive, pour la même raison. Les notes, elles, viennent bien du fichier de changements — cette moitié-là était juste. Les deux légendes disent maintenant ce que l'image montre, et « Limites connues » gagne l'entrée qui manquait : la publication automatisée est en place, sa garde fonctionne, et il lui manque la première version qui la franchisse.
… captures qui restent (#217) `bandeau-erreur.png` reprenait l'écran entier de l'entrée multijoueur, déjà montré deux figures plus haut : quatre-vingt-quinze pour cent de l'image répétait la précédente, et la bande rouge dont parle la légende occupait deux lignes tout en bas. Recadré sur la bande. C'est d'ailleurs la proportion que le gabarit d'origine réservait — 1280 × 300 — avant que la vraie capture ne soit prise en plein écran. `Docs/captures/A-PRENDRE.md` : la marche à suivre pour `rich-presence` et `activity-salon-vocal`, les deux seules qui exigent un vrai client Discord. Mise en place, ce qui doit être lisible dans chacune, le cadrage attendu, et le piège des App Testers qui n'ont pas accepté leur invitation. Aucune image supprimée : les dix-huit captures et les douze visuels de `design/out/` sont tous référencés par le document, vérifié par recoupement des appels `capture()` et `visuel()` avec le contenu des dossiers.
`Docs/DEPLOIEMENT.md`. Le `DEPLOY.md` généré dans l'archive de release décrit le binaire ; celui-ci décrit l'hébergement — le passage de « ça tourne pendant que je teste » à « les joueurs peuvent jouer n'importe quand ». Trois bloqueurs, aucun dans le code : le quick tunnel change d'adresse à chaque redémarrage, rien ne relance le serveur tout seul, et une Activity non publiée reste invisible à qui n'est pas App Tester. Et une correction : ce ne sont pas trois services mais DEUX. En production le binaire Rust sert lui-même le build de Vite — le `npm run dev` du démarrage rapide n'existe qu'en développement. Le reste : tunnel nommé Cloudflare pour une URL stable, unités systemd pour le backend et le tunnel, secrets dans un EnvironmentFile en 0600, base SQLite en chemin absolu et sauvegardée par `.backup` et non par `cp`. Deux avertissements qui viennent du code lui-même. `VITE_DISCORD_CLIENT_ID` est figée à la compilation par Vite : sans elle le bundle part en mode dev, tous les joueurs deviennent `dev-player-1`, et ça ne se voit qu'une fois déployé. Et les Rooms vivent en mémoire : un `systemctl restart` éjecte toute course en cours, d'où `Restart=on-failure` et pas de redémarrage périodique.
`backend/.env` est fusionné dans le `.env` de la racine, et `.env.example` le suit. Un seul fichier d'environnement pour tout le dépôt. Ça marche sans une ligne de code : `dotenvy::dotenv()` cherche `.env` dans le dossier courant PUIS remonte les parents (`find.rs`, `directory.parent()`). `cd backend && cargo run` le trouve donc à la racine. Vérifié en démarrant le serveur avec le seul `.env` racine — aucun «⚠️ MODE DEV » au démarrage, donc les secrets Discord sont bien lus. Une exception, notée dans les deux fichiers : `VITE_DISCORD_CLIENT_ID` reste dans `frontend/.env`. Vite ne remonte pas les dossiers parents, et cette variable est de toute façon figée dans le bundle à la compilation. Le `.env` racine portait un `API_NINJA_KEY` vide, que rien ne lit — le backend attend `APININJAS_API_KEY`. Il disparaît dans la fusion. Le commentaire de `main.rs`, `CONTEXT.md` et le README disaient tous « backend/.env » ; ils disent maintenant où le fichier vit réellement.
… lieu d'être (#217) `backend/.env.example` était déjà parti au commit précédent, fusionné dans celui de la racine. Restait `frontend/.env.example`, et il était encore nécessaire : Vite ne remonte pas les dossiers parents, il fallait donc un second fichier avec `VITE_DISCORD_CLIENT_ID` recopiée — deux copies d'une même valeur, qui finissent toujours par diverger. `envDir: ".."` dans `vite.config.ts` lève la contrainte. Vite lit désormais le `.env` de la racine, la variable y est documentée, et les deux fichiers du frontend disparaissent. La question qui se pose évidemment : est-ce que pointer Vite sur un fichier qui contient `DISCORD_CLIENT_SECRET` fait fuiter le secret dans le bundle ? Non — `envPrefix` vaut `VITE_` par défaut, et rien d'autre n'est injecté. Vérifié plutôt que supposé, en construisant le bundle et en l'inspectant : VITE_DISCORD_CLIENT_ID présent ← attendu, il est public DISCORD_CLIENT_SECRET absent APININJAS_API_KEY absent DATABASE_URL absent 382 tests toujours au vert. Deux références mortes corrigées au passage : `Docs/agents/git-workflow.md` et le commentaire du travail `release` de la CI pointaient encore vers `frontend/.env.example`.
Le premier des trois bloqueurs du déploiement permanent est levé. Domaine acheté chez Cloudflare Registrar, tunnel nommé créé, CNAME posé sur l'apex, et `~/.cloudflared/config.yml` écrit. `Docs/DEPLOIEMENT.md` porte maintenant les vraies valeurs plutôt qu'un exemple : l'identifiant du tunnel, le hostname, et la séquence exacte qui a été jouée. L'étape que le message d'erreur de `cloudflared tunnel create` ne nomme pas est consignée : c'est `tunnel login` qui produit le `cert.pem` sans lequel `create` refuse de partir — et `login` exige un domaine déjà Active dans le compte. Vérifié de bout en bout, tunnel et backend lancés à la main : GET https://typperacer.uk/api/health → 200 GET https://typperacer.uk/ → 200, le jeu est servi bundle servi → porte VITE_DISCORD_CLIENT_ID Ce dernier point vaut d'être noté : il prouve que la consolidation du `.env` et le `envDir` de Vite tiennent jusqu'au bundle réellement servi en public. La section `cloudflared.service` dit maintenant que `service install` lit `/etc/cloudflared/` et non le dossier personnel — les deux fichiers sont à recopier et le chemin `credentials-file` à corriger.
Le document décrivait un service sous /srv/typeracer avec un compte dédié — une cible, pas ce qui tourne. Il décrit maintenant l'unité réelle : le binaire de release lancé depuis le dépôt, sous le compte anthonyb. Pas d'EnvironmentFile : le binaire lit le .env de la racine lui-même, puisque WorkingDirectory est dans backend/ et que dotenvy remonte les parents. Une source de vérité au lieu de deux, et aucun risque que systemd et dotenvy interprètent la même ligne différemment. La contrepartie est écrite noir sur blanc : le service sert ce qu'il y a dans le dépôt, donc un git checkout change ce qui tourne. Le chemin vers /srv et un compte de service est décrit pour le jour où ça deviendra gênant.
…tion-typst # Conflicts: # Docs/documentation.pdf # Docs/documentation.typ # design/composants.typ
Closed
14 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Suite de #218, dont seul le premier commit avait été poussé.
Docs/documentation.typ, refonte visuelle (tableaux, schéma réseau, blocs de code), sortie en deux thèmes depuis une source unique.cloudflarednommé surtypperacer.uk, unité systemd du backend..envet un seul.env.example, à la racine — celui du frontend n'a plus lieu d'être.Closes #217