v3 : interface Svelte — accueil, groupes, lecteur du texte en vigueur, panneau contextuel, éditeur à assistant, trustinWallet

- Accueil sobre, menu complet à gauche (Décisions, Docs de référence, Mandats,
  Protocoles), intro demandée, quatre sections, connexion Ğ1.
- Lecteur : texte en vigueur en Literata, actions contextuelles par article
  (le vote, propositions, proposer), propositions en cours sous la « surface »
  avec leur niveau d'eau, aperçus « si les bien cotées passent » / « si tout
  passe », langue du texte fr/en/es (traductions éditoriales).
- Panneau : vote pour/contre, seuil et formule (MathML), inertie et son
  changement (voté en inertie +), coquille sans vote, traduction, signalement
  de contradiction, historique des corrections.
- trustinWallet (/acces) : groupes et membres, invitations à usage unique,
  administrateurs, retours d'usage.
- Contrôles : vitest (markdown, catalogues, anti-lexique), Playwright C1–C10 + M1.
- Sections du seed portées jusqu'à l'écran.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-21 02:58:34 +02:00
co-authored by Claude Opus 5.5
parent 67ddee28b2
commit 7a022dd35e
66 changed files with 7771 additions and 15 deletions
+38
View File
@@ -0,0 +1,38 @@
// The one door to the server. Mutations carry the anti-CSRF header the server
// requires; errors come back as a stable code the interface translates.
export class ApiError extends Error {
constructor(
public status: number,
public code: string,
) {
super(code);
}
}
async function call<T>(method: string, path: string, body?: unknown): Promise<T> {
const headers: Record<string, string> = { 'x-requested-with': 'libredecision' };
if (body !== undefined) headers['content-type'] = 'application/json';
let res: Response;
try {
res = await fetch(`/api${path}`, {
method,
headers,
credentials: 'same-origin',
body: body === undefined ? undefined : JSON.stringify(body),
});
} catch {
throw new ApiError(0, 'network');
}
const data = await res.json().catch(() => null);
if (!res.ok) throw new ApiError(res.status, (data && typeof data.error === 'string' && data.error) || 'internal');
return data as T;
}
export const api = {
get: <T>(path: string) => call<T>('GET', path),
post: <T>(path: string, body: unknown = {}) => call<T>('POST', path, body),
patch: <T>(path: string, body: unknown) => call<T>('PATCH', path, body),
put: <T>(path: string, body: unknown) => call<T>('PUT', path, body),
del: <T>(path: string) => call<T>('DELETE', path),
};