Files
decision/web/src/lib/labels.ts
T
YvvandClaude Opus 5.5 8528a269fc Lot U3 : l'interface de l'Acte d'engagement (Yvv, 03/11) — avant le texte, hors vote, « Info ouverture doc de réf. » (OpeningInfo, lib/opening.ts) : le paragraphe d'Yvv mot pour mot à la place de doc.empty.seeds (sa première phrase tant qu'aucun article n'est en vigueur, puis « entrent en vigueur » où « y » n'avait plus rien à reprendre), le cran d'ouverture des Propositions d'amorce, du titre, de l'ordre de chaque section et des autres articles (avant tout changement voté) et le quorum de chaque cran, lus dans l'API ; une séparation (un filet de la couleur de la clé de voûte, jamais une bordure) ; plus les titres « Introduction » et « Engagements fondamentaux » dans le texte. Les informations de F1, F1.1, F1.2, N1, N1.1 et O1 là où elles servent : vote.permanent, vote.rival, vote.others.rule, formula.Tprime, formula.round, formula.B.wot, formula.G, settings.share.note, settings.usual.quorum, settings.gradient, order.edit.effect ; les termes définis de T3c (« à la participation habituelle », « quorum de l'article », « part exigée », « repose sur moins de N propositions votées depuis un an »), plus de « comme d'habitude ». « Proposition d'amorce » (Starter proposal, Propuesta de arranque). Le mot d'Yvv pour qui n'est pas invité, à l'accueil sans session (à la place du bouton Ğ1 tant que la connexion Ğ1 n'est pas active) et sur « Se connecter » (signin.g1.pending sans redite). Réordonner : la ligne sous le pointeur lue dans la mise en page (offsetTop), pas là où animate:flip la montre — une poignée tirée sur plusieurs lignes ne revient plus en arrière ; le pointeur capturé par la liste. API : seedFeatured sur chaque version (seul changement sous crates/). C1, C3, C20, C27, C35, C38 suivent les mots
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 22:28:41 +01:00

91 lines
4.2 KiB
TypeScript

import { i18n, t, type Key } from './i18n/index.svelte';
import type { Inertia, Proposal, Unit } from './types';
/** « 3 / 5 · moyenne ». */
export function inertiaLevel(k: Inertia): string {
return t('inertia.level', { k, name: t(`inertia.name.${k}`) });
}
/** « inertie 3 / 5 · moyenne ». */
export function inertiaBadge(k: Inertia): string {
return t('inertia.badge', { level: inertiaLevel(k) });
}
const SECTIONS = ['introduction', 'fondamental', 'technique', 'conclusion', 'annexe', 'formule', 'inertie', 'ordonnancement'];
/** The name of a section of the seed, or null. */
export function sectionLabel(s: string | null | undefined): string | null {
return s && SECTIONS.includes(s) ? t(`section.${s}` as Key) : null;
}
/**
* The sections whose articles carry the title already (Yvv, 2026-11-03: « Enlève les titres
* Introduction et Engagements fondamentaux, les titres sont déjà dans les contenus à voter. Ça
* embrouille. »): not named again where the text is read. They keep their name elsewhere — the
* order of the section, the basket, the open proposals.
*/
const TITLED_BY_THEIR_ARTICLES = ['introduction', 'fondamental'];
/** The name of a section where the text is read, or null. */
export function readerSectionLabel(s: string | null | undefined): string | null {
return s && TITLED_BY_THEIR_ARTICLES.includes(s) ? null : sectionLabel(s);
}
/** The name of a module (ADR 0009): « Titre du document », « Ordre des articles · Conclusion ». */
export function moduleName(unit: Unit): string {
if (unit.kind === 'title') return t('module.title.name');
const section = sectionLabel(unit.section) ?? unit.section;
return section ? t('module.order.named', { section }) : t('module.order.name');
}
/** What a proposal is, in words, whatever its kind. */
export function titleOf(p: Proposal, unit: Unit | undefined): string {
if (p.kind === 'inertia' && p.inertiaTarget) {
return t('deep.inertia', { level: inertiaLevel(p.inertiaTarget) });
}
if (p.kind === 'remove') return t('deep.removal', { title: unit?.inForce?.title ?? p.title });
if (p.kind === 'order') return unit ? moduleName(unit) : t('module.order.name');
return p.title;
}
/** Its relation to the text in force. */
export function relationOf(p: Proposal, unit: Unit | undefined): string {
if (p.kind !== 'text' && p.kind !== 'title') return '';
if (unit?.inForce && unit.inForce.id !== p.id) return t('deep.rival', { title: unit.inForce.title });
return '';
}
/** What an order version says of itself: completed at the end, or the order in force already. */
export function orderNote(p: Proposal): string[] {
const o = p.order;
if (!o || p.status === 'in_force') return [];
const notes: string[] = [];
if (o.added.length) notes.push(i18n.plural('module.order.added', o.added.length));
if (o.dropped.length) notes.push(i18n.plural('module.order.dropped', o.dropped.length));
if (o.same) notes.push(t('module.order.same'));
return notes;
}
/**
* How many « oui » pass a version, how many « non » block it (Yvv, 2026-11-02: « combien il
* faut de oui pour passer et combien de non pour maintenir l'opposition »), from what the
* server says (ADR 0013): `yesNeeded`, the total of *for* that passes with the *against* cast
* now; `noNeeded`, the total of *against* that holds the *for* cast now in check — 0 when
* the quorum holds them alone. Before any vote: « 9 oui sur 12 votants ». The quorum goes
* to the tooltip (`title`), never to the title of a line.
*/
export function needOf(p: Proposal): { long: string; short: string; title: string } | null {
const e = p.evaluation;
if (!e || e.yesNeeded === undefined || e.noNeeded === undefined || e.quorum === undefined) return null;
const yes = e.yesNeeded;
const no = e.noNeeded;
const quorum = e.quorum;
const title = i18n.plural('vote.need.title', quorum);
if (e.cast === 0) {
return { long: i18n.plural('vote.need.first', quorum, { yes, quorum }), short: t('vote.need.short.first', { yes, quorum }), title };
}
if (no === 0) return { long: t('vote.need.quorum', { yes }), short: t('vote.need.short.quorum', { yes }), title };
return { long: t('vote.need', { yes, no }), short: t('vote.need.short', { yes, no }), title };
}