From d228d7038775e87d82d554bd0add44a13b6eb512 Mon Sep 17 00:00:00 2001 From: Pierre MARTIN Date: Tue, 3 Nov 2026 20:50:33 +0100 Subject: [PATCH] =?UTF-8?q?Mes=20parties=20:=20la=20feuille=20se=20ferme?= =?UTF-8?q?=20=C3=A0=20la=20reprise=20;=20plan=201b=20:=20r=C3=A9sultat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- app/src/ui/games-view.ts | 5 ++- docs/plans/2026-11-03-parties-enregistrees.md | 42 ++++++++++++++++++- tests/browser/play.mjs | 10 ++++- 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/app/src/ui/games-view.ts b/app/src/ui/games-view.ts index 6b83f4f..20891c8 100644 --- a/app/src/ui/games-view.ts +++ b/app/src/ui/games-view.ts @@ -110,7 +110,10 @@ export function createGamesView(options: GamesViewOptions): GamesView { actions.append( button( GAMES_TEXTS.resume, - run(() => options.resume(game.id)), + run(async () => { + await options.resume(game.id); + hide(); + }), "", "resume", ), diff --git a/docs/plans/2026-11-03-parties-enregistrees.md b/docs/plans/2026-11-03-parties-enregistrees.md index 8afba18..e6dd7b0 100644 --- a/docs/plans/2026-11-03-parties-enregistrees.md +++ b/docs/plans/2026-11-03-parties-enregistrees.md @@ -54,4 +54,44 @@ rechargement). Commits fréquents ; pas de push ni de MR. ## Résultat -À remplir à la clôture. +Réalisé le 2026-11-03 sur la branche `parties-enregistrees`, fusionnée avec +main après la révision UX (MR !35). + +- **Stockage** (`app/src/storage/`) : `GameLibrary` (list, load, save, + remove, current, setCurrent) sur IndexedDB (base « ustagammon » v1 : + magasins games, journals, settings ; résumé et journal dans la même + transaction), en repli sur localStorage puis sur la mémoire. Opérations + exécutées dans l'ordre des appels. La partie de l'étape 0 + (`ustagammon.current-game`) est migrée puis effacée. `GameSession` tient + la partie à l'écran : nouvelle partie, « Reprendre l'ancienne » (relue + par son identifiant, la nouvelle est supprimée), reprise d'une partie ou + d'une branche. +- **Domaine** : `branches(journal)` et `switchToBranch` (branches.ts), + `Game.branches` et `Game.switchBranch`, résumé d'une partie + (saved-game.ts), export SGF de GNU Backgammon avec les branches en + variantes, la branche courante d'abord (sgf.ts). +- **Interface** : « Mes parties », ligne du menu en feuille, ouvre une + feuille (games-view.ts) : reprendre une partie ou une branche (changement + de partie à l'écran, sans recharger), exporter en SGF, supprimer en deux + touchers (jamais la partie affichée). +- **Vérification SGF** : `tests/test_web.sh` joue 6 parties sur le vrai + moteur (complètes, branches, videau, positions particulières), les + exporte (9 fichiers, un par branche) et les fait relire par le gnubg de + référence (`tests/sgf/check.py`) : après chaque coup, sa position égale + celle du journal, et le résultat concorde. Une altération volontaire d'un + coup ou de la position de départ est détectée. +- **Scénarios** : `docs/scenarios/mes-parties.feature` (reprendre une partie + parmi plusieurs, rejouer d'ici sans perdre l'original, exporter) ; le + scénario « Reprendre l'ancienne » de `regler-l-application.feature` passe + par la session. Glossaire : partie-enregistree.md, sgf.md, branche.md + (code: Branch). +- **Tests** : `make lint`, `make test` (10/10, 20 scénarios) et + `make test-browser` (Chromium et Firefox : parties listées, SGF + téléchargé, première partie reprise sans rechargement, « Reprendre + l'ancienne » sans partie en trop) passent. + +Limites connues : gnubg 1.08 ne lit que la première variante d'un SGF ; +l'enregistrement n'est pas attendu (une fermeture dans les millisecondes +qui suivent une action peut la perdre) ; pas de score de match avant 1c ; +le schéma IndexedDB devient un contrat durable (amendement de l'ADR 0007 à +envisager). L'adresse `#/parties` viendra avec 0e.4. diff --git a/tests/browser/play.mjs b/tests/browser/play.mjs index b2d73ad..f0973ea 100644 --- a/tests/browser/play.mjs +++ b/tests/browser/play.mjs @@ -1188,7 +1188,15 @@ step("mes parties"); await shot("4b-mes-parties"); const loads = await page.evaluate(() => performance.getEntriesByType("navigation").length); await oldest.locator('[data-action="resume"]').click(); - await page.waitForSelector("#games-veil", { state: "hidden" }); + await page.waitForSelector("#games-veil", { state: "hidden", timeout: 20_000 }).catch(async () => { + const state = await page.evaluate(() => ({ + message: document.querySelector("#games .games-message")?.textContent, + busy: document.body.classList.contains("busy"), + info: document.getElementById("info")?.textContent, + panel: !document.getElementById("panel-veil").hidden, + })); + throw new Error(`reprise bloquée : ${JSON.stringify(state)}`); + }); await page.waitForSelector("#panel"); const resumed = await savedJournal(); check(JSON.stringify(resumed) === JSON.stringify(finished), "la première partie n'a pas été reprise");