v3 : modules d'ordre et de titre — l'interface, restes mineurs (L11, C25–C27)

Chaque section du lecteur finit par l'ordre de ses articles (inertie 2) : ses
articles numérotés, son vote déplié, « Proposer un autre ordre » — une liste où
l'on clique un article puis sa place, ses flèches, ou le clavier. Le titre du
document (inertie 4) se vote et se propose sous le titre, « traduction à
revoir » quand il a changé. Les deux sont dans la liste des propositions, le
panier et la chaîne Ğ1 ; un nouvel article se propose dans une section, à sa fin.
La liste du groupe compte les propositions ouvertes des modules.

Restes mineurs : la participation ramenée se dit en deux temps (la mesure, puis
ce que le groupe demande), les lignes « habituelle » des réglages resserrées, le
sélecteur d'inertie se parcourt aux flèches (le cran actuel atteignable, jamais
choisi), la marque « seed a minima » porte le vrai nombre de votes.

Contrôles : C25 (ordre, dont la chaîne et le téléphone), C26 (titre), C27
(participation ramenée) ; C3, C17 (41 modules), C20 mis à jour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 05:35:02 +01:00
co-authored by Claude Opus 5.5
parent 3831ddb1bf
commit 785580c78f
31 changed files with 1480 additions and 136 deletions
+23 -7
View File
@@ -5,7 +5,7 @@
import { api, ApiError } from '$lib/api';
import { shown, type DocState } from '$lib/doc.svelte';
import { i18n, t, type Key } from '$lib/i18n/index.svelte';
import { titleOf } from '$lib/labels';
import { inertiaBadge, orderNote, titleOf } from '$lib/labels';
import { toasts } from '$lib/toast.svelte';
import { announce } from '$lib/vote';
import type { DiffPart, Entered, Inertia, Lang, Proposal } from '$lib/types';
@@ -179,7 +179,7 @@
<article class="details">
<header>
<span class="eyebrow">{p.status === 'in_force' ? t('panel.inforce') : t('panel.proposal')}</span>
<h3>{p.kind === 'text' ? text.title : titleOf(p, unit)}</h3>
<h3>{p.kind === 'text' || p.kind === 'title' ? text.title : titleOf(p, unit)}</h3>
<p class="meta faint">
{#if p.seedMinimal}<span class="tag water" title={t('doc.seed.long')}>{t('doc.seed')}</span>
{:else if p.confirmedAt}<span class="tag moss">{t('doc.confirmed')}</span>
@@ -200,6 +200,15 @@
{#if text.translated}<p class="faint note">{t('doc.lang.translation')}{#if text.stale} · <b>{t('doc.lang.stale')}</b>{/if}</p>{/if}
{:else if p.kind === 'remove' && detail.inForce}
<del class="removed"><Prose text={detail.inForce.body} /></del>
{:else if p.kind === 'order'}
{@const order = doc.proposal(p.id)?.order}
<ol class="order" aria-label={t(p.status === 'in_force' ? 'module.order.inforce' : 'module.order.proposal')}>
{#each order?.ids ?? [] as id (id)}<li>{doc.articleTitle(id, lang, source)}</li>{/each}
</ol>
{#each orderNote({ ...p, order }) as note (note)}<p class="faint note">{note}</p>{/each}
<p class="faint note">{t('module.order.rule')}</p>
{:else if p.kind === 'title'}
<p class="faint note">{t('module.title.name')} · {inertiaBadge(detail.unit.inertia)}{#if text.translated} · {t('doc.lang.translation')}{#if text.stale} · <b>{t('doc.lang.stale')}</b>{/if}{/if}</p>
{/if}
{#if votable}
@@ -209,17 +218,17 @@
</p>
{/if}
{#if p.kind === 'text' && (p.status === 'in_force' || p.origin === 'seed' || p.mine) && votable}
{#if (p.kind === 'text' || p.kind === 'title') && (p.status === 'in_force' || p.origin === 'seed' || p.mine) && votable}
<section class="sub">
<details bind:open={typoOpen}>
<summary>{p.mine && p.status === 'open' ? t('propose.edit') : t('typo.title')}</summary>
<form class="form" onsubmit={saveTypo}>
<p class="faint small">{p.mine && p.status === 'open' ? t('propose.rule') : t('typo.note')}</p>
<input class="input" bind:value={typoTitle} maxlength="120" aria-label={t('propose.title')} />
<textarea class="textarea" bind:value={typoBody} rows="6" aria-label={t('propose.body')}></textarea>
{#if p.kind === 'text'}<textarea class="textarea" bind:value={typoBody} rows="6" aria-label={t('propose.body')}></textarea>{/if}
{#if typoSubstantive}
<p class="warn">{t('typo.substantive')}</p>
<button class="btn small" type="button" onclick={() => onpropose({ title: typoTitle, body: typoBody })}>{t('typo.asProposal')}</button>
{#if p.kind === 'text'}<button class="btn small" type="button" onclick={() => onpropose({ title: typoTitle, body: typoBody })}>{t('typo.asProposal')}</button>{/if}
{/if}
<button class="btn small primary" type="submit" disabled={busy}>{t('typo.save')}</button>
</form>
@@ -227,14 +236,14 @@
</section>
{/if}
{#if lang !== source && p.kind === 'text'}
{#if lang !== source && (p.kind === 'text' || p.kind === 'title')}
<section class="sub">
<details>
<summary>{t('translate.title', { lang: lang.toUpperCase() })}</summary>
<form class="form" onsubmit={saveTranslation}>
<p class="faint small">{t('translate.note')}</p>
<input class="input" bind:value={trTitle} maxlength="120" aria-label={t('propose.title')} />
<textarea class="textarea" bind:value={trBody} rows="6" aria-label={t('propose.body')}></textarea>
{#if p.kind === 'text'}<textarea class="textarea" bind:value={trBody} rows="6" aria-label={t('propose.body')}></textarea>{/if}
<button class="btn small" type="submit" disabled={busy}>{t('translate.save')}</button>
</form>
</details>
@@ -329,6 +338,13 @@
.removed {
opacity: 0.6;
}
.order {
margin: 0;
padding-left: 1.6rem;
display: grid;
gap: 0.2rem;
font-family: var(--font-text);
}
.tally {
font-size: 0.88rem;
font-weight: 700;