diff --git a/doc/captures/01b-passer-en-ecriture.png b/doc/captures/01b-passer-en-ecriture.png index 1e99797..d619c41 100644 Binary files a/doc/captures/01b-passer-en-ecriture.png and b/doc/captures/01b-passer-en-ecriture.png differ diff --git a/doc/captures/02-import-apercu.png b/doc/captures/02-import-apercu.png index e613ac7..497f6b4 100644 Binary files a/doc/captures/02-import-apercu.png and b/doc/captures/02-import-apercu.png differ diff --git a/doc/captures/02b-import-cp1252.png b/doc/captures/02b-import-cp1252.png index 71a56ab..04a2b53 100644 Binary files a/doc/captures/02b-import-cp1252.png and b/doc/captures/02b-import-cp1252.png differ diff --git a/doc/captures/03-import-rapport.png b/doc/captures/03-import-rapport.png index 66b214d..e1f9237 100644 Binary files a/doc/captures/03-import-rapport.png and b/doc/captures/03-import-rapport.png differ diff --git a/doc/captures/04-grille-participants.png b/doc/captures/04-grille-participants.png index 8ac7e60..fc754ee 100644 Binary files a/doc/captures/04-grille-participants.png and b/doc/captures/04-grille-participants.png differ diff --git a/doc/captures/05-tables-etat.png b/doc/captures/05-tables-etat.png index 7f7d7f5..b4ff9ed 100644 Binary files a/doc/captures/05-tables-etat.png and b/doc/captures/05-tables-etat.png differ diff --git a/doc/captures/06-defaut-consequence.png b/doc/captures/06-defaut-consequence.png index 86ab39f..feb8185 100644 Binary files a/doc/captures/06-defaut-consequence.png and b/doc/captures/06-defaut-consequence.png differ diff --git a/doc/captures/07-plan-tables.png b/doc/captures/07-plan-tables.png index b4b1d79..f899442 100644 Binary files a/doc/captures/07-plan-tables.png and b/doc/captures/07-plan-tables.png differ diff --git a/doc/captures/07a-cibles-sieges.png b/doc/captures/07a-cibles-sieges.png index aada406..0664904 100644 Binary files a/doc/captures/07a-cibles-sieges.png and b/doc/captures/07a-cibles-sieges.png differ diff --git a/doc/captures/07a-cibles-tables.png b/doc/captures/07a-cibles-tables.png index 7c64107..57839da 100644 Binary files a/doc/captures/07a-cibles-tables.png and b/doc/captures/07a-cibles-tables.png differ diff --git a/doc/captures/07b-nombre-de-tours.png b/doc/captures/07b-nombre-de-tours.png index a31b690..c39a813 100644 Binary files a/doc/captures/07b-nombre-de-tours.png and b/doc/captures/07b-nombre-de-tours.png differ diff --git a/doc/captures/08-animateurs-designes.png b/doc/captures/08-animateurs-designes.png index 2dfcce9..08d102f 100644 Binary files a/doc/captures/08-animateurs-designes.png and b/doc/captures/08-animateurs-designes.png differ diff --git a/doc/captures/09-refus-sur-place.png b/doc/captures/09-refus-sur-place.png index d01aa7c..3833717 100644 Binary files a/doc/captures/09-refus-sur-place.png and b/doc/captures/09-refus-sur-place.png differ diff --git a/doc/captures/10-diagnostic.png b/doc/captures/10-diagnostic.png index 3a72049..8a411aa 100644 Binary files a/doc/captures/10-diagnostic.png and b/doc/captures/10-diagnostic.png differ diff --git a/doc/captures/11-propositions.png b/doc/captures/11-propositions.png index 3555cee..00c930e 100644 Binary files a/doc/captures/11-propositions.png and b/doc/captures/11-propositions.png differ diff --git a/doc/captures/19-ajustement.png b/doc/captures/19-ajustement.png index 58248f7..a76c33d 100644 Binary files a/doc/captures/19-ajustement.png and b/doc/captures/19-ajustement.png differ diff --git a/doc/captures/20-mode-lecture.png b/doc/captures/20-mode-lecture.png index f1c50ff..1d870dc 100644 Binary files a/doc/captures/20-mode-lecture.png and b/doc/captures/20-mode-lecture.png differ diff --git a/doc/captures/manifeste.json b/doc/captures/manifeste.json index dcc22f3..8263075 100644 --- a/doc/captures/manifeste.json +++ b/doc/captures/manifeste.json @@ -1,22 +1,22 @@ {"captures":[ {"cadre":{"hauteur":700,"largeur":1366},"empreinte":"3d457536d0600103ea1141c08871a4aebb7c5ef455311fe8201a817f38d7bca0","etape":"1","etat":null,"facteurEchelle":1,"fichier":"01-premier-ecran","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"22433fa483e23340e474b89d0dcf084f9ab6b6d254f3867fe5006846ee1fb0a1"},"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-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-page-texte","ratio":14.62,"role":"texte","seuil":4.5}]},"texte":["Gestion table tournante Libre","construction de documentation","documentation","Clair","Sombre","À propos","Liste des événements","version","construction de documentation","Le dossier de l'exécutable n'est pas connu : les événements se rangent dans les Documents.","Dossier de travail","C:\\Users\\Exemple\\Documents\\Gestion table tournante Libre","Ouvrir dans l’explorateur","Créer un événement","Nom de l’événement","Date, facultative (AAAA-MM-JJ)","Créer l’événement","Importer un événement depuis un fichier…","Démonstrations","Chacune se charge comme un nouvel événement, numéroté et ouvert en lecture ; aucun autre événement n’est touché.","Grande démonstration","Grande démonstration sans exception","Petite démonstration","Petite démonstration, conflit inévitable"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"18f2d427edc5d649e7c9391843d78a0dd6c441e24de40968c5235e07a60d39cd","etape":"1b","etat":"ab468ad775d39cda60057d23f67ba34d787dda85b66c87b5500b41bff1b2b630","facteurEchelle":1,"fichier":"01b-passer-en-ecriture","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"983dde9ee5e27d64c9741ac634e04cda267835484a923dc3c0b5e095fbd556f8"},"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-refus","ratio":8.76,"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-refus","ratio":9.24,"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","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Tables et tours","Places par défaut","Nombre de tours","Tables","Ajouter une table ronde","L'événement est ouvert en lecture : « Modifier » permet de le changer.","Modifier","Ajouter une table carrée","Aucune table : ajoutez-en une, ronde ou carrée."],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"e3bdd85dca712877cfe2cb2f2b6411dde35f401473ec8aec26b4efd0df9beb91","etape":"2","etat":"4acf3230b0733a35d33c6baf84b7d4f312e98a5b799147dcea6b3bb5d4f082d5","facteurEchelle":1,"fichier":"02-import-apercu","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"7ac33f9edc7df268ba3a5d21511f9cfaa5c623b3e2e8eb7d4ae132b20d83205c"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Aperçu","Mode de l'import","Ajouter","Mettre à jour","Remplacer","Fichier","participants_cas_limites.csv","Encodage","UTF-8 avec marque","Séparateur","point-virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Importer","Enregistrer les lignes refusées","Fermer l'aperçu"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"6174e5e9194ef74b8730b18e444723596ac85d0bc0a21272cfe976555671d9df","etape":"2","etat":"4acf3230b0733a35d33c6baf84b7d4f312e98a5b799147dcea6b3bb5d4f082d5","facteurEchelle":1,"fichier":"02b-import-cp1252","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"42391359098507ccc1bbac0b9ee4230e0aed3cd49acabe090518a390e82ae704"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Aperçu","Mode de l'import","Ajouter","Mettre à jour","Remplacer","Fichier","participants_cas_limites_cp1252.csv","Encodage","windows-1252","Séparateur","virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Importer","Enregistrer les lignes refusées","Fermer l'aperçu"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"3a44dd113f4b0585f56ea6009936b4ab1ed89d4e9efe6219ad175b0d62285c21","etape":"2","etat":"b3119a32dda90f0c824bb31f31cb2f5a58af891b3338215d7efabf785b0a5d48","facteurEchelle":1,"fichier":"03-import-rapport","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"dab7ed78b70211fba11d29f9d25a8ed1ad8c78325b6095844087b9de782c4dc2"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Il manque 4 places à chaque tour pour asseoir toutes les personnes : ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.","Ouvrir les tables","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Rapport d'import","13","personnes ajoutées","0","personne mise à jour","0","personne inchangée","0","personne retirée","0","réservation retirée","Fichier","participants_cas_limites.csv","Encodage","UTF-8 avec marque","Séparateur","point-virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Enregistrer les lignes refusées","Fermer le rapport"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"ee8c4993e3e41b5f12fa7e487920d4f89147c7de2a509ea532c88a5b26fc70b7","etape":"3","etat":"a220b2f80cc83728c43e0bcca0bb785ce7e7b4fa5c3fea5289d095d09a911535","facteurEchelle":1,"fichier":"04-grille-participants","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"f182c0f76642f0fd76bb9f2a05e0de43a4b6d4b65da5b2c0497e79825f401262"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Il manque 4 places à chaque tour pour asseoir toutes les personnes : ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.","Ouvrir les tables","Participants","Ajouter une personne","Nom","Prénom","Appartenance","Courriel","Titre pressenti","Notes","Ajouter","Filtrer","Grouper par","Aucun groupement","Nom","Prénom","Appartenance","Courriel","Titre pressenti","Exclusion","Notes","Statut","13 participants","Exporter la liste","Nom","(décroissant)","Prénom","Appartenance","Courriel","Titre pressenti","Exclusion","Notes","Actions","Tourterel","Héloïse","Cercle des Saules","exclue","S’est désistée la veille","Fiche","Voir sur le plan","Réintégrer","Supprimer","Sablonnet","Ysaline","Chœur de l’Anse","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Ormelin","Faustine","Société des Hérons","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Marbrelle","Gaspard","Cercle des Saules","ondine@exemple.test","animation","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Lanterneau","Basile","Chœur de l’Anse","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Fenouillet","Côme","Société des Hérons","présente","végétarien; sans noix, ni arachides","Fiche","Voir sur le plan","Exclure","Supprimer","Cressonnier","Bertille","Cercle des Saules","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Coudraie","Apolline","Chœur de l’Anse","présente","Arrive après le premier tour repart avant le dessert","Fiche","Voir sur le plan","Exclure","Supprimer","Brunœil","Cercle des Saules","brunoeil@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Ardoisier","Edgar","Société des Hérons","présente","Fiche","Voir sur le plan","Exclure","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"9e4224610e8ebd2c57a76c312230fed74cd889948ec42084392b5b744eb86e04","etape":"4","etat":"421c6294b4348fa49aa0105ea643fd11c80806a9fbb1e079435adac079e6b260","facteurEchelle":1,"fichier":"05-tables-etat","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"37dcb4e6b45de5e66feb2fb9d25675e7beebfce09e52eed6de14098353bfd392"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Tables et tours","Places par défaut","Nombre de tours","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"6f910f302b41f1fb4e3262fd6f9da5d06a4f5f1ecc82fe1024e1743197b4662f","etape":"4","etat":"421c6294b4348fa49aa0105ea643fd11c80806a9fbb1e079435adac079e6b260","facteurEchelle":1,"fichier":"06-defaut-consequence","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"ca63fc45db4cc745b8b2d4238e48cd50b79b6e24469ae087fe344de7f182871c"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Tables et tours","Places par défaut","1 table suit le défaut et passe de 8 à 10 places ; 1 table surchargée (8) ne change pas.","Total : 16 → 18 sièges pour 12 participants.","Appliquer","Appliquer et réaligner la surchargée","Garder le défaut actuel","Nombre de tours","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"df353cbd2ef92f6cae9fd7dc1fb51245caba287db9eb0327d583ce3d46925bdc","etape":"4","etat":"7d42132a2caa999f9d046f580b1093732ebb44984b8fdec5b27da7e42801678e","facteurEchelle":1,"fichier":"07-plan-tables","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"23f2e6e11539afac2867b17187ed851b2e5d0c334c8d40c12db4b2fc6e07d943"},"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-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"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-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-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"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-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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Plan","Réservations","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (libre)","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","9. (libre)","10. (libre)","1. (libre)","2. (libre)","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 12 mobiles","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Basile Lanterneau","— titre pressenti","animation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Basile Lanterneau","Capucine Pommerel","Gaspard Marbrelle","Ysaline Sablonnet","Anselme Givrecœur","Côme Fenouillet","Apolline Coudraie","Basile Lanterneau","Brunœil","Faustine Ormelin","Edgar Ardoisier","Bertille Cressonnier"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"7e8e359a42eb8908ef0ba0462c886bea152496853d400daf5cc21dacb3682d34","etape":"4","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"07a-cibles-sieges","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"18396caf4c054bdc9a23223eb6104838ebbd07183f0868277a00419177810767"},"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-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-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-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-echelle","ratio":14.62,"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-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","Grande démonstration 1","Brouillon","Proposé","Retenu","Bloqué","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Plan","Réservations","Tour affiché","1","2","3","4","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (réservée, tous les tours)","Manon Urvançon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Guillaume Luskic…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Fatima Korvouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Antoine Bralmault","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Manon Tolveron","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Philippe Voskeval","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Félix Voskande","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Michel Korveron","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Gabriel Ivreval","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marc Vandergrol…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Étienne Tolvançon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Sophie Pruvotte","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Fatima Abrevanç…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marie Urvard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Catherine Hulver…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Camille Neskard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Youssef Neskière","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Guillaume Dulvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Philippe Ivroubre","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Claire Jusvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Youssef Jusvard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Louise Neskichon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Hugo Bralmouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Gabriel Abrevault","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Louis Selvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Jean Fralvumet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marie Bralmesson","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Thomas Selvignac","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Émilie Abrevière","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Geneviève Dulvu…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","François Obrouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","François Ivrivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","Samuel Bralmeux","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","écart d'itinéraire maximal : plancher 1, au moins 24 mobiles finissent sous leur plafond a priori","plafond a priori 24 rencontres pour 4 ancrés","plafond a priori 28 rencontres pour 29 ancrés et 227 mobiles","99 retours imposés par 33 ancrages","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Aucun.","Réserve","Personnes en attente au tour 1","Isabelle Mirvault","Félix Abrevande","Hugo Fralvançon","Manon Fralveux","Catherine Luskotte","Zoé WAMMERWOLM-MAWWEMMEL","Catherine Urvesson","Karim Hulvivet","Caroline Selveron","Catherine Korvault","Caroline Fralvotte","Geneviève Jusvesson","Paul Abrevumet","Léa Fralvault","Fatima Obrivet","Zoé Dulveval","Paul Korveux","Julie Luskière","Émilie Clovande","Paul Hulvande","Hugo Fralvesson","Isabelle Urvière","Thomas Rozvotte","Isabelle Bralmumet","Catherine Dulvouze","Marc Voskichon","Simon Mirvivet","Louise Clovault","Lucie Voskeux","Amina Eskignac","Luc Selvoubre","Étienne Fralvichon","Philippe Voskivet","Louise Quilmançon","Sophie Luskande","Pierre Abreveron","Philippe Neskeux","Amina Neskançon","Léa Tolvande","Antoine Quilmeron","Guillaume Clovumet","Michel Eskeux","Louise Hulvault","Samuel Eskumet","Claire Fralvivet","Léa Mirveux","Karim Gorzesson","Sylvie Quilmande","François Gorzichon","Amina Pruvesson","Lucie Quilmière","Fatima Korvesson","Nicolas Selvesson","Louise Neskesson","Lucie Tolvesson","Olivier Quilmesson","Guillaume Luskivet","Gabriel Neskault","Antoine Urvumet","Olivier Jusvançon","Simon Dulvançon","Chloé Gorzard","Gabriel Pruveux","Manon Clovière","Karim Tolveval","Hugo Bérêçàuvïlle-Dœûmôntëÿ","Claire Gorzeron","Hélène Luskumet","Louis Fralvande","Émilie Selvault","Léa Neskumet","Isabelle Korvichon","Michel Obrande","Isabelle Luskeval","Fatima Tolvoubre","Zoé Gorzière","Sylvie Korvignac","Antoine Obreval","Camille Tolvumet","Marie Dulvesson","Guillaume Tolvignac","Gabriel Mirvotte","Paul Neskignac","Raphaël Ivrotte","Marc Luskouze","Sylvie Jusvotte","Véronique Bralmoubre","Pierre Selvard","Marie Hulveux","Karim Fralveval","Philippe Gorzignac","Étienne Selvouze","Louis Hulvichon","Sylvie Urvoubre","Pierre Pruveval","Guillaume Hulvotte","Geneviève Abrevignac","Amina Bralmignac","Olivier Bralmançon","Marc Luskoubre","Geneviève Abrevoubre","Raphaël Korvoubre","Pierre Mirvard","Camille Jusvumet","Samuel Cloveval","Antoine Selvière","Véronique Jusvouze","Paul Neskivet","Samuel Dulvande","Lucie Selveval","Olivier Tolvichon","Fatima Hulvard","Alexandre Ivreux","Félix Korveval","Karim Urvichon","Émilie Dulvichon","Luc Tolvouze","Amina Quilmault","François Ivrouze","Hélène Gorzumet","Sophie Ivrault","Louise Bralmière","Nicolas Korvande","Geneviève Ivrumet","Geneviève Obresson","Guillaume Obrançon","Fatima Urvignac","Claire Bralmotte","Félix Hulvignac","François Clovotte","Philippe Dulvoubre","Samuel Rozveron","Pierre Hulvoubre","Émilie Urvouze","Félix Korvançon","Émilie Neskande","Chantal Mirvumet","Alexandre Korvotte","Zoé Eskeval","Nathalie Voskoubre","Sophie Pruvière","Paul Obrichon","Nathalie Eskière","Caroline Ivreron","Véronique Neskoubre","Anne Urvivet","Olivier Hulvançon","Karim Clovançon","Zoé Quilmotte","Félix Eskouze","Amina Rozvichon","Simon Fralvière","Pierre Hulvière","Raphaël Jusvault","Pierre Fralvoubre","Hélène Selveux","Caroline Rozvançon","Olivier Korvière","Nicolas Ivrichon","Félix Korvumet","Guillaume Selvumet","Michel Urvotte","Émilie Eskault","Hélène Bralmeval","Guillaume Hulvesson","Caroline Jusvoubre","Félix Luskeux","Antoine Pruvouze","Julie Quilmignac","Hugo Voskumet","Manon Quilmumet","Marc Voskouze","Samuel Abrevard","Manon Clovesson","Simon Hulvumet","Lucie Voskeron","Jean Neskeron","Manon Pruvivet","Sophie Clovard","Étienne Gorzeval","Gabriel Luskault","Émilie Korvard","Thomas Quilmichon","Alexandre Eskançon","Émilie Luskançon","Marc Dulvault","Hélène Rozveval","Julie Pruvançon","Louis Mirveron","François Eskard","Louis Ivrançon","Karim Clovivet","Léa Rozvande","Amina Hulvouze","Étienne Eskesson","Philippe Abreveval","Émilie Quilmeux","Simon Voskotte","Chantal Obrignac","Guillaume Eskotte","Léa Obrard","Guillaume Gorzivet","Geneviève Mirvançon","Julie Selvotte","Amina Mirvesson","Isabelle Luskignac","Chloé Obrumet","Anne Abrevesson","Fatima Rozvumet","Guillaume Mirvoubre","Véronique Rozvoubre","Claire Gorzotte","Antoine Ivrière","Isabelle Selvichon","Isabelle Urvault","Julie Voskesson","Chantal Abrevotte","Amina Neskotte","Étienne Voskançon","Zoé Fralveron","Marc Rozvouze","Sylvie Abreveux","Karim Pruvignac","Zoé Mirveval","Philippe Fralvignac","Gabriel Gorzeux","Jean Urveval"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"41b9b3979a70ee689aac4187240a636fcc5d19bc2447d5d42ab172d58f53bbc4","etape":"4","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"07a-cibles-tables","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"a0ca6b13c5c44500df3f250c0eb9cf6d9e0f322ef7c1e37afeb644f5c986f98f"},"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-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"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-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-cadre-lecture","ratio":7.34,"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-page-texte","ratio":14.62,"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","Lecture","Liste des événements","Clair","Sombre","À propos","Modifier","Lecture","Défaire","Grande démonstration 1","Brouillon","Proposé","Retenu","Bloqué","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Plan","Réservations","Tour affiché","1","2","3","4","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","5","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","écart d'itinéraire maximal : plancher 1, au moins 24 mobiles finissent sous leur plafond a priori","plafond a priori 24 rencontres pour 4 ancrés","plafond a priori 28 rencontres pour 29 ancrés et 227 mobiles","99 retours imposés par 33 ancrages","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Aucun.","Réserve","Personnes en attente au tour 1","Isabelle Mirvault","Félix Abrevande","Hugo Fralvançon","Manon Fralveux","Catherine Luskotte","Zoé WAMMERWOLM-MAWWEMMEL","Catherine Urvesson","Karim Hulvivet","Caroline Selveron","Catherine Korvault","Caroline Fralvotte","Geneviève Jusvesson","Paul Abrevumet","Léa Fralvault","Fatima Obrivet","Zoé Dulveval","Paul Korveux","Julie Luskière","Émilie Clovande","Paul Hulvande","Hugo Fralvesson","Isabelle Urvière","Thomas Rozvotte","Isabelle Bralmumet","Catherine Dulvouze","Marc Voskichon","Simon Mirvivet","Louise Clovault","Lucie Voskeux","Amina Eskignac","Luc Selvoubre","Étienne Fralvichon","Philippe Voskivet","Louise Quilmançon","Sophie Luskande","Pierre Abreveron","Philippe Neskeux","Amina Neskançon","Léa Tolvande","Antoine Quilmeron","Guillaume Clovumet","Michel Eskeux","Louise Hulvault","Samuel Eskumet","Claire Fralvivet","Léa Mirveux","Karim Gorzesson","Sylvie Quilmande","François Gorzichon","Amina Pruvesson","Lucie Quilmière","Fatima Korvesson","Nicolas Selvesson","Louise Neskesson","Lucie Tolvesson","Olivier Quilmesson","Guillaume Luskivet","Gabriel Neskault","Antoine Urvumet","Olivier Jusvançon","Simon Dulvançon","Chloé Gorzard","Gabriel Pruveux","Manon Clovière","Karim Tolveval","Hugo Bérêçàuvïlle-Dœûmôntëÿ","Claire Gorzeron","Hélène Luskumet","Louis Fralvande","Émilie Selvault","Léa Neskumet","Isabelle Korvichon","Michel Obrande","Isabelle Luskeval","Fatima Tolvoubre","Zoé Gorzière","Sylvie Korvignac","Antoine Obreval","Camille Tolvumet","Marie Dulvesson","Guillaume Tolvignac","Gabriel Mirvotte","Paul Neskignac","Raphaël Ivrotte","Marc Luskouze","Sylvie Jusvotte","Véronique Bralmoubre","Pierre Selvard","Marie Hulveux","Karim Fralveval","Philippe Gorzignac","Étienne Selvouze","Louis Hulvichon","Sylvie Urvoubre","Pierre Pruveval","Guillaume Hulvotte","Geneviève Abrevignac","Amina Bralmignac","Olivier Bralmançon","Marc Luskoubre","Geneviève Abrevoubre","Raphaël Korvoubre","Pierre Mirvard","Camille Jusvumet","Samuel Cloveval","Antoine Selvière","Véronique Jusvouze","Paul Neskivet","Samuel Dulvande","Lucie Selveval","Olivier Tolvichon","Fatima Hulvard","Alexandre Ivreux","Félix Korveval","Karim Urvichon","Émilie Dulvichon","Luc Tolvouze","Amina Quilmault","François Ivrouze","Hélène Gorzumet","Sophie Ivrault","Louise Bralmière","Nicolas Korvande","Geneviève Ivrumet","Geneviève Obresson","Guillaume Obrançon","Fatima Urvignac","Claire Bralmotte","Félix Hulvignac","François Clovotte","Philippe Dulvoubre","Samuel Rozveron","Pierre Hulvoubre","Émilie Urvouze","Félix Korvançon","Émilie Neskande","Chantal Mirvumet","Alexandre Korvotte","Zoé Eskeval","Nathalie Voskoubre","Sophie Pruvière","Paul Obrichon","Nathalie Eskière","Caroline Ivreron","Véronique Neskoubre","Anne Urvivet","Olivier Hulvançon","Karim Clovançon","Zoé Quilmotte","Félix Eskouze","Amina Rozvichon","Simon Fralvière","Pierre Hulvière","Raphaël Jusvault","Pierre Fralvoubre","Hélène Selveux","Caroline Rozvançon","Olivier Korvière","Nicolas Ivrichon","Félix Korvumet","Guillaume Selvumet","Michel Urvotte","Émilie Eskault","Hélène Bralmeval","Guillaume Hulvesson","Caroline Jusvoubre","Félix Luskeux","Antoine Pruvouze","Julie Quilmignac","Hugo Voskumet","Manon Quilmumet","Marc Voskouze","Samuel Abrevard","Manon Clovesson","Simon Hulvumet","Lucie Voskeron","Jean Neskeron","Manon Pruvivet","Sophie Clovard","Étienne Gorzeval","Gabriel Luskault","Émilie Korvard","Thomas Quilmichon","Alexandre Eskançon","Émilie Luskançon","Marc Dulvault","Hélène Rozveval","Julie Pruvançon","Louis Mirveron","François Eskard","Louis Ivrançon","Karim Clovivet","Léa Rozvande","Amina Hulvouze","Étienne Eskesson","Philippe Abreveval","Émilie Quilmeux","Simon Voskotte","Chantal Obrignac","Guillaume Eskotte","Léa Obrard","Guillaume Gorzivet","Geneviève Mirvançon","Julie Selvotte","Amina Mirvesson","Isabelle Luskignac","Chloé Obrumet","Anne Abrevesson","Fatima Rozvumet","Guillaume Mirvoubre","Véronique Rozvoubre","Claire Gorzotte","Antoine Ivrière","Isabelle Selvichon","Isabelle Urvault","Julie Voskesson","Chantal Abrevotte","Amina Neskotte","Étienne Voskançon","Zoé Fralveron","Marc Rozvouze","Sylvie Abreveux","Karim Pruvignac","Zoé Mirveval","Philippe Fralvignac","Gabriel Gorzeux","Jean Urveval"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"def31a8b9a29d547c788b037763e62ad83e0ab45dfb0a1f7c462ef5602641dea","etape":"5","etat":"7e19a3b61721017693ee5895e1b4a82e2f1656e35e71989ac4d29ea9cfc59fe8","facteurEchelle":1,"fichier":"07b-nombre-de-tours","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"cfb03caea84f7ef00343f710d2db08807e8b67273d9b5d24d8dcf8266773c9be"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5}],"sombre":[{"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Tables et tours","Places par défaut","Nombre de tours","Passer de 3 à 2 tours ? Ce changement emporte :","Basile Lanterneau : réservation à la table 1 au tour 3, détruite","Annuler","Confirmer","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, -{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"7e9d50eb42534aca92ac1c25418df31a6cf39ad65aa4b7b7d131056d2002e98a","etape":"6","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"08-animateurs-designes","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"c635998d91cfba432e1355807acb2a417152256321b205c3606fa602b324ae4f"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"1dac90f8e72931ca0be3f940d047311709153b4979a8094c74454e681fea09a7","etape":"7","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"09-refus-sur-place","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"ec44cf4519fc7245432c5ad3d9659633f84327cc9ab3a62fd27da04deb27f65e"},"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-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},{"jeton":"rgb(255, 255, 255)","ratio":5.33,"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"ee36c3b91d3c64a086d46e111a3240c1fb5dba0bc5f59f3c4aca3d185f3b93d9","etape":"8","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"10-diagnostic","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"316870a5b600b76b054b972d27096c8a8bc00e8ae70098df30bb114a8cd150df"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"3b02f6576c9ba053182280869141b2bd68809d140be1fd7ced68e90221c47520","etape":"9","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"11-propositions","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"f1e18d3337e3d9ebae04b1b4f2b2ec85f10a727175f075621bd1cdecc6eb9387"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"baa328a99035a6771145a49ac8c8f8530a4f558aa26db87be66cafc3f3e0541d","etape":"11","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"19-ajustement","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"f31cf69b4afb247d3a1714bb994257df04afdd0114c8281869aa9de708273a38"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"b08a852c1d9e924ff9b3415806c8abfae0a47ba4d33934c8a4f24dea95511baa","etape":"11","etat":"20666d70d9b2503335efc1f2cece5453a199ab77f4864835baf4bccf5aff17d5","facteurEchelle":1,"fichier":"20-mode-lecture","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"b5ef6ac5383188a47783bb46834ccb49fb545dc6442f1d35304cd458940c6c18"},"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é","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","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":"0023540aac46fdec821d303affd3f2c56887ddaece22471a203ad0abec7f14b1","etape":"1b","etat":"ab468ad775d39cda60057d23f67ba34d787dda85b66c87b5500b41bff1b2b630","facteurEchelle":1,"fichier":"01b-passer-en-ecriture","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"4d431e8aff586a750d02909023fe62e9ade39429937d58a054b26c5ebc14d826"},"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-refus","ratio":8.76,"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-refus","ratio":9.24,"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","Soirée d’essai","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Tables et tours","Places par défaut","Nombre de tours","Tables","Ajouter une table ronde","L'événement est ouvert en lecture : « Modifier » permet de le changer.","Modifier","Ajouter une table carrée","Aucune table : ajoutez-en une, ronde ou carrée."],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"3ad6f4328868a85de8f0c0aae7164d41cf786b90882aaaf6a2eb244eea48f4ec","etape":"2","etat":"4acf3230b0733a35d33c6baf84b7d4f312e98a5b799147dcea6b3bb5d4f082d5","facteurEchelle":1,"fichier":"02-import-apercu","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"67c04c11a309a028bc0718b7d0c34ca5b71845e96f646740be57593ff63275f3"},"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","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Aperçu","Mode de l'import","Ajouter","Mettre à jour","Remplacer","Fichier","participants_cas_limites.csv","Encodage","UTF-8 avec marque","Séparateur","point-virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Importer","Enregistrer les lignes refusées","Fermer l'aperçu"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"a9c6a351e2789fd1c5a5fcc3fc9b7fa2f4317d74f5a03628fc4ae9f743f66350","etape":"2","etat":"4acf3230b0733a35d33c6baf84b7d4f312e98a5b799147dcea6b3bb5d4f082d5","facteurEchelle":1,"fichier":"02b-import-cp1252","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"1bf8d5408e9410f02765011540c1824e5b0137e3c8a29a0ba740e98e000fb186"},"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","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Aperçu","Mode de l'import","Ajouter","Mettre à jour","Remplacer","Fichier","participants_cas_limites_cp1252.csv","Encodage","windows-1252","Séparateur","virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Importer","Enregistrer les lignes refusées","Fermer l'aperçu"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"8f44e483f92bcc714de0a16d062d218393b778797bb5e9a50497d0e3a24b9170","etape":"2","etat":"b3119a32dda90f0c824bb31f31cb2f5a58af891b3338215d7efabf785b0a5d48","facteurEchelle":1,"fichier":"03-import-rapport","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"5bf126478eaa23cec5b164a9e2d43477995fa2f04384e717ce642e917cf37952"},"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","Il manque 4 places à chaque tour pour asseoir toutes les personnes : ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.","Ouvrir les tables","Import","Choisissez un fichier CSV ou déposez-le sur la fenêtre ; une plage collée dans la grille des participants arrive ici. Rien ne s'importe avant l'aperçu.","Choisir un fichier CSV…","Rapport d'import","13","personnes ajoutées","0","personne mise à jour","0","personne inchangée","0","personne retirée","0","réservation retirée","Fichier","participants_cas_limites.csv","Encodage","UTF-8 avec marque","Séparateur","point-virgule","Colonnes","En-tête","Devient","Nom","nom","Prénom","prénom","Organisation","appartenance","Courriel","courriel","Rôle","titre pressenti","Exclu","exclu","Notes","notes","Téléphone","non reconnue : ignorée","13","lignes valides","Ligne","nom","prénom","appartenance","courriel","titre pressenti","exclu","notes","2","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","3","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","4","Marbrelle","Gaspard","Cercle des Saules","animation","5","Sablonnet","Ysaline","Chœur de l’Anse","6","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","7","Tourterel","Héloïse","Cercle des Saules","oui","S’est désistée la veille","8","Fenouillet","Côme","Société des Hérons","végétarien; sans noix, ni arachides","9","Coudraie","Apolline","Chœur de l’Anse","Arrive après le premier tour repart avant le dessert","10","Lanterneau","Basile","Chœur de l’Anse","11","Brunœil","Cercle des Saules","brunoeil@exemple.test","12","Ormelin","Faustine","Société des Hérons","14","Ardoisier","Edgar","Société des Hérons","15","Cressonnier","Bertille","Cercle des Saules","Lignes refusées","Ligne","Motif","13","Le nom manque.","Orthographes fondues en une appartenance","affichée :","Société des Hérons","Société des Hérons","3","lignes","Societe des herons","1","ligne","Doublons signalés, jamais fusionnés","lignes","2","10","Enregistrer les lignes refusées","Fermer le rapport"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"57beb21ea94036118cb17e1dcd3815d26c5b4398c1f6b0a5b75f52830b9d26a0","etape":"3","etat":"a220b2f80cc83728c43e0bcca0bb785ce7e7b4fa5c3fea5289d095d09a911535","facteurEchelle":1,"fichier":"04-grille-participants","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"e877a0c8abfae2558ec08a72b8eb5bb27ba50c7121724e9a9ba7fff5c88cf6ab"},"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","Il manque 4 places à chaque tour pour asseoir toutes les personnes : ajouter des tables ou des sièges, ou exclure des personnes, rend la génération possible.","Ouvrir les tables","Participants","Ajouter une personne","Nom","Prénom","Appartenance","Courriel","Titre pressenti","Notes","Ajouter","Filtrer","Grouper par","Aucun groupement","Nom","Prénom","Appartenance","Courriel","Titre pressenti","Exclusion","Notes","Statut","13 participants","Exporter la liste","Nom","(décroissant)","Prénom","Appartenance","Courriel","Titre pressenti","Exclusion","Notes","Actions","Tourterel","Héloïse","Cercle des Saules","exclue","S’est désistée la veille","Fiche","Voir sur le plan","Réintégrer","Supprimer","Sablonnet","Ysaline","Chœur de l’Anse","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Pommerel","Capucine","Société des Hérons","capucine.pommerel@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Ormelin","Faustine","Société des Hérons","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Marbrelle","Gaspard","Cercle des Saules","ondine@exemple.test","animation","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Lanterneau","Basile","Chœur de l’Anse","basile.lanterneau@exemple.test","animation","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Lanterneau","Basile","Chœur de l’Anse","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Givrecœur","Anselme","anselme.givrecoeur@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Fenouillet","Côme","Société des Hérons","présente","végétarien; sans noix, ni arachides","Fiche","Voir sur le plan","Exclure","Supprimer","Cressonnier","Bertille","Cercle des Saules","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Coudraie","Apolline","Chœur de l’Anse","présente","Arrive après le premier tour repart avant le dessert","Fiche","Voir sur le plan","Exclure","Supprimer","Brunœil","Cercle des Saules","brunoeil@exemple.test","présente","Fiche","Voir sur le plan","Exclure","Supprimer","Ardoisier","Edgar","Société des Hérons","présente","Fiche","Voir sur le plan","Exclure","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"082a133e76c809940ead417be7cdf66077972ad229b85bac12e80f4b007f3307","etape":"4","etat":"421c6294b4348fa49aa0105ea643fd11c80806a9fbb1e079435adac079e6b260","facteurEchelle":1,"fichier":"05-tables-etat","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"6eb751e9553f8dcf77d07c918f43c5da865b15f2a7f516bb9eb8b87cd8a87a1f"},"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","Tables et tours","Places par défaut","Nombre de tours","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"6a48fe8ea1922ed946072eefdd7ca53833d7fe4bd2420543e9ebd3c40612e179","etape":"4","etat":"421c6294b4348fa49aa0105ea643fd11c80806a9fbb1e079435adac079e6b260","facteurEchelle":1,"fichier":"06-defaut-consequence","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"742079465f67a44ae4bcc8028b79b4a1e9bab8c4656d9b633b4d8d6c8455f9a6"},"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","Tables et tours","Places par défaut","1 table suit le défaut et passe de 8 à 10 places ; 1 table surchargée (8) ne change pas.","Total : 16 → 18 sièges pour 12 participants.","Appliquer","Appliquer et réaligner la surchargée","Garder le défaut actuel","Nombre de tours","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"e650f15bb5c51bec4160ff76219fe77902bf42d1e9ab47d46b9f81e4c5055844","etape":"4","etat":"7d42132a2caa999f9d046f580b1093732ebb44984b8fdec5b27da7e42801678e","facteurEchelle":1,"fichier":"07-plan-tables","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"ba04243df820caa656e7884ce0e9db02912b6d6203cd0efe889ff5dbe0052354"},"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-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"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-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-trait","ratio":8.89,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":14.62,"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-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","Plan","Réservations","Tour affiché","1","2","3","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (libre)","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","9. (libre)","10. (libre)","1. (libre)","2. (libre)","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 12 mobiles","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Basile Lanterneau","— titre pressenti","animation","Gaspard Marbrelle","— titre pressenti","animation","Réserve","Personnes en attente au tour 1","Basile Lanterneau","Capucine Pommerel","Gaspard Marbrelle","Ysaline Sablonnet","Anselme Givrecœur","Côme Fenouillet","Apolline Coudraie","Basile Lanterneau","Brunœil","Faustine Ormelin","Edgar Ardoisier","Bertille Cressonnier"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"4fc279e4a15bec53d4d467639d0cf660bdee15cd02f5d875f6e26c6058240b99","etape":"4","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"07a-cibles-sieges","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"9ab4237de86815cd8124695b59d7bf55e5bb87ba95361df45fd0df5393e428f0"},"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-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-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-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-echelle","ratio":14.62,"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-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","Grande démonstration 1","Brouillon","Proposé","Retenu","Bloqué","Imprimer…","Import","Participants","Tables et tours","Plan","Diagnostic","Génération","Propositions","Plan","Réservations","Tour affiché","1","2","3","4","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","1. (réservée, tous les tours)","Manon Urvançon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Guillaume Luskic…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Fatima Korvouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Antoine Bralmault","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Manon Tolveron","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Philippe Voskeval","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Félix Voskande","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Michel Korveron","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Gabriel Ivreval","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marc Vandergrol…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Étienne Tolvançon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Sophie Pruvotte","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Fatima Abrevanç…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marie Urvard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Catherine Hulver…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Camille Neskard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Youssef Neskière","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Guillaume Dulvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Philippe Ivroubre","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Claire Jusvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Youssef Jusvard","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Louise Neskichon","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Hugo Bralmouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Gabriel Abrevault","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Louis Selvivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Jean Fralvumet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Marie Bralmesson","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Thomas Selvignac","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Émilie Abrevière","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","8. (libre)","1. (réservée, tous les tours)","Geneviève Dulvu…","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","François Obrouze","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","François Ivrivet","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1. (réservée, tous les tours)","Samuel Bralmeux","—","animation","2. (libre)","3. (libre)","4. (libre)","5. (libre)","6. (libre)","7. (libre)","1","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","écart d'itinéraire maximal : plancher 1, au moins 24 mobiles finissent sous leur plafond a priori","plafond a priori 24 rencontres pour 4 ancrés","plafond a priori 28 rencontres pour 29 ancrés et 227 mobiles","99 retours imposés par 33 ancrages","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Aucun.","Réserve","Personnes en attente au tour 1","Isabelle Mirvault","Félix Abrevande","Hugo Fralvançon","Manon Fralveux","Catherine Luskotte","Zoé WAMMERWOLM-MAWWEMMEL","Catherine Urvesson","Karim Hulvivet","Caroline Selveron","Catherine Korvault","Caroline Fralvotte","Geneviève Jusvesson","Paul Abrevumet","Léa Fralvault","Fatima Obrivet","Zoé Dulveval","Paul Korveux","Julie Luskière","Émilie Clovande","Paul Hulvande","Hugo Fralvesson","Isabelle Urvière","Thomas Rozvotte","Isabelle Bralmumet","Catherine Dulvouze","Marc Voskichon","Simon Mirvivet","Louise Clovault","Lucie Voskeux","Amina Eskignac","Luc Selvoubre","Étienne Fralvichon","Philippe Voskivet","Louise Quilmançon","Sophie Luskande","Pierre Abreveron","Philippe Neskeux","Amina Neskançon","Léa Tolvande","Antoine Quilmeron","Guillaume Clovumet","Michel Eskeux","Louise Hulvault","Samuel Eskumet","Claire Fralvivet","Léa Mirveux","Karim Gorzesson","Sylvie Quilmande","François Gorzichon","Amina Pruvesson","Lucie Quilmière","Fatima Korvesson","Nicolas Selvesson","Louise Neskesson","Lucie Tolvesson","Olivier Quilmesson","Guillaume Luskivet","Gabriel Neskault","Antoine Urvumet","Olivier Jusvançon","Simon Dulvançon","Chloé Gorzard","Gabriel Pruveux","Manon Clovière","Karim Tolveval","Hugo Bérêçàuvïlle-Dœûmôntëÿ","Claire Gorzeron","Hélène Luskumet","Louis Fralvande","Émilie Selvault","Léa Neskumet","Isabelle Korvichon","Michel Obrande","Isabelle Luskeval","Fatima Tolvoubre","Zoé Gorzière","Sylvie Korvignac","Antoine Obreval","Camille Tolvumet","Marie Dulvesson","Guillaume Tolvignac","Gabriel Mirvotte","Paul Neskignac","Raphaël Ivrotte","Marc Luskouze","Sylvie Jusvotte","Véronique Bralmoubre","Pierre Selvard","Marie Hulveux","Karim Fralveval","Philippe Gorzignac","Étienne Selvouze","Louis Hulvichon","Sylvie Urvoubre","Pierre Pruveval","Guillaume Hulvotte","Geneviève Abrevignac","Amina Bralmignac","Olivier Bralmançon","Marc Luskoubre","Geneviève Abrevoubre","Raphaël Korvoubre","Pierre Mirvard","Camille Jusvumet","Samuel Cloveval","Antoine Selvière","Véronique Jusvouze","Paul Neskivet","Samuel Dulvande","Lucie Selveval","Olivier Tolvichon","Fatima Hulvard","Alexandre Ivreux","Félix Korveval","Karim Urvichon","Émilie Dulvichon","Luc Tolvouze","Amina Quilmault","François Ivrouze","Hélène Gorzumet","Sophie Ivrault","Louise Bralmière","Nicolas Korvande","Geneviève Ivrumet","Geneviève Obresson","Guillaume Obrançon","Fatima Urvignac","Claire Bralmotte","Félix Hulvignac","François Clovotte","Philippe Dulvoubre","Samuel Rozveron","Pierre Hulvoubre","Émilie Urvouze","Félix Korvançon","Émilie Neskande","Chantal Mirvumet","Alexandre Korvotte","Zoé Eskeval","Nathalie Voskoubre","Sophie Pruvière","Paul Obrichon","Nathalie Eskière","Caroline Ivreron","Véronique Neskoubre","Anne Urvivet","Olivier Hulvançon","Karim Clovançon","Zoé Quilmotte","Félix Eskouze","Amina Rozvichon","Simon Fralvière","Pierre Hulvière","Raphaël Jusvault","Pierre Fralvoubre","Hélène Selveux","Caroline Rozvançon","Olivier Korvière","Nicolas Ivrichon","Félix Korvumet","Guillaume Selvumet","Michel Urvotte","Émilie Eskault","Hélène Bralmeval","Guillaume Hulvesson","Caroline Jusvoubre","Félix Luskeux","Antoine Pruvouze","Julie Quilmignac","Hugo Voskumet","Manon Quilmumet","Marc Voskouze","Samuel Abrevard","Manon Clovesson","Simon Hulvumet","Lucie Voskeron","Jean Neskeron","Manon Pruvivet","Sophie Clovard","Étienne Gorzeval","Gabriel Luskault","Émilie Korvard","Thomas Quilmichon","Alexandre Eskançon","Émilie Luskançon","Marc Dulvault","Hélène Rozveval","Julie Pruvançon","Louis Mirveron","François Eskard","Louis Ivrançon","Karim Clovivet","Léa Rozvande","Amina Hulvouze","Étienne Eskesson","Philippe Abreveval","Émilie Quilmeux","Simon Voskotte","Chantal Obrignac","Guillaume Eskotte","Léa Obrard","Guillaume Gorzivet","Geneviève Mirvançon","Julie Selvotte","Amina Mirvesson","Isabelle Luskignac","Chloé Obrumet","Anne Abrevesson","Fatima Rozvumet","Guillaume Mirvoubre","Véronique Rozvoubre","Claire Gorzotte","Antoine Ivrière","Isabelle Selvichon","Isabelle Urvault","Julie Voskesson","Chantal Abrevotte","Amina Neskotte","Étienne Voskançon","Zoé Fralveron","Marc Rozvouze","Sylvie Abreveux","Karim Pruvignac","Zoé Mirveval","Philippe Fralvignac","Gabriel Gorzeux","Jean Urveval"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"5e94a05db66d052a94435895fb62675b9438247e0ce319cfdb38a743cdb3e547","etape":"4","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"07a-cibles-tables","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"7ee04170e649d97b27eb4136d30cfb240a5e734a1b4f22eceabc6dc9f17e1961"},"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-chaise-trait","ratio":7.58,"role":"marque","seuil":3},{"jeton":"--couleur-echelle","ratio":15.1,"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-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-cadre-lecture","ratio":7.34,"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-page-texte","ratio":14.62,"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","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","Plan","Réservations","Tour affiché","1","2","3","4","Zoom +","Zoom −","Ajuster","Listes de noms","Légende","Plein écran","5","m","disposition relative — l'encombrement dans la salle n'est pas vérifié","Diagnostic","écart d'itinéraire maximal : plancher 1, au moins 24 mobiles finissent sous leur plafond a priori","plafond a priori 24 rencontres pour 4 ancrés","plafond a priori 28 rencontres pour 29 ancrés et 227 mobiles","99 retours imposés par 33 ancrages","Listes de travail","Titres de place non pourvus","Aucun.","Titres pressentis sans réservation","Aucun.","Réserve","Personnes en attente au tour 1","Isabelle Mirvault","Félix Abrevande","Hugo Fralvançon","Manon Fralveux","Catherine Luskotte","Zoé WAMMERWOLM-MAWWEMMEL","Catherine Urvesson","Karim Hulvivet","Caroline Selveron","Catherine Korvault","Caroline Fralvotte","Geneviève Jusvesson","Paul Abrevumet","Léa Fralvault","Fatima Obrivet","Zoé Dulveval","Paul Korveux","Julie Luskière","Émilie Clovande","Paul Hulvande","Hugo Fralvesson","Isabelle Urvière","Thomas Rozvotte","Isabelle Bralmumet","Catherine Dulvouze","Marc Voskichon","Simon Mirvivet","Louise Clovault","Lucie Voskeux","Amina Eskignac","Luc Selvoubre","Étienne Fralvichon","Philippe Voskivet","Louise Quilmançon","Sophie Luskande","Pierre Abreveron","Philippe Neskeux","Amina Neskançon","Léa Tolvande","Antoine Quilmeron","Guillaume Clovumet","Michel Eskeux","Louise Hulvault","Samuel Eskumet","Claire Fralvivet","Léa Mirveux","Karim Gorzesson","Sylvie Quilmande","François Gorzichon","Amina Pruvesson","Lucie Quilmière","Fatima Korvesson","Nicolas Selvesson","Louise Neskesson","Lucie Tolvesson","Olivier Quilmesson","Guillaume Luskivet","Gabriel Neskault","Antoine Urvumet","Olivier Jusvançon","Simon Dulvançon","Chloé Gorzard","Gabriel Pruveux","Manon Clovière","Karim Tolveval","Hugo Bérêçàuvïlle-Dœûmôntëÿ","Claire Gorzeron","Hélène Luskumet","Louis Fralvande","Émilie Selvault","Léa Neskumet","Isabelle Korvichon","Michel Obrande","Isabelle Luskeval","Fatima Tolvoubre","Zoé Gorzière","Sylvie Korvignac","Antoine Obreval","Camille Tolvumet","Marie Dulvesson","Guillaume Tolvignac","Gabriel Mirvotte","Paul Neskignac","Raphaël Ivrotte","Marc Luskouze","Sylvie Jusvotte","Véronique Bralmoubre","Pierre Selvard","Marie Hulveux","Karim Fralveval","Philippe Gorzignac","Étienne Selvouze","Louis Hulvichon","Sylvie Urvoubre","Pierre Pruveval","Guillaume Hulvotte","Geneviève Abrevignac","Amina Bralmignac","Olivier Bralmançon","Marc Luskoubre","Geneviève Abrevoubre","Raphaël Korvoubre","Pierre Mirvard","Camille Jusvumet","Samuel Cloveval","Antoine Selvière","Véronique Jusvouze","Paul Neskivet","Samuel Dulvande","Lucie Selveval","Olivier Tolvichon","Fatima Hulvard","Alexandre Ivreux","Félix Korveval","Karim Urvichon","Émilie Dulvichon","Luc Tolvouze","Amina Quilmault","François Ivrouze","Hélène Gorzumet","Sophie Ivrault","Louise Bralmière","Nicolas Korvande","Geneviève Ivrumet","Geneviève Obresson","Guillaume Obrançon","Fatima Urvignac","Claire Bralmotte","Félix Hulvignac","François Clovotte","Philippe Dulvoubre","Samuel Rozveron","Pierre Hulvoubre","Émilie Urvouze","Félix Korvançon","Émilie Neskande","Chantal Mirvumet","Alexandre Korvotte","Zoé Eskeval","Nathalie Voskoubre","Sophie Pruvière","Paul Obrichon","Nathalie Eskière","Caroline Ivreron","Véronique Neskoubre","Anne Urvivet","Olivier Hulvançon","Karim Clovançon","Zoé Quilmotte","Félix Eskouze","Amina Rozvichon","Simon Fralvière","Pierre Hulvière","Raphaël Jusvault","Pierre Fralvoubre","Hélène Selveux","Caroline Rozvançon","Olivier Korvière","Nicolas Ivrichon","Félix Korvumet","Guillaume Selvumet","Michel Urvotte","Émilie Eskault","Hélène Bralmeval","Guillaume Hulvesson","Caroline Jusvoubre","Félix Luskeux","Antoine Pruvouze","Julie Quilmignac","Hugo Voskumet","Manon Quilmumet","Marc Voskouze","Samuel Abrevard","Manon Clovesson","Simon Hulvumet","Lucie Voskeron","Jean Neskeron","Manon Pruvivet","Sophie Clovard","Étienne Gorzeval","Gabriel Luskault","Émilie Korvard","Thomas Quilmichon","Alexandre Eskançon","Émilie Luskançon","Marc Dulvault","Hélène Rozveval","Julie Pruvançon","Louis Mirveron","François Eskard","Louis Ivrançon","Karim Clovivet","Léa Rozvande","Amina Hulvouze","Étienne Eskesson","Philippe Abreveval","Émilie Quilmeux","Simon Voskotte","Chantal Obrignac","Guillaume Eskotte","Léa Obrard","Guillaume Gorzivet","Geneviève Mirvançon","Julie Selvotte","Amina Mirvesson","Isabelle Luskignac","Chloé Obrumet","Anne Abrevesson","Fatima Rozvumet","Guillaume Mirvoubre","Véronique Rozvoubre","Claire Gorzotte","Antoine Ivrière","Isabelle Selvichon","Isabelle Urvault","Julie Voskesson","Chantal Abrevotte","Amina Neskotte","Étienne Voskançon","Zoé Fralveron","Marc Rozvouze","Sylvie Abreveux","Karim Pruvignac","Zoé Mirveval","Philippe Fralvignac","Gabriel Gorzeux","Jean Urveval"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"03fa9a9b0f1b629d6d43dd536ae6da66ddfb105dd8bd6a68f65807235644183d","etape":"5","etat":"7e19a3b61721017693ee5895e1b4a82e2f1656e35e71989ac4d29ea9cfc59fe8","facteurEchelle":1,"fichier":"07b-nombre-de-tours","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"d49f53ecabd801b0b4681d5c4ccbd277c71ebe2b15c46e6d77e08a741faf4a90"},"instant":"2026-10-06T19:00:00-04:00","mode":"chromium-headless-shell","pdf":null,"source":"navigateur","styles":{"clair":[{"jeton":"--couleur-page-texte","ratio":15.1,"role":"texte","seuil":4.5}],"sombre":[{"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","Tables et tours","Places par défaut","Nombre de tours","Passer de 3 à 2 tours ? Ce changement emporte :","Basile Lanterneau : réservation à la table 1 au tour 3, détruite","Annuler","Confirmer","Tables","Ajouter une table ronde","Ajouter une table carrée","Table","1","Ronde","Suit le défaut","Places","Supprimer","Table","2","Ronde","Surchargée","Places","Réaligner","Supprimer"],"theme":"clair","versionReelle":"2026.10.05.01"}, +{"cadre":{"hauteur":700,"largeur":1366},"empreinte":"683bc10e0c646219a177996b828093c6033d6ae959c82e6528967f61151b8227","etape":"6","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"08-animateurs-designes","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"86e95804c0abe8acbe9b34fa0a43642ad26b39e7a56a6e335603c167988a49c2"},"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","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":"a0193f21920d9a2d6740a92d8fe78cb2d5d8d551be7c3cbc4f8c31aabe511add","etape":"7","etat":"0ad50f28307185683e15e5660c2da2f3398d783d68988d637c519ef3ba0658a5","facteurEchelle":1,"fichier":"09-refus-sur-place","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"4eb952f4bba4537ca9c306f59c39eab22b4ee1d9c7cd2e2a7f40735a888eb6c1"},"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","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":"7ecb8436808e6bb30b06224c9fcd64bd94b58a6b7142d1e5d4252e6ff4a76bc0","etape":"8","etat":"4d37d30748068354162752743c26aa7e0ab00d6e9b2aa1996b6f9fb410848280","facteurEchelle":1,"fichier":"10-diagnostic","graine":null,"image":{"hauteur":700,"largeur":1366,"sha256":"fa29e3517f71a8001d18734854f1f03fe908fe060727aa995b2f7d38ec39766e"},"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","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":"5469971810061d85c542f58b14f231f6238d16087cee4311155e9f6c3c140a4b","etape":"9","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"11-propositions","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"fa6d9de0b2e35b38cf40e343b99000f03e4cbf20081a93d21b67e2358152ce9d"},"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","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":"56511b4b7b473f6c7532a44d940149ad7ace45451fb5c1fb8276813790bad305","etape":"11","etat":"78c47299a88ae35b3dc4c09bededdd6e0e9dc695a35d313d74f46c9cbede5573","facteurEchelle":1,"fichier":"19-ajustement","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"c51555af8cfa902df73c13e69111b42371d9e665175ccb4d69a5a434c5f85187"},"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","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":"169488d265a583d3459bec989f0f4ab8a0526431e546cf5a130374df469e22ae","etape":"11","etat":"20666d70d9b2503335efc1f2cece5453a199ab77f4864835baf4bccf5aff17d5","facteurEchelle":1,"fichier":"20-mode-lecture","graine":4242,"image":{"hauteur":700,"largeur":1366,"sha256":"ca2ab13caba0f64fc26749675dd5a5a1b2319240ea259ac5cf08f519ffd0eb05"},"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","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"} ],"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/src/interface/App.svelte b/src/interface/App.svelte index e6a4694..44a8ccc 100644 --- a/src/interface/App.svelte +++ b/src/interface/App.svelte @@ -28,10 +28,13 @@ // événement après l'autre, et l'accueil montre chacun dès qu'il arrive. // // L'écran suit la séance : la liste sans événement ouvert, l'événement - // sinon ; le panneau « À propos » les remplace tant qu'il est ouvert. Chaque - // ouverture d'un événement remet l'état de navigation à son départ. Le - // bandeau, présent sur tout écran, ne défile pas : un écran plus haut que - // la fenêtre défile sous lui. + // sinon ; le panneau « À propos » les remplace tant qu'il est ouvert, et + // l'aperçu avant impression remplace l'événement tant que l'état de + // navigation en porte un (§ 11.7). Fermer l'aperçu rend la section d'où il + // s'est ouvert et le focus à sa commande « Imprimer… ». Chaque ouverture + // d'un événement remet l'état de navigation à son départ. Le bandeau, + // présent sur tout écran, ne défile pas : un écran plus haut que la + // fenêtre défile sous lui. // // Le thème part de la propriété themeInitial, ou de la préférence du // système lue au montage ; le réglage de l'opérateur le change pour la @@ -52,6 +55,7 @@ import { creerPas } from './accueil/chargement.js'; import Bandeau from './Bandeau.svelte'; import Evenement from './evenement/Evenement.svelte'; + import Apercu from './impression/Apercu.svelte'; import { blocAPropos, provenanceVisible, versionVisible } from './apropos.js'; import { creerEtatUi } from './etat_ui.js'; import { TABLES_INTERFACE } from './libelles.js'; @@ -245,6 +249,17 @@ const version = versionVisible(provenance, produitVersion, t); const provenanceAffichee = provenanceVisible(provenance, t); + // L'élément racine de l'application, où le focus se rend après l'aperçu. + let racineApp = $state(null); + + // Ferme l'aperçu : l'événement revient sur la section d'origine, et le + // focus sur la commande « Imprimer… » de sa tête. + async function fermerApercu() { + uiApp.fermerApercu(); + await tick(); + racineApp?.querySelector('[data-ecran="evenement"] [data-commande="apercu"]')?.focus(); + } + // « Liste des événements » : ferme l'événement, sans question ; sous un // support rompu, la séance le refuse, et le refus s'affiche à côté de la // commande. @@ -302,7 +317,7 @@ {titre} -
+
uiApp.fermerApropos()} /> {:else if etatSeance.evenement === null} + {:else if $uiApp.apercu !== null} + {:else} {/if} diff --git a/src/interface/etat_ui.js b/src/interface/etat_ui.js index bf72575..d5d150b 100644 --- a/src/interface/etat_ui.js +++ b/src/interface/etat_ui.js @@ -5,8 +5,9 @@ // propos », le tour montré, la personne mise en évidence, la proposition // demandée au plan, le texte d'un import en attente, les listes de noms, le // plein écran, la place sélectionnée, la fiche ouverte, le choix en attente -// du plan, le refus posé sur une place et l'appartenance collante du -// formulaire d'ajout. Ce qu'il tient survit au démontage des sections. +// du plan, le refus posé sur une place, l'appartenance collante du +// formulaire d'ajout et l'aperçu avant impression. Ce qu'il tient survit au +// démontage des sections. // Regarder n'écrit jamais (§ 8.4) : cet état ne reçoit ni la séance, ni la // façade, ni un système de fichiers, et rien de ce qu'il tient ne quitte la // mémoire. @@ -42,6 +43,12 @@ import { SECTION_OUVERTURE, SECTIONS } from './evenement/sections.js'; * Plan.svelte montre sur la place (§ 5.9) * @property {string} appartenance l'appartenance collante du formulaire d'ajout * des participants : celle du dernier ajout (§ 10.2), '' au départ + * @property {null|{vue: 'plan'|'accueil'|'tables', tour: number|'tous', listes: boolean, + * decoupe: 'auto'|'unePage'|'decoupe', parTour: boolean, retour: string}} apercu + * l'aperçu avant impression ouvert (§ 11.7), null fermé : la vue imprimée ; le + * tour du plan, 'tous' pour une vue de la soirée, jamais pour le plan ; les + * listes de noms du plan ; sa découpe en pages ; la feuille par table, une + * par tour ou la soirée en colonnes ; la section où l'aperçu se referme */ // L'état de navigation d'un événement qui s'ouvre, panneau « À propos » @@ -59,6 +66,22 @@ const DEPART = Object.freeze({ choix: null, pose: null, appartenance: '', + apercu: null, +}); + +/** Les vues que l'aperçu imprime (§ 11.7). */ +export const VUES_IMPRESSION = Object.freeze(['plan', 'accueil', 'tables']); + +/** Les découpes du plan imprimé, celles de decouperPlan (src/geometrie/pages.js). */ +export const DECOUPES = Object.freeze(['auto', 'unePage', 'decoupe']); + +// Le domaine de chaque réglage de l'aperçu que reglerApercu change. +const REGLAGES_APERCU = Object.freeze({ + vue: (v) => VUES_IMPRESSION.includes(v), + tour: (v) => v === 'tous' || entierPositif(v), + listes: (v) => typeof v === 'boolean', + decoupe: (v) => DECOUPES.includes(v), + parTour: (v) => typeof v === 'boolean', }); const GENRES_CHOIX = new Set(['personne', 'reservation']); @@ -71,6 +94,23 @@ const entierPositif = (n) => Number.isInteger(n) && n >= 1; const estPlace = (place) => place !== null && typeof place === 'object' && entierPositif(place.table) && (place.siege === null || entierPositif(place.siege)); +// L'aperçu que donnent les changements reçus sur courant, figé ; RangeError +// pour un réglage inconnu ou hors de son domaine, et pour le plan à « tous » : +// le plan s'imprime tour par tour. +function apercuValide(courant, changements) { + if (changements === null || typeof changements !== 'object') { + throw new RangeError(`aperçu : réglages attendus, reçu ${String(changements)}`); + } + for (const [nom, valeur] of Object.entries(changements)) { + if (!Object.hasOwn(REGLAGES_APERCU, nom) || !REGLAGES_APERCU[nom](valeur)) { + throw new RangeError(`aperçu : ${nom} hors du domaine, reçu ${JSON.stringify(valeur)}`); + } + } + const suivant = { ...courant, ...changements }; + if (suivant.vue === 'plan' && suivant.tour === 'tous') throw new RangeError('aperçu : le plan s’imprime à un tour, non à « tous »'); + return Object.freeze(suivant); +} + // Le choix du contrat, figé ; RangeError hors du contrat. function choixValide(choix) { const forme = choix !== null && typeof choix === 'object'; @@ -103,7 +143,8 @@ function choixValide(choix) { * prendreImport: () => null|{texte: string, origine: string}, * basculerListes: Function, basculerPleinEcran: Function, * selectionner: Function, ouvrirFiche: Function, choisir: Function, - * poserSurPlace: Function, garderAppartenance: Function}} + * poserSurPlace: Function, garderAppartenance: Function, + * ouvrirApercu: Function, reglerApercu: Function, fermerApercu: Function}} */ export function creerEtatUi() { let etat = Object.freeze({ ...DEPART, apropos: false }); @@ -203,6 +244,30 @@ export function creerEtatUi() { if (typeof texte !== 'string') throw new RangeError(`appartenance : chaîne attendue, reçu ${String(texte)}`); changer({ appartenance: texte }); }, + /** Ouvre l'aperçu avant impression sur la vue et le tour reçus : les + * listes du plan telles que l'écran les montre, la découpe proposée par + * défaut, la feuille par table couvrant la soirée ; la section courante + * est celle où il se refermera. RangeError hors du domaine + * (reglerApercu), sans rien changer. */ + ouvrirApercu(demande) { + if (demande === null || typeof demande !== 'object' || !Object.hasOwn(demande, 'vue') || !Object.hasOwn(demande, 'tour')) { + throw new RangeError(`aperçu : { vue, tour } attendu, reçu ${JSON.stringify(demande)}`); + } + const depart = { vue: 'plan', tour: 1, listes: etat.listes, decoupe: 'auto', parTour: false, retour: etat.section }; + changer({ apercu: apercuValide(depart, { vue: demande.vue, tour: demande.tour }) }); + }, + /** Change les réglages de l'aperçu ouvert — vue, tour, listes, decoupe, + * parTour — chacun dans son domaine ; RangeError hors de lui, pour un + * autre nom, ou sans aperçu ouvert, sans rien changer. */ + reglerApercu(changements) { + if (etat.apercu === null) throw new RangeError('aperçu : aucun aperçu ouvert'); + changer({ apercu: apercuValide(etat.apercu, changements) }); + }, + /** Ferme l'aperçu et rend la section d'où il s'est ouvert. */ + fermerApercu() { + if (etat.apercu === null) return; + changer({ apercu: null, section: etat.apercu.retour }); + }, /** Ouvre la fiche de la personne id, entier positif, dans la section des * participants ; null referme la fiche, la section reste. RangeError * sinon. */ diff --git a/src/interface/etat_ui.test.js b/src/interface/etat_ui.test.js index 361cfdb..ef9f46c 100644 --- a/src/interface/etat_ui.test.js +++ b/src/interface/etat_ui.test.js @@ -24,6 +24,7 @@ const DEPART = { choix: null, pose: null, appartenance: '', + apercu: null, }; // Un état et le relevé de ce que reçoit son abonné. @@ -308,3 +309,97 @@ describe('état de navigation : le choix en attente (§ 5.8, § 7.1)', () => { assert.equal(ui.etat().pose, null, 'une autre section'); }); }); + +describe('état de navigation : l’aperçu avant impression (§ 11.7)', () => { + test('ouvrirApercu ouvre la vue demandée au tour demandé, les listes du plan, la découpe proposée, la soirée en colonnes, et retient la section d’origine ; l’état est figé', () => { + const ui = creerEtatUi(); + ui.allerA('propositions'); + ui.basculerListes(); + ui.ouvrirApercu({ vue: 'plan', tour: 2 }); + assert.deepEqual(ui.etat(), { + ...DEPART, + section: 'propositions', + listes: false, + apercu: { vue: 'plan', tour: 2, listes: false, decoupe: 'auto', parTour: false, retour: 'propositions' }, + }); + assert.ok(Object.isFrozen(ui.etat().apercu)); + ui.ouvrirApercu({ vue: 'accueil', tour: 'tous' }); + assert.deepEqual(ui.etat().apercu, { vue: 'accueil', tour: 'tous', listes: false, decoupe: 'auto', parTour: false, retour: 'propositions' }); + }); + + test('ouvrirApercu refuse une vue inconnue, un tour qui n’est ni un entier positif ni « tous », et « tous » pour le plan, sans rien changer', () => { + const ui = creerEtatUi(); + const avant = ui.etat(); + const fautes = [ + { vue: 'badges', tour: 1 }, + { vue: 'Plan', tour: 1 }, + { vue: 'plan', tour: 0 }, + { vue: 'plan', tour: 1.5 }, + { vue: 'plan', tour: '2' }, + { vue: 'plan', tour: 'tous' }, + { vue: 'tables', tour: null }, + { tour: 1 }, + null, + undefined, + ]; + for (const faute of fautes) assert.throws(() => ui.ouvrirApercu(faute), RangeError, JSON.stringify(faute)); + assert.equal(ui.etat(), avant); + }); + + test('reglerApercu change la vue, le tour, les listes, la découpe et la feuille par tour, chacun dans son domaine ; hors de lui, ou sans aperçu ouvert, RangeError sans rien changer', () => { + const ui = creerEtatUi(); + assert.throws(() => ui.reglerApercu({ vue: 'accueil' }), RangeError); + ui.ouvrirApercu({ vue: 'plan', tour: 1 }); + ui.reglerApercu({ tour: 3 }); + ui.reglerApercu({ listes: false, decoupe: 'unePage' }); + ui.reglerApercu({ vue: 'tables', parTour: true }); + assert.deepEqual(ui.etat().apercu, { vue: 'tables', tour: 3, listes: false, decoupe: 'unePage', parTour: true, retour: DEPART.section }); + assert.ok(Object.isFrozen(ui.etat().apercu)); + const avant = ui.etat(); + for (const faute of [ + { vue: 'badges' }, + { tour: 0 }, + { listes: 'oui' }, + { decoupe: 'deux' }, + { parTour: 1 }, + { retour: 'plan' }, + { inconnu: true }, + null, + ]) { + assert.throws(() => ui.reglerApercu(faute), RangeError, JSON.stringify(faute)); + } + ui.reglerApercu({ vue: 'accueil', tour: 'tous' }); + assert.throws(() => ui.reglerApercu({ vue: 'plan' }), RangeError, 'le plan à « tous »'); + ui.reglerApercu({ vue: 'plan', tour: 2 }); + assert.equal(ui.etat().apercu.vue, 'plan'); + assert.notEqual(ui.etat(), avant); + }); + + test('fermerApercu ferme l’aperçu et rend la section d’où il s’est ouvert ; fermé, il ne publie rien', () => { + const { ui, recus } = suivi(); + ui.allerA('participants'); + ui.ouvrirApercu({ vue: 'plan', tour: 1 }); + ui.fermerApercu(); + assert.deepEqual(ui.etat(), { ...DEPART, section: 'participants' }); + const publies = recus.length; + ui.fermerApercu(); + assert.equal(recus.length, publies); + }); + + test('fermerApercu rend la section d’origine même quand une autre s’est ouverte pendant l’aperçu — par le remède d’un refus', () => { + const ui = creerEtatUi(); + ui.allerA('tables'); + ui.ouvrirApercu({ vue: 'accueil', tour: 'tous' }); + ui.allerA('propositions'); + ui.fermerApercu(); + assert.equal(ui.etat().section, 'tables'); + assert.equal(ui.etat().apercu, null); + }); + + test('reinitialiser ferme l’aperçu : un autre événement s’ouvre sur sa section d’ouverture', () => { + const ui = creerEtatUi(); + ui.ouvrirApercu({ vue: 'tables', tour: 'tous' }); + ui.reinitialiser(); + assert.equal(ui.etat().apercu, null); + }); +}); diff --git a/src/interface/evenement/EnteteEvenement.svelte b/src/interface/evenement/EnteteEvenement.svelte index 27d17ab..c204d90 100644 --- a/src/interface/evenement/EnteteEvenement.svelte +++ b/src/interface/evenement/EnteteEvenement.svelte @@ -12,12 +12,17 @@ // sous lui. Le nom se coupe où il le faut, même sans espace : un nom long // de glyphes larges passe à la ligne au lieu d'élargir la page. Chaque état // est un bloc : la commande et son refus en sont. + // + // « Imprimer… » ouvre l'aperçu avant impression (§ 11.7) sur le plan, au + // tour montré : toute impression passe par lui. Vivante dans tout mode, + // elle n'écrit rien. import { getContext } from 'svelte'; import Commande from '../primitives/Commande.svelte'; import { ETATS_PLAN, cleEtat } from './modele.js'; const t = getContext('t'); const seance = getContext('seance'); + const ui = getContext('ui'); const courant = $derived($seance.evenement?.etat ?? null); @@ -31,6 +36,7 @@
{/each}
+ ui.ouvrirApercu({ vue: 'plan', tour: $ui.tour })} /> diff --git a/src/interface/impression/Feuille.svelte b/src/interface/impression/Feuille.svelte new file mode 100644 index 0000000..9bf63c9 --- /dev/null +++ b/src/interface/impression/Feuille.svelte @@ -0,0 +1,75 @@ + + + + +
+
+ {#if !gabarit} + {@render contenu?.()} + {/if} +
+ {@render bande?.()} + + {#if feuille.filigrane && !gabarit} + + {/if} +
+ + diff --git a/src/interface/impression/FeuillesTables.svelte b/src/interface/impression/FeuillesTables.svelte new file mode 100644 index 0000000..676bef6 --- /dev/null +++ b/src/interface/impression/FeuillesTables.svelte @@ -0,0 +1,34 @@ + + + + + + {#snippet contenu()} +

{t('impression.tables.titre')}

+ {/snippet} +
+ + diff --git a/src/interface/impression/Filigrane.svelte b/src/interface/impression/Filigrane.svelte new file mode 100644 index 0000000..f3a2442 --- /dev/null +++ b/src/interface/impression/Filigrane.svelte @@ -0,0 +1,61 @@ + + + + + + {t('impression.filigrane')} + + + diff --git a/src/interface/impression/ListeAccueil.svelte b/src/interface/impression/ListeAccueil.svelte new file mode 100644 index 0000000..0a53ca9 --- /dev/null +++ b/src/interface/impression/ListeAccueil.svelte @@ -0,0 +1,34 @@ + + + + + + {#snippet contenu()} +

{t('impression.accueil.titre')}

+ {/snippet} +
+ + diff --git a/src/interface/impression/PiedDePage.svelte b/src/interface/impression/PiedDePage.svelte new file mode 100644 index 0000000..91443f1 --- /dev/null +++ b/src/interface/impression/PiedDePage.svelte @@ -0,0 +1,62 @@ + + + + + + + diff --git a/src/interface/impression/PlanImprime.svelte b/src/interface/impression/PlanImprime.svelte new file mode 100644 index 0000000..1bc4a8e --- /dev/null +++ b/src/interface/impression/PlanImprime.svelte @@ -0,0 +1,62 @@ + + + + +{#if decoupe === null} + +{:else} + {#each decoupe.pages as page (page.rang)} + + {#snippet contenu()} +

{t('impression.plan.page', { page: page.cartouche.page, total: page.cartouche.total })}

+ + {/snippet} +
+ {/each} +{/if} + + diff --git a/src/interface/impression/modele.js b/src/interface/impression/modele.js new file mode 100644 index 0000000..09de4b9 --- /dev/null +++ b/src/interface/impression/modele.js @@ -0,0 +1,258 @@ +// © 2026 TechnoLibre (http://www.technolibre.ca) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +// Le modèle de l'aperçu avant impression (§ 11.7, § 8.5, § 18.6), sans DOM : +// ce qu'une vue imprime — papier, orientation, tour —, les dimensions d'une +// page et de sa zone de contenu, la bande mesurée, la page nommée que la +// feuille d'impression déclare pour chaque papier et chaque orientation, le +// pied de cinq éléments et le filigrane d'une feuille, la source que +// l'aperçu imprime et la découpe du plan. Le modèle ne rend que des +// nombres, des codes, des clés et des données ; les composants les +// traduisent et les posent. +import { etatPlaces } from '../../application/places.js'; +import { decouperPlan } from '../../geometrie/pages.js'; +import { PAPIERS } from '../../geometrie/papiers.js'; +import { englobantImpression, geometrieTable } from '../../geometrie/tables.js'; +import { DECOUPES, VUES_IMPRESSION } from '../etat_ui.js'; + +/** Millimètres par pixel CSS : 96 px au pouce, 25,4 mm au pouce. */ +export const MM_PAR_PX = 25.4 / 96; + +/** Les papiers que l'aperçu offre, ceux de PAPIERS, dans leur ordre. */ +export const PAPIERS_OFFERTS = Object.freeze(Object.keys(PAPIERS)); + +const ORIENTATIONS = Object.freeze(['portrait', 'paysage']); + +// Le diviseur de la diagonale de la page qui donne le corps du filigrane : +// « BROUILLON » couvre environ 6 corps de Noto Sans en capitales, et tient +// alors dans la diagonale avec sa marge. +const DIVISEUR_CORPS_FILIGRANE = 9; + +// L'épaisseur du contour du filigrane, en mm de papier. +const TRAIT_FILIGRANE_MM = 0.6; + +function exigerPapier(papier) { + if (typeof papier !== 'string' || !Object.hasOwn(PAPIERS, papier)) { + throw new RangeError(`papier inconnu : ${JSON.stringify(papier)}`); + } +} + +function exigerOrientation(orientation) { + if (!ORIENTATIONS.includes(orientation)) throw new RangeError(`orientation inconnue : ${JSON.stringify(orientation)}`); +} + +const nombreFini = (valeur) => typeof valeur === 'number' && Number.isFinite(valeur); + +/** + * Ce que l'aperçu imprime, tiré de son état (EtatUi.apercu) et du papier de + * la machine (EtatSeance.papier). Les listes — liste d'accueil, feuilles + * par table — s'impriment en portrait et couvrent la soirée ; le plan + * s'imprime à son tour, dans l'orientation que choisit sa découpe (null + * ici). Sans papier choisi, question est vrai : l'aperçu pose la question + * du papier avant de rien mettre en page (§ 8.5, § 11.2). + * + * @param {{vue: string, tour: number|'tous', listes: boolean, decoupe: string, parTour: boolean}} apercu + * @param {'A4'|'Lettre'|null} papier + * @returns {{vue: string, papier: string|null, orientation: 'portrait'|null, tour: number|'tous', + * listes: boolean, decoupe: string, parTour: boolean, question: boolean}} + * @throws {TypeError} sans aperçu ouvert + * @throws {RangeError} une vue ou un papier hors de leur domaine + */ +export function optionsApercu(apercu, papier) { + if (apercu === null || typeof apercu !== 'object') throw new TypeError('optionsApercu : aucun aperçu ouvert'); + if (!VUES_IMPRESSION.includes(apercu.vue)) throw new RangeError(`vue inconnue : ${JSON.stringify(apercu.vue)}`); + if (papier !== null) exigerPapier(papier); + const plan = apercu.vue === 'plan'; + return { + vue: apercu.vue, + papier, + orientation: plan ? null : 'portrait', + tour: plan ? apercu.tour : 'tous', + listes: apercu.listes, + decoupe: apercu.decoupe, + parTour: apercu.parTour, + question: papier === null, + }; +} + +/** Les tours que le plan imprimé offre : de 1 à tours, dans l'ordre. */ +export function toursOfferts(tours) { + if (!Number.isSafeInteger(tours) || tours < 1) throw new RangeError(`tours : entier ≥ 1 attendu, reçu ${String(tours)}`); + return Array.from({ length: tours }, (_, rang) => rang + 1); +} + +/** Les dimensions d'une page, en mm : le papier, couché en paysage. */ +export function dimensionsPage(papier, orientation) { + exigerPapier(papier); + exigerOrientation(orientation); + const { largeur, hauteur } = PAPIERS[papier]; + return orientation === 'portrait' ? { largeur, hauteur } : { largeur: hauteur, hauteur: largeur }; +} + +/** Les clés de libellé d'un papier, sous son nom en minuscules : son nom + * seul, et le choix qui le décrit. */ +export function clesPapier(papier) { + exigerPapier(papier); + const cle = papier.toLowerCase(); + return { nom: `impression.papier.nom.${cle}`, choix: `impression.papier.choix.${cle}` }; +} + +/** La clé du libellé d'une vue imprimée. */ +export function cleVue(vue) { + if (!VUES_IMPRESSION.includes(vue)) throw new RangeError(`vue inconnue : ${JSON.stringify(vue)}`); + return `impression.vue.${vue}`; +} + +/** La clé du libellé d'une découpe du plan. */ +export function cleDecoupe(decoupe) { + if (!DECOUPES.includes(decoupe)) throw new RangeError(`découpe inconnue : ${JSON.stringify(decoupe)}`); + return `impression.decoupe.${decoupe}`; +} + +/** + * Le nom de la page que la feuille d'impression déclare pour un papier et + * une orientation, le papier en minuscules : impression.css porte une règle + * @page de ce nom, à la taille du papier, sans marge, et chaque feuille la + * demande par sa propriété page. + */ +export function nomPage(papier, orientation) { + exigerPapier(papier); + exigerOrientation(orientation); + return `gtt-${papier.toLowerCase()}-${orientation}`; +} + +/** + * La hauteur de la zone de contenu d'une feuille, en mm : la page, moins la + * marge de chaque bord, moins la bande — ce que la feuille porte hors de + * son contenu, mesuré sur la feuille gabarit. La découpe du plan reçoit la + * même bande : la zone de contenu est la hauteur utile d'une page. + * + * @throws {RangeError} une marge ou une bande négative ou non finie ; une + * page sans contenu + */ +export function hauteurContenu({ papier, orientation, margeMm, bandeMm }) { + const { hauteur } = dimensionsPage(papier, orientation); + for (const [nom, valeur] of [['marge', margeMm], ['bande', bandeMm]]) { + if (!nombreFini(valeur) || valeur < 0) throw new RangeError(`${nom} : nombre fini ≥ 0 attendu, reçu ${String(valeur)}`); + } + const reste = hauteur - 2 * margeMm - bandeMm; + if (!(reste > 0)) throw new RangeError(`page ${papier} ${orientation} sans contenu : ${reste} mm`); + return reste; +} + +/** + * La bande d'une feuille gabarit, en mm (HAUTEUR_BANDE_IMPRESSION, § 14.1) : + * tout ce qu'elle porte hors de sa zone de contenu — pied, et ce que la vue + * y ajoute —, soit sa hauteur rendue moins celle du contenu, moins la marge + * de chaque bord, que la feuille porte en retrait. + * + * @param {{feuillePx: number, contenuPx: number, margeMm: number}} mesure px CSS + * @returns {number} + * @throws {RangeError} une mesure non finie, une bande négative + */ +export function bandeDeFeuille({ feuillePx, contenuPx, margeMm }) { + const bande = (feuillePx - contenuPx) * MM_PAR_PX - 2 * margeMm; + if (!nombreFini(bande) || bande < 0) { + throw new RangeError(`bande : la feuille de ${feuillePx} px ne porte pas son contenu de ${contenuPx} px et ses marges de ${margeMm} mm`); + } + return bande; +} + +// Un élément du pied : son libellé traduit, ou null ; sa donnée, ou null ; +// la forme sous laquelle la donnée s'écrit. +const element = (nom, libelle, donnee = null, forme = 'texte') => ({ element: nom, libelle, donnee, forme }); +const libelle = (cle, details = {}) => ({ cle, details }); + +/** + * La feuille d'un pied (piedDePage, src/application/sorties.js) : ses cinq + * éléments dans l'ordre du § 11.7 — nom de l'événement, date, tour, + * horodatage du placement, version —, chacun un libellé à traduire et une + * donnée à poser telle quelle (forme 'texte') ou à mettre en forme ('date', + * 'horodatage'), et le filigrane, qui suit brouillon (estBrouillon). Une + * date ou un horodatage absents s'écrivent par un libellé qui le dit. + * + * @param {{nom: string, date: string|null, tour: number|'tous', horodatage: string|null, version: string}} pied + * @param {boolean} brouillon + * @returns {{elements: Array<{element: string, libelle: {cle: string, details: Object}|null, + * donnee: string|null, forme: 'texte'|'date'|'horodatage'}>, filigrane: boolean}} + * @throws {TypeError} un pied hors du contrat, un brouillon non booléen + */ +export function feuilleDe(pied, brouillon) { + const { nom, date, tour, horodatage, version } = pied; + const texteOuNull = (v) => v === null || typeof v === 'string'; + const valide = + typeof nom === 'string' && + typeof version === 'string' && + texteOuNull(date) && + texteOuNull(horodatage) && + (tour === 'tous' || (Number.isSafeInteger(tour) && tour >= 1)); + if (!valide) throw new TypeError(`feuilleDe : pied hors du contrat ${JSON.stringify(pied)}`); + if (typeof brouillon !== 'boolean') throw new TypeError(`feuilleDe : brouillon booléen attendu, reçu ${String(brouillon)}`); + return { + elements: [ + element('nom', null, nom), + date === null ? element('date', libelle('impression.pied.sansDate')) : element('date', null, date, 'date'), + element('tour', tour === 'tous' ? libelle('impression.pied.tousLesTours') : libelle('impression.pied.tour', { tour })), + horodatage === null + ? element('horodatage', libelle('impression.pied.placementInconnu')) + : element('horodatage', libelle('impression.pied.placement'), horodatage, 'horodatage'), + element('version', libelle('impression.pied.version'), version), + ], + filigrane: brouillon, + }; +} + +/** + * Le filigrane d'une page, en mm de papier : la page entière pour cadre, + * son centre, l'angle de sa diagonale montante, en degrés — négatif, le + * sens de rotation de SVG —, le corps du texte, tiré de la diagonale, et + * l'épaisseur de son contour. + */ +export function filigraneDe(papier, orientation) { + const { largeur, hauteur } = dimensionsPage(papier, orientation); + return { + largeur, + hauteur, + x: largeur / 2, + y: hauteur / 2, + angle: -(Math.atan2(hauteur, largeur) * 180) / Math.PI, + corps: Math.hypot(largeur, hauteur) / DIVISEUR_CORPS_FILIGRANE, + trait: TRAIT_FILIGRANE_MM, + }; +} + +/** + * La sortie que l'aperçu imprime : l'état des places de son tour + * (etatPlaces) — le tour du plan, borné aux tours de la soirée, ou le + * premier pour une vue de la soirée —, sa source et la proposition qu'elle + * désigne, null hors de la source proposition, et le tour du pied. + * + * @param {Object} charge + * @param {{tour: number|'tous'}} options celles d'optionsApercu + * @param {number|null} proposition la proposition demandée au plan + * @returns {{etat: Object, source: 'retenu'|'proposition'|'reservations', proposition: number|null, + * tour: number|'tous'}} + */ +export function sortieApercu(charge, { tour }, proposition) { + const tourEtat = tour === 'tous' ? 1 : Math.min(tour, charge.evenement.tours); + const etat = etatPlaces(charge, { tour: tourEtat, proposition }); + return { + etat, + source: etat.source, + proposition: etat.source === 'proposition' ? etat.proposition : null, + tour: tour === 'tous' ? 'tous' : tourEtat, + }; +} + +/** + * La découpe du plan d'un tour en pages (decouperPlan, § 11.7) : les + * géométries des tables de l'état des places, à leur capacité, leur + * englobant, et la bande mesurée sur la feuille gabarit. + * + * @param {Object} etat EtatPlaces + * @param {{papier: string, bandeMm: number, decoupe: string}} options + */ +export function decoupeDuPlan(etat, { papier, bandeMm, decoupe }) { + const geometries = etat.tables.map((table) => geometrieTable(table, table.capacite)); + return decouperPlan(englobantImpression(geometries), { papier, bandeMm, mode: decoupe, geometries }); +} diff --git a/src/interface/impression/modele.test.js b/src/interface/impression/modele.test.js new file mode 100644 index 0000000..5860a1b --- /dev/null +++ b/src/interface/impression/modele.test.js @@ -0,0 +1,273 @@ +// © 2026 TechnoLibre (http://www.technolibre.ca) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +// Le modèle de l'aperçu avant impression (§ 11.7, § 8.5, § 18.6), sans DOM : +// les options d'une vue, la feuille et son pied, la bande mesurée, les pages +// nommées de la feuille d'impression et le filigrane. Les libellés que le +// modèle désigne existent, en français comme en langue témoin. +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { describe, test } from '../../../test/lanceur.js'; +import { TABLES_APPLICATION } from '../../application/libelles.js'; +import { etatPlaces } from '../../application/places.js'; +import { MARQUE_TEMOIN, creerTraduction } from '../../application/traduction.js'; +import { constante } from '../../geometrie/constantes.js'; +import { decouperPlan } from '../../geometrie/pages.js'; +import { PAPIERS } from '../../geometrie/papiers.js'; +import { englobantImpression, geometrieTable } from '../../geometrie/tables.js'; +import { texteLivre } from '../../demo/livrees.js'; +import { analyser } from '../../stockage/document.js'; +import { TABLES_INTERFACE } from '../libelles.js'; +import { + MM_PAR_PX, + PAPIERS_OFFERTS, + bandeDeFeuille, + clesPapier, + cleDecoupe, + cleVue, + decoupeDuPlan, + dimensionsPage, + feuilleDe, + filigraneDe, + hauteurContenu, + nomPage, + optionsApercu, + sortieApercu, + toursOfferts, +} from './modele.js'; + +const APERCU = Object.freeze({ vue: 'plan', tour: 2, listes: true, decoupe: 'auto', parTour: false, retour: 'plan' }); +const PIED = Object.freeze({ nom: 'Soirée des Merles', date: '2026-10-09', tour: 2, horodatage: '2026-10-09T19:00:00-04:00', version: 'v' }); + +const petite = () => analyser(texteLivre('petite')).charge; + +// La petite démonstration et une proposition écrite à la main, d'un seul +// tour répété : chaque table garde ses trois personnes. +function avecProposition() { + const charge = structuredClone(petite()); + const tables = charge.tables.map(({ id }) => id); + const sieges = [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]; + charge.propositions = [ + { + id: 1, + graine: 1, + arret: 300, + historique: 1000, + produitVersion: 'v', + siegesAttribues: false, + tables, + capacites: tables.map(() => 3), + tours: charge.evenement.tours, + participants: sieges.flat(), + placement: Array.from({ length: charge.evenement.tours }, () => ({ sieges: sieges.map((s) => [...s]), reserve: [] })), + }, + ]; + charge.prochainsIds.proposition = 2; + return charge; +} + +describe('optionsApercu : ce que l’aperçu imprime', () => { + test('le plan : son tour, ses listes, sa découpe ; l’orientation, celle que la découpe choisira (null)', () => { + assert.deepEqual(optionsApercu(APERCU, 'A4'), { + vue: 'plan', papier: 'A4', orientation: null, tour: 2, listes: true, decoupe: 'auto', parTour: false, question: false, + }); + }); + + test('les listes s’impriment en portrait et couvrent la soirée, quel que soit le tour du plan', () => { + for (const vue of ['accueil', 'tables']) { + const options = optionsApercu({ ...APERCU, vue, parTour: true }, 'Lettre'); + assert.equal(options.orientation, 'portrait', vue); + assert.equal(options.tour, 'tous', vue); + assert.equal(options.papier, 'Lettre', vue); + assert.equal(options.parTour, true, vue); + } + }); + + test('sans papier choisi, l’aperçu pose la question : question vraie, papier null', () => { + const options = optionsApercu(APERCU, null); + assert.equal(options.question, true); + assert.equal(options.papier, null); + assert.equal(optionsApercu(APERCU, 'A4').question, false); + }); + + test('un aperçu fermé, une vue ou un papier hors du domaine lèvent', () => { + assert.throws(() => optionsApercu(null, 'A4'), TypeError); + assert.throws(() => optionsApercu({ ...APERCU, vue: 'badges' }, 'A4'), RangeError); + assert.throws(() => optionsApercu(APERCU, 'A3'), RangeError); + }); +}); + +describe('les tours que le plan imprimé offre', () => { + test('de 1 au nombre de tours de la soirée, dans l’ordre ; un nombre hors du domaine lève', () => { + assert.deepEqual(toursOfferts(4), [1, 2, 3, 4]); + assert.deepEqual(toursOfferts(1), [1]); + for (const faute of [0, 1.5, Number.NaN, '3']) assert.throws(() => toursOfferts(faute), RangeError, String(faute)); + }); +}); + +describe('les dimensions d’une page (§ 11.2, § 11.7)', () => { + test('A4 et Lettre, portrait et paysage, en mm, tirés de PAPIERS', () => { + assert.deepEqual(dimensionsPage('A4', 'portrait'), { largeur: 210, hauteur: 297 }); + assert.deepEqual(dimensionsPage('A4', 'paysage'), { largeur: 297, hauteur: 210 }); + assert.deepEqual(dimensionsPage('Lettre', 'portrait'), { largeur: 215.9, hauteur: 279.4 }); + assert.deepEqual(dimensionsPage('Lettre', 'paysage'), { largeur: 279.4, hauteur: 215.9 }); + assert.throws(() => dimensionsPage('A3', 'portrait'), RangeError); + assert.throws(() => dimensionsPage('A4', 'carre'), RangeError); + }); + + test('les papiers offerts sont les clés de PAPIERS, chacun nommé et décrit par sa table', () => { + assert.deepEqual(PAPIERS_OFFERTS, Object.keys(PAPIERS)); + assert.deepEqual(clesPapier('A4'), { nom: 'impression.papier.nom.a4', choix: 'impression.papier.choix.a4' }); + assert.deepEqual(clesPapier('Lettre'), { nom: 'impression.papier.nom.lettre', choix: 'impression.papier.choix.lettre' }); + assert.throws(() => clesPapier('A3'), RangeError); + }); + + test('la hauteur du contenu : la page, moins la marge de chaque bord, moins la bande mesurée', () => { + assert.equal(hauteurContenu({ papier: 'A4', orientation: 'portrait', margeMm: 10, bandeMm: 7.5 }), 297 - 20 - 7.5); + assert.equal(hauteurContenu({ papier: 'Lettre', orientation: 'paysage', margeMm: 10, bandeMm: 0 }), 215.9 - 20); + assert.throws(() => hauteurContenu({ papier: 'A4', orientation: 'portrait', margeMm: 10, bandeMm: 277 }), RangeError); + assert.throws(() => hauteurContenu({ papier: 'A4', orientation: 'portrait', margeMm: 10, bandeMm: -1 }), RangeError); + assert.throws(() => hauteurContenu({ papier: 'A4', orientation: 'portrait', margeMm: 10, bandeMm: Number.NaN }), RangeError); + }); +}); + +describe('la bande mesurée (HAUTEUR_BANDE_IMPRESSION, § 14.1)', () => { + test('ce que la feuille gabarit porte hors de son contenu, en mm : sa hauteur moins le contenu, moins les deux marges', () => { + assert.equal(MM_PAR_PX, 25.4 / 96); + const feuillePx = (20 + 12) / MM_PAR_PX; + assert.ok(Math.abs(bandeDeFeuille({ feuillePx, contenuPx: 0, margeMm: 10 }) - 12) < 1e-9); + const contenuPx = 50 / MM_PAR_PX; + assert.ok(Math.abs(bandeDeFeuille({ feuillePx: feuillePx + contenuPx, contenuPx, margeMm: 10 }) - 12) < 1e-9); + }); + + test('une bande négative ou non finie lève : la feuille ne porte pas ses marges', () => { + assert.throws(() => bandeDeFeuille({ feuillePx: 10, contenuPx: 0, margeMm: 10 }), RangeError); + assert.throws(() => bandeDeFeuille({ feuillePx: Number.NaN, contenuPx: 0, margeMm: 10 }), RangeError); + }); + + test('la constante est mesurée à l’exécution, sans valeur au registre', () => { + assert.equal(constante('HAUTEUR_BANDE_IMPRESSION'), null); + }); +}); + +describe('les pages nommées de la feuille d’impression (impression.css)', () => { + const css = readFileSync(new URL('../impression.css', import.meta.url), 'utf8').replace(/\/\*[\s\S]*?\*\//g, ''); + const TAILLES = { A4: 'A4', Lettre: 'letter' }; + const SENS = { portrait: 'portrait', paysage: 'landscape' }; + + test('une page nommée par papier et orientation, à la taille du papier, sans marge', () => { + const noms = []; + for (const papier of PAPIERS_OFFERTS) { + for (const orientation of ['portrait', 'paysage']) { + const nom = nomPage(papier, orientation); + noms.push(nom); + const regle = new RegExp(`@page\\s+${nom}\\s*\\{\\s*size:\\s*${TAILLES[papier]}\\s+${SENS[orientation]};\\s*margin:\\s*0;\\s*\\}`); + assert.match(css, regle, nom); + } + } + assert.equal(new Set(noms).size, 4); + assert.throws(() => nomPage('A3', 'portrait'), RangeError); + }); + + test('le masquage d’impression est gardé par body:has([data-impression]) : hors de l’aperçu, rien n’est masqué', () => { + const impression = css.slice(css.indexOf('@media print')); + assert.ok(css.includes('@media print')); + for (const masque of impression.matchAll(/([^{}]*)\{[^{}]*display:\s*none/g)) { + assert.match(masque[1], /body:has\(\[data-impression\]\)/, masque[1].trim()); + } + }); +}); + +describe('feuilleDe : le pied de cinq éléments et le filigrane (§ 11.7, § 18.6)', () => { + test('nom, date, tour, horodatage du placement, version, dans cet ordre ; le filigrane suit brouillon', () => { + const feuille = feuilleDe(PIED, true); + assert.deepEqual(feuille.elements.map(({ element }) => element), ['nom', 'date', 'tour', 'horodatage', 'version']); + assert.deepEqual(feuille.elements[0], { element: 'nom', libelle: null, donnee: 'Soirée des Merles', forme: 'texte' }); + assert.deepEqual(feuille.elements[1], { element: 'date', libelle: null, donnee: '2026-10-09', forme: 'date' }); + assert.deepEqual(feuille.elements[2], { element: 'tour', libelle: { cle: 'impression.pied.tour', details: { tour: 2 } }, donnee: null, forme: 'texte' }); + assert.deepEqual(feuille.elements[3], { + element: 'horodatage', libelle: { cle: 'impression.pied.placement', details: {} }, donnee: '2026-10-09T19:00:00-04:00', forme: 'horodatage', + }); + assert.deepEqual(feuille.elements[4], { element: 'version', libelle: { cle: 'impression.pied.version', details: {} }, donnee: 'v', forme: 'texte' }); + assert.equal(feuille.filigrane, true); + assert.equal(feuilleDe(PIED, false).filigrane, false); + }); + + test('sans date, sans horodatage, à « tous » : le libellé qui le dit, aucune donnée', () => { + const { elements } = feuilleDe({ ...PIED, date: null, horodatage: null, tour: 'tous' }, false); + assert.deepEqual(elements[1], { element: 'date', libelle: { cle: 'impression.pied.sansDate', details: {} }, donnee: null, forme: 'texte' }); + assert.deepEqual(elements[2], { element: 'tour', libelle: { cle: 'impression.pied.tousLesTours', details: {} }, donnee: null, forme: 'texte' }); + assert.deepEqual(elements[3], { element: 'horodatage', libelle: { cle: 'impression.pied.placementInconnu', details: {} }, donnee: null, forme: 'texte' }); + }); + + test('un pied hors du contrat, un brouillon qui n’est pas un booléen, lèvent', () => { + assert.throws(() => feuilleDe({ ...PIED, tour: 0 }, false), TypeError); + assert.throws(() => feuilleDe({ ...PIED, nom: 3 }, false), TypeError); + assert.throws(() => feuilleDe(PIED, 'oui'), TypeError); + }); +}); + +describe('le filigrane BROUILLON (§ 11.7, § 14.5)', () => { + test('centré sur la page, couché sur sa diagonale montante, d’un corps tiré de la diagonale', () => { + for (const [papier, orientation] of [['A4', 'portrait'], ['A4', 'paysage'], ['Lettre', 'portrait']]) { + const { largeur, hauteur } = dimensionsPage(papier, orientation); + const f = filigraneDe(papier, orientation); + assert.deepEqual([f.largeur, f.hauteur, f.x, f.y], [largeur, hauteur, largeur / 2, hauteur / 2], papier); + assert.ok(Math.abs(f.angle + (Math.atan2(hauteur, largeur) * 180) / Math.PI) < 1e-9, `${papier} ${orientation}`); + assert.ok(f.corps > 0 && f.corps < Math.hypot(largeur, hauteur) / 5, `${papier} ${orientation}`); + assert.ok(f.trait > 0, 'contour non nul'); + } + }); +}); + +describe('la sortie de l’aperçu et la découpe du plan', () => { + test('sans placement, la source des réservations, sans proposition ; le tour du plan borné aux tours de la soirée', () => { + const charge = petite(); + const sortie = sortieApercu(charge, { tour: 9 }, null); + assert.equal(sortie.source, 'reservations'); + assert.equal(sortie.proposition, null); + assert.equal(sortie.tour, charge.evenement.tours); + assert.deepEqual(sortie.etat, etatPlaces(charge, { tour: charge.evenement.tours })); + assert.equal(sortieApercu(charge, { tour: 'tous' }, null).tour, 'tous'); + }); + + test('une proposition montrée est la source de la sortie, par son identifiant', () => { + const sortie = sortieApercu(avecProposition(), { tour: 1 }, 1); + assert.deepEqual([sortie.source, sortie.proposition, sortie.tour], ['proposition', 1, 1]); + }); + + test('la découpe du plan reçoit la bande mesurée : une bande plus haute réduit la surface utile', () => { + const etat = etatPlaces(petite(), { tour: 1 }); + const geometries = etat.tables.map((table) => geometrieTable(table, table.capacite)); + for (const bandeMm of [0, 12.5]) { + const attendue = decouperPlan(englobantImpression(geometries), { papier: 'A4', bandeMm, mode: 'unePage', geometries }); + assert.deepEqual(decoupeDuPlan(etat, { papier: 'A4', bandeMm, decoupe: 'unePage' }), attendue, String(bandeMm)); + } + const basse = decoupeDuPlan(etat, { papier: 'A4', bandeMm: 0, decoupe: 'unePage' }); + const haute = decoupeDuPlan(etat, { papier: 'A4', bandeMm: 150, decoupe: 'unePage' }); + assert.ok(haute.echelle < basse.echelle, `${haute.echelle} < ${basse.echelle}`); + }); +}); + +describe('les libellés de l’aperçu (§ 14.6)', () => { + const temoin = creerTraduction([...TABLES_APPLICATION, ...TABLES_INTERFACE], { langue: 'temoin' }).t; + const { ouverture, fermeture } = MARQUE_TEMOIN; + + test('chaque clé que le modèle désigne existe et se marque en langue témoin', () => { + const cles = [ + ...['plan', 'accueil', 'tables'].map(cleVue), + ...['auto', 'unePage', 'decoupe'].map(cleDecoupe), + ...PAPIERS_OFFERTS.flatMap((papier) => Object.values(clesPapier(papier))), + ...[feuilleDe(PIED, true), feuilleDe({ ...PIED, date: null, horodatage: null, tour: 'tous' }, false)].flatMap(({ elements }) => + elements.filter(({ libelle }) => libelle !== null).map(({ libelle }) => libelle.cle), + ), + ]; + assert.ok(cles.length >= 15); + for (const cle of cles) { + const texte = temoin(cle, { tour: 2 }); + assert.ok(texte.startsWith(ouverture) && texte.endsWith(fermeture), cle); + } + assert.throws(() => cleVue('badges'), RangeError); + assert.throws(() => cleDecoupe('deux'), RangeError); + }); +}); diff --git a/src/interface/jetons.css b/src/interface/jetons.css index 8ceb5c7..9e77241 100644 --- a/src/interface/jetons.css +++ b/src/interface/jetons.css @@ -33,6 +33,13 @@ La hachure d'une place à pourvoir peint des traits --couleur-hachure sur le fond de la chaise, --couleur-chaise-fond : ce sont ses deux couleurs. + Le papier est clair quel que soit le thème (§ 11.7) : le bloc du clair + porte aussi le sélecteur [data-impression], et le document imprimé de + l'aperçu redéclare sur lui-même chaque jeton du clair, à l'écran comme + sur la page. Le fond d'une feuille, --couleur-papier, et le contour du + filigrane BROUILLON, --couleur-filigrane, ne se peignent que sur ce + document : chacun garde la même valeur dans les deux thèmes. + La police de l'interface est la police embarquée, Noto Sans (§ 11.1, § 19.4) : deux faces, 400 et 700, dont les fichiers sont ceux de src/polices, que la production de la planche embarque aussi. La @@ -105,7 +112,8 @@ textarea { } :root, -:root[data-theme='clair'] { +:root[data-theme='clair'], +[data-impression] { color-scheme: light; /* Page. */ @@ -156,6 +164,10 @@ textarea { --couleur-liste-texte: #1c2128; --couleur-echelle: #1c2128; --couleur-fantome: #b040f0; + + /* Le document imprimé : le papier, et le contour du filigrane. */ + --couleur-papier: #ffffff; + --couleur-filigrane: #6e6e6e; } :root[data-theme='sombre'] { @@ -201,4 +213,7 @@ textarea { --couleur-liste-texte: #e3e6ea; --couleur-echelle: #e3e6ea; --couleur-fantome: #b040f0; + + --couleur-papier: #ffffff; + --couleur-filigrane: #6e6e6e; } diff --git a/src/interface/libelles.js b/src/interface/libelles.js index 1e161f5..6af57f4 100644 --- a/src/interface/libelles.js +++ b/src/interface/libelles.js @@ -17,6 +17,7 @@ import { TABLE as DIAGNOSTIC } from './libelles/diagnostic.js'; import { TABLE as EVENEMENT } from './libelles/evenement.js'; import { TABLE as GENERATION } from './libelles/generation.js'; import { TABLE as IMPORT } from './libelles/import.js'; +import { TABLE as IMPRESSION } from './libelles/impression.js'; import { TABLE as PARTICIPANTS } from './libelles/participants.js'; import { TABLE as PLAN } from './libelles/plan.js'; import { TABLE as PROPOSITIONS } from './libelles/propositions.js'; @@ -32,6 +33,7 @@ export const TABLES_INTERFACE = Object.freeze([ EVENEMENT, GENERATION, IMPORT, + IMPRESSION, PARTICIPANTS, PLAN, PROPOSITIONS, diff --git a/src/interface/libelles.test.js b/src/interface/libelles.test.js index c5670ae..7d4a780 100644 --- a/src/interface/libelles.test.js +++ b/src/interface/libelles.test.js @@ -65,12 +65,12 @@ async function tablesDuDossier() { } describe('libellés : les tables des écrans (§ 14.6)', () => { - test('chaque module de libelles/ exporte TABLE, une Map ; TABLES_INTERFACE les assemble, douze, par ordre alphabétique', async () => { + test('chaque module de libelles/ exporte TABLE, une Map ; TABLES_INTERFACE les assemble, treize, par ordre alphabétique', async () => { const tables = await tablesDuDossier(); - assert.equal(tables.length, 12); + assert.equal(tables.length, 13); assert.deepEqual(tables.map(({ table }) => table), [ 'accueil', 'communs', 'coquille', 'diagnostic', 'evenement', 'generation', - 'import', 'participants', 'plan', 'propositions', 'raccourcis', 'tables', + 'import', 'impression', 'participants', 'plan', 'propositions', 'raccourcis', 'tables', ]); for (const { table, module } of tables) assert.ok(module.TABLE instanceof Map, table); assert.equal(TABLES_INTERFACE.length, tables.length); diff --git a/src/interface/libelles/impression.js b/src/interface/libelles/impression.js new file mode 100644 index 0000000..c590332 --- /dev/null +++ b/src/interface/libelles/impression.js @@ -0,0 +1,58 @@ +// © 2026 TechnoLibre (http://www.technolibre.ca) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +// Les libellés de l'aperçu avant impression et de la feuille imprimée +// (§ 11.7, § 8.5, § 11.2, § 18.6), et ceux de la question du papier, que +// l'aperçu et la section des badges partagent. Chaque clé commence par +// « impression. ». Une donnée — le nom de l'événement, une date, un +// horodatage, la version — n'entre dans aucun de ces textes : la feuille la +// pose dans son propre nœud, à côté. +export const TABLE = new Map([ + // --- L'écran et sa barre (§ 11.7) ------------------------------------------ + ['impression.titre', 'Aperçu avant impression'], + ['impression.barre', "Commandes de l'aperçu"], + ['impression.commande.ouvrir', 'Imprimer…'], + ['impression.commande.imprimer', 'Imprimer'], + ['impression.commande.fermer', "Fermer l'aperçu"], + + // --- Ce que l'aperçu imprime ---------------------------------------------- + ['impression.vue.titre', 'Vue imprimée'], + ['impression.vue.plan', 'Plan de salle'], + ['impression.vue.accueil', "Liste d'accueil"], + ['impression.vue.tables', 'Feuilles par table'], + ['impression.tour.titre', 'Tour imprimé'], + ['impression.tour.option', ({ tour }) => `Tour ${tour}`], + ['impression.listes', 'Listes de noms'], + ['impression.decoupe.titre', 'Pages du plan'], + ['impression.decoupe.auto', 'Découpage proposé'], + ['impression.decoupe.unePage', 'Tout sur une page'], + ['impression.decoupe.decoupe', 'Découper en pages'], + ['impression.parTour', 'Une feuille par tour'], + + // --- La question du papier (§ 8.5, § 11.2) ----------------------------------- + ['impression.papier.question', 'Sur quel papier imprimez-vous ?'], + [ + 'impression.papier.explication', + "Le format est celui du bac de l'imprimante de cette machine : il est retenu ici, et ne se redemande plus.", + ], + ['impression.papier.choix.a4', 'A4 (210 × 297 mm)'], + ['impression.papier.choix.lettre', 'Lettre (215,9 × 279,4 mm)'], + ['impression.papier.titre', 'Papier :'], + ['impression.papier.nom.a4', 'A4'], + ['impression.papier.nom.lettre', 'Lettre'], + ['impression.papier.changer', 'Changer'], + + // --- La feuille imprimée (§ 11.7, § 18.6) ----------------------------------- + ['impression.filigrane', 'BROUILLON'], + ['impression.pied.sansDate', 'Date non fixée'], + ['impression.pied.tour', ({ tour }) => `Tour ${tour}`], + ['impression.pied.tousLesTours', 'Tous les tours'], + ['impression.pied.placement', 'Placement du'], + ['impression.pied.placementInconnu', 'Placement non daté'], + ['impression.pied.version', 'Version'], + + // --- Les vues, avant leur mise en page (§ 11.7) ------------------------------ + ['impression.plan.page', ({ page, total }) => `Plan, page ${page} sur ${total}`], + ['impression.accueil.titre', "Liste d'accueil"], + ['impression.tables.titre', 'Feuilles par table'], +]); diff --git a/src/interface/marques.js b/src/interface/marques.js index 8acdc01..01611d1 100644 --- a/src/interface/marques.js +++ b/src/interface/marques.js @@ -99,6 +99,10 @@ export const MARQUES = Object.freeze([ // Le signe du chargement d'une valeur de la liste des événements (§ 8.1) : // un arc tracé de l'encre secondaire, sur la page. marque('chargement', 'page-texte-secondaire', ['page-fond']), + // Le filigrane BROUILLON (§ 11.7) : un texte au trait, par-dessus le + // contenu d'une feuille imprimée, mesuré sur le papier, clair dans les + // deux thèmes. + marque('filigrane', 'filigrane', ['papier']), ]); /** Les textes et leurs fonds, même forme, seuil 4,5 : page, secondaire, bandeau, refus, diff --git a/src/interface/marques.test.js b/src/interface/marques.test.js index 7218a34..a982f0b 100644 --- a/src/interface/marques.test.js +++ b/src/interface/marques.test.js @@ -18,7 +18,7 @@ const NOMS_MARQUES = [ 'cadenasFerme', 'cadenasOuvert', 'anneau', 'hachure', 'remplissagePlein', 'contourConflit', 'trameAppartenance', 'trameVoisins', 'trameTable', 'pastille', 'poignee', 'gel', 'miseEnEvidence', 'selection', 'cadreLecture', 'cadreEcriture', 'cadreBloque', 'focus', - 'contourTable', 'contourChaise', 'echelle', 'fantome', 'chargement', + 'contourTable', 'contourChaise', 'echelle', 'fantome', 'chargement', 'filigrane', ]; // Les remplissages d'une chaise : fond, pleine, et les deux couleurs de la @@ -39,7 +39,7 @@ const FONDS_REQUIS = { const parNom = (registre, nom) => registre.find((e) => e.nom === nom); describe('registre des marques', () => { - test('les vingt-trois marques du contrat, noms uniques, seuil 3, au moins un fond chacune', () => { + test('les vingt-quatre marques du contrat, noms uniques, seuil 3, au moins un fond chacune', () => { assert.equal(MARQUES.length, NOMS_MARQUES.length); assert.deepEqual(MARQUES.map((m) => m.nom), NOMS_MARQUES); assert.equal(new Set(MARQUES.map((m) => m.nom)).size, MARQUES.length); @@ -91,6 +91,27 @@ describe('registre des marques', () => { }); }); +describe('le filigrane BROUILLON sur le papier (§ 11.7, § 14.5)', () => { + test('une marque du registre, peinte de --couleur-filigrane sur le papier, à 3:1', () => { + const filigrane = parNom(MARQUES, 'filigrane'); + assert.deepEqual({ ...filigrane, fonds: [...filigrane.fonds] }, { + nom: 'filigrane', + jeton: '--couleur-filigrane', + fonds: ['--couleur-papier'], + seuil: 3, + }); + }); + + test('le papier est clair dans les deux thèmes : le filigrane et le papier gardent leur valeur d’un thème à l’autre, et le papier est celle du clair', () => { + const [clair, sombre] = THEMES.map((theme) => lireJetons(JETONS, theme)); + for (const nom of ['--couleur-filigrane', '--couleur-papier']) { + assert.notEqual(clair.get(nom), undefined, nom); + assert.deepEqual(sombre.get(nom), clair.get(nom), nom); + } + assert.deepEqual(clair.get('--couleur-papier'), [255, 255, 255, 1]); + }); +}); + describe('les trois sortes de conflit (§ 7.5, § 12.7)', () => { test('une par règle localisable, dans l’ordre du moteur : sa règle, la marque de sa trame, le jeton de sa teinte, la forme de son motif', () => { assert.deepEqual( diff --git a/src/interface/palette.js b/src/interface/palette.js index 1c041da..1b3998a 100644 --- a/src/interface/palette.js +++ b/src/interface/palette.js @@ -27,8 +27,13 @@ const ECART_TEINTE = 30; const PREFIXE_COULEUR = '--couleur-'; // Les seuls sélecteurs sous lesquels une couleur se déclare : :root, ce qui -// s'applique sans attribut, et un bloc par thème. +// s'applique sans attribut, et un bloc par thème. Le document imprimé +// (§ 11.7) redéclare les jetons du clair sur lui-même : [data-impression] +// ne se pose que sur le bloc du clair, et le papier reste clair dans les +// deux thèmes. const SELECTEURS_COULEUR = [':root', ...THEMES.map((t) => `:root[data-theme='${t}']`)]; +const SELECTEUR_CLAIR = ":root[data-theme='clair']"; +const SELECTEUR_PAPIER = '[data-impression]'; /** Matrices de Machado, Oliveira et Fernandes (2009), sévérité 1, appliquées en * RGB linéaire. Chaque ligne somme à 1 : le blanc et le noir sont des points @@ -125,12 +130,14 @@ function couleurLitterale(valeur) { * est :root seul, ce qui s'applique sans attribut. Lève sur un thème sans * bloc, une valeur illisible, un renvoi sans cible ou circulaire, et sur un * bloc qui déclare une couleur sous un autre sélecteur que :root et - * :root[data-theme='clair' | 'sombre'] : dans le navigateur, il masquerait - * le jeton que la lecture mesure. */ + * :root[data-theme='clair' | 'sombre'] — [data-impression] excepté, sur le + * bloc du clair seul : dans le navigateur, il masquerait le jeton que la + * lecture mesure. */ export function lireJetons(texteCss, theme) { const tous = lireBlocs(texteCss); for (const bloc of tous) { - const autres = bloc.selecteurs.filter((s) => !SELECTEURS_COULEUR.includes(s)); + const papierClair = bloc.selecteurs.includes(SELECTEUR_CLAIR); + const autres = bloc.selecteurs.filter((s) => !SELECTEURS_COULEUR.includes(s) && !(s === SELECTEUR_PAPIER && papierClair)); const couleur = [...bloc.declarations.keys()].find((n) => n.startsWith(PREFIXE_COULEUR)); if (autres.length > 0 && couleur !== undefined) { throw new Error(`couleur ${couleur} déclarée sous un sélecteur non lu : ${autres.join(', ')}`); diff --git a/src/interface/palette.test.js b/src/interface/palette.test.js index ad27af3..b57fb56 100644 --- a/src/interface/palette.test.js +++ b/src/interface/palette.test.js @@ -52,6 +52,17 @@ function hex(valeur) { const enHex = (c) => `#${c.map((v) => v.toString(16).padStart(2, '0')).join('')}`; describe('lireBlocs et lireJetons', () => { + test('le bloc du clair porte aussi [data-impression] : ses couleurs se lisent comme celles du clair, le papier imprimé les redéclare', () => { + const css = [ + ":root, :root[data-theme='clair'], [data-impression] { --couleur-a: #ffffff; --couleur-b: #102030; }", + ':root[data-theme="sombre"] { --couleur-a: #000000; --couleur-b: #a0b0c0; }', + ].join('\n'); + assert.deepEqual(lireBlocs(css)[0].selecteurs, [':root', ":root[data-theme='clair']", '[data-impression]']); + assert.deepEqual([...lireJetons(css, 'clair')], [['--couleur-a', [255, 255, 255, 1]], ['--couleur-b', [16, 32, 48, 1]]]); + assert.deepEqual(lireJetons(css, null), lireJetons(css, 'clair')); + assert.deepEqual([...lireJetons(css, 'sombre')], [['--couleur-a', [0, 0, 0, 1]], ['--couleur-b', [160, 176, 192, 1]]]); + }); + test('lit les blocs dans leur ordre, commentaires ôtés, et les couleurs de chaque thème', () => { const css = [ "/* { un commentaire n'ouvre aucun bloc } */", @@ -123,6 +134,17 @@ describe('lireBlocs et lireJetons', () => { } assert.throws(() => lireJetons(`${JETONS}\n${intrus}`, 'sombre'), /sélecteur/, intrus); } + // Le document imprimé ne prend que les jetons du clair : [data-impression] + // seul, ou posé sur le bloc du sombre, masquerait le jeton lu. + for (const intrus of [ + '[data-impression] { --couleur-sol: #ff8a80; }', + ":root[data-theme='sombre'], [data-impression] { --couleur-sol: #ff8a80; --couleur-focus: #e8e8e8; }", + ':root, [data-impression] { --couleur-sol: #ff8a80; }', + ]) { + for (const theme of ['clair', 'sombre', null]) { + assert.throws(() => lireJetons(`${base}\n${intrus}`, theme), /sélecteur/, `${theme} : ${intrus}`); + } + } // Un autre sélecteur sans couleur n'est pas lu, et ne lève pas. assert.deepEqual(lireJetons(`${base}\n.plan { --espace-1: 1rem; }`, 'clair'), lireJetons(base, 'clair')); }); @@ -378,6 +400,13 @@ describe('jetons.css', () => { assert.ok(tailles.size >= 3, `${tailles.size} tailles distinctes`); }); + test('le document imprimé prend les jetons du clair dans les deux thèmes (§ 11.7) : le bloc du clair porte [data-impression], aucun autre', () => { + const avecPapier = blocs().filter((b) => b.selecteurs.includes('[data-impression]')); + assert.equal(avecPapier.length, 1); + assert.ok(avecPapier[0].selecteurs.includes(":root[data-theme='clair']")); + assert.ok([...avecPapier[0].declarations.keys()].some((nom) => nom.startsWith('--couleur-'))); + }); + test('deux thèmes posés par data-theme, de mêmes jetons de couleur ; sans attribut, le clair ; aucune préférence du système lue', () => { const clair = lireJetons(JETONS, 'clair'); const sombre = lireJetons(JETONS, 'sombre'); diff --git a/src/interface/sorties/Papier.navigateur.test.js b/src/interface/sorties/Papier.navigateur.test.js new file mode 100644 index 0000000..24d8091 --- /dev/null +++ b/src/interface/sorties/Papier.navigateur.test.js @@ -0,0 +1,137 @@ +// © 2026 TechnoLibre (http://www.technolibre.ca) +// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) + +// La question du papier dans un vrai moteur de rendu (§ 8.5, § 11.2) : le +// composant que l'aperçu et la section des badges partagent. Sans papier, +// il pose la question, un choix par papier de PAPIERS ; avec un papier, il +// le montre, « Papier : A4 », et offre de le changer. Choisir passe par +// seance.choisirPapier ; un refus s'affiche sur le choix, la question reste +// et onChoisi n'est pas appelé. Le composant est monté seul, sur une séance +// d'épreuve qui suit le contrat de magasin de la séance et relève ce qu'on +// lui demande ; l'aperçu l'éprouve sur la vraie séance +// (Apercu.navigateur.test.js). +// +// Le projet navigateur emploie expect, et test et describe de Vitest +// (§ 14.8). +import { flushSync, mount, unmount } from 'svelte'; +import { afterEach, describe, expect, test, vi } from 'vitest'; +import { creerHorlogePilotee } from '../../application/horloge.js'; +import { TABLES_APPLICATION } from '../../application/libelles.js'; +import { creerTraduction } from '../../application/traduction.js'; +import { PAPIERS } from '../../geometrie/papiers.js'; +import { attendre } from '../../../test/navigateur/ecrans.js'; +import { TABLES_INTERFACE } from '../libelles.js'; +import Papier from './Papier.svelte'; + +const { t } = creerTraduction([...TABLES_APPLICATION, ...TABLES_INTERFACE]); + +// Une séance d'épreuve : le contrat de magasin, le papier de l'état, et +// choisirPapier, qui relève chaque demande, puis lève refus s'il est donné, +// ou pose le papier et publie. +function seanceDEpreuve(papier, { refus = null } = {}) { + let etat = Object.freeze({ papier }); + const abonnes = new Set(); + const demandes = []; + return { + demandes, + subscribe(fn) { + abonnes.add(fn); + fn(etat); + return () => abonnes.delete(fn); + }, + etat: () => etat, + async choisirPapier(choix) { + demandes.push(choix); + if (refus !== null) throw refus; + etat = Object.freeze({ papier: choix }); + for (const fn of [...abonnes]) fn(etat); + }, + }; +} + +const montes = []; +afterEach(() => { + for (const { composant, conteneur } of montes.splice(0)) { + unmount(composant); + conteneur.remove(); + } +}); + +// Monte Papier dans un conteneur neuf, sur la séance donnée. +function monter(seance, props = {}) { + const conteneur = document.createElement('div'); + document.body.prepend(conteneur); + const contexte = new Map([ + ['t', t], + ['seance', seance], + ['horloge', creerHorlogePilotee()], + ]); + const composant = mount(Papier, { target: conteneur, props, context: contexte }); + flushSync(); + montes.push({ composant, conteneur }); + return conteneur; +} + +const texte = (element) => element.textContent.replace(/\s+/g, ' ').trim(); +const choix = (conteneur, papier) => conteneur.querySelector(`[data-papier="question"] [data-cible="${papier}"] [data-commande="choisirPapier"]`); + +describe('la question du papier (§ 8.5, § 11.2)', () => { + test('sans papier : la question, son explication, un choix par papier de PAPIERS, dans leur ordre, chacun décrit ; rien n’est montré comme choisi', () => { + const conteneur = monter(seanceDEpreuve(null)); + const question = conteneur.querySelector('[data-papier="question"]'); + expect(question).not.toBeNull(); + expect(texte(question)).toContain(t('impression.papier.question')); + expect(texte(question)).toContain(t('impression.papier.explication')); + const offerts = [...question.querySelectorAll('[data-cible]')].map((e) => e.dataset.cible); + expect(offerts).toEqual(Object.keys(PAPIERS)); + expect(texte(choix(conteneur, 'A4'))).toBe(t('impression.papier.choix.a4')); + expect(texte(choix(conteneur, 'Lettre'))).toBe(t('impression.papier.choix.lettre')); + expect(conteneur.querySelector('[data-papier="choisi"]')).toBeNull(); + }); + + test('choisir demande le papier à la séance, une fois ; la question cède la place au papier choisi, et onChoisi le reçoit', async () => { + const seance = seanceDEpreuve(null); + const onChoisi = vi.fn(); + const conteneur = monter(seance, { onChoisi }); + choix(conteneur, 'Lettre').click(); + await attendre('le papier choisi', () => conteneur.querySelector('[data-papier="choisi"]')); + // onChoisi suit l'écriture faite : il s'appelle une fois le papier rendu. + await attendre('onChoisi appelé', () => onChoisi.mock.calls.length > 0); + expect(seance.demandes).toEqual(['Lettre']); + expect(onChoisi).toHaveBeenCalledTimes(1); + expect(onChoisi).toHaveBeenCalledWith('Lettre'); + expect(conteneur.querySelector('[data-papier="question"]')).toBeNull(); + const choisi = texte(conteneur.querySelector('[data-papier="choisi"]')); + expect(choisi).toContain(t('impression.papier.titre')); + expect(choisi).toContain(t('impression.papier.nom.lettre')); + }); + + test('avec un papier : « Papier : A4 — Changer » ; changer rouvre la question, et le nouveau choix se demande', async () => { + const seance = seanceDEpreuve('A4'); + const conteneur = monter(seance); + expect(conteneur.querySelector('[data-papier="question"]')).toBeNull(); + const choisi = conteneur.querySelector('[data-papier="choisi"]'); + expect(texte(choisi)).toContain(t('impression.papier.nom.a4')); + const changer = choisi.querySelector('[data-commande="changerPapier"]'); + expect(texte(changer)).toBe(t('impression.papier.changer')); + changer.click(); + await attendre('la question rouverte', () => conteneur.querySelector('[data-papier="question"]')); + choix(conteneur, 'Lettre').click(); + await attendre('le papier changé', () => conteneur.querySelector('[data-papier="choisi"]')); + expect(seance.demandes).toEqual(['Lettre']); + expect(texte(conteneur.querySelector('[data-papier="choisi"]'))).toContain(t('impression.papier.nom.lettre')); + }); + + test('un choix refusé : le refus s’affiche sur le choix, la question reste, onChoisi n’est pas appelé', async () => { + const refus = { code: 'REGLAGES_ILLISIBLES', details: { chemin: 'reglages_locaux.json', raison: 'JSON' } }; + const seance = seanceDEpreuve(null, { refus }); + const onChoisi = vi.fn(); + const conteneur = monter(seance, { onChoisi }); + choix(conteneur, 'A4').click(); + const affiche = await attendre('le refus', () => conteneur.querySelector('[data-cible="A4"] [data-refus="REGLAGES_ILLISIBLES"]')); + expect(texte(affiche.querySelector('p'))).toBe(t(refus.code, refus.details).replace(/\s+/g, ' ').trim()); + expect(conteneur.querySelector('[data-papier="question"]')).not.toBeNull(); + expect(onChoisi).not.toHaveBeenCalled(); + expect(seance.demandes).toEqual(['A4']); + }); +}); diff --git a/src/interface/sorties/Papier.svelte b/src/interface/sorties/Papier.svelte new file mode 100644 index 0000000..78b168f --- /dev/null +++ b/src/interface/sorties/Papier.svelte @@ -0,0 +1,93 @@ + + + + +{#if question} +
+

{t('impression.papier.question')}

+

{t('impression.papier.explication')}

+
+ {#each PAPIERS_OFFERTS as choix (choix)} +
+ choisir(choix)} /> +
+ {/each} +
+
+{:else} +
+ {t('impression.papier.titre')} + {t(clesPapier(papier).nom)} + (changer = true)} /> +
+{/if} + + diff --git a/src/interface/transversal.navigateur.test.js b/src/interface/transversal.navigateur.test.js index 8644a28..cadc379 100644 --- a/src/interface/transversal.navigateur.test.js +++ b/src/interface/transversal.navigateur.test.js @@ -705,6 +705,24 @@ describe('les gardes relèvent la faute qu’un fragment porte (§ 14.2)', () => expect(fautes[2]).toContain('1.00:1'); }); + test('marques : un texte au trait du registre — le filigrane — se mesure à son contour sur ce qui est peint sous lui, non comme un texte ; un contour noyé dans son fond est relevé ; un texte du document imprimé ne se compare pas d’un thème à l’autre', () => { + const fragment = (fond) => + `

papier

` + + 'BROUILLON
'; + const lire = (fond) => avecFragment(fragment(fond), (c) => ({ marques: releverMarques(c), textes: releverTextes(c) })); + const net = lire('#ffffff'); + expect(net.marques.vues).toEqual(['filigrane']); + expect(net.marques.mesures).toBe(1); + expect(net.marques.fautes).toEqual([]); + expect(net.textes.mesures).toBe(1); + expect(net.textes.fautes).toEqual([]); + expect([...net.textes.couleurs]).toEqual([]); + const noye = lire(rgb(couleurDeJeton('--couleur-filigrane'))); + expect(noye.marques.fautes).toHaveLength(1); + expect(noye.marques.fautes[0]).toContain('1.00:1'); + }); + test('marques sous une fenêtre modale : une marque HTML de la page, qui ne s’y mesure pas, ne compte pas comme vue ; une marque dans la fenêtre, si', () => { const { vues } = avecFragment( '

dehors

' + diff --git a/test/navigateur/captures.js b/test/navigateur/captures.js index 3d46e59..73b7f46 100644 --- a/test/navigateur/captures.js +++ b/test/navigateur/captures.js @@ -152,7 +152,12 @@ export const CHAMPS = 'input:not([type="radio"]):not([type="checkbox"]):not([typ * Contrastes des textes rendus sous racine (§ 14.5) : chaque porteur de * texte et chaque champ de saisie, son texte indicatif compris, sur le fond * peint dessous ; un texte SVG par son fill sur la forme dessous. Un porteur - * que l'œil ne voit pas — couvert, rogné, de boîte vide — n'est pas mesuré. + * que l'œil ne voit pas — couvert, rogné, de boîte vide — n'est pas mesuré, + * ni un texte SVG du registre des marques — le filigrane BROUILLON, un texte + * au trait —, que releverMarques mesure à 3:1. Les textes du document + * imprimé de l'aperçu ([data-impression]) se mesurent, mais n'entrent pas + * dans couleurs : le papier est clair dans les deux thèmes (§ 11.7), et + * leurs couleurs ne diffèrent pas d'un thème à l'autre. * → { fautes, attenuations, couleurs: Map(clé → couleurs), mesures, ratios }, * ratios : { couleur, fond, ratio } de chaque texte mesuré, le fond le plus * défavorable, dans l'ordre des mesures. fenetre vrai : seuls les éléments @@ -165,6 +170,7 @@ export function releverTextes(racine, { fenetre = false } = {}) { const couleurs = new Map(); const ratios = []; let mesures = 0; + // cle null : un texte qui ne se compare pas d'un thème à l'autre. const mesurer = (cle, nom, couleurDe, fondDe, cible) => { let couleur; let fonds; @@ -187,13 +193,14 @@ export function releverTextes(racine, { fenetre = false } = {}) { } catch (erreur) { fautes.push(`${nom} : ${erreur.message}`); } - couleurs.set(cle, `${rgb(couleur)} sur ${fonds.map(rgb).join(' + ')}`); + if (cle !== null) couleurs.set(cle, `${rgb(couleur)} sur ${fonds.map(rgb).join(' + ')}`); for (const a of attenuations(cible)) fautesAttenuation.push(`${nom} : ${designation(a)} ${a.propriete}: ${a.valeur}`); }; for (const [rang, porteur] of porteursDeTexte(racine).entries()) { const { element } = porteur; if (fenetre && !coupeLaFenetre(element)) continue; if (!rendu(element)) continue; + if (element instanceof SVGTextContentElement && element.hasAttribute('data-marque')) continue; const b = element.getBoundingClientRect(); if (b.width === 0 || b.height === 0) continue; if (!fenetre) amener(element); @@ -205,7 +212,8 @@ export function releverTextes(racine, { fenetre = false } = {}) { if (svg && !centreDansLeDessin(element)) continue; const texte = porteur.pseudo === null ? [...element.childNodes].filter((n) => n.nodeType === Node.TEXT_NODE).map((n) => n.textContent).join('') : ''; const nom = `${situer(element)}${porteur.pseudo ?? ''} « ${extrait(texte)} »`; - mesurer(`${rang}:${designation(porteur)}`, nom, () => couleurTexte(porteur), () => fondPeintSous(porteur), porteur); + const cle = element.closest('[data-impression]') === null ? `${rang}:${designation(porteur)}` : null; + mesurer(cle, nom, () => couleurTexte(porteur), () => fondPeintSous(porteur), porteur); } for (const [rang, champ] of [...racine.querySelectorAll(CHAMPS)].entries()) { if (fenetre && !coupeLaFenetre(champ)) continue; @@ -317,11 +325,18 @@ export function bordsPeints(style) { * voisins }. Une forme SVG : son remplissage — une trame se mesure contre * les autres couleurs de son motif, un aplat contre ce qui est peint dessous * en un point qu'il couvre — ou son trait, contre ce qui est dessous en un - * point du trait et contre son propre remplissage. Un élément HTML : sa - * bordure ou son fond, contre ce qui est peint sous son parent, et son fond - * propre pour une bordure. peinture null : le jeton n'est peint nulle part. + * point du trait et contre son propre remplissage. Un texte SVG au trait — + * le filigrane — : son contour, contre ce qui est peint sous le centre de sa + * boîte. Un élément HTML : sa bordure ou son fond, contre ce qui est peint + * sous son parent, et son fond propre pour une bordure. peinture null : le + * jeton n'est peint nulle part. */ function voisinage(element, jeton) { + if (element instanceof SVGTextContentElement) { + const { stroke } = couleursPeintes(element); + const trace = Number.parseFloat(getComputedStyle(element).strokeWidth) > 0; + return trace && stroke.some((c) => egales(c, jeton)) ? { peinture: 'trait', voisins: fondPeintSous(element) } : { peinture: null, voisins: [] }; + } if (element instanceof SVGGeometryElement) { const motif = /^url\(/.test(getComputedStyle(element).fill); const { fill, stroke } = couleursPeintes(element); diff --git a/test/navigateur/designations.js b/test/navigateur/designations.js index 799d7d3..de6bf58 100644 --- a/test/navigateur/designations.js +++ b/test/navigateur/designations.js @@ -34,7 +34,7 @@ const ENTREES = [ ['data-chargement', 'accueil : le signe d’un calcul en cours', ['']], ['data-chargement-liste', 'accueil : l’annonce du calcul des colonnes de la liste', ['']], ['data-choix-siege', 'plan : un siège offert par le panneau d’une table, par son rang', null], - ['data-cible', 'la cible d’une commande : section, groupe ou colonne', null], + ['data-cible', 'la cible d’une commande : section, groupe, colonne, vue imprimée ou papier', null], ['data-cible-redondance', 'propositions : le champ de la cible de redondance', ['']], ['data-classement', 'propositions : les lignes classées', ['']], ['data-coeurs', 'génération : le réglage des cœurs de calcul', ['']], @@ -42,6 +42,7 @@ const ENTREES = [ ['data-collage', 'import : l’origine collage de l’aperçu', ['']], ['data-colonne', 'une colonne d’un tableau, par son nom', null], ['data-colonnes', 'import : l’association des colonnes', ['']], + ['data-contenu', 'aperçu : la zone de contenu d’une feuille imprimée', ['']], ['data-commande', 'un bouton de commande, par son nom', null], ['data-compte', 'import : un compte du rapport, par son nom', null], ['data-conflit', 'participants : une ligne de conflit d’un groupe', ['']], @@ -61,7 +62,7 @@ const ENTREES = [ ['data-doublon', 'un doublon : base de la liste, ou ligne de l’aperçu', null], ['data-doublons', 'import : les doublons de l’aperçu', ['']], ['data-duree', 'génération : la durée estimée', ['']], - ['data-ecran', 'un écran de l’application', ['accueil', 'apropos', 'evenement', 'recuperation']], + ['data-ecran', 'un écran de l’application', ['accueil', 'apercu', 'apropos', 'evenement', 'recuperation']], ['data-encodage', 'import : l’encodage nommé', ['']], ['data-enonce', 'un énoncé : diagnostic, ou défaut des tables', null], ['data-enregistre', 'import : le chemin des refus enregistrés', ['']], @@ -73,7 +74,9 @@ const ENTREES = [ ['data-evidence', 'plan : la personne mise en évidence', null], ['data-fantome', 'plan : le fantôme d’une personne glissée', ['']], ['data-fautive', 'événement : une proposition fautive', null], + ['data-feuille', 'aperçu : une feuille du document imprimé, une page, par son rang', null], ['data-fiche', 'participants : la fiche d’une personne', null], + ['data-filigrane', 'aperçu : le filigrane BROUILLON d’une feuille', ['']], ['data-fichier', 'import : le fichier lu', ['']], ['data-focus-initial', 'plan : le siège qui reçoit le focus à l’ouverture du panneau', null], ['data-fusion', 'import : un groupe d’orthographes fondu', ['']], @@ -86,6 +89,7 @@ const ENTREES = [ ['data-groupe-ligne', 'participants : le groupe qui compte une ligne', null], ['data-historique', 'événement : la commande de l’historique', ['']], ['data-homonyme', 'import : une ligne homonyme', null], + ['data-impression', 'aperçu : le document imprimé', ['']], ['data-hors-classement', 'propositions : les lignes hors classement', ['']], ['data-issue', 'génération : l’issue de la dernière génération', ['']], ['data-legende', 'plan : la légende des conflits', ['']], @@ -112,10 +116,12 @@ const ENTREES = [ ['data-orthographe', 'import : une orthographe d’un groupe', ['']], ['data-panneau', 'plan : un panneau', ['place', 'table']], ['data-panneau-diagnostic', 'plan : le panneau du diagnostic', ['']], + ['data-papier', 'le papier de la machine : la question, ou le papier choisi', ['choisi', 'question']], ['data-participant', 'une personne, par son identifiant', null], ['data-partie', 'plan : une partie d’une ligne de liste ou du fantôme', null], ['data-pastille', 'plan : la pastille d’une table', null], ['data-phrase', 'une phrase : diagnostic ou défaut des tables', ['']], + ['data-pied', 'aperçu : le pied d’une feuille imprimée', ['']], ['data-place-fixe', 'événement : la place fixe du cadre de mode', ['']], ['data-plan', 'plan : le du plan', ['']], ['data-poignee', 'plan : la poignée d’une table', null], @@ -153,6 +159,7 @@ const ENTREES = [ ['data-verrou-seance', 'événement : la séance du verrou', ['']], ['data-version', 'accueil : la version affichée', ['']], ['data-vue', 'plan : le groupe de la vue', ['']], + ['data-vue-impression', 'aperçu : la vue du document imprimé — plan, accueil ou tables', null], ]; // La Map des entrées, gelée : Object.freeze ne gèle pas le contenu d'une diff --git a/test/navigateur/ecrans.js b/test/navigateur/ecrans.js index 42a77e7..cc04fea 100644 --- a/test/navigateur/ecrans.js +++ b/test/navigateur/ecrans.js @@ -27,6 +27,7 @@ import { rechercher } from '../../src/moteur/recherche.js'; import { serialiser } from '../../src/stockage/canonique.js'; import { analyser, capacite, configurationDepuisCharge } from '../../src/stockage/document.js'; import { SUFFIXES } from '../../src/stockage/noms.js'; +import { FICHIER_REGLAGES, serialiserReglages } from '../../src/stockage/reglages_locaux.js'; import { VERSION } from '../../src/version.genere.js'; import urlCasLimites from '../../exemples/participants_cas_limites.csv?url'; import { pointer, touche } from './entrees.js'; @@ -219,8 +220,8 @@ function avecTours(charge, tours) { return suivante; } -// La principale, sa proposition retenue, puis le plan bloqué. -function chargeBloquee() { +/** La principale, sa proposition retenue, puis le plan bloqué. */ +export function chargeBloquee() { const principale = chargePrincipale(); const charge = executer(principale, 'retenirProposition', { id: principale.propositions[0].id }); return executer(charge, 'changerEtat', { etat: 'bloque' }); @@ -251,6 +252,14 @@ export const fichierEtat = (charge, base = BASE) => ({ texte: serialiser(charge, { revision: 1, produitVersion: VERSION.affichee }), }); +/** Les réglages locaux de la machine, posés dans les Documents, le + * dossier de travail des épreuves : le papier choisi, aucun cadrage. */ +export const fichierReglages = (papier) => ({ + racine: 'documents', + chemin: FICHIER_REGLAGES, + texte: serialiserReglages({ format: 1, papier, cadrages: [] }), +}); + /** Les octets du CSV des cas limites, tels que le fichier les porte. */ export async function octetsCasLimites() { return new Uint8Array(await (await fetch(urlCasLimites)).arrayBuffer()); @@ -315,6 +324,26 @@ export async function ouvrirEvenement(contexte, charge, { ecriture = false, sect return m; } +/** + * Ouvre l'événement de la charge, sur la section donnée, puis l'aperçu + * avant impression par la commande « Imprimer… » de sa tête (§ 11.7), et + * rend le montage. Le papier de la machine est posé avant le montage dans + * les réglages locaux ; null n'en pose aucun, et l'aperçu s'ouvre sur la + * question du papier. Avec un papier, l'aperçu rend son document. + * + * @param {Object} contexte + * @param {Object} charge + * @param {{papier?: 'A4'|'Lettre'|null, ecriture?: boolean, section?: string, options?: Object}} [reglages] + */ +export async function ouvrirApercu(contexte, charge, { papier = 'A4', ecriture = false, section = 'plan', options = {} } = {}) { + const fichiers = [...(papier === null ? [] : [fichierReglages(papier)]), ...(options.fichiers ?? [])]; + const m = await ouvrirEvenement(contexte, charge, { ecriture, section, options: { ...options, fichiers } }); + await cliquer(m, '[data-ecran="evenement"] [data-commande="apercu"]'); + await element(m, '[data-ecran="apercu"]'); + if (papier !== null) await element(m, '[data-impression] [data-feuille]'); + return m; +} + // Un écran d'événement sur la principale, sur la section donnée, au mode // donné ; la bloquée pour 'bloque'. function ecranDeSection(section, mode) { @@ -766,6 +795,17 @@ export const ECRANS = Object.freeze([ return m.racine; }, }, + { + nom: 'apercu', + mode: 'lecture', + async atteindre(contexte) { + // L'aperçu avant impression du plan de la principale, sa proposition + // montrée : un brouillon, chaque feuille sous son filigrane. + const m = await ouvrirApercu(contexte, chargePrincipale(), { section: 'participants' }); + await element(m, '[data-impression] [data-filigrane]'); + return m.racine; + }, + }, { nom: 'refus.modeLecture', mode: 'lecture', diff --git a/vitest.config.js b/vitest.config.js index 376be0c..5a71d78 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -153,6 +153,10 @@ export default defineConfig({ testsToRun: () => [...RELANCES_D_UN_COMPOSANT] }, { pattern: /(^|\/)src\/interface\/jetons\.css$/, testsToRun: () => [...RELANCES_DES_JETONS] }, + // La feuille d'impression de l'aperçu (§ 11.7) : les gardes des + // feuilles de style, et le modèle de l'aperçu, qui la lit par le disque. + { pattern: /(^|\/)src\/interface\/impression\.css$/, + testsToRun: () => [...RELANCES_DES_JETONS, 'src/interface/impression/modele.test.js'] }, // Les documents porteurs de régions engendrées et le manifeste des // captures, que scripts/documentation.js lit par le disque (§ 19.8). { pattern: /(^|\/)(README\.md|GUIDE-[^/]*\.md|doc\/captures\/manifeste\.json)$/,