[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
// © 2026 TechnoLibre (http://www.technolibre.ca)
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
// Le registre des marques porteuses d'information et des textes (§ 14.5,
// § 7.5, § 7.6, § 7.7), et la palette de jetons.css validée par calcul sur
// lui (§ 12.7) : chaque entrée tient son seuil sur chacun de ses fonds, dans
// les deux thèmes, en couleurs d'origine puis sous les trois dichromaties.
[ADD] engine for quality: A_max, facilitator diversity, run trace, pairs
The quality page needs figures the engine did not yet produce. It now
bounds each person's distinct memberships (A_max) and checks it with
the indicator guard; reads facilitator diversity over the evening and
per round; records the search's moves and record points; lists repeated
and imposed pairs; and lays out charts through one geometry module with
two bench thresholds. Chart colour tokens carry their own validation.
Default plans still equal the reference fingerprints; nothing new is
written to the file yet.
Checked: node 4768, navigateur 967, node-long 128; new modules at 100 %.
--- FR ---
[ADD] moteur de la qualité : A_max, diversité, course, paires
La page de qualité demande des chiffres que le moteur ne produisait pas.
Il borne désormais les appartenances distinctes de chacun (A_max) et la
garde des indicateurs la vérifie ; il lit la diversité des animateurs sur
la soirée et par tour ; il relève les mouvements et les points de la
recherche ; il liste les paires répétées et imposées ; un seul module de
géométrie dispose les graphiques, avec deux seuils au banc. Les jetons
des graphiques portent leur propre validation. Les plans par défaut
égalent les empreintes ; rien de neuf ne s'écrit encore au fichier.
Vérifié : node 4768, navigateur 967, longues 128 ; nouveaux modules à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-12 04:29:27 -04:00
// La famille « graphique » (§ 12.7, § 13.3) a ses listes à part, hors de
// MARQUES et de TEXTES, éprouvées de même.
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
import assert from 'node:assert/strict' ;
import { readFileSync } from 'node:fs' ;
import { describe , test } from '../../test/lanceur.js' ;
[ADD] engine for quality: A_max, facilitator diversity, run trace, pairs
The quality page needs figures the engine did not yet produce. It now
bounds each person's distinct memberships (A_max) and checks it with
the indicator guard; reads facilitator diversity over the evening and
per round; records the search's moves and record points; lists repeated
and imposed pairs; and lays out charts through one geometry module with
two bench thresholds. Chart colour tokens carry their own validation.
Default plans still equal the reference fingerprints; nothing new is
written to the file yet.
Checked: node 4768, navigateur 967, node-long 128; new modules at 100 %.
--- FR ---
[ADD] moteur de la qualité : A_max, diversité, course, paires
La page de qualité demande des chiffres que le moteur ne produisait pas.
Il borne désormais les appartenances distinctes de chacun (A_max) et la
garde des indicateurs la vérifie ; il lit la diversité des animateurs sur
la soirée et par tour ; il relève les mouvements et les points de la
recherche ; il liste les paires répétées et imposées ; un seul module de
géométrie dispose les graphiques, avec deux seuils au banc. Les jetons
des graphiques portent leur propre validation. Les plans par défaut
égalent les empreintes ; rien de neuf ne s'écrit encore au fichier.
Vérifié : node 4768, navigateur 967, longues 128 ; nouveaux modules à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-12 04:29:27 -04:00
import {
CONFLITS ,
ECHELLE _GRAPHIQUE ,
FONDS _GRAPHIQUE ,
JETONS _CONFLIT ,
MARQUES ,
MARQUES _GRAPHIQUE ,
NIVEAUX _GRAPHIQUE ,
SEPARATIONS ,
SIGNES ,
TEINTES _APPARTENANCE ,
TEXTES ,
TEXTES _GRAPHIQUE ,
} from './marques.js' ;
import {
contraste ,
lireBlocs ,
lireJetons ,
simuler ,
teinte ,
verifierClartes ,
verifierPalette ,
verifierSeparation ,
verifierTeintesReservees ,
} from './palette.js' ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
const JETONS = readFileSync ( new URL ( './jetons.css' , import . meta . url ) , 'utf8' ) ;
const THEMES = [ 'clair' , 'sombre' ] ;
const NOMS _MARQUES = [
'cadenasFerme' , 'cadenasOuvert' , 'anneau' , 'hachure' , 'remplissagePlein' ,
[ADD] plan: one hue and pattern per conflict rule, legend, appartenance
The plan drew every conflict in one hue, so a table that seats colleagues
together looked like one where people meet again. Each active rule now has
its own reserved hue and pattern: same appartenance hatched one way, met
again hatched the other, back at a table dotted; rules on one table
overlap, the badge keeps the total. A legend from the view bar shows each
active rule's sample. A table's tooltip names the appartenance that brings
a pair together. The hues are validated by computation in both themes and
the spec (7.5, 12.7) follows. Checked: 2848 node, 580 browser tests.
--- FR ---
[ADD] plan : une teinte et une trame par règle, légende, appartenance
Le plan dessinait tout conflit d'une seule teinte : une table qui réunit
des collègues ressemblait à une table où des gens se revoient. Chaque
règle active a sa teinte réservée et sa trame : même appartenance hachurée
dans un sens, voisins revus dans l'autre, table revue en pointillé ; les
règles d'une table se superposent, la pastille garde le total. Une légende
dans la barre de vue montre l'échantillon de chaque règle active.
L'infobulle nomme l'appartenance qui réunit une paire. Teintes validées
par calcul dans les deux thèmes ; spec § 7.5, § 12.7. Vérifié : 2848, 580.
Assisted-by: Claude Opus 5.5
2026-10-08 04:33:43 -04:00
'contourConflit' , 'trameAppartenance' , 'trameVoisins' , 'trameTable' , 'pastille' , 'poignee' , 'gel' , 'miseEnEvidence' ,
[IMP] interface: full-width event screen, no mode frame, fewer columns
The 6 px mode frame around the event screen read as a dashed box that
narrowed the work surface, and the screen stopped at the 87rem page
width. The event screen now spans the window; the mode band under the
app header, the header label and the pill beside « Modifier » carry the
mode. The event list drops both « Collisions cumulées » columns, which
repeated a measure the proposals screen already shows: 7 columns, not 9.
Checked: node 4009, browser 872, long 105; full width at 1366 and 1920.
--- FR ---
[IMP] interface : événement pleine fenêtre, sans cadre, liste allégée
Le cadre de mode de 6 px autour de l'écran d'événement se lisait comme
un encadré pointillé qui rétrécissait la surface, et l'écran s'arrêtait
à 87rem. L'écran de l'événement occupe la fenêtre ; la bande sous le
bandeau, le libellé du bandeau et la pastille près de « Modifier »
portent le mode. La liste des événements perd ses deux colonnes
« Collisions cumulées », déjà montrées par les propositions : 7
colonnes au lieu de 9. Vérifié : node 4009, navigateur 872, long 105 ;
pleine largeur à 1366 et 1920.
Assisted-by: Claude Opus 5.5
2026-10-11 02:44:40 -04:00
'selection' , 'focus' ,
[IMP] card and priority editor: red circled cross, number weight field
A tag's « Retirer » text button read like plain text, and the weight of
a criterion took a whole column for a number from 1 to 100. The card
now removes a tag with a red circled cross, shown in write mode only,
named « Retirer « tag » » for screen readers, with a 24 px target; the
red has its own token, exempted from the conflict-hue rule and limited
to card surfaces. The weight is a narrow number field, still read and
refused as before.
Checked: node 4597, navigateur 965; captures 04a and 10a republished.
--- FR ---
[IMP] fiche et priorités : croix rouge cerclée, poids en champ nombre
Le bouton texte « Retirer » d'une étiquette se lisait comme du texte, et
le poids d'un critère prenait toute une colonne pour un nombre de 1 à
100. La fiche retire désormais une étiquette par une croix rouge cerclée,
en mode écriture seulement, nommée « Retirer « étiquette » » pour les
lecteurs d'écran, sur une cible de 24 px ; le rouge a son jeton, exempté
de la règle des teintes de conflit et borné aux surfaces de la fiche. Le
poids est un champ nombre étroit, lu et refusé comme avant.
Vérifié : node 4597, navigateur 965 ; captures 04a et 10a republiées.
Assisted-by: Claude Opus 5.5
2026-10-11 23:34:28 -04:00
'contourTable' , 'contourChaise' , 'echelle' , 'fantome' , 'chargement' , 'filigrane' , 'retrait' ,
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
] ;
// Les remplissages d'une chaise : fond, pleine, et les deux couleurs de la
// hachure — ses traits sur le fond de la chaise.
const REMPLISSAGES _CHAISE = [ '--couleur-chaise-fond' , '--couleur-chaise-pleine' , '--couleur-hachure' ] ;
// Fonds minimaux des marques de désignation et du fantôme. « chaise » vaut
// chacun des remplissages d'une chaise : la marque se pose sur une chaise
// vide, pourvue ou à pourvoir.
const c = ( nom ) => ` --couleur- ${ nom } ` ;
const FONDS _REQUIS = {
focus : [ ... [ 'page-fond' , 'bandeau-fond' , 'sol' , 'table-fond' ] . map ( c ) , ... REMPLISSAGES _CHAISE ] ,
selection : [ ... [ 'page-fond' , 'sol' , 'table-fond' ] . map ( c ) , ... REMPLISSAGES _CHAISE ] ,
miseEnEvidence : [ ... [ 'page-fond' , 'sol' , 'table-fond' ] . map ( c ) , ... REMPLISSAGES _CHAISE ] ,
fantome : [ ... [ 'sol' , 'table-fond' ] . map ( c ) , ... REMPLISSAGES _CHAISE ] ,
} ;
const parNom = ( registre , nom ) => registre . find ( ( e ) => e . nom === nom ) ;
describe ( 'registre des marques' , ( ) => {
[IMP] card and priority editor: red circled cross, number weight field
A tag's « Retirer » text button read like plain text, and the weight of
a criterion took a whole column for a number from 1 to 100. The card
now removes a tag with a red circled cross, shown in write mode only,
named « Retirer « tag » » for screen readers, with a 24 px target; the
red has its own token, exempted from the conflict-hue rule and limited
to card surfaces. The weight is a narrow number field, still read and
refused as before.
Checked: node 4597, navigateur 965; captures 04a and 10a republished.
--- FR ---
[IMP] fiche et priorités : croix rouge cerclée, poids en champ nombre
Le bouton texte « Retirer » d'une étiquette se lisait comme du texte, et
le poids d'un critère prenait toute une colonne pour un nombre de 1 à
100. La fiche retire désormais une étiquette par une croix rouge cerclée,
en mode écriture seulement, nommée « Retirer « étiquette » » pour les
lecteurs d'écran, sur une cible de 24 px ; le rouge a son jeton, exempté
de la règle des teintes de conflit et borné aux surfaces de la fiche. Le
poids est un champ nombre étroit, lu et refusé comme avant.
Vérifié : node 4597, navigateur 965 ; captures 04a et 10a republiées.
Assisted-by: Claude Opus 5.5
2026-10-11 23:34:28 -04:00
test ( 'les vingt-deux marques du contrat, noms uniques, seuil 3, au moins un fond chacune' , ( ) => {
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
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 ) ;
for ( const marque of MARQUES ) {
assert . deepEqual ( Object . keys ( marque ) . sort ( ) , [ 'fonds' , 'jeton' , 'nom' , 'seuil' ] , marque . nom ) ;
assert . equal ( marque . seuil , 3 , marque . nom ) ;
assert . ok ( marque . fonds . length > 0 , marque . nom ) ;
assert . equal ( new Set ( marque . fonds ) . size , marque . fonds . length , ` ${ marque . nom } : fond en double ` ) ;
}
} ) ;
test ( 'le registre est figé' , ( ) => {
assert . ok ( Object . isFrozen ( MARQUES ) && Object . isFrozen ( TEXTES ) ) ;
assert . ok ( MARQUES . every ( ( m ) => Object . isFrozen ( m ) && Object . isFrozen ( m . fonds ) ) ) ;
assert . ok ( TEXTES . every ( ( m ) => Object . isFrozen ( m ) && Object . isFrozen ( m . fonds ) ) ) ;
} ) ;
test ( 'le focus, la sélection, la mise en évidence et le fantôme se mesurent sur chaque fond où ils se peignent, chaque remplissage de chaise compris' , ( ) => {
for ( const nom of [ 'focus' , 'selection' , 'miseEnEvidence' , 'fantome' ] ) {
const marque = parNom ( MARQUES , nom ) ;
assert . notEqual ( marque , undefined , nom ) ;
for ( const fond of FONDS _REQUIS [ nom ] ) {
assert . ok ( marque . fonds . includes ( fond ) , ` ${ nom } : ${ fond } ` ) ;
}
}
} ) ;
test ( 'une marque posée sur une chaise se mesure sur chacun de ses remplissages' , ( ) => {
const surChaise = [ 'cadenasFerme' , 'cadenasOuvert' , 'anneau' ] ;
for ( const nom of surChaise ) {
for ( const fond of REMPLISSAGES _CHAISE ) {
assert . ok ( parNom ( MARQUES , nom ) . fonds . includes ( fond ) , ` ${ nom } : ${ fond } ` ) ;
}
}
assert . deepEqual ( parNom ( MARQUES , 'hachure' ) . fonds , [ '--couleur-chaise-fond' ] ) ;
} ) ;
test ( 'chaque jeton et chaque fond du registre existe dans les deux thèmes' , ( ) => {
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
const entrees = [ ... MARQUES , ... TEXTES , ... SIGNES ] ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
assert . ok ( entrees . length > 20 ) ;
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
for ( const entree of entrees ) {
for ( const nom of [ entree . jeton , ... entree . fonds ] ) {
assert . ok ( jetons . has ( nom ) , ` ${ theme } : ${ entree . nom } → ${ nom } ` ) ;
}
}
}
} ) ;
} ) ;
[ADD] print: preview screen, printable sheet, paper question
Printed pages must show exactly what will come out of the printer. An
Imprimer button in the event header opens a preview screen where each page
is a sheet at paper size, light whatever the theme, with its footer and,
under the strict draft rule, BROUILLON in outline; a proposal shown on a
retained plan prints as a draft. The paper is asked once per machine and
can be changed there. The table sheets offer one sheet per round. A failed
print shows IMPRESSION_ECHEC. The captures are republished with the button.
Checked: red first; 3651 node, 707 browser and 101 long tests green from
the index alone.
--- FR ---
[ADD] impression : aperçu, feuille imprimable, question du papier
Une page imprimée doit montrer exactement ce que l'imprimante rendra. Un
bouton Imprimer de la tête de l'événement ouvre un aperçu où chaque page
est une feuille au format du papier, claire quel que soit le thème, avec
son pied et, sous la règle stricte, BROUILLON au trait ; une proposition
montrée sur un plan retenu s'imprime en brouillon. Le papier se demande une
fois par machine et s'y change. Les feuilles par table offrent une feuille
par tour. Une impression échouée affiche IMPRESSION_ECHEC. Les captures
sont republiées avec le bouton.
Vérifié : rouge d'abord ; 3651 node, 707 navigateur et 101 longues vertes
depuis l'index seul.
Assisted-by: Claude Opus 5.5
2026-10-09 17:09:29 -04:00
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 ] ) ;
} ) ;
} ) ;
[ADD] plan: one hue and pattern per conflict rule, legend, appartenance
The plan drew every conflict in one hue, so a table that seats colleagues
together looked like one where people meet again. Each active rule now has
its own reserved hue and pattern: same appartenance hatched one way, met
again hatched the other, back at a table dotted; rules on one table
overlap, the badge keeps the total. A legend from the view bar shows each
active rule's sample. A table's tooltip names the appartenance that brings
a pair together. The hues are validated by computation in both themes and
the spec (7.5, 12.7) follows. Checked: 2848 node, 580 browser tests.
--- FR ---
[ADD] plan : une teinte et une trame par règle, légende, appartenance
Le plan dessinait tout conflit d'une seule teinte : une table qui réunit
des collègues ressemblait à une table où des gens se revoient. Chaque
règle active a sa teinte réservée et sa trame : même appartenance hachurée
dans un sens, voisins revus dans l'autre, table revue en pointillé ; les
règles d'une table se superposent, la pastille garde le total. Une légende
dans la barre de vue montre l'échantillon de chaque règle active.
L'infobulle nomme l'appartenance qui réunit une paire. Teintes validées
par calcul dans les deux thèmes ; spec § 7.5, § 12.7. Vérifié : 2848, 580.
Assisted-by: Claude Opus 5.5
2026-10-08 04:33:43 -04:00
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 (
CONFLITS . map ( ( c ) => [ c . regle , c . marque , c . jeton , c . forme ] ) ,
[
[ 'separerAppartenances' , 'trameAppartenance' , '--couleur-conflit-appartenance' , 'hachuresDescendantes' ] ,
[ 'nouveauxVoisins' , 'trameVoisins' , '--couleur-conflit-voisins' , 'hachuresMontantes' ] ,
[ 'nouvelleTable' , 'trameTable' , '--couleur-conflit-table' , 'points' ] ,
] ,
) ;
assert . ok ( CONFLITS . every ( ( c ) => Object . keys ( c ) . sort ( ) . join ( ) === 'forme,jeton,marque,regle' ) ) ;
assert . ok ( Object . isFrozen ( CONFLITS ) && CONFLITS . every ( ( c ) => Object . isFrozen ( c ) ) ) ;
assert . deepEqual ( JETONS _CONFLIT , CONFLITS . map ( ( { jeton } ) => jeton ) ) ;
assert . ok ( Object . isFrozen ( JETONS _CONFLIT ) ) ;
// Les seuils d'écart perçu : entre deux teintes de conflit, et d'une
// teinte de conflit à une autre marque du plan.
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
assert . deepEqual ( SEPARATIONS , { entreConflits : 12 , autresMarques : 5 , entreAppartenances : 10 , clarteAppartenances : 10 } ) ;
[ADD] plan: one hue and pattern per conflict rule, legend, appartenance
The plan drew every conflict in one hue, so a table that seats colleagues
together looked like one where people meet again. Each active rule now has
its own reserved hue and pattern: same appartenance hatched one way, met
again hatched the other, back at a table dotted; rules on one table
overlap, the badge keeps the total. A legend from the view bar shows each
active rule's sample. A table's tooltip names the appartenance that brings
a pair together. The hues are validated by computation in both themes and
the spec (7.5, 12.7) follows. Checked: 2848 node, 580 browser tests.
--- FR ---
[ADD] plan : une teinte et une trame par règle, légende, appartenance
Le plan dessinait tout conflit d'une seule teinte : une table qui réunit
des collègues ressemblait à une table où des gens se revoient. Chaque
règle active a sa teinte réservée et sa trame : même appartenance hachurée
dans un sens, voisins revus dans l'autre, table revue en pointillé ; les
règles d'une table se superposent, la pastille garde le total. Une légende
dans la barre de vue montre l'échantillon de chaque règle active.
L'infobulle nomme l'appartenance qui réunit une paire. Teintes validées
par calcul dans les deux thèmes ; spec § 7.5, § 12.7. Vérifié : 2848, 580.
Assisted-by: Claude Opus 5.5
2026-10-08 04:33:43 -04:00
assert . ok ( Object . isFrozen ( SEPARATIONS ) ) ;
} ) ;
test ( 'chaque trame est au registre, peinte de son jeton sur le fond du plateau' , ( ) => {
for ( const { marque , jeton } of CONFLITS ) {
const entree = parNom ( MARQUES , marque ) ;
assert . notEqual ( entree , undefined , marque ) ;
assert . equal ( entree . jeton , jeton , marque ) ;
assert . deepEqual ( entree . fonds , [ '--couleur-table-fond' ] , marque ) ;
}
} ) ;
test ( 'teinte réservée au registre : une teinte de conflit ne peint que sa trame, aucune autre marque ni aucun texte' , ( ) => {
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
const peintes = [ ... MARQUES , ... TEXTES , ... SIGNES ] . filter ( ( e ) => JETONS _CONFLIT . includes ( e . jeton ) || e . fonds . some ( ( f ) => JETONS _CONFLIT . includes ( f ) ) ) ;
[ADD] plan: one hue and pattern per conflict rule, legend, appartenance
The plan drew every conflict in one hue, so a table that seats colleagues
together looked like one where people meet again. Each active rule now has
its own reserved hue and pattern: same appartenance hatched one way, met
again hatched the other, back at a table dotted; rules on one table
overlap, the badge keeps the total. A legend from the view bar shows each
active rule's sample. A table's tooltip names the appartenance that brings
a pair together. The hues are validated by computation in both themes and
the spec (7.5, 12.7) follows. Checked: 2848 node, 580 browser tests.
--- FR ---
[ADD] plan : une teinte et une trame par règle, légende, appartenance
Le plan dessinait tout conflit d'une seule teinte : une table qui réunit
des collègues ressemblait à une table où des gens se revoient. Chaque
règle active a sa teinte réservée et sa trame : même appartenance hachurée
dans un sens, voisins revus dans l'autre, table revue en pointillé ; les
règles d'une table se superposent, la pastille garde le total. Une légende
dans la barre de vue montre l'échantillon de chaque règle active.
L'infobulle nomme l'appartenance qui réunit une paire. Teintes validées
par calcul dans les deux thèmes ; spec § 7.5, § 12.7. Vérifié : 2848, 580.
Assisted-by: Claude Opus 5.5
2026-10-08 04:33:43 -04:00
assert . deepEqual ( peintes . map ( ( e ) => e . nom ) , CONFLITS . map ( ( { marque } ) => marque ) ) ;
// Le contour épaissi d'une table en conflit est commun aux trois règles :
// il ne porte aucune de leurs teintes.
assert . ok ( ! JETONS _CONFLIT . includes ( parNom ( MARQUES , 'contourConflit' ) . jeton ) ) ;
} ) ;
} ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
describe ( 'registre des textes' , ( ) => {
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
test ( 'page, secondaire, bandeau, refus, avis, listes du plan, pastille, puis les textes des cartes, des étiquettes, de la bande de mode, du bandeau, de l’ action et des messages ; seuil 4,5' , ( ) => {
assert . deepEqual ( TEXTES . map ( ( t ) => t . nom ) , [
'page' , 'secondaire' , 'bandeau' , 'refus' , 'avis' , 'listes' , 'pastille' ,
'carte' , 'secondaireCarte' , 'etiquette' , 'bandeMode' , 'bandeauSecondaire' , 'bandeauChoisi' , 'action' , 'lien' , 'information' ,
] ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
assert . ok ( TEXTES . every ( ( t ) => t . seuil === 4.5 && t . fonds . length > 0 ) ) ;
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
assert . equal ( new Set ( TEXTES . map ( ( t ) => t . nom ) ) . size , TEXTES . length ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
assert . deepEqual ( parNom ( TEXTES , 'listes' ) . fonds , [ '--couleur-sol' ] ) ;
assert . deepEqual ( parNom ( TEXTES , 'pastille' ) . fonds , [ '--couleur-pastille-fond' ] ) ;
} ) ;
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
test ( 'le texte d’ une étiquette est l’ encre de la page, sur chaque fond teinté d’ une pastille (§ 12.7) ; celui d’ un message, l’ encre de son message sur son fond' , ( ) => {
const etiquette = parNom ( TEXTES , 'etiquette' ) ;
assert . equal ( etiquette . jeton , '--couleur-page-texte' ) ;
const fondsTeintes = [
'neutre' , 'etat-brouillon' , 'etat-propose' , 'etat-retenu' , 'etat-bloque' , 'avis' , 'refus' , 'information' ,
... TEINTES _APPARTENANCE . map ( ( { rang } ) => ` appartenance- ${ rang } ` ) ,
] . map ( ( teinte ) => ` --couleur- ${ teinte } -fond ` ) ;
for ( const fond of fondsTeintes ) assert . ok ( etiquette . fonds . includes ( fond ) , fond ) ;
assert . deepEqual ( parNom ( TEXTES , 'bandeMode' ) . fonds , [ 'ecriture' , 'lecture' , 'bloque' ] . map ( ( m ) => ` --couleur-mode- ${ m } -fond ` ) ) ;
for ( const message of [ 'refus' , 'avis' , 'information' ] ) {
const entree = parNom ( TEXTES , message ) ;
assert . equal ( entree . jeton , ` --couleur- ${ message } ` , message ) ;
for ( const fond of [ ` --couleur- ${ message } -fond ` , '--couleur-page-fond' , '--couleur-carte-fond' ] ) assert . ok ( entree . fonds . includes ( fond ) , ` ${ message } : ${ fond } ` ) ;
}
} ) ;
} ) ;
describe ( 'les signes des pastilles, des messages, de la bande de mode et de l’ action' , ( ) => {
const NOMS _SIGNES = [
'etatBrouillon' , 'etatPropose' , 'etatRetenu' , 'etatBloque' , 'neutre' ,
'appartenance1' , 'appartenance2' , 'appartenance3' , 'appartenance4' , 'appartenance5' , 'appartenance6' ,
'signeAvis' , 'signeRefus' , 'signeInformation' , 'bandeEcriture' , 'bandeLecture' , 'bandeBloque' , 'actionPrincipale' ,
] ;
test ( 'le registre des signes : noms uniques, hors de MARQUES, seuil 3, figé' , ( ) => {
assert . deepEqual ( SIGNES . map ( ( s ) => s . nom ) , NOMS _SIGNES ) ;
assert . ok ( Object . isFrozen ( SIGNES ) && SIGNES . every ( ( s ) => Object . isFrozen ( s ) && Object . isFrozen ( s . fonds ) ) ) ;
assert . ok ( SIGNES . every ( ( s ) => s . seuil === 3 && s . fonds . length > 0 ) ) ;
const marques = new Set ( MARQUES . map ( ( m ) => m . nom ) ) ;
assert . deepEqual ( SIGNES . filter ( ( s ) => marques . has ( s . nom ) ) . map ( ( s ) => s . nom ) , [ ] ) ;
} ) ;
test ( 'le signe d’ un état, du neutre ou d’ une appartenance se mesure sur son fond teinté, la carte, son fond sourd et la page' , ( ) => {
const teintes = [
[ 'etatBrouillon' , 'etat-brouillon' ] , [ 'etatPropose' , 'etat-propose' ] , [ 'etatRetenu' , 'etat-retenu' ] , [ 'etatBloque' , 'etat-bloque' ] , [ 'neutre' , 'neutre' ] ,
... TEINTES _APPARTENANCE . map ( ( { rang } ) => [ ` appartenance ${ rang } ` , ` appartenance- ${ rang } ` ] ) ,
] ;
for ( const [ nom , teinte ] of teintes ) {
const signe = parNom ( SIGNES , nom ) ;
assert . equal ( signe . jeton , ` --couleur- ${ teinte } -marque ` , nom ) ;
assert . deepEqual ( signe . fonds , [ ` --couleur- ${ teinte } -fond ` , '--couleur-carte-fond' , '--couleur-carte-fond-sourd' , '--couleur-page-fond' ] , nom ) ;
}
[IMP] interface: full-width event screen, no mode frame, fewer columns
The 6 px mode frame around the event screen read as a dashed box that
narrowed the work surface, and the screen stopped at the 87rem page
width. The event screen now spans the window; the mode band under the
app header, the header label and the pill beside « Modifier » carry the
mode. The event list drops both « Collisions cumulées » columns, which
repeated a measure the proposals screen already shows: 7 columns, not 9.
Checked: node 4009, browser 872, long 105; full width at 1366 and 1920.
--- FR ---
[IMP] interface : événement pleine fenêtre, sans cadre, liste allégée
Le cadre de mode de 6 px autour de l'écran d'événement se lisait comme
un encadré pointillé qui rétrécissait la surface, et l'écran s'arrêtait
à 87rem. L'écran de l'événement occupe la fenêtre ; la bande sous le
bandeau, le libellé du bandeau et la pastille près de « Modifier »
portent le mode. La liste des événements perd ses deux colonnes
« Collisions cumulées », déjà montrées par les propositions : 7
colonnes au lieu de 9. Vérifié : node 4009, navigateur 872, long 105 ;
pleine largeur à 1366 et 1920.
Assisted-by: Claude Opus 5.5
2026-10-11 02:44:40 -04:00
// L'icône de la bande de mode porte la teinte de son mode, sur le fond de
// la bande.
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
for ( const mode of [ 'Ecriture' , 'Lecture' , 'Bloque' ] ) {
const signe = parNom ( SIGNES , ` bande ${ mode } ` ) ;
[REF] event screen: no frame names, list summaries trimmed to use
The event list shows only the retained plan's redundancy gap and the id
of the best proposal, yet its summaries still carried cumulative
collisions and redundancy totals. They now carry what the list reads.
The mode frame is gone from the screen, so its names go too: the mode
surface component, the mode colour tokens and the bar's mode label are
named for what they are. Values, pixels and captures are unchanged.
Checked: node 4039, navigateur 884 with the walkthrough, node-long 105.
--- FR ---
[REF] écran d'événement : sans noms de cadre, résumés de liste réduits
La liste des événements ne montre que l'écart de redondance du retenu et
l'identifiant de la meilleure proposition, mais ses résumés portaient
encore les collisions cumulées et les totaux de redondance. Ils portent
désormais ce que la liste lit. Le cadre de mode a quitté l'écran, ses noms
aussi : le composant de surface, les jetons de couleur du mode et le
libellé de mode de la barre portent le nom de ce qu'ils sont. Valeurs,
pixels et captures inchangés.
Vérifié : node 4039, navigateur 884 avec le parcours, node-long 105.
Assisted-by: Claude Opus 5.5
2026-10-11 06:56:17 -04:00
assert . deepEqual ( [ signe . jeton , ... signe . fonds ] , [ ` --couleur-mode- ${ mode . toLowerCase ( ) } ` , ` --couleur-mode- ${ mode . toLowerCase ( ) } -fond ` ] ) ;
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
}
} ) ;
test ( 'chaque signe tient 3:1 sur chacun de ses fonds, dans les deux thèmes, sous les trois simulations' , ( ) => {
assert . deepEqual ( verifierPalette ( JETONS , SIGNES ) , [ ] ) ;
} ) ;
test ( 'le focus se mesure aussi sur la carte et son fond sourd' , ( ) => {
for ( const fond of [ '--couleur-carte-fond' , '--couleur-carte-fond-sourd' ] ) assert . ok ( parNom ( MARQUES , 'focus' ) . fonds . includes ( fond ) , fond ) ;
} ) ;
} ) ;
describe ( 'la palette des appartenances (listes seulement)' , ( ) => {
test ( 'six teintes au plus, rangées de 1 à n, chacune un fond et une marque' , ( ) => {
assert . ok ( TEINTES _APPARTENANCE . length > 0 && TEINTES _APPARTENANCE . length <= 6 ) ;
assert . ok ( Object . isFrozen ( TEINTES _APPARTENANCE ) && TEINTES _APPARTENANCE . every ( Object . isFrozen ) ) ;
TEINTES _APPARTENANCE . forEach ( ( entree , i ) => {
assert . deepEqual ( entree , { rang : i + 1 , marque : ` --couleur-appartenance- ${ i + 1 } -marque ` , fond : ` --couleur-appartenance- ${ i + 1 } -fond ` } ) ;
} ) ;
} ) ;
const marques = ( ) => TEINTES _APPARTENANCE . map ( ( { marque } ) => marque ) ;
test ( 'chaque marque est chromatique dans les deux thèmes : la teinte la porte, avec le nom' , ( ) => {
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
for ( const nom of marques ( ) ) assert . notEqual ( teinte ( jetons . get ( nom ) ) , null , ` ${ theme } : ${ nom } ` ) ;
}
} ) ;
test ( ` les marques se séparent deux à deux de ${ SEPARATIONS . entreAppartenances } au moins (OKLab × 100), dans les deux thèmes, en couleurs d’ origine et sous les trois simulations ` , ( ) => {
assert . deepEqual ( verifierSeparation ( JETONS , marques ( ) , [ ] , SEPARATIONS . entreAppartenances ) , [ ] ) ;
} ) ;
test ( ` deux marques de teintes voisines se distinguent aussi en clarté, de ${ SEPARATIONS . clarteAppartenances } au moins (L d’ OKLab × 100) ` , ( ) => {
assert . deepEqual ( verifierClartes ( JETONS , marques ( ) , SEPARATIONS . clarteAppartenances ) , [ ] ) ;
} ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
} ) ;
describe ( 'palette de jetons.css validée par calcul (§ 12.7, § 14.5)' , ( ) => {
test ( 'aucun jeton de texte ou de marque, ni aucun de leurs fonds, n’ a de canal alpha' , ( ) => {
[ADD] interface: visual foundations, pills, cards, icons, navy header
Screens need colour that separates information without ever carrying it
alone. Tokens gain state, membership and status tints whose marks hold
3:1 and texts 4.5:1 in both themes and under three dichromacies, and
every hue stays 30 degrees from the reserved conflict hues. Commands get
three levels; Pastille, Carte and an icon module join the primitives;
memberships map to six stable tints over sorted names. A pill label
wraps and never truncates, and keeps one line beside a nowrap neighbour.
Checked: node 3949; browser 767, only the walkthrough captures differ.
--- FR ---
[ADD] interface : fondations, pastilles, cartes, icônes, bandeau marine
Les écrans ont besoin d'une couleur qui distingue l'information sans
jamais la porter seule. Les jetons gagnent des teintes d'état,
d'appartenance et de statut dont les marques tiennent 3:1 et les textes
4,5:1 dans les deux thèmes et sous trois dichromaties, chaque teinte à
30 degrés des teintes de conflit réservées. Les commandes ont trois
niveaux ; Pastille, Carte et un module d'icônes rejoignent les
primitives ; les appartenances reçoivent six teintes stables sur les
noms triés. Un libellé de pastille se replie, jamais tronqué.
Vérifié : node 3949 ; navigateur 767, seules les captures du parcours.
Assisted-by: Claude Opus 5.5
2026-10-10 22:59:51 -04:00
const noms = new Set ( [ ... MARQUES , ... TEXTES , ... SIGNES ] . flatMap ( ( e ) => [ e . jeton , ... e . fonds ] ) ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
assert . ok ( noms . size > 20 ) ;
const blocs = lireBlocs ( JETONS ) ;
for ( const theme of THEMES ) {
const selecteur = ` :root[data-theme=' ${ theme } '] ` ;
const brutes = new Map (
blocs . filter ( ( b ) => b . selecteurs . includes ( selecteur ) ) . flatMap ( ( b ) => [ ... b . declarations ] ) ,
) ;
const jetons = lireJetons ( JETONS , theme ) ;
for ( const nom of noms ) {
assert . equal ( jetons . get ( nom ) [ 3 ] , 1 , ` ${ theme } : ${ nom } ` ) ;
assert . doesNotMatch (
brutes . get ( nom ) ,
/rgba|hsla|transparent|#[0-9a-f]{8}\b|#[0-9a-f]{4}\b|\/\s*[\d.]+%?\s*\)/i ,
` ${ theme } : ${ nom } ` ,
) ;
}
}
} ) ;
test ( 'chaque marque tient 3:1 sur chacun de ses fonds, dans les deux thèmes, sous les trois simulations' , ( ) => {
assert . deepEqual ( verifierPalette ( JETONS , MARQUES ) , [ ] ) ;
} ) ;
test ( 'chaque texte tient 4,5:1 sur chacun de ses fonds, dans les deux thèmes, sous les trois simulations' , ( ) => {
assert . deepEqual ( verifierPalette ( JETONS , TEXTES ) , [ ] ) ;
} ) ;
[ADD] plan: one hue and pattern per conflict rule, legend, appartenance
The plan drew every conflict in one hue, so a table that seats colleagues
together looked like one where people meet again. Each active rule now has
its own reserved hue and pattern: same appartenance hatched one way, met
again hatched the other, back at a table dotted; rules on one table
overlap, the badge keeps the total. A legend from the view bar shows each
active rule's sample. A table's tooltip names the appartenance that brings
a pair together. The hues are validated by computation in both themes and
the spec (7.5, 12.7) follows. Checked: 2848 node, 580 browser tests.
--- FR ---
[ADD] plan : une teinte et une trame par règle, légende, appartenance
Le plan dessinait tout conflit d'une seule teinte : une table qui réunit
des collègues ressemblait à une table où des gens se revoient. Chaque
règle active a sa teinte réservée et sa trame : même appartenance hachurée
dans un sens, voisins revus dans l'autre, table revue en pointillé ; les
règles d'une table se superposent, la pastille garde le total. Une légende
dans la barre de vue montre l'échantillon de chaque règle active.
L'infobulle nomme l'appartenance qui réunit une paire. Teintes validées
par calcul dans les deux thèmes ; spec § 7.5, § 12.7. Vérifié : 2848, 580.
Assisted-by: Claude Opus 5.5
2026-10-08 04:33:43 -04:00
// Les autres marques du plan : celles qui se peignent sur le sol, le
// plateau ou une chaise, trames de conflit exceptées, chaque jeton une fois.
const FONDS _DU _PLAN = new Set ( [ '--couleur-sol' , '--couleur-table-fond' , ... REMPLISSAGES _CHAISE ] ) ;
const autresMarquesDuPlan = ( ) => [
... new Set ( MARQUES . filter ( ( m ) => ! JETONS _CONFLIT . includes ( m . jeton ) && m . fonds . some ( ( f ) => FONDS _DU _PLAN . has ( f ) ) ) . map ( ( m ) => m . jeton ) ) ,
] ;
test ( ` les trois teintes de conflit se séparent deux à deux de ${ SEPARATIONS . entreConflits } au moins (OKLab × 100), dans les deux thèmes, en couleurs d’ origine et sous les trois simulations ` , ( ) => {
assert . equal ( JETONS _CONFLIT . length , 3 ) ;
assert . deepEqual ( verifierSeparation ( JETONS , JETONS _CONFLIT , [ ] , SEPARATIONS . entreConflits ) , [ ] ) ;
} ) ;
test ( ` chaque teinte de conflit se sépare de ${ SEPARATIONS . autresMarques } au moins de chaque autre marque du plan, dans les deux thèmes, sous les trois simulations ` , ( ) => {
const autres = autresMarquesDuPlan ( ) ;
assert . ok ( autres . length >= 10 , ` ${ autres . length } autres marques ` ) ;
for ( const jeton of [ '--couleur-gel' , '--couleur-mise-en-evidence' , '--couleur-selection' , '--couleur-focus' , '--couleur-fantome' , '--couleur-pastille-fond' ] ) {
assert . ok ( autres . includes ( jeton ) , jeton ) ;
}
assert . deepEqual ( verifierSeparation ( JETONS , JETONS _CONFLIT , autres , SEPARATIONS . autresMarques ) , [ ] ) ;
} ) ;
[ADD] interface: colour tokens, two themes, checked palette, marks
The plan's colours carry meaning — a conflict, a reservation, a mode — so
they must stay readable for every reader and in both themes. jetons.css
defines the tokens for the light and dark themes, set by data-theme on the
root; palette.js reads them from the stylesheet text and checks, by
computation, contrast to WCAG and separation under the three
dichromacies, keeping the conflict hue reserved. The theme follows the
system at each start and the operator's choice lasts the session.
Checked: 1709 node and 43 browser tests.
--- FR ---
[ADD] interface : jetons de couleur, deux thèmes, palette, marques
Les couleurs du plan portent un sens — un conflit, une réservation, un mode
— et doivent rester lisibles pour tout lecteur et dans les deux thèmes.
jetons.css définit les jetons des thèmes clair et sombre, posés par
data-theme sur la racine ; palette.js les lit dans le texte de la feuille
et contrôle, par calcul, le contraste au sens des WCAG et la séparation
sous les trois dichromaties, la teinte du conflit restant réservée. Le
thème suit le système à chaque démarrage ; le choix de l'opérateur vaut
pour la séance.
Vérifié : 1709 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:12 -04:00
} ) ;
[ADD] engine for quality: A_max, facilitator diversity, run trace, pairs
The quality page needs figures the engine did not yet produce. It now
bounds each person's distinct memberships (A_max) and checks it with
the indicator guard; reads facilitator diversity over the evening and
per round; records the search's moves and record points; lists repeated
and imposed pairs; and lays out charts through one geometry module with
two bench thresholds. Chart colour tokens carry their own validation.
Default plans still equal the reference fingerprints; nothing new is
written to the file yet.
Checked: node 4768, navigateur 967, node-long 128; new modules at 100 %.
--- FR ---
[ADD] moteur de la qualité : A_max, diversité, course, paires
La page de qualité demande des chiffres que le moteur ne produisait pas.
Il borne désormais les appartenances distinctes de chacun (A_max) et la
garde des indicateurs la vérifie ; il lit la diversité des animateurs sur
la soirée et par tour ; il relève les mouvements et les points de la
recherche ; il liste les paires répétées et imposées ; un seul module de
géométrie dispose les graphiques, avec deux seuils au banc. Les jetons
des graphiques portent leur propre validation. Les plans par défaut
égalent les empreintes ; rien de neuf ne s'écrit encore au fichier.
Vérifié : node 4768, navigateur 967, longues 128 ; nouveaux modules à 100 %.
Assisted-by: Claude Opus 5.5
2026-10-12 04:29:27 -04:00
// --- La famille « graphique » (§ 12.7, § 13.3, D30, D43) ---------------------
const g = ( nom ) => ` --couleur-graphique- ${ nom } ` ;
// Les seize jetons de la famille, dans l'ordre du contrat.
const JETONS _GRAPHIQUE = [
'serie-1' , 'serie-2' , 'repere' , 'borne' , 'axe' , 'grille' , 'prouve' , 'reste' ,
'niveau-0' , 'niveau-1' , 'niveau-2' , 'niveau-3' , 'encre-claire' , 'encre-foncee' , 'bordure-case' , 'marque' ,
] . map ( g ) ;
const NOMS _MARQUES _GRAPHIQUE = [
'graphiqueSerie1' , 'graphiqueSerie2' , 'graphiqueRepere' , 'graphiqueBorne' , 'graphiqueAxe' ,
'graphiqueProuve' , 'graphiqueReste' , 'graphiqueNiveau1' , 'graphiqueNiveau2' , 'graphiqueNiveau3' ,
'graphiqueBordureCase' , 'graphiqueMarque' ,
] ;
const CARTE = '--couleur-carte-fond' ;
const PAGE = '--couleur-page-fond' ;
const SIMULATIONS _ET _ORIGINE = [ 'origine' , 'protanopie' , 'deuteranopie' , 'tritanopie' ] ;
describe ( 'la famille « graphique » : ses listes, hors de MARQUES et de TEXTES' , ( ) => {
test ( 'les seize jetons du contrat existent dans les deux thèmes, et aucun autre jeton ne porte le préfixe de la famille' , ( ) => {
for ( const theme of THEMES ) {
const famille = [ ... lireJetons ( JETONS , theme ) . keys ( ) ] . filter ( ( n ) => n . startsWith ( '--couleur-graphique-' ) ) ;
assert . deepEqual ( [ ... famille ] . sort ( ) , [ ... JETONS _GRAPHIQUE ] . sort ( ) , theme ) ;
}
} ) ;
test ( 'MARQUES_GRAPHIQUE : douze marques, seuil 3, figées, chacune peinte de son jeton de la famille ; le repère d’ un plafond et celui d’ une borne ont chacun le leur' , ( ) => {
assert . deepEqual ( MARQUES _GRAPHIQUE . map ( ( m ) => m . nom ) , NOMS _MARQUES _GRAPHIQUE ) ;
assert . ok ( Object . isFrozen ( MARQUES _GRAPHIQUE ) && MARQUES _GRAPHIQUE . every ( ( m ) => Object . isFrozen ( m ) && Object . isFrozen ( m . fonds ) ) ) ;
for ( const marque of MARQUES _GRAPHIQUE ) {
assert . deepEqual ( Object . keys ( marque ) . sort ( ) , [ 'fonds' , 'jeton' , 'nom' , 'seuil' ] , marque . nom ) ;
assert . equal ( marque . seuil , 3 , marque . nom ) ;
assert . ok ( marque . jeton . startsWith ( '--couleur-graphique-' ) , marque . nom ) ;
assert . ok ( marque . fonds . includes ( CARTE ) , ` ${ marque . nom } : ${ CARTE } ` ) ;
}
assert . equal ( parNom ( MARQUES _GRAPHIQUE , 'graphiqueRepere' ) . jeton , g ( 'repere' ) ) ;
assert . equal ( parNom ( MARQUES _GRAPHIQUE , 'graphiqueBorne' ) . jeton , g ( 'borne' ) ) ;
for ( const nom of [ 'graphiqueSerie1' , 'graphiqueSerie2' ] ) {
assert . ok ( parNom ( MARQUES _GRAPHIQUE , nom ) . fonds . includes ( PAGE ) , ` ${ nom } : ${ PAGE } ` ) ;
}
} ) ;
test ( 'les listes de la famille restent hors de MARQUES et de TEXTES : aucun nom ni aucun jeton partagé, MARQUES garde ses vingt-deux entrées' , ( ) => {
assert . equal ( MARQUES . length , NOMS _MARQUES . length ) ;
const nomsGraphique = [ ... MARQUES _GRAPHIQUE , ... TEXTES _GRAPHIQUE , ... FONDS _GRAPHIQUE ] . map ( ( e ) => e . nom ) ;
assert . equal ( new Set ( nomsGraphique ) . size , nomsGraphique . length ) ;
const registres = [ ... MARQUES , ... TEXTES , ... SIGNES ] ;
assert . deepEqual ( registres . filter ( ( e ) => nomsGraphique . includes ( e . nom ) ) . map ( ( e ) => e . nom ) , [ ] ) ;
assert . deepEqual ( registres . filter ( ( e ) => e . jeton . startsWith ( '--couleur-graphique-' ) ) . map ( ( e ) => e . nom ) , [ ] ) ;
} ) ;
test ( 'l’ échelle : quatre niveaux, 0 à 3, chacun son jeton et l’ encre de son compte ; deux encres au plus ; figée' , ( ) => {
assert . deepEqual (
NIVEAUX _GRAPHIQUE . map ( ( { niveau , jeton } ) => [ niveau , jeton ] ) ,
[ 0 , 1 , 2 , 3 ] . map ( ( k ) => [ k , g ( ` niveau- ${ k } ` ) ] ) ,
) ;
assert . ok ( Object . isFrozen ( NIVEAUX _GRAPHIQUE ) && NIVEAUX _GRAPHIQUE . every ( Object . isFrozen ) ) ;
const encres = [ ... new Set ( NIVEAUX _GRAPHIQUE . map ( ( { encre } ) => encre ) ) ] ;
assert . ok ( encres . length >= 1 && encres . length <= 2 , ` ${ encres . length } encres ` ) ;
for ( const encre of encres ) assert . ok ( [ g ( 'encre-claire' ) , g ( 'encre-foncee' ) ] . includes ( encre ) , encre ) ;
assert . deepEqual ( Object . keys ( ECHELLE _GRAPHIQUE ) . sort ( ) , [ 'clarte' , 'ecartTeinte' ] ) ;
assert . ok ( Object . isFrozen ( ECHELLE _GRAPHIQUE ) ) ;
} ) ;
test ( 'TEXTES_GRAPHIQUE : une entrée par encre, seuil 4,5, mesurée sur chaque niveau que NIVEAUX_GRAPHIQUE lui donne et sur aucun autre' , ( ) => {
const encres = [ ... new Set ( NIVEAUX _GRAPHIQUE . map ( ( { encre } ) => encre ) ) ] ;
assert . deepEqual ( TEXTES _GRAPHIQUE . map ( ( t ) => t . jeton ) , encres ) ;
assert . ok ( Object . isFrozen ( TEXTES _GRAPHIQUE ) && TEXTES _GRAPHIQUE . every ( ( t ) => Object . isFrozen ( t ) && Object . isFrozen ( t . fonds ) ) ) ;
for ( const texte of TEXTES _GRAPHIQUE ) {
assert . equal ( texte . seuil , 4.5 , texte . nom ) ;
assert . deepEqual ( [ ... texte . fonds ] , NIVEAUX _GRAPHIQUE . filter ( ( n ) => n . encre === texte . jeton ) . map ( ( n ) => n . jeton ) , texte . nom ) ;
}
} ) ;
test ( 'FONDS_GRAPHIQUE : la grille et le niveau 0, sans seuil, hors de la mesure 3:1 ; les niveaux 1 à 3 sont des marques, mesurées sur la carte' , ( ) => {
assert . deepEqual ( FONDS _GRAPHIQUE . map ( ( f ) => [ f . nom , f . jeton ] ) , [ [ 'graphiqueGrille' , g ( 'grille' ) ] , [ 'graphiqueNiveau0' , g ( 'niveau-0' ) ] ] ) ;
assert . ok ( Object . isFrozen ( FONDS _GRAPHIQUE ) && FONDS _GRAPHIQUE . every ( Object . isFrozen ) ) ;
assert . ok ( FONDS _GRAPHIQUE . every ( ( f ) => ! ( 'seuil' in f ) ) ) ;
const jetonsMarques = MARQUES _GRAPHIQUE . map ( ( m ) => m . jeton ) ;
for ( const { jeton } of FONDS _GRAPHIQUE ) assert . ok ( ! jetonsMarques . includes ( jeton ) , jeton ) ;
for ( const k of [ 1 , 2 , 3 ] ) assert . deepEqual ( [ ... parNom ( MARQUES _GRAPHIQUE , ` graphiqueNiveau ${ k } ` ) . fonds ] , [ CARTE ] , ` niveau ${ k } ` ) ;
} ) ;
test ( 'chaque jeton et chaque fond des listes de la famille existe dans les deux thèmes, sans canal alpha' , ( ) => {
const noms = new Set ( [
... [ ... MARQUES _GRAPHIQUE , ... TEXTES _GRAPHIQUE ] . flatMap ( ( e ) => [ e . jeton , ... e . fonds ] ) ,
... FONDS _GRAPHIQUE . map ( ( f ) => f . jeton ) ,
] ) ;
for ( const nom of JETONS _GRAPHIQUE ) assert . ok ( noms . has ( nom ) , nom ) ;
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
for ( const nom of noms ) {
assert . ok ( jetons . has ( nom ) , ` ${ theme } : ${ nom } ` ) ;
assert . equal ( jetons . get ( nom ) [ 3 ] , 1 , ` ${ theme } : ${ nom } ` ) ;
}
}
} ) ;
} ) ;
describe ( 'la famille « graphique » validée par calcul (§ 12.7, § 14.5, D43)' , ( ) => {
const series = [ g ( 'serie-1' ) , g ( 'serie-2' ) ] ;
const appartenances = ( ) => TEINTES _APPARTENANCE . map ( ( { marque } ) => marque ) ;
test ( 'chaque marque de la famille tient 3:1 sur chacun de ses fonds — séries sur la carte et la page, repère et borne sur la carte —, dans les deux thèmes, sous les trois simulations' , ( ) => {
assert . deepEqual ( verifierPalette ( JETONS , MARQUES _GRAPHIQUE ) , [ ] ) ;
} ) ;
test ( 'chaque encre tient 4,5:1 sur chaque niveau qu’ elle sert, dans les deux thèmes, sous les trois simulations' , ( ) => {
assert . deepEqual ( verifierPalette ( JETONS , TEXTES _GRAPHIQUE ) , [ ] ) ;
} ) ;
test ( ` les deux séries se séparent l’ une de l’ autre et des six teintes d’ appartenance de ${ SEPARATIONS . autresMarques } au moins (OKLab × 100), sous les trois simulations ` , ( ) => {
assert . deepEqual ( verifierSeparation ( JETONS , series , appartenances ( ) , SEPARATIONS . autresMarques ) , [ ] ) ;
} ) ;
test ( ` « prouvé inévitable » et « reste » se séparent l’ un de l’ autre de ${ SEPARATIONS . autresMarques } au moins, et « reste », chromatique, des teintes d’ appartenance, sous les trois simulations ` , ( ) => {
assert . deepEqual ( verifierSeparation ( JETONS , [ g ( 'prouve' ) , g ( 'reste' ) ] , [ ] , SEPARATIONS . autresMarques ) , [ ] ) ;
assert . deepEqual ( verifierSeparation ( JETONS , [ g ( 'reste' ) ] , appartenances ( ) , SEPARATIONS . autresMarques ) , [ ] ) ;
} ) ;
test ( 'aucun jeton de la famille n’ est à moins de 30° d’ une teinte de conflit, ni ne porte la valeur d’ une teinte de conflit, d’ appartenance ou du retrait' , ( ) => {
const fautes = verifierTeintesReservees ( JETONS , JETONS _CONFLIT ) . filter ( ( f ) => f . jeton . startsWith ( '--couleur-graphique-' ) ) ;
assert . deepEqual ( fautes , [ ] ) ;
const reservees = [ ... JETONS _CONFLIT , ... appartenances ( ) , '--couleur-retrait' ] ;
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
const valeurs = new Set ( reservees . map ( ( n ) => String ( jetons . get ( n ) ) ) ) ;
assert . deepEqual ( JETONS _GRAPHIQUE . filter ( ( n ) => valeurs . has ( String ( jetons . get ( n ) ) ) ) , [ ] , theme ) ;
}
} ) ;
test ( 'le repère, la borne, l’ axe et les deux encres sont neutres dans les deux thèmes : sans teinte' , ( ) => {
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
for ( const nom of [ 'repere' , 'borne' , 'axe' , 'encre-claire' , 'encre-foncee' ] . map ( g ) ) {
assert . equal ( teinte ( jetons . get ( nom ) ) , null , ` ${ theme } : ${ nom } ` ) ;
}
}
} ) ;
test ( 'la bordure des cases tient 3:1 sur la carte ; le niveau 0 et la grille ne sont pas mesurés : le niveau 0 se tient sous 3:1, au plus près du fond' , ( ) => {
assert . deepEqual ( [ ... parNom ( MARQUES _GRAPHIQUE , 'graphiqueBordureCase' ) . fonds ] , [ CARTE ] ) ;
for ( const theme of THEMES ) {
const jetons = lireJetons ( JETONS , theme ) ;
for ( const simulation of SIMULATIONS _ET _ORIGINE ) {
const vue = ( c ) => ( simulation === 'origine' ? c : simuler ( c , simulation ) ) ;
assert . ok ( contraste ( vue ( jetons . get ( g ( 'niveau-0' ) ) ) , vue ( jetons . get ( CARTE ) ) ) < 3 , ` ${ theme } ${ simulation } ` ) ;
}
}
} ) ;
} ) ;