[ADD] application: clock, translation, formatting, displayed name
The screens need one clock, one translation mechanism and one way to
format and name, or each component grows its own. horloge.js is the only
code that reads time and arms timers, injected so tests drive it.
traduction.js keys texts by domain, in two families of tables, raises on a
missing key and never shows a raw key; libelles/ splits the label table by
domain. format.js formats numbers and dates in French, and personnes.js
composes the displayed name the frozen labels use. Nothing persists
between sessions.
Checked: 1773 node and 43 browser tests.
--- FR ---
[ADD] application : horloge, traduction, mise en forme, nom affiché
Les écrans exigent une horloge, un mécanisme de traduction et une façon de
mettre en forme et de nommer, sans quoi chaque composant ferait les siens.
horloge.js est le seul code qui lit l'heure et arme des minuteries, injecté
pour que les épreuves le pilotent. traduction.js range les textes par
domaine, en deux familles de tables, lève sur une clé absente et ne montre
jamais une clé brute ; libelles/ découpe la table des libellés par domaine.
format.js met en forme nombres et dates en français, et personnes.js
compose le nom affiché qu'emploient les libellés figés. Rien ne persiste
d'une séance à l'autre.
Vérifié : 1773 node et 43 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 02:33:39 -04:00
|
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
// License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
[ADD] interface: generation screen — settings, progress, worker, cancel
The operator launches a generation without the window freezing. The
screen sets the number of propositions, the seed, the stop count and the
history, refuses an out-of-range setting where it was typed, and asks
before a run estimated far beyond the default. The search runs in a worker
that terminate() cancels with no partial result, and the progress shows a
count, never a promised duration. The last duration lives for the session
only.
Checked: 2374 node and 391 browser tests.
--- FR ---
[ADD] interface : écran de génération — réglages, avancement, annulation
L'opérateur lance une génération sans figer la fenêtre. L'écran règle le
nombre de propositions, la graine, l'arrêt et l'historique, refuse un
réglage hors de son domaine là où il a été saisi, et demande avant un
calcul estimé bien au-delà du défaut. La recherche tourne dans un
travailleur que terminate() annule sans résultat partiel, et l'avancement
montre un compte, jamais une durée promise. La dernière durée ne vit que
le temps de la séance.
Vérifié : 2374 node et 391 navigateur.
Assisted-by: Claude Opus 5.5
2026-10-07 09:10:12 -04:00
|
|
|
// Les libellés de la génération : réglages, durée observée, question d'une
|
|
|
|
|
// génération longue, avancement et issue (§ 5.7, § 5.10, § 14.6). Chaque
|
|
|
|
|
// clé commence par « generation. ». Le libellé d'un réglage porte son unité,
|
|
|
|
|
// entre parenthèses : l'étiquette du champ la dit, et le nom accessible du
|
|
|
|
|
// champ avec elle. Un nombre et une durée se mettent en forme par
|
|
|
|
|
// creerFormat, sur la composition que la traduction passe à l'entrée ; les
|
|
|
|
|
// détails reçus sont des nombres.
|
|
|
|
|
import { creerFormat } from '../../application/format.js';
|
|
|
|
|
import { compte } from '../../application/libelles/aides.js';
|
|
|
|
|
|
|
|
|
|
// Ce que vaut un calcul mesuré : sa durée, puis ses réglages, nombre de
|
|
|
|
|
// propositions et compte d'arrêt, dont le produit est le nombre de
|
|
|
|
|
// mouvements évalués.
|
|
|
|
|
function calculMesure({ dureeMs, nombre, arret }, texte) {
|
|
|
|
|
const format = creerFormat(texte);
|
|
|
|
|
return `${format.duree(dureeMs)} pour ${format.nombre(nombre)} \u{00D7} ${format.nombre(arret)} mouvements`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const TABLE = new Map([
|
|
|
|
|
// --- Les réglages (§ 5.7, § 5.10) ---------------------------------------------
|
|
|
|
|
['generation.reglage.graine', 'Graine (nombre entier, de 0 à 4\u{202F}294\u{202F}967\u{202F}295)'],
|
|
|
|
|
['generation.reglage.nombre', 'Propositions par génération (nombre de propositions)'],
|
|
|
|
|
['generation.reglage.arret', "Compte d'arrêt (mouvements évalués par proposition)"],
|
|
|
|
|
['generation.reglage.historique', "Historique d'acceptation (longueur, en mouvements)"],
|
|
|
|
|
|
|
|
|
|
// --- La durée du dernier calcul de cette séance (§ 5.10) --------------------
|
|
|
|
|
[
|
|
|
|
|
'generation.duree.inconnue',
|
|
|
|
|
"Aucun calcul n'est encore mesuré sur ce poste pendant cette séance : la durée d'une génération est inconnue.",
|
|
|
|
|
],
|
|
|
|
|
['generation.duree.observee', (details, texte) => `Dernier calcul sur ce poste : ${calculMesure(details, texte)}.`],
|
|
|
|
|
// secondes : l'estimation des réglages courants, d'après le dernier calcul.
|
|
|
|
|
[
|
|
|
|
|
'generation.duree.estimee',
|
|
|
|
|
({ secondes }, texte) => `À ces réglages, une génération prendrait ${creerFormat(texte).ordreDeGrandeur(secondes)}.`,
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- La génération longue, annoncée avant de partir (§ 5.10) ----------------
|
|
|
|
|
// estimation : celle de CONFIRMATION_REQUISE, { inconnue: true } sans
|
|
|
|
|
// mesure, sinon { secondes, auDelaDuPlafond, mesure: { dureeMs, mouvements } }.
|
|
|
|
|
[
|
|
|
|
|
'generation.longue.question',
|
|
|
|
|
({ estimation }, texte) => {
|
|
|
|
|
if (estimation.inconnue === true) {
|
|
|
|
|
return (
|
|
|
|
|
"Aucune durée n'est encore mesurée sur ce poste pendant cette séance, et ce réglage demande plus de dix " +
|
|
|
|
|
'fois le réglage par défaut. La lancer quand même ?'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
const format = creerFormat(texte);
|
|
|
|
|
const { mesure } = estimation;
|
|
|
|
|
const plafond = estimation.auDelaDuPlafond ? ", au-delà du plafond d'usage de trente minutes" : '';
|
|
|
|
|
return (
|
|
|
|
|
`Cette génération prendrait ${format.ordreDeGrandeur(estimation.secondes)} à ce réglage${plafond}, ` +
|
|
|
|
|
`d'après le dernier calcul : ${format.duree(mesure.dureeMs)} pour ${format.nombre(mesure.mouvements)} ` +
|
|
|
|
|
'mouvements. La lancer quand même ?'
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
['generation.longue.confirmer', 'Générer quand même'],
|
|
|
|
|
['generation.longue.renoncer', 'Ne pas générer'],
|
|
|
|
|
|
|
|
|
|
// --- Les commandes ----------------------------------------------------------------
|
|
|
|
|
['generation.commande.generer', 'Générer'],
|
|
|
|
|
['generation.commande.annuler', 'Annuler la génération'],
|
|
|
|
|
|
|
|
|
|
// --- L'avancement (§ 5.10) --------------------------------------------------------
|
|
|
|
|
['generation.avancement.barre', 'Avancement de la génération'],
|
|
|
|
|
[
|
|
|
|
|
'generation.avancement.texte',
|
|
|
|
|
({ fait, total }, texte) => {
|
|
|
|
|
const { nombre } = creerFormat(texte);
|
|
|
|
|
return `Génération en cours : ${nombre(fait)} mouvements évalués sur ${nombre(total)}.`;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- L'issue d'une génération -------------------------------------------------------
|
|
|
|
|
// ajoutees : le nombre de propositions enregistrées ; identiques : celles
|
|
|
|
|
// que le plan d'une proposition présente a écartées.
|
|
|
|
|
[
|
|
|
|
|
'generation.issue.ajoutees',
|
|
|
|
|
({ ajoutees, identiques }) => {
|
|
|
|
|
const nouvelles =
|
|
|
|
|
ajoutees === 0 ? 'Aucune proposition nouvelle' : `${compte(ajoutees, 'proposition ajoutée', 'propositions ajoutées')}`;
|
|
|
|
|
const ecartees =
|
|
|
|
|
identiques === 0
|
|
|
|
|
? ''
|
|
|
|
|
: ` ; ${compte(identiques, 'placement identique à une proposition présente, écarté', 'placements identiques à des propositions présentes, écartés')}`;
|
|
|
|
|
return `${nouvelles}${ecartees}.`;
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
['generation.issue.annuleeCommande', "Génération annulée : rien n'est enregistré."],
|
|
|
|
|
['generation.issue.annuleeSeance', "Génération annulée par le retour en lecture : rien n'est enregistré."],
|
|
|
|
|
]);
|