@mostajs/wagon-ui 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,13 @@
5
5
 
6
6
 
7
7
 
8
+ ## 0.6.0 — 2026-10-09
9
+
10
+ **Ajouté** : `creerWagonUi({ css })` — la feuille de style **de l'application** (sa configuration, de confiance),
11
+ posée après la feuille de base et le thème, sur **toutes** les pages (y compris `pageDocumentation`) : une
12
+ application peut enfin restyler les pages du wagon qu'elle ne rend pas (connexion, aide, à propos, pages légales).
13
+ Le thème Palette Studio reste borné à `:root{…}`. Une feuille qui contient `</` est écartée entière. Essai T-WUI-25.
14
+
8
15
  ## 0.5.3 — 2026-10-09
9
16
 
10
17
  **Ajouté** (audit de travel, référencement) : chaque page porte l'icône de l'onglet
package/llms.txt CHANGED
@@ -26,6 +26,7 @@ Rendre les écrans communs à tout SaaS, identiques d'un produit à l'autre, san
26
26
  `/`, `/tarifs`, `/aide`, `/apropos`, `/inscription` (POST : plan, nom, courriel, motdepasse), `/connexion` (POST : courriel, motdepasse), `/deconnexion`, `/compte`, `/compte/cles` (POST : libelle), `/compte/cles/revoquer` (POST : prefix), `/compte/portail` (POST), `/paiement` (POST : compte, plan), `/admin/plans` (POST), `/admin/usage`, `/mentions-legales`, `/cgu`, `/cgv`, `/confidentialite`.
27
27
 
28
28
  ## PIÈGES
29
+ - 0.6.0 : `creerWagonUi({ css })` — feuille de l'application (de confiance) après base et thème, sur chaque page ; écartée si elle contient `</`.
29
30
  - 0.5.3 : icône `/favicon.svg` dans chaque page ; `og:image` depuis `page({ image })` ou `marque.image` (chemin du site ou https).
30
31
  - 0.5.1 : `page({ chemin })` — canonique et liens de langue sur la page réelle.
31
32
  - 0.5.0 : `creerWagonUi({ …, collant: true, appel: { href, libelle }, selecteurLangue: 'menu' })` — en-tête collant, bouton d'appel, langue en menu déroulant sans script ; défauts inchangés.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/wagon-ui",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "description": "Les pages du wagon d'un SaaS — mise en page (langues, pied légal, en-têtes pour les moteurs), accueil, tarifs, inscription, connexion (mot de passe ou code), espace client (palier, jauges, clés, factures, gestion de l'abonnement), administration (paliers, usage), pages légales — en fr, en, ar. Extraites de mdseal, sans ce qu'il vend : l'identité et les ressources mesurées viennent de la configuration. Vues pures, zéro dépendance.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
package/src/index.js CHANGED
@@ -172,7 +172,7 @@ const DOCUMENTS_LEGAUX = Object.freeze([['mentions-legales', 'mentions'], ['cgu'
172
172
  * parPeriode?: string|object, sansLimite?: string|object, aucun?: string|object }>} [o.ressources]
173
173
  * @param {object} [o.textes] surcharges des libellés, par langue : { fr: { tarifs: { note: '…' } } }
174
174
  */
175
- export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar'], liens = [], ressources = [], textes = {}, theme = null, entete = 'simple', collant = false, appel = null, selecteurLangue = 'liens' } = {}) {
175
+ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar'], liens = [], ressources = [], textes = {}, theme = null, entete = 'simple', collant = false, appel = null, selecteurLangue = 'liens', css = '' } = {}) {
176
176
  if (!String(marque?.nom ?? '').trim()) throw new Error('@mostajs/wagon-ui : `marque.nom` requis — le nom du service');
177
177
  if (!String(marque?.auteur ?? '').trim()) throw new Error('@mostajs/wagon-ui : `marque.auteur` requis — qui édite le service');
178
178
  const LANGUES = langues.filter((l) => CATALOGUES[l]);
@@ -180,6 +180,9 @@ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar
180
180
  const C = Object.fromEntries(Object.entries({ ...REPLI, ...couleurs }).map(([k, v]) => [k, COULEUR.test(String(v)) ? v : REPLI[k]]));
181
181
  const STYLE = style(C);
182
182
  /** Le thème courant (Palette Studio), relu à chaque page ; rejeté s'il sort de :root{…}. */
183
+ // 0.6.0 — la feuille DE L'APPLICATION (sa configuration, de confiance), après la base et le thème, sur chaque page.
184
+ // Elle ne peut pas fermer la balise <style> : une feuille qui contient « </ » est écartée entière.
185
+ const feuilleApp = typeof css === 'string' && css.trim() && !/<\//.test(css) ? `<style id="feuille-app">${css}</style>` : '';
183
186
  const themeCss = () => { try { const t = typeof theme === 'function' ? String(theme() ?? '') : ''; return THEME_SAIN.test(t) ? t : ''; } catch { return ''; } };
184
187
 
185
188
  /**
@@ -250,7 +253,7 @@ export function creerWagonUi({ marque, couleurs = {}, langues = ['fr', 'en', 'ar
250
253
  const boutonAppel = appel?.href ? `<a class="appel" href="${lien(appel.href)}">${esc(dansLangue(appel.libelle, L))}</a>` : '';
251
254
  return `<!doctype html><html lang="${L}" dir="${dirDe(L)}"><head><meta charset="utf-8">
252
255
  <meta name="viewport" content="width=device-width, initial-scale=1"><title>${esc(titre)} — ${nom}</title>
253
- <link rel="icon" href="/favicon.svg" type="image/svg+xml">${tete}<style>${STYLE}</style>${themeCss() ? `<style id="theme-palette">${themeCss()}</style>` : ''}</head><body>
256
+ <link rel="icon" href="/favicon.svg" type="image/svg+xml">${tete}<style>${STYLE}</style>${themeCss() ? `<style id="theme-palette">${themeCss()}</style>` : ''}${feuilleApp}</head><body>
254
257
  <header${classesTete ? ` class="${classesTete}"` : ''}><a class="brand" href="/">${marque.symbole ? `${esc(marque.symbole)} ` : ''}${nom}</a><input type="checkbox" id="wagon-menu" class="bascule"><label for="wagon-menu" class="burger" aria-label="${esc(tr('layout.menu'))}">☰</label><div class="menu"><nav>${nav.map(([h, l]) => `<a href="${lien(h)}"${actif === h ? ' class="on"' : ''}>${esc(l)}</a>`).join('')}</nav><span class="moi"><a href="${connecte ? '/compte' : '/connexion'}">${esc(tr(connecte ? 'espace.compte' : 'espace.seConnecter'))}</a>${connecte ? ` · <a href="/deconnexion">${esc(tr('espace.seDeconnecter'))}</a>` : ''}</span>${boutonAppel}${choixLangue}</div></header>
255
258
  <main>${contenu}</main>
256
259
  <footer><b>${nom}</b>${slogan ? ` — ${esc(slogan)}` : ''} · ${esc(tr('layout.auteur'))} : <b>${esc(marque.auteur)}</b>${marque.courriel ? ` &lt;${esc(marque.courriel)}&gt;` : ''} · ${esc(marque.licence ?? 'AGPL-3.0')} · ${esc(tr('layout.propulse'))} <code>@mostajs/*</code><br><span class="legal">${DOCUMENTS_LEGAUX.map(([h, k]) => `<a href="/${h}">${esc(tr(`legal.${k}`))}</a>`).join(' · ')}</span></footer>
@@ -509,7 +512,7 @@ ${anonyme ? `<h2>${esc(tr('usage.anonyme'))}</h2>
509
512
  : `<a href="${lien(p.href)}"${p.href === actif ? ' class="on" aria-current="page"' : ''}>${esc(p.titre)}</a>`);
510
513
  return `<!doctype html><html lang="${L}" dir="${dirDe(L)}"><head><meta charset="utf-8">
511
514
  <meta name="viewport" content="width=device-width, initial-scale=1"><title>${esc(titre)} — ${esc(tr('doc.documentation'))} ${nom}</title>
512
- ${canonique ? `<link rel="canonical" href="${esc(canonique)}">${sousTitre ? `<meta name="description" content="${esc(sousTitre)}">` : ''}` : ''}<style>${STYLE}${STYLE_DOC}</style>${themeCss() ? `<style id="theme-palette">${themeCss()}</style>` : ''}</head><body>
515
+ ${canonique ? `<link rel="canonical" href="${esc(canonique)}">${sousTitre ? `<meta name="description" content="${esc(sousTitre)}">` : ''}` : ''}<style>${STYLE}${STYLE_DOC}</style>${themeCss() ? `<style id="theme-palette">${themeCss()}</style>` : ''}${feuilleApp}</head><body>
513
516
  <header class="doc-tete"><a class="brand" href="/">${marque.symbole ? `${esc(marque.symbole)} ` : ''}${nom}</a><span class="doc-mot">${esc(tr('doc.documentation'))}</span>${version ? `<span class="version">${esc(version)}</span>` : ''}<span class="moi"><a href="/">${esc(tr('doc.accueil'))}</a></span>${choixLangue}</header>
514
517
  <input type="checkbox" id="doc-menu" class="bascule">
515
518
  <div class="doc-barre"><label for="doc-menu" class="burger" aria-label="${esc(tr('layout.menu'))}">☰</label><nav class="onglets">${onglets.map((o) => `<a href="${lien(o.href)}"${o.href === onglet ? ' class="on"' : ''}>${esc(dansLangue(o.libelle, L))}</a>`).join('')}</nav><form class="recherche" method="get" action="${lien(recherche)}"><input type="search" name="q" value="${esc(q)}" placeholder="${esc(tr('doc.rechercher'))}" aria-label="${esc(tr('doc.rechercher'))}"></form></div>