[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)
|
|
|
|
|
|
|
|
|
|
// Les textes des codes de la séance (§ 8.3 à § 8.8, § 14.6), et les libellés
|
|
|
|
|
// figés que la séance écrit au journal : jalons, reprise, retours. Un libellé
|
|
|
|
|
// d'entrée se compose une fois, à l'écriture du geste, et le journal en garde
|
|
|
|
|
// le texte, jamais le code (§ 8.2).
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
import {
|
|
|
|
|
capitale,
|
|
|
|
|
causeEntreParentheses,
|
|
|
|
|
cite,
|
|
|
|
|
compte,
|
|
|
|
|
depuisQuand,
|
|
|
|
|
enumeration,
|
|
|
|
|
nomEtat,
|
|
|
|
|
ouEcrire,
|
|
|
|
|
phraseDeRaison,
|
|
|
|
|
} from './aides.js';
|
|
|
|
|
|
|
|
|
|
// Le placement qu'une dérive touche : le retenu, ou une proposition par son
|
|
|
|
|
// numéro.
|
|
|
|
|
const nomDuPlacement = (id) => (id === 'retenu' ? 'le placement retenu' : `la proposition n° ${id}`);
|
|
|
|
|
|
|
|
|
|
// Les raisons d'un placement, chacune par son texte.
|
|
|
|
|
const phrasesDe = (raisons, texte) => raisons.map((raison) => phraseDeRaison(raison.code, raison, texte)).join(' ');
|
|
|
|
|
|
|
|
|
|
// Une violation du retenu : la réservation qu'il ne tient pas par le texte
|
|
|
|
|
// de RESERVATION_NON_HONOREE, toute autre par le texte de son code.
|
|
|
|
|
const phraseDeViolation = (violation, texte) =>
|
|
|
|
|
violation.code === 'RESERVATION'
|
|
|
|
|
? texte('RESERVATION_NON_HONOREE', violation)
|
|
|
|
|
: phraseDeRaison(violation.code, violation, texte);
|
[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
|
|
|
|
|
|
|
|
export const TABLE = new Map([
|
|
|
|
|
// --- La séance (§ 8.3, § 8.4, § 8.5, § 8.6, § 8.8) -------------------------
|
|
|
|
|
['AUCUN_EVENEMENT', () => "Aucun événement n'est ouvert."],
|
|
|
|
|
['MODE_LECTURE', () => "L'événement est ouvert en lecture : « Modifier » permet de le changer."],
|
|
|
|
|
['FORMAT', (details, texte) => texte('FORMAT_PLUS_RECENT', details)],
|
|
|
|
|
['BLOQUE', () => 'Le plan est bloqué.'],
|
|
|
|
|
// echec nomme ce que le support n'a pas reçu : la ligne d'un geste
|
|
|
|
|
// (ECRITURE), l'état d'un geste inscrit au journal (ETAT_NON_ECRIT), le
|
|
|
|
|
// jalon d'un geste enregistré (JALON_NON_ECRIT), ou le verrou d'un passage
|
|
|
|
|
// en écriture (VERROU_NON_ECRIT), qui ne perd rien.
|
|
|
|
|
[
|
|
|
|
|
'SUPPORT_ROMPU',
|
|
|
|
|
({ echec, ...details }, texte) => {
|
|
|
|
|
const refus = `Le support ne prend plus les écritures : ${ouEcrire(details, texte)} n'a pas pu s'écrire`;
|
|
|
|
|
const ailleurs = "« Écrire ailleurs » enregistre l'événement dans un autre dossier.";
|
|
|
|
|
if (echec === 'ECRITURE' || echec === 'ETAT_NON_ECRIT') {
|
|
|
|
|
const perte =
|
|
|
|
|
echec === 'ETAT_NON_ECRIT'
|
|
|
|
|
? "le dernier geste est inscrit à l'historique, mais pas dans l'état"
|
|
|
|
|
: "le dernier geste n'est pas enregistré";
|
|
|
|
|
return `${refus}, et ${perte}. Sur ce support, l'historique et l'état ne se correspondent plus ; ${ailleurs}`;
|
|
|
|
|
}
|
|
|
|
|
if (echec === 'JALON_NON_ECRIT') {
|
|
|
|
|
return (
|
|
|
|
|
`${refus}. Le dernier geste est enregistré, mais pas le jalon qui le nomme, ` +
|
|
|
|
|
`et aucun geste ne s'y enregistrerait plus ; ${ailleurs}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (echec === 'VERROU_NON_ECRIT') {
|
|
|
|
|
return (
|
|
|
|
|
`${refus}. L'événement y reste tel qu'il s'affiche, ` +
|
|
|
|
|
`mais aucun geste ne s'y enregistrerait ; ${ailleurs}`
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
throw new RangeError(`libelle : échec du support inconnu ${JSON.stringify(echec)}`);
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
['SUPPORT_INTACT', () => "Le support prend encore les écritures : l'événement reste où il est."],
|
|
|
|
|
// Quitter l'événement d'un support rompu : la séance tient la seule copie
|
|
|
|
|
// que l'écriture ailleurs peut encore sauver ; ETAT_NON_ECRIT dit que son
|
|
|
|
|
// dernier geste n'est qu'à l'historique de ce support.
|
|
|
|
|
[
|
|
|
|
|
'SUPPORT_ROMPU_NON_SAUVE',
|
|
|
|
|
({ echec, dossier }) => {
|
|
|
|
|
const geste =
|
|
|
|
|
echec === 'ETAT_NON_ECRIT' ? ", et son dernier geste n'y est inscrit qu'à l'historique" : '';
|
|
|
|
|
return (
|
|
|
|
|
`Le support ${cite(dossier)} ne prend plus les écritures${geste} : ` +
|
|
|
|
|
"l'événement ouvert n'est sauvé dans aucun autre dossier. " +
|
|
|
|
|
"« Écrire ailleurs » l'enregistre d'abord ; le quitter maintenant l'abandonne à ce support."
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'EVENEMENT_CHANGE',
|
|
|
|
|
({ revision }) =>
|
|
|
|
|
`Une autre séance a modifié cet événement depuis son ouverture : il s'affiche tel qu'elle l'a laissé, ` +
|
|
|
|
|
`à la révision ${revision}, et les gestes suivants s'y ajoutent.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'VERROU_REPRIS',
|
[ADD] interface: event list, open event, participants, import, plan
The working screens of an evening. The home screen lists the events of
the working folder, asks its question, loads demos, copies, renames,
imports and deletes, and shows what the trash holds. An open event frames
its mode, offers Modifier, Débloquer and a confirmed lock takeover, and
shows its warnings. Participants get a grid, a form and a file; an import
goes through a preview and a report; tables, the default and tours have
their screens; the plan draws tables, chairs, empty seats and names.
Checked: 2342 node, 356 browser, 60 node-long; the Electron start check.
--- FR ---
[ADD] interface : liste, événement, participants, import, tables, plan
Les écrans de travail d'une soirée. L'accueil liste les événements du
dossier de travail, pose sa question, charge les démonstrations, copie,
renomme, importe et supprime, et montre ce que tient la corbeille. Un
événement ouvert encadre son mode, offre Modifier, Débloquer et une
reprise de verrou confirmée, et montre ses avertissements. Les
participants ont une grille, un formulaire et une fiche ; un import passe
par un aperçu et un rapport ; tables, défaut et tours ont leurs écrans ;
le plan dessine tables, chaises, sièges vides et noms.
Vérifié : 2342 node, 356 navigateur, 60 node-long ; démarrage d'Electron.
Assisted-by: Claude Opus 5.5
2026-10-07 08:12:27 -04:00
|
|
|
({ depuis }, texte) => `Le verrou qu'une autre séance tenait${depuisQuand(depuis, texte)} est repris.`,
|
[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
|
|
|
],
|
[ADD] application: observable session, events facade, places and seats
The screens of iteration 3 need state they can subscribe to and commands
that keep the plan sound. The session follows the Svelte store contract,
unblocks a plan, cancels a gesture, keeps the framing, and orders the
warnings of a gesture. The facade lists events, settles the working folder
and runs copy, rename, import and delete. Places cover reservations, titles,
designation and the retained plan: each gesture reads seats as the display
shows them, reserved people keep their table, and a person removed frees
her chair while every other seat stays.
Checked: 1973 node, 127 browser, 60 node-long tests; coverage floors kept.
--- FR ---
[ADD] application : séance observable, façade des événements, places
Les écrans de l'itération 3 exigent un état auquel s'abonner et des
commandes qui gardent le plan sain. La séance suit le contrat des magasins
de Svelte, débloque un plan, annule un geste, garde le cadrage et ordonne
les avertissements d'un geste. La façade liste les événements, règle le
dossier de travail et mène copie, renommage, import et suppression. Les
places couvrent réservations, titres, désignation et plan retenu : chaque
geste lit les sièges comme l'écran les montre, une personne réservée garde
sa table, et une personne retirée libère sa chaise sans déplacer les autres.
Vérifié : 1973 node, 127 navigateur, 60 node-long ; seuils de couverture.
Assisted-by: Claude Opus 5.5
2026-10-07 04:14:24 -04:00
|
|
|
[
|
|
|
|
|
'RETOUR_AUTOMATIQUE',
|
|
|
|
|
() =>
|
|
|
|
|
"Faute d'activité, l'événement est revenu seul en lecture ; chaque geste achevé était déjà enregistré. " +
|
|
|
|
|
'« Modifier » permet de reprendre.',
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
// --- Ce qu'un geste fait naître (§ 5.9, § 9, § 14.12) ----------------------
|
|
|
|
|
[
|
|
|
|
|
'DERIVE_NOUVELLE',
|
|
|
|
|
({ touchees }, texte) => {
|
|
|
|
|
const noms = touchees.map(({ id }) => nomDuPlacement(id));
|
|
|
|
|
const accord = touchees.length > 1 ? 'ne décrivent plus' : 'ne décrit plus';
|
|
|
|
|
const parPlacement = touchees.map(({ id, raisons }) => `${capitale(nomDuPlacement(id))} : ${phrasesDe(raisons, texte)}`);
|
|
|
|
|
return (
|
|
|
|
|
`Après ce geste, ${enumeration(noms)} ${accord} la liste ou les tables actuelles, et rien n'est effacé. ` +
|
|
|
|
|
parPlacement.join(' ')
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'RESERVATION_NON_HONOREE',
|
|
|
|
|
({ participant, table, tour }) =>
|
|
|
|
|
`Le placement retenu n'honore pas la réservation de la personne n° ${participant} ` +
|
|
|
|
|
`à la table d'identifiant ${table} au tour ${tour} : il la place ailleurs.`,
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'INVARIANT_VIOLE',
|
|
|
|
|
({ proposition, violations }, texte) =>
|
|
|
|
|
`Le placement retenu, issu de la proposition n° ${proposition}, enfreint ` +
|
|
|
|
|
`${compte(violations.length, 'règle', 'règles')} que la dérive ne dit pas. ` +
|
|
|
|
|
violations.map((violation) => phraseDeViolation(violation, texte)).join(' '),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
'CONFIGURATION_INCOHERENTE',
|
|
|
|
|
({ code, details }, texte) =>
|
|
|
|
|
`Après ce geste, la configuration ne se tient plus : ${phraseDeRaison(code, details, texte)} ` +
|
|
|
|
|
'Le diagnostic et la génération la refusent tant que rien ne la corrige.',
|
|
|
|
|
],
|
[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
|
|
|
[
|
|
|
|
|
'VERROU_NON_RENDU',
|
|
|
|
|
({ cause }, texte) =>
|
|
|
|
|
`Le verrou de l'événement n'a pas pu être rendu${causeEntreParentheses(cause, texte)} : ` +
|
|
|
|
|
'une autre séance de ce poste le reprendra une fois ce logiciel fermé.',
|
|
|
|
|
],
|
|
|
|
|
['RIEN_A_DEFAIRE', () => "Rien à défaire : l'historique commence à cet instant."],
|
|
|
|
|
['RIEN_A_REFAIRE', () => 'Rien à refaire : seul un retour se refait, tant que nul geste ne le suit.'],
|
|
|
|
|
['INSTANT_NON_RESTITUABLE', ({ revision }) => `L'historique ne restitue plus l'instant ${revision}.`],
|
|
|
|
|
[
|
|
|
|
|
'JALON_NON_ECRIT',
|
|
|
|
|
({ dossier, cause }, texte) =>
|
|
|
|
|
`Le geste est enregistré, mais le jalon qui le nomme n'a pas pu s'écrire ` +
|
|
|
|
|
`dans le dossier ${cite(dossier)}${causeEntreParentheses(cause, texte)}.`,
|
|
|
|
|
],
|
|
|
|
|
['SANS_EFFET', () => "Ce geste ne change rien : rien n'est enregistré."],
|
|
|
|
|
|
|
|
|
|
// --- Les libellés figés du journal (§ 8.2, § 8.3) --------------------------
|
|
|
|
|
['JALON_CREATION', () => 'Création'],
|
|
|
|
|
['JALON_ETAT', ({ etat }) => `État ${cite(nomEtat(etat))}`],
|
|
|
|
|
['JALON_AVANT_GENERATION', () => 'Avant la génération'],
|
|
|
|
|
['ENTREE_REPRISE', () => "Reprise de l'état trouvé sur le disque"],
|
|
|
|
|
['ENTREE_DEFAIRE', ({ libelle: instant }) => `Défait : ${cite(instant)}`],
|
|
|
|
|
['ENTREE_REFAIRE', ({ libelle: instant }) => `Refait : ${cite(instant)}`],
|
|
|
|
|
['ENTREE_REVENIR', ({ libelle: instant }) => `Retour à ${cite(instant)}`],
|
|
|
|
|
]);
|