diff --git a/GUIDE-USAGE.md b/GUIDE-USAGE.md index 15adf72..d22333d 100644 --- a/GUIDE-USAGE.md +++ b/GUIDE-USAGE.md @@ -360,6 +360,24 @@ en filigrane ; une planche tirée d’une proposition l’est toujours. Chaque p imprimée porte en pied le nom et la date de l’événement, le tour, l’instant où le placement a pris sa forme, et la version du logiciel. +L’aperçu imprime ce que le plan montre. Une proposition ouverte depuis la +section « Propositions » s’imprime en brouillon, même quand un autre placement +est retenu : + + + +*Le filigrane BROUILLON couvre chaque page. Sous le plan, le repère de la +page, l’échelle graphique et la mention « disposition relative » ; en pied, le +tour et l’instant où cette proposition a été générée.* + +Revenu au placement retenu, l’aperçu sort sans filigrane : + + + +*Le même cadrage, sur le placement retenu : le filigrane a disparu, et le pied +porte l’instant de ce placement. C’est cette page qui part à l’imprimante pour +le montage de la salle.* + Une fois le plan imprimé, bloquez-le : choisissez l’état « Bloqué » en haut de l’événement. Plus rien ne le modifie, jusqu’à « Débloquer ». diff --git a/README.md b/README.md index 426a750..0bac77a 100644 --- a/README.md +++ b/README.md @@ -86,11 +86,12 @@ sur les captures. | `--couleur-cadre-ecriture` | marque | 3 | 4,78 | 02-import-apercu | 9,10 | 02-import-apercu | | `--couleur-cadre-lecture` | marque | 3 | 5,29 | 01b-passer-en-ecriture | 7,34 | 01b-passer-en-ecriture | | `--couleur-chaise-pleine` | marque | 3 | 15,38 | 07a-cibles-sieges | 14,94 | 07a-cibles-sieges | -| `--couleur-chaise-trait` | marque | 3 | 7,58 | 07-plan-tables | 8,89 | 07-plan-tables | -| `--couleur-conflit-appartenance` | marque | 3 | 8,92 | 19-ajustement | 6,81 | 19-ajustement | -| `--couleur-contour-fort` | marque | 3 | 12,54 | 19-ajustement | 11,62 | 19-ajustement | +| `--couleur-chaise-trait` | marque | 3 | 7,58 | 07-plan-tables | 8,13 | 22-plan-brouillon | +| `--couleur-conflit-appartenance` | marque | 3 | 8,92 | 20-mode-lecture | 6,81 | 20-mode-lecture | +| `--couleur-contour-fort` | marque | 3 | 12,54 | 20-mode-lecture | 11,62 | 20-mode-lecture | | `--couleur-echelle` | marque | 3 | 15,10 | 07-plan-tables | 14,62 | 07-plan-tables | | `--couleur-fantome` | marque | 3 | 4,34 | 19-ajustement | 4,10 | 19-ajustement | +| `--couleur-filigrane` | marque | 3 | 5,10 | 22-plan-brouillon | 5,10 | 22-plan-brouillon | | `--couleur-hachure` | marque | 3 | 16,48 | 08-animateurs-designes | 15,21 | 08-animateurs-designes | | `--couleur-marque` | marque | 3 | 3,63 | 07a-cibles-sieges | 3,50 | 07a-cibles-sieges | | `--couleur-page-texte` | texte | 4,5 | 15,10 | 01-premier-ecran | 11,39 | 19-ajustement | @@ -103,6 +104,7 @@ sur les captures. | `--couleur-selection` | marque | 3 | 4,66 | 08-animateurs-designes | 4,18 | 08-animateurs-designes | | `--couleur-table-trait` | marque | 3 | 7,58 | 07-plan-tables | 5,59 | 07-plan-tables | | `rgb(0, 0, 0)` | texte | 4,5 | 18,26 | 09-refus-sur-place | — | — | +| `rgb(28, 33, 40)` | texte | 4,5 | — | — | 16,18 | 22-plan-brouillon | diff --git a/doc/captures/19-ajustement.png b/doc/captures/19-ajustement.png index 25dacc1..d643ac3 100644 Binary files a/doc/captures/19-ajustement.png and b/doc/captures/19-ajustement.png differ diff --git a/doc/captures/22-plan-brouillon.png b/doc/captures/22-plan-brouillon.png new file mode 100644 index 0000000..4377303 Binary files /dev/null and b/doc/captures/22-plan-brouillon.png differ diff --git a/doc/captures/23-plan-retenu.png b/doc/captures/23-plan-retenu.png new file mode 100644 index 0000000..2070606 Binary files /dev/null and b/doc/captures/23-plan-retenu.png differ diff --git a/doc/captures/manifeste.json b/doc/captures/manifeste.json index df1aa61..4238ac8 100644 --- a/doc/captures/manifeste.json +++ b/doc/captures/manifeste.json @@ -14,9 +14,11 @@ {"cadre":{"hauteur":700,"largeur":1366},"empreinte":"e58549be9ac69cdfd1605e945ae0ae16fcdff26d031b180a1cc8cda286aaa615","etape":"6","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"08-animateurs-designes","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"443072cde6446f90d3d543e4ee751ace5cbaa9e496e4b52eb6a0b6a3e8fa0f0e"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.66,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.18,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Réservations","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","9. (libre)","10. (libre)","1. (libre)","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Gaspard Marbrelle","Ysaline Sablonnet","Anselme Givrecœur","Côme Fenouillet","Apolline Coudraie","Basile Lanterneau","Brunœil","Faustine Ormelin","Edgar Ardoisier","Bertille Cressonnier","Table 2, siège 1","1. (libre)","— titre","animation","non pourvu","Choisissez d'abord une personne : dans la réserve, dans la liste des pressentis, ou par « Déplacer » dans son menu.","Portée","Tour 1","Tous les tours","Libellé du titre","Assigner","Réserver","Désigner","Retirer le titre"],"theme":"clair","versionReelle":"2026.10.05.01"}, {"cadre":{"hauteur":700,"largeur":1366},"empreinte":"2c9afe336a74750c7349bcab924d0495733d1cca77f43967cf6e361e3b439339","etape":"7","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"09-refus-sur-place","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"868fac3a5dde24aa5a84890a0385e946dbb0de4af8dd42953532ab801c133add"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-refus","ratio":8.76,"role":"texte","seuil":4.5},{"jeton":"--couleur-selection","ratio":4.66,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"rgb(0, 0, 0)","ratio":18.26,"role":"texte","seuil":4.5}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5},{"jeton":"--couleur-papier","ratio":5.33,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-refus","ratio":9.24,"role":"texte","seuil":4.5},{"jeton":"--couleur-selection","ratio":4.18,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Réservations","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","9. (libre)","10. (libre)","1. (libre)","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Table 1, siège 1","Le siège 1 est réservé à une autre personne à tous les tours : déplacer sa réservation libère la place.","Choisir une place","Fermer","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Gaspard Marbrelle","Ysaline Sablonnet","Anselme Givrecœur","Côme Fenouillet","Apolline Coudraie","Basile Lanterneau","Brunœil","Faustine Ormelin","Edgar Ardoisier","Bertille Cressonnier","Table 1, siège 1","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","Personne choisie :","Gaspard Marbrelle","Assigner","Retirer la réservation","Retirer le titre","Oublier le choix","Menu de","Basile Lanterneau","Déplacer","Échanger","Retirer du tour","Déplacer sa réservation","Exclure","Mettre en évidence","Ouvrir la fiche"],"theme":"clair","versionReelle":"2026.10.05.01"}, {"cadre":{"hauteur":700,"largeur":1366},"empreinte":"78463eac80ed918ee56db0c81dd2b1e83a41ecf0d05b774580ac37c439171bfe","etape":"8","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"10-diagnostic","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"74b0aca5bfb2eb45593f8c3578da9fbd98e88a5b5e15ef8b48487f551975a61f"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":5.29,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-page-texte-secondaire","ratio":7.05,"role":"texte","seuil":4.5}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":7.34,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5},{"jeton":"--couleur-page-texte-secondaire","ratio":7.98,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Lecture","Liste des événements","Clair","Sombre","À propos","Modifier","Lecture","Défaire","Grande démonstration 1","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Diagnostic","écart d'itinéraire maximal : plancher 1, au moins 24 mobiles finissent sous leur plafond a priori","aucune relance ne le réduit : il tient aux tables pleines dont les capacités ou les ancrages diffèrent","plafond a priori 24 rencontres pour 4 ancrés","seules les capacités et les réservations le changent : aucune relance ne le dépasse","plafond a priori 28 rencontres pour 29 ancrés et 227 mobiles","seules les capacités et les réservations le changent : aucune relance ne le dépasse","99 retours imposés par 33 ancrages","les réservations les imposent : ils ne comptent pas parmi les retours que choisit le moteur"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"609cee6886e7a3e30dbf520fc2fc7e6432d889032bc2dbde76f90ad04fcfacf6","etape":"9","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"11-propositions","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"7e0d73294910ac8c344f871d1f6e217352a70978b557f80b6516c13049278af5"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Propositions","Ordre du classement, chaque critère au plus faible d'abord :","écart au plafond a priori",", puis","excédent de collisions",", puis","collisions cumulées",", puis","rencontres répétées",", puis","redondance d'appartenance",", puis","l'identifiant croissant",".","Propositions classées","Rang","Proposition","Personnes distinctes rencontrées, minimum : tous","Personnes distinctes rencontrées, minimum : mobiles","Personnes distinctes rencontrées, minimum : ancrés","Collisions cumulées (paire et tour)","Paires distinctes de même appartenance","Excédent de collisions (retrouvailles au-delà de la première)","Rencontres répétées choisies (paires)","Rencontres d'une même paire, au plus","Retours choisis à une table déjà occupée","Appartenances rencontrées A(p), minimum","Redondance d'appartenance, somme Σ r(p)","Taux de diversité d(p), minimum","Plus grand manque (personnes)","Personnes à qui il en manque","Écart d'itinéraire maximal (personnes)","Écart au plafond a priori maximal (personnes)","Certificat","Imposé par les réservations","Commandes","Placement retenu","3","Tel que la proposition n° 3.","11","11","11","26","15","11","30","3","11","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","1","3","retenue","11","11","11","26","15","11","30","3","11","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","2","1","11","11","11","27","15","12","19","3","16","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","3","2","11","11","11","33","15","18","54","3","16","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","4","5","10","10","10","20","15","5","22","3","12","3","83","0,27","1","1","1","1","atteignabilité inconnue","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","5","4","10","10","11","31","14","17","46","3","17","3","82","0,30","1","4","0","1","atteignabilité inconnue","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","Le numéro de la proposition, donné à sa génération ; un numéro effacé ne revient jamais. Il sert de dernier départage : deux propositions égales sur tous les critères se rangent par numéro croissant.","Cible de la somme de redondance","La liste des événements montre, pour le placement retenu, la différence sans signe entre cette cible et la somme Σ r(p) de la colonne « Redondance d'appartenance ». Un nombre entier, 0 ou plus.","Effacer les propositions"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"b0493a03b5a4aa0ffcaa1f2c87c3fa02a066f9795ac839925461f0d65daeecbd","etape":"11","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"19-ajustement","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"de987d6b42e8a6f51bc7675e9e0f2c7ec2e0902e8eb7783d37f8281e077d63ec"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":8.92,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":12.54,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-fantome","ratio":4.34,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":6.81,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-fantome","ratio":4.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":11.39,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-pastille-texte","ratio":14.51,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Placement retenu","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","9","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Gaspard Marbrelle","3.","Ysaline Sablonnet","4.","Anselme Givrecœur","5.","Côme Fenouillet","6.","Apolline Coudraie","7.","Brunœil","8.","Faustine Ormelin","9.","Edgar Ardoisier","10.","Bertille Cressonnier","1.","Basile Lanterneau","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Gaspard Marbrelle","déplace dans le placement retenu","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Personne en attente à ce tour."],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"4ccadc12b652215c6611b87fd70c9579e0cf4cfd48b5a9b98bba9f80f471cfe2","etape":"11","etat":"20666d70d9b2503335efc1f2cece5453a199ab77f4864835baf4bccf5aff17d5","facteurEchelle":1,"fichier":"20-mode-lecture","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"0d66820beae96b1724c177ed6a2b949173051c8dec9c564692461aac01877c1b"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":5.29,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":8.92,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":12.54,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.66,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":7.34,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":6.81,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-pastille-texte","ratio":14.51,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.18,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Lecture","Liste des événements","Clair","Sombre","À propos","Modifier","Lecture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Placement retenu","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","7","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Ysaline Sablonnet","3.","Anselme Givrecœur","4.","Côme Fenouillet","5.","Apolline Coudraie","6.","Brunœil","7.","Faustine Ormelin","8.","Edgar Ardoisier","9.","Bertille Cressonnier","10. (libre)","1.","Gaspard Marbrelle","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3.","Basile Lanterneau","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Table 2, siège 4","L'événement est ouvert en lecture : « Modifier » permet de le changer.","Modifier","Fermer","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Personne en attente à ce tour.","Table 2, siège 4","4. (libre)","Choisissez d'abord une personne : dans la réserve, dans la liste des pressentis, ou par « Déplacer » dans son menu.","Portée","Tour 1","Tous les tours","Libellé du titre","Assigner","Réserver","Donner un titre","Désigner"],"theme":"clair","versionReelle":"2026.10.05.01"} +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"a2f41502a104dd60c5f492c964003a8c44419f6fb9453f8e50c1c934e48c2f6e","etape":"9","etat":"57d0ccc945de5920a86bb8d5ded2dbc38a74bdac28324bb130db867677c0d624","facteurEchelle":1,"fichier":"11-propositions","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"7e0d73294910ac8c344f871d1f6e217352a70978b557f80b6516c13049278af5"},"instant":"2026-10-06T19:02:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Propositions","Ordre du classement, chaque critère au plus faible d'abord :","écart au plafond a priori",", puis","excédent de collisions",", puis","collisions cumulées",", puis","rencontres répétées",", puis","redondance d'appartenance",", puis","l'identifiant croissant",".","Propositions classées","Rang","Proposition","Personnes distinctes rencontrées, minimum : tous","Personnes distinctes rencontrées, minimum : mobiles","Personnes distinctes rencontrées, minimum : ancrés","Collisions cumulées (paire et tour)","Paires distinctes de même appartenance","Excédent de collisions (retrouvailles au-delà de la première)","Rencontres répétées choisies (paires)","Rencontres d'une même paire, au plus","Retours choisis à une table déjà occupée","Appartenances rencontrées A(p), minimum","Redondance d'appartenance, somme Σ r(p)","Taux de diversité d(p), minimum","Plus grand manque (personnes)","Personnes à qui il en manque","Écart d'itinéraire maximal (personnes)","Écart au plafond a priori maximal (personnes)","Certificat","Imposé par les réservations","Commandes","Placement retenu","3","Tel que la proposition n° 3.","11","11","11","26","15","11","30","3","11","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","1","3","retenue","11","11","11","26","15","11","30","3","11","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","2","1","11","11","11","27","15","12","19","3","16","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","3","2","11","11","11","33","15","18","54","3","16","3","85","0,27","0","0","0","0","minimum atteint","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","4","5","10","10","10","20","15","5","22","3","12","3","83","0,27","1","1","1","1","atteignabilité inconnue","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","5","4","10","10","11","31","14","17","46","3","17","3","82","0,30","1","4","0","1","atteignabilité inconnue","2 retours imposés par 1 ancrage.","Aucune rencontre répétée imposée.","Voir sur le plan","Retenir","Le numéro de la proposition, donné à sa génération ; un numéro effacé ne revient jamais. Il sert de dernier départage : deux propositions égales sur tous les critères se rangent par numéro croissant.","Cible de la somme de redondance","La liste des événements montre, pour le placement retenu, la différence sans signe entre cette cible et la somme Σ r(p) de la colonne « Redondance d'appartenance ». Un nombre entier, 0 ou plus.","Effacer les propositions"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"f81b312de09cfd21e9799849f73d4401c2374bbfbeece4cd6540e2b74af0d57d","etape":"11","etat":"57d0ccc945de5920a86bb8d5ded2dbc38a74bdac28324bb130db867677c0d624","facteurEchelle":1,"fichier":"19-ajustement","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"064c2f47d01eeba5bbd82816965df9fe1ec9e89bd563cfea9321e0225877dd3e"},"instant":"2026-10-06T19:02:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":4.78,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-fantome","ratio":4.34,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-ecriture","ratio":9.1,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-fantome","ratio":4.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":11.39,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-pastille-texte","ratio":14.51,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Revenir en lecture","Écriture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Placement retenu","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","9","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Gaspard Marbrelle","3.","Ysaline Sablonnet","4.","Anselme Givrecœur","5.","Côme Fenouillet","6.","Apolline Coudraie","7.","Brunœil","8.","Faustine Ormelin","9.","Edgar Ardoisier","10.","Bertille Cressonnier","1.","Basile Lanterneau","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Gaspard Marbrelle","déplace dans le placement retenu","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Personne en attente à ce tour."],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"1e58ff6f61c457c8a2b80606ced5ff9f07f7d5dadb5e8e95e77df40c4ae2ae73","etape":"11","etat":"011f06a5e584c50389bf75e8cfcb9eb6c90e8fb488e80a76227f08c9faf5f3a0","facteurEchelle":1,"fichier":"20-mode-lecture","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"0d66820beae96b1724c177ed6a2b949173051c8dec9c564692461aac01877c1b"},"instant":"2026-10-06T19:02:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":5.29,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":15.38,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":8.92,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":12.54,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-poignee","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.66,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":7.58,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-cadre-lecture","ratio":7.34,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-pleine","ratio":14.94,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":6.81,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":15.21,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.5,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":14.62,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":11.62,"role":"marque","seuil":3},{"jeton":"--couleur-pastille-texte","ratio":14.51,"role":"texte","seuil":4.5},{"jeton":"--couleur-poignee","ratio":11.32,"role":"marque","seuil":3},{"jeton":"--couleur-selection","ratio":4.18,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":5.59,"role":"marque","seuil":3}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Lecture","Liste des événements","Clair","Sombre","À propos","Modifier","Lecture","Défaire","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Badges","Plan","Placement retenu","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","7","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Ysaline Sablonnet","3.","Anselme Givrecœur","4.","Côme Fenouillet","5.","Apolline Coudraie","6.","Brunœil","7.","Faustine Ormelin","8.","Edgar Ardoisier","9.","Bertille Cressonnier","10. (libre)","1.","Gaspard Marbrelle","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3.","Basile Lanterneau","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Table 2, siège 4","L'événement est ouvert en lecture : « Modifier » permet de le changer.","Modifier","Fermer","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","Chœur de l’Anse","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Société des Hérons","4 personnes : au moins 6 collisions cumulées sur les 3 tours, portées par au moins 2 paires distinctes","Cercle des Saules","3 personnes : au moins 3 collisions cumulées sur les 3 tours, portées par au moins 1 paire distincte","au moins 8 appartenances recroisées par personne","plafond a priori 11 rencontres pour 1 ancré et 11 mobiles","2 retours imposés par 1 ancrage","Listes de travail","Titres de place non pourvus","table 2 : titre","animation","non pourvu","Titres pressentis sans réservation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Personne en attente à ce tour.","Table 2, siège 4","4. (libre)","Choisissez d'abord une personne : dans la réserve, dans la liste des pressentis, ou par « Déplacer » dans son menu.","Portée","Tour 1","Tous les tours","Libellé du titre","Assigner","Réserver","Donner un titre","Désigner"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"5e26088ad87296bbfbb8f6b06dd67f165f1fb927624176f50fca320302aa3aa3","etape":"12","etat":"57d0ccc945de5920a86bb8d5ded2dbc38a74bdac28324bb130db867677c0d624","facteurEchelle":1,"fichier":"22-plan-brouillon","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"61ec89a98ba17914c825634660cd43aa6ce4e65e464f9052487a513585a17e0c"},"instant":"2026-10-06T19:02:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-chaise-pleine","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":16.18,"role":"marque","seuil":3},{"jeton":"--couleur-filigrane","ratio":5.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":16.18,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":8.13,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-chaise-pleine","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":16.18,"role":"marque","seuil":3},{"jeton":"--couleur-filigrane","ratio":5.1,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-papier","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"rgb(28, 33, 40)","ratio":16.18,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Aperçu avant impression","Écriture","Plan de salle","Liste d'accueil","Feuilles par table","Tour imprimé","Tour 1","Tour 2","Tour 3","Listes de noms","Pages du plan","Découpage proposé","Tout sur une page","Découper en pages","Papier :","A4","Changer","Imprimer","Fermer l'aperçu","Page 888 sur 888 — colonne WW, rangée 888","au-dessus : page 888","à droite : page 888","au-dessous : page 888","à gauche : page 888","500","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","listes de noms hors d'échelle — le plan à l'échelle du bâtiment s'imprime listes masquées","Soirée d’essai","Date non fixée","Tour 1","Placement du","6 octobre 2026, 19 h 00","Version","construction de documentation","6","1","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Ysaline Sablonnet","3.","Anselme Givrecœur","4.","Côme Fenouillet","5.","Apolline Coudraie","6.","Brunœil","7.","Faustine Ormelin","8.","Edgar Ardoisier","9. (libre)","10. (libre)","1.","Gaspard Marbrelle","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3.","Basile Lanterneau","4.","Bertille Cressonnier","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Page 1 sur 1 — colonne A, rangée 1","2","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","listes de noms hors d'échelle — le plan à l'échelle du bâtiment s'imprime listes masquées","Soirée d’essai","Date non fixée","Tour 1","Placement du","6 octobre 2026, 19 h 00","Version","construction de documentation","BROUILLON"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"c8901face8aa9f7ba6bcaa9d1251bbf340a268b0da6ab5c88ce0a114eb3451bd","etape":"12","etat":"57d0ccc945de5920a86bb8d5ded2dbc38a74bdac28324bb130db867677c0d624","facteurEchelle":1,"fichier":"23-plan-retenu","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"bf2e36213d7d341099d00b0c24e29846b24f316b5a77fe2d45ff82dd2b6c33d9"},"instant":"2026-10-06T19:02:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-bandeau-fond","ratio":14.26,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-chaise-pleine","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":8.92,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":16.18,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-page-texte","ratio":16.18,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":8.13,"role":"marque","seuil":3}],"sombre":[{"jeton":"--couleur-bandeau-fond","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-bandeau-texte","ratio":12.84,"role":"texte","seuil":4.5},{"jeton":"--couleur-chaise-pleine","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-chaise-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"--couleur-conflit-appartenance","ratio":8.92,"role":"marque","seuil":3},{"jeton":"--couleur-contour-fort","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":16.18,"role":"marque","seuil":3},{"jeton":"--couleur-hachure","ratio":16.48,"role":"marque","seuil":3},{"jeton":"--couleur-marque","ratio":3.63,"role":"marque","seuil":3},{"jeton":"--couleur-papier","ratio":13.44,"role":"texte","seuil":4.5},{"jeton":"--couleur-pastille-fond","ratio":13.44,"role":"marque","seuil":3},{"jeton":"--couleur-table-trait","ratio":8.13,"role":"marque","seuil":3},{"jeton":"rgb(28, 33, 40)","ratio":16.18,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Écriture","Liste des événements","Clair","Sombre","À propos","Aperçu avant impression","Écriture","Plan de salle","Liste d'accueil","Feuilles par table","Tour imprimé","Tour 1","Tour 2","Tour 3","Listes de noms","Pages du plan","Découpage proposé","Tout sur une page","Découper en pages","Papier :","A4","Changer","Imprimer","Fermer l'aperçu","Page 888 sur 888 — colonne WW, rangée 888","au-dessus : page 888","à droite : page 888","au-dessous : page 888","à gauche : page 888","500","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","listes de noms hors d'échelle — le plan à l'échelle du bâtiment s'imprime listes masquées","Soirée d’essai","Date non fixée","Tour 1","Placement du","6 octobre 2026, 19 h 02","Version","construction de documentation","9","1. (réservée, tous les tours)","Basile Lanterneau","—","animation","2.","Gaspard Marbrelle","3.","Ysaline Sablonnet","4.","Anselme Givrecœur","5.","Côme Fenouillet","6.","Apolline Coudraie","7.","Brunœil","8.","Faustine Ormelin","9.","Edgar Ardoisier","10.","Bertille Cressonnier","1.","Basile Lanterneau","— titre","animation","non pourvu","2. (réservée, tour 1)","Capucine Pommerel","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","Page 1 sur 1 — colonne A, rangée 1","2","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","listes de noms hors d'échelle — le plan à l'échelle du bâtiment s'imprime listes masquées","Soirée d’essai","Date non fixée","Tour 1","Placement du","6 octobre 2026, 19 h 02","Version","construction de documentation"],"theme":"clair","versionReelle":"2026.10.05.01"} ],"format":1,"pilote":{"cadre":{"hauteur":700,"largeur":1366},"facteurEchelle":1,"mode":"chromium-headless-shell","navigateur":"chromium","versionNavigateur":"153.0.8010.12"},"reportees":[ {"etape":"10","fichier":"12-qualite-profil","raison":"iteration5"}, {"etape":"10","fichier":"13-qualite-collisions","raison":"iteration5"}, diff --git a/scripts/documentation/manifeste.js b/scripts/documentation/manifeste.js index e27d7c7..133e41e 100644 --- a/scripts/documentation/manifeste.js +++ b/scripts/documentation/manifeste.js @@ -80,7 +80,7 @@ export const REPORTEES = Object.freeze([ // Captures déclarées dans CAPTURES et pas encore photographiées : la // publication et le contrôle d'ordre les admettent absentes du manifeste. -export const A_VENIR = Object.freeze(['22-plan-brouillon', '23-plan-retenu', '24-badges-controle', '25-badges-planche']); +export const A_VENIR = Object.freeze(['24-badges-controle', '25-badges-planche']); // --- Forme canonique et empreintes --------------------------------------------- diff --git a/scripts/documentation/manifeste.test.js b/scripts/documentation/manifeste.test.js index 8c2d58c..026cee3 100644 --- a/scripts/documentation/manifeste.test.js +++ b/scripts/documentation/manifeste.test.js @@ -142,8 +142,9 @@ describe('manifeste : la liste déclarée (§ 19.3, Q8 a)', () => { for (const reportee of REPORTEES) assert.ok(Object.isFrozen(reportee), reportee.fichier); }); - test('A_VENIR nomme les quatre captures de l’étape 12, toutes déclarées', () => { - assert.deepEqual(A_VENIR, ['22-plan-brouillon', '23-plan-retenu', '24-badges-controle', '25-badges-planche']); + test('A_VENIR nomme les deux captures de la planche, à l’étape 12, déclarées ; 22 et 23 sont photographiées', () => { + assert.deepEqual(A_VENIR, ['24-badges-controle', '25-badges-planche']); + for (const fichier of A_VENIR) assert.equal(CAPTURES.find((c) => c.fichier === fichier)?.etape, '12', fichier); const declarees = new Set(CAPTURES.map((c) => c.fichier)); for (const fichier of A_VENIR) assert.ok(declarees.has(fichier), fichier); assert.ok(Object.isFrozen(A_VENIR)); @@ -319,9 +320,15 @@ describe('manifeste : l’ordre contre les étapes du § 2.1 (§ 19.3)', () => { assert.deepEqual(verifierOrdre(manifesteDeclare({ sans: A_VENIR })), []); }); - test('A_VENIR vidé, l’étape 12 sans capture est une faute', () => { - const fautes = verifierOrdre(manifesteDeclare({ sans: A_VENIR }), { aVenir: [] }); - assert.deepEqual(fautes, [{ chemin: CHEMIN_MANIFESTE, faute: 'étape 12 : aucune capture ni reportée' }]); + test('A_VENIR vidé, les captures d’A_VENIR absentes : 22 et 23 couvrent l’étape 12', () => { + assert.deepEqual(verifierOrdre(manifesteDeclare({ sans: A_VENIR }), { aVenir: [] }), []); + }); + + test('A_VENIR vidé, sans 22 ni 23 : l’étape 12 sans capture est une faute', () => { + const photographiees = ['22-plan-brouillon', '23-plan-retenu']; + assert.deepEqual(verifierOrdre(manifesteDeclare({ sans: [...photographiees, ...A_VENIR] }), { aVenir: [] }), [ + { chemin: CHEMIN_MANIFESTE, faute: 'étape 12 : aucune capture ni reportée' }, + ]); }); test('une étape sans capture ni reportée est une faute qui la nomme', () => { diff --git a/src/interface/parcours.navigateur.test.js b/src/interface/parcours.navigateur.test.js index 716082e..72861d8 100644 --- a/src/interface/parcours.navigateur.test.js +++ b/src/interface/parcours.navigateur.test.js @@ -14,7 +14,8 @@ // Le parcours est le parcours photographié (§ 19.1, § 19.3) : il suit les // captures du § 19.3 que l'itération photographie — 01, 01b, 02, 02b, 03 à // 07, 07b, 08 à 10, les deux 07a sur la grande démonstration que 10 a -// chargée, 11, 19, 20 —, puis l'ordre de lecture du § 2.1 sur +// chargée, 11, 19, 20, puis 22 et 23, l'aperçu avant impression d'une +// proposition et du retenu —, puis l'ordre de lecture du § 2.1 sur // l'événement retenu, les fenêtres — aucune pendant un geste non // destructeur, une qui nomme ce qui part pour chaque geste qui détruit —, // enfin chaque famille de geste modifiant en lecture puis sur un plan @@ -39,10 +40,12 @@ import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, test } fr import { cdp, commands } from 'vitest/browser'; import { flushSync } from 'svelte'; import { bilanRemplacement, cleNormalisee } from '../application/csv.js'; +import { creerFormat } from '../application/format.js'; import { creerExecuteurDirect } from '../application/generation.js'; import { TABLES_APPLICATION } from '../application/libelles.js'; import { nomAffiche } from '../application/personnes.js'; import { etatPlaces } from '../application/places.js'; +import { estBrouillon } from '../application/sorties.js'; import { creerTraduction } from '../application/traduction.js'; import { versionVisible } from '../application/version.js'; import { apercevoir } from '../csv/apercu.js'; @@ -66,7 +69,7 @@ import { SUFFIXES } from '../stockage/noms.js'; import { planDepuisFichier } from '../stockage/placements.js'; import { VERSION } from '../version.genere.js'; import urlCp1252 from '../../exemples/participants_cas_limites_cp1252.csv?url'; -import { CADRE, capturesPrises, cloreCaptures, modeFourni, photographier } from '../../test/navigateur/captures.js'; +import { CADRE, capturesPrises, cloreCaptures, entiereDansLaFenetre, modeFourni, photographier } from '../../test/navigateur/captures.js'; import { NOM_CAS_LIMITES, attendre, centre, octetsCasLimites } from '../../test/navigateur/ecrans.js'; import { fixerCadre, pointer, touche } from '../../test/navigateur/entrees.js'; import { poserFeuille } from '../../test/navigateur/feuilles.js'; @@ -84,6 +87,10 @@ const NOM_SOIREE = 'Soirée d\u{2019}essai'; const GRAINE = 4242; const ARRET = 300; +// L'écart de l'horloge pilotée entre la génération et la retenue : plus +// d'une minute, la précision d'un horodatage mis en forme. +const ECART_RETENUE_MS = 2 * 60 * 1000; + // Le CSV des cas limites enregistré en windows-1252 (§ 10.3, capture 02b). const NOM_CP1252 = 'participants_cas_limites_cp1252.csv'; @@ -272,9 +279,8 @@ async function cliquerPointeur(element) { await images(2); } -// Presse au point depuis et avance par pas jusqu'au point vers, sans relâcher. -async function presserEtBouger(depuis, vers, pas = 10) { - await pointer('presser', depuis.x, depuis.y); +// Avance le pointeur par pas du point depuis au point vers. +async function bouger(depuis, vers, pas = 10) { for (let i = 1; i <= pas; i += 1) { await pointer('bouger', depuis.x + ((vers.x - depuis.x) * i) / pas, depuis.y + ((vers.y - depuis.y) * i) / pas); await uneImage(); @@ -282,6 +288,12 @@ async function presserEtBouger(depuis, vers, pas = 10) { flushSync(); } +// Presse au point depuis et avance par pas jusqu'au point vers, sans relâcher. +async function presserEtBouger(depuis, vers, pas = 10) { + await pointer('presser', depuis.x, depuis.y); + await bouger(depuis, vers, pas); +} + async function glisserVers(depuis, vers) { await presserEtBouger(depuis, vers); await pointer('relacher', vers.x, vers.y); @@ -438,6 +450,21 @@ function dansLeCadre(element) { return b.width > 0 && x >= cadre.left + 8 && x <= cadre.right - 8 && y >= cadre.top + 8 && y <= cadre.bottom - 8; } +// La marge, en px, que le libellé du fantôme garde au bord droit du plan. +const MARGE_FANTOME_PX = 24; + +// Fait glisser la vue de dx px, par un appui sur le sol du plan : un point +// de la bande haute du cadre où le pointeur n'atteint ni table, ni chaise, +// ni poignée. +async function glisserLaVue(dx) { + const cadre = dans('[data-plan]').getBoundingClientRect(); + const sol = { x: cadre.left + cadre.width / 2, y: cadre.top + 12 }; + const atteint = atteintAuPoint(sol); + expect(dans('[data-plan]').contains(atteint), 'le sol, dans le plan').toBe(true); + expect(atteint.closest('[data-table], [data-siege], [data-poignee]'), 'le sol, hors de toute table').toBeNull(); + await glisserVers(sol, { x: sol.x + dx, y: sol.y }); +} + // La première chaise du tour montré qui répond au prédicat et dont le // dessin est dans le cadre. function chaiseVue(predicat) { @@ -506,6 +533,63 @@ async function refusPres(porteur, code, remede, geste) { return refus; } +// --- L'aperçu avant impression ---------------------------------------------------------- + +// Ce que chaque photographie de l'aperçu affirme de la première feuille : +// le cartouche, l'échelle graphique, le pied qui porte l'horodatage et la +// marque de provenance. +const CONDITIONS_FEUILLE = Object.freeze([ + '[data-impression] [data-feuille] [data-cartouche]', + '[data-impression] [data-feuille] [data-marque="echelle"]', + '[data-impression] [data-feuille] [data-pied]', +]); + +const format = creerFormat(t); + +// Les chemins que les primitives d'écriture ont reçus depuis le rang donné +// de la trace, sans doublon, dans l'ordre du premier appel. +const cheminsEcrits = (rang) => [...new Set(m.trace.slice(rang).map(([, , chemin]) => chemin).filter((chemin) => chemin !== ''))]; + +// Les feuilles du plan imprimé de la sortie {source, proposition, tour}, +// chacune affirmée : son cartouche, son échelle graphique et la mention +// « disposition relative » (§ 7.7) ; son pied — le nom de l'événement, le +// tour, l'horodatage lu sur le placement qui s'imprime, mis en forme comme +// l'application le met, la marque de provenance et jamais la version +// (§ 11.7, § 18.6). Rend les feuilles. +async function feuillesDuPlan(sortie) { + const placement = sortie.source === 'retenu' ? charge().retenu : charge().propositions.find(({ id }) => id === sortie.proposition); + const horodatage = placement?.horodatage ?? null; + expect(horodatage, 'l’horodatage de la sortie').not.toBeNull(); + const feuilles = await attendre('les feuilles du plan', () => { + const lues = tous('[data-impression][data-vue-impression="plan"] [data-feuille]'); + return lues.length > 0 && lues.every((feuille) => feuille.querySelector('svg[role="img"] [data-table]')) ? lues : null; + }); + for (const feuille of feuilles) { + const page = `feuille ${feuille.dataset.feuille}`; + expect(texteDe(feuille.querySelector('[data-cartouche]')).length, page).toBeGreaterThan(0); + expect(feuille.querySelector('[data-marque="echelle"]').getBoundingClientRect().width, page).toBeGreaterThan(0); + expect(texteDe(feuille.querySelector('[data-mention="relative"]')), page).toBe(t('plan.echelle.mention')); + const pied = texteDe(feuille.querySelector('[data-pied]')); + expect(pied, page).toContain(charge().evenement.nom); + expect(pied, page).toContain(t('impression.pied.tour', { tour: sortie.tour })); + expect(pied, page).toContain(format.horodatage(horodatage)); + expect(pied, page).toContain(MARQUE_PROVENANCE); + expect(pied, page).not.toContain(VERSION.affichee); + } + return feuilles; +} + +// Fait défiler la fenêtre jusqu'à centrer la réunion des boîtes des +// éléments, qui doit tenir dans sa hauteur : l'image les montre ensemble. +async function cadrerSur(elements) { + const boites = elements.map((element) => element.getBoundingClientRect()); + const haut = Math.min(...boites.map((b) => b.top)); + const bas = Math.max(...boites.map((b) => b.bottom)); + expect(bas - haut, 'les éléments à montrer tiennent dans la hauteur de la fenêtre').toBeLessThanOrEqual(innerHeight); + window.scrollBy(0, Math.round((haut + bas - innerHeight) / 2)); + await images(2); +} + // --- Le parcours (§ 2.1, § 19.3) ------------------------------------------------------ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE }, () => { @@ -959,7 +1043,9 @@ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE await zoomerJusqua('sieges', 'zoomer'); for (let cran = 0; cran < CRANS_AU_DESSUS; cran += 1) await cliquer('[data-commande="zoomer"]'); await images(2); - const vue = chaiseVue(() => true); + // Une chaise que l'image montre entière : la condition de la capture + // juge la boîte, pas son seul centre. + const vue = chaiseVue((c) => chaise(c.table, c.siege) !== null && entiereDansLaFenetre(chaise(c.table, c.siege))); const disque = chaise(vue.table, vue.siege).querySelector('circle'); const point = await centre(disque); expect(atteintAuPoint(point)?.closest('[data-siege]'), 'l’élément au centre de la chaise').toBe(chaise(vue.table, vue.siege)); @@ -1023,6 +1109,9 @@ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE expect(colonnes).not.toContain('redondance'); for (const th of section.querySelectorAll('th')) expect(texteDe(th)).not.toMatch(/moyenne/i); + // Le retenu se date à l'instant où il est retenu, après la génération : + // le pied de l'aperçu (étape 12) distingue les deux horodatages. + m.horloge.avancer(ECART_RETENUE_MS); r = revision(); memoire.retenue = ordre[0]; const vues = await sousObservation(async () => { @@ -1041,17 +1130,46 @@ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE memoire.avantAjustement = serialiserCharge(charge()); const [t1, t2] = charge().tables.map(({ id }) => id); const depuis = chaiseOccupee(t1); - // La chaise libre de t2 la plus à gauche : le libellé du fantôme, à - // droite du pointeur, reste dans le cadre du plan. + // La chaise libre de t2 la plus à gauche : la plus proche de t1. const libres = chaisesMontrees().filter((c) => c.table === t2 && c.occupant === null && c.reservation === null && c.titre === null); expect(libres.length).toBeGreaterThan(0); const gauche = (c) => chaise(t2, c.siege).getBoundingClientRect().left; const vers = libres.reduce((a, b) => (gauche(b) < gauche(a) ? b : a)); + const glisser = async () => presserEtBouger(await centre(chaise(t1, depuis.siege)), await centre(chaise(t2, vers.siege))); + const libelleEtat = () => dans('[data-fantome] [data-partie="etat"]'); + + // Le libellé du fantôme se pose à droite du pointeur, et le panneau du + // diagnostic borne le plan à droite. Un premier glissement, ramené à sa + // chaise de départ, mesure ce que le libellé déborde du plan ; la vue + // glisse ensuite par le sol, vers la gauche, d'autant et d'une marge, + // pour que l'image le montre entier. + const r = revision(); + await glisser(); + const deborde = (await attendre('le libellé mesuré', libelleEtat)).getBoundingClientRect().right - dans('[data-plan]').getBoundingClientRect().right; + // Le libellé que le bord du plan coupe refuse la capture, avant toute + // image : le parcours ne photographie pas un libellé illisible. Le + // débordement mesuré est la condition de ce refus, et s'affirme : une + // disposition où le libellé tient déjà dans le plan rend l'épreuve + // rouge, au lieu d'en sauter le contrôle. + expect(deborde, 'le libellé mesuré déborde du plan').toBeGreaterThan(0); + await expect(photographier(m, '19-ajustement', { condition: '[data-fantome] [data-partie="etat"]', graine: GRAINE })).rejects.toThrow( + 'l’image la coupe', + ); + const depart = await centre(chaise(t1, depuis.siege)); + await bouger(await centre(chaise(t2, vers.siege)), depart); + await pointer('relacher', depart.x, depart.y); + await images(3); + await attendre('le glissement ramené', () => libelleEtat() === null); + expect(revision()).toBe(r); + expect(m.ui.etat().pose).toBeNull(); + await glisserLaVue(-(deborde + MARGE_FANTOME_PX)); + expect(dansLeCadre(chaise(t1, depuis.siege)), 'la chaise de départ, dans le cadre').toBe(true); + memoire.ajustement = { depuis, vers, revision: revision() }; - await presserEtBouger(await centre(chaise(t1, depuis.siege)), await centre(chaise(t2, vers.siege))); - const etatDonnees = await attendre('le libellé de l’état des données', () => dans('[data-fantome] [data-partie="etat"]')); + await glisser(); + const etatDonnees = await attendre('le libellé de l’état des données', libelleEtat); expect(texteDe(etatDonnees)).toBe(t('plan.fantome.retenu')); - await photographier(m, '19-ajustement', { condition: ['[data-fantome] [data-partie="etat"]'], graine: GRAINE }); + await photographier(m, '19-ajustement', { condition: ['[data-fantome] [data-partie="nom"]', '[data-fantome] [data-partie="etat"]'], graine: GRAINE }); }); test('19 le dépôt : une entrée qui nomme la personne, le retenu changé', async () => { @@ -1063,6 +1181,8 @@ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE expect(libelle).toContain(nomAffiche(charge().participants.find((p) => p.id === depuis.occupant))); expect(serialiserCharge(charge())).not.toBe(memoire.avantAjustement); expect(charge().retenu.proposition).toBe(memoire.retenue); + // La vue revient à l'ajustement que les étapes suivantes attendent. + await cliquer('[data-commande="ajuster"]'); }); // Glisser une personne, une table, tirer une poignée, poser une réservation. @@ -1111,6 +1231,107 @@ describe('le parcours par gestes, depuis un répertoire neuf', { timeout: DUREE await uneEntree(r, 'défaire'); expect(serialiserCharge(charge())).toBe(memoire.avantAjustement); }); + + // Étape 12 (§ 11.7, § 19.3) : l'aperçu avant impression du plan, d'une + // proposition que le retenu ne porte pas, puis du retenu. La paire montre + // la règle du filigrane — présent, puis absent — sur la première page, au + // même cadrage, de deux placements distincts ; le pied porte l'horodatage + // de ce qui s'imprime et la marque de provenance à la place de la version. + test('22 l’aperçu d’une proposition non retenue : le papier demandé une fois, BROUILLON sur chaque feuille, le pied de la proposition', async () => { + expect(charge().evenement.etat).toBe('retenu'); + const proposition = charge().propositions.find(({ id }) => id !== charge().retenu.proposition).id; + const avant = fichiers(); + const rang = m.trace.length; + await ouvrirSection('propositions'); + await cliquer(`[data-proposition="${proposition}"] [data-commande="voir"]`); + await attendre('la proposition montrée au plan', () => m.ui.etat().proposition === proposition && dans('[data-section="plan"]')); + await cliquer('[data-ecran="evenement"] [data-commande="apercu"]'); + // Le premier aperçu de la machine pose la question du papier, avant tout + // document (§ 8.5). + await attendre('la question du papier', () => dans('[data-ecran="apercu"] [data-papier="question"]')); + expect(dans('[data-impression]')).toBeNull(); + await cliquer('[data-papier="question"] [data-cible="A4"] [data-commande="choisirPapier"]'); + await attendre('le document imprimé', () => dans('[data-impression] [data-feuille]') && dans('[data-papier="choisi"]')); + expect(etat().papier).toBe('A4'); + expect(m.ui.etat().apercu).toMatchObject({ vue: 'plan', tour: 1 }); + + const sortie = { source: 'proposition', proposition, tour: 1 }; + expect(estBrouillon(charge(), sortie)).toBe(true); + memoire.horodatageProposition = charge().propositions.find(({ id }) => id === proposition).horodatage; + const feuilles = await feuillesDuPlan(sortie); + for (const feuille of feuilles) { + const filigranes = feuille.querySelectorAll('[data-filigrane]'); + expect(filigranes, `feuille ${feuille.dataset.feuille}`).toHaveLength(1); + expect(texteDe(filigranes[0])).toBe(t('impression.filigrane')); + } + // Seul le papier de la machine s'écrit ; rien de l'événement. + expect(cheminsEcrits(rang)).toEqual(['reglages_locaux.json']); + await rienEcrit(avant, 'l’aperçu d’une proposition'); + + // Le cadrage : le filigrane et le pied de la première feuille, ensemble + // dans la fenêtre. + const filigrane = dans('[data-impression] [data-feuille] [data-filigrane] [data-marque="filigrane"]'); + await cadrerSur([filigrane, dans('[data-impression] [data-feuille] [data-pied]')]); + memoire.cadrage = window.scrollY; + await photographier(m, '22-plan-brouillon', { + condition: [ + '[data-impression] [data-feuille] [data-filigrane] [data-marque="filigrane"]', + ...CONDITIONS_FEUILLE, + ], + graine: GRAINE, + }); + }); + + test('23 l’aperçu du retenu : le papier gardé, aucun filigrane, le pied du retenu ; rien d’écrit', async () => { + await cliquer('[data-ecran="apercu"] [data-commande="fermerApercu"]'); + await attendre('le plan, de retour', () => dans('[data-ecran="evenement"] [data-section="plan"]')); + await cliquer('[data-commande="quitterProposition"]'); + await attendre('le retenu montré', () => m.ui.etat().proposition === null); + const avant = fichiers(); + const rang = m.trace.length; + await cliquer('[data-ecran="evenement"] [data-commande="apercu"]'); + await attendre('le document imprimé', () => dans('[data-impression] [data-feuille]')); + // Le papier choisi au premier aperçu n'est pas redemandé. + expect(dans('[data-papier="question"]')).toBeNull(); + expect(dans('[data-papier="choisi"]')).not.toBeNull(); + + const sortie = { source: 'retenu', proposition: null, tour: 1 }; + expect(estBrouillon(charge(), sortie)).toBe(false); + const feuilles = await feuillesDuPlan(sortie); + // Le pied date le retenu, pas la proposition de 22. + expect(charge().retenu.horodatage).not.toBe(memoire.horodatageProposition); + for (const feuille of feuilles) expect(texteDe(feuille.querySelector('[data-pied]'))).not.toContain(format.horodatage(memoire.horodatageProposition)); + expect(tous('[data-impression] [data-filigrane]')).toHaveLength(0); + expect(cheminsEcrits(rang)).toEqual([]); + await rienEcrit(avant, 'l’aperçu du retenu'); + + // Le cadrage de 22 : la même position de défilement, le placement + // retenu à la place de la proposition. + window.scrollTo(0, memoire.cadrage); + await images(2); + expect(window.scrollY).toBe(memoire.cadrage); + await photographier(m, '23-plan-retenu', { condition: CONDITIONS_FEUILLE, graine: GRAINE }); + + await cliquer('[data-ecran="apercu"] [data-commande="fermerApercu"]'); + await attendre('le plan, de retour', () => dans('[data-ecran="evenement"] [data-section="plan"]')); + window.scrollTo(0, 0); + await images(2); + }); + + // Un échec de 22 ou de 23 laisse l'aperçu ouvert, la proposition montrée + // et la fenêtre défilée : l'état est rendu ici, pour que les épreuves + // suivantes jugent leurs propres gardes et non ce reste. + afterAll(async () => { + if (dans('[data-ecran="apercu"] [data-commande="fermerApercu"]') !== null) { + await cliquer('[data-ecran="apercu"] [data-commande="fermerApercu"]'); + await attendre('le plan, de retour', () => dans('[data-ecran="evenement"]')); + } + if (m.ui.etat().proposition !== null && dans('[data-commande="quitterProposition"]') !== null) { + await cliquer('[data-commande="quitterProposition"]'); + await attendre('le retenu montré', () => m.ui.etat().proposition === null); + } + window.scrollTo(0, 0); + }); }); // --- L'ordre de lecture (§ 2.1) --------------------------------------------------------- diff --git a/src/interface/transversal.navigateur.test.js b/src/interface/transversal.navigateur.test.js index 9cae75c..06964dd 100644 --- a/src/interface/transversal.navigateur.test.js +++ b/src/interface/transversal.navigateur.test.js @@ -1232,6 +1232,60 @@ describe('les captures d’écran : chaque contrôle échoue en nommant la captu }); }); + // Une condition se juge sur la boîte entière de l'élément (§ 19.11) : + // un élément dont le centre se voit, mais que le bord de la fenêtre ou un + // ancêtre qui rogne coupe, refuse la capture ; le même, entier, passe. Le + // bord de la fenêtre coupe un élément HTML avant même la boîte entière : + // visible de rendu.js le refuse déjà, et nomme la fenêtre. + test('condition : un élément HTML ou SVG dont le centre se voit, coupé par la fenêtre ou par un ancêtre qui rogne, refuse la capture ; entier, il passe', async () => { + await avecMontage(async (m, { ecrire }) => { + const fixe = 'position: fixed; top: 300px'; + const coupe = 'sa boîte déborde de la fenêtre ou d’un ancêtre qui la rogne : l’image la coupe'; + const cas = [ + [ + 'HTML rogné par un ancêtre overflow: hidden', + `
`, + ``, + coupe, + ], + [ + 'HTML coupé par le bord de la fenêtre', + ``, + ``, + 'hors de la fenêtre', + ], + [ + 'SVG rogné par son