humalis 1.1.0 → 1.3.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.
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs';
3
3
  import { createServer } from 'node:http';
4
4
  import { dirname, join, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
+ import { AIDE_REPERES } from '../cibles/commun/reperes.js';
6
7
  export function dependances(fichier, source) {
7
8
  return [...source.matchAll(/^\s*utiliser\s+(?:fichier|style)\s+"([^"]+)"/gmu)]
8
9
  .map((capture) => resolve(dirname(fichier), capture[1]));
@@ -72,7 +73,8 @@ export async function developper(fichier, port) {
72
73
  reponse.setHeader('Access-Control-Allow-Origin', '*');
73
74
  if (requete.url === '/diagnostics.js') {
74
75
  reponse.setHeader('Content-Type', 'text/javascript');
75
- reponse.end(`let panneau=document.createElement('pre');panneau.style='position:fixed;inset:0 0 auto;z-index:999999;background:#420;color:white;padding:1rem;white-space:pre-wrap;display:none';document.body.append(panneau);setInterval(async()=>{let e=await(await fetch('http://localhost:${port + 1}/diagnostics')).text();panneau.textContent=e;panneau.style.display=e?'block':'none'},500);`);
76
+ // L'aide Alt + survol (le fichier et la ligne Humalis d'un élément), puis le panneau des erreurs.
77
+ reponse.end(AIDE_REPERES + `let panneau=document.createElement('pre');panneau.style='position:fixed;inset:0 0 auto;z-index:999999;background:#420;color:white;padding:1rem;white-space:pre-wrap;display:none';document.body.append(panneau);setInterval(async()=>{let e=await(await fetch('http://localhost:${port + 1}/diagnostics')).text();panneau.textContent=e;panneau.style.display=e?'block':'none'},500);`);
76
78
  }
77
79
  else {
78
80
  reponse.setHeader('Content-Type', 'text/plain; charset=utf-8');
@@ -56,7 +56,7 @@ export function analyserStylePropre(flux, propriete) {
56
56
  return undefined;
57
57
  }
58
58
  const APPAREILS = ['telephone', 'tablette', 'ordinateur'];
59
- const DRAPEAUX = ['modifiable', 'ouvert', 'plusieurs', 'pourcentage', 'boucle'];
59
+ const DRAPEAUX = ['modifiable', 'ouvert', 'plusieurs', 'pourcentage', 'boucle', 'fixe'];
60
60
  /** « sur telephone : », « sur tablette ou ordinateur : » (en tête de ligne, suivi d'un appareil). */
61
61
  export function estSurAppareil(flux) {
62
62
  return flux.verifier('MOT_CLE', 'sur') && estMot(flux.regarder(1)) && APPAREILS.includes(normaliser(flux.regarder(1).valeur));
@@ -776,7 +776,7 @@ function estBlocMiseEnPage(flux) {
776
776
  const suivant = flux.regarder(1);
777
777
  return ((suivant.type === 'PONCTUATION' && suivant.valeur === ':') ||
778
778
  (suivant.type === 'MOT_CLE' && suivant.valeur === 'avec') ||
779
- (genre === 'menu' && estLeMot(suivant, 'repliable')) ||
779
+ (genre === 'menu' && (estLeMot(suivant, 'repliable') || estLeMot(suivant, 'vertical'))) ||
780
780
  ((genre === 'section' || GENRES_AVEC_TITRE.includes(genre)) && suivant.type === 'CHAINE'));
781
781
  }
782
782
  function analyserBlocMiseEnPage(flux) {
@@ -786,9 +786,15 @@ function analyserBlocMiseEnPage(flux) {
786
786
  flux.avancer();
787
787
  const genre = filAriane ? 'filAriane' : GENRES_BLOC.find((g) => estLeMot(debut, g));
788
788
  // « menu repliable : » : sur un petit écran, un bouton « Menu » ouvre et ferme les liens.
789
- const repliable = genre === 'menu' && estLeMot(flux.actuel(), 'repliable');
790
- if (repliable)
791
- flux.avancer();
789
+ // « menu vertical : » : les liens en colonne. Les deux se combinent, dans n'importe quel ordre.
790
+ let repliable = false;
791
+ let vertical = false;
792
+ while (genre === 'menu' && (estLeMot(flux.actuel(), 'repliable') || estLeMot(flux.actuel(), 'vertical'))) {
793
+ if (estLeMot(flux.avancer(), 'repliable'))
794
+ repliable = true;
795
+ else
796
+ vertical = true;
797
+ }
792
798
  // Le titre d'une section devient son premier élément : un « titre » comme les autres.
793
799
  const titre = [];
794
800
  let titreBloc;
@@ -824,7 +830,7 @@ function analyserBlocMiseEnPage(flux) {
824
830
  } while (flux.accepter('MOT_CLE', 'et'));
825
831
  }
826
832
  flux.attendre('PONCTUATION', ':', `deux-points à la fin de la ligne « ${genre} », puis son contenu en dessous, décalé`);
827
- return { type: 'Bloc', genre, styles, classes, ...(titreBloc ? { titre: titreBloc } : {}), ...(ancre !== undefined ? { ancre } : {}), ...(nom !== undefined ? { nom } : {}), ...(repliable ? { repliable } : {}), corps: [...titre, ...analyserBloc(flux)], ligne: debut.ligne };
833
+ return { type: 'Bloc', genre, styles, classes, ...(titreBloc ? { titre: titreBloc } : {}), ...(ancre !== undefined ? { ancre } : {}), ...(nom !== undefined ? { nom } : {}), ...(repliable ? { repliable } : {}), ...(vertical ? { vertical } : {}), corps: [...titre, ...analyserBloc(flux)], ligne: debut.ligne };
828
834
  }
829
835
  function lireNombre(flux, description) {
830
836
  const negatif = flux.accepter('PONCTUATION', '-');
@@ -691,6 +691,9 @@ class Construction {
691
691
  // « menu repliable » : la marque que lisent le CSS et le bouton « Menu ».
692
692
  if (instruction.repliable)
693
693
  options.push({ nom: 'menu', valeur: 'repliable' });
694
+ // « menu vertical » : la colonne que lit le CSS.
695
+ if (instruction.vertical)
696
+ options.push({ nom: 'menu', valeur: 'vertical' });
694
697
  const noeud = {
695
698
  type: 'bloc',
696
699
  genre: instruction.genre,
@@ -100,6 +100,10 @@ export const ENTREES_ECRAN = [
100
100
  style('ombre', 'Une ombre sous l’élément : aucune, legere, moyenne ou forte.', PAGE(' bloc avec ombre legere :\n texte "Poisson braisé"'), 'box-shadow', 'StyleSheet shadow* et elevation'),
101
101
  style('degrade', 'Un fond en dégradé entre deux couleurs : « degrade bleu vers violet », puis au besoin vertical ou horizontal (diagonal sinon).', PAGE(' bloc avec degrade bleu vers violet :\n titre "Réservez" avec texte blanc'), 'linear-gradient', 'LinearGradient (expo-linear-gradient)', undefined, 'Deux couleurs seulement (de base ou du thème) ; un dégradé plus fin s’écrit en CSS.'),
102
102
  style('colle', 'L’élément reste collé en haut de l’écran quand on défile ; « colle en bas » le colle en bas.', PAGE(' bloc avec colle :\n texte "Toujours visible"'), 'position: sticky', null, undefined, 'Sur le téléphone, seuls l’en-tête et le pied de page restent collés ; ailleurs, un avertissement le dit.'),
103
+ // ─── Défilement et hauteur (plan, apparence sans CSS) ────────────────
104
+ style('defilement', 'Le bloc défile seul, avec sa propre barre, sans faire bouger le reste de la page : « defilement interne ».', PAGE(' ligne avec hauteur ecran :\n colonne avec defilement interne :\n texte "Un long chapitre."'), 'overflow-y: auto', null, [...BLOCS, 'menu'], 'Sur le téléphone, toute la page défile : un avertissement le dit.'),
105
+ style('hauteur', 'Le bloc prend toute la hauteur de l’écran, sous l’en-tête : « hauteur ecran ».', PAGE(' ligne avec hauteur ecran :\n colonne avec defilement interne :\n texte "Un long chapitre."'), 'height: 100dvh', null, BLOCS, 'Sur le téléphone, toute la page défile : un avertissement le dit.'),
106
+ { ...style('fixe', 'L’en-tête ne bouge jamais : seule la zone en dessous défile, avec sa barre (« entete avec fixe : »).', 'entete avec fixe :\n lien "Accueil" vers "accueil"\n\npage "accueil" :\n titre "Accueil"', 'position: fixed', null, ['entete'], 'Seulement sur l’en-tête. Sur le téléphone, l’en-tête reste en haut comme avec « colle ».'), valeurLibre: true },
103
107
  style('sur', 'Dans un bloc « sur telephone : », les appareils où le contenu s’affiche.', PAGE(' sur telephone :\n texte "Petit écran"'), 'display', 'SurAppareil (largeur de la fenêtre)', ['appareil']),
104
108
  // ─── La publication (plan 2026-10-30-qualite-publication, parties 3 à 5) ──
105
109
  {
@@ -389,6 +393,8 @@ export const ENTREES_ECRAN = [
389
393
  valeur('forte', ['ombre'], 'Une ombre marquée.', PAGE(' bloc avec ombre forte :\n texte "a"'), ['fort']),
390
394
  valeur('douce', ['bordure', 'transition'], 'Des coins légèrement arrondis ; pour une transition, des changements adoucis (200 ms).', PAGE(' bloc avec bordure douce :\n texte "a"'), ['doux']),
391
395
  valeur('ronde', ['bordure'], 'Une forme ronde : un cercle pour un élément carré.', PAGE(' bloc avec bordure ronde :\n texte "a"'), ['rond']),
396
+ valeur('interne', ['defilement'], 'Le bloc a son propre défilement : « defilement interne ».', PAGE(' colonne avec defilement interne :\n texte "a"')),
397
+ valeur('ecran', ['hauteur'], 'La hauteur de l’écran, moins celle de l’en-tête : « hauteur ecran ».', PAGE(' ligne avec hauteur ecran :\n texte "a"'), [], 'écran'),
392
398
  valeur('haut', ['colle'], 'Collé en haut de l’écran (par défaut).', PAGE(' bloc avec colle :\n texte "a"')),
393
399
  valeur('bas', ['colle'], 'Collé en bas de l’écran : « colle en bas ».', PAGE(' bloc avec colle en bas :\n texte "a"')),
394
400
  valeur('telephone', ['sur'], 'Un écran de moins de 640 pixels de large.', PAGE(' sur telephone :\n texte "a"'), ['telephones'], 'téléphone'),
package/docs/guide.en.md CHANGED
@@ -187,8 +187,10 @@ Keep the levels in order, without skipping: screen readers use them to move
187
187
  through the page.
188
188
 
189
189
  Inside a block placed under a subtitle, a `soustitre` goes down one level on its
190
- own: a card in a section becomes level 3. `avec niveau` (with level) still lets
191
- you choose.
190
+ own: a card in a section becomes level 3. In a `section`, the first subtitle is
191
+ the section's title, even when it sits in its own block; every other subtitle of
192
+ the section goes down one level. `avec niveau` (with level) still lets you
193
+ choose.
192
194
 
193
195
  ```humalis
194
196
  page "home" :
@@ -406,6 +408,11 @@ CSS can also come from a file: `utiliser style "./theme.css"` at the top of
406
408
  the file. CSS applies to the web, static and desktop targets; mobile only
407
409
  translates named styles (colour, background, size, position, border).
408
410
 
411
+ `humalis verifier` warns when a class used with `avec style` is missing from
412
+ the project's CSS and suggests a close name. It also warns when an
413
+ `!important` rule targets a `.humalis-…` class: remove `!important` or use
414
+ your own class.
415
+
409
416
  ---
410
417
 
411
418
  ## 6. Layout
@@ -444,6 +451,33 @@ Block settings: `largeur` (width: `etroite` narrow, `large` wide),
444
451
  `alignement centre` (centred), `degrade bleu vers violet` (gradient), `colle`
445
452
  (sticks to the top while scrolling).
446
453
 
454
+ ### A page that fits the screen
455
+
456
+ `entete avec fixe :` (fixed header) keeps the header still: only the area below
457
+ scrolls, with its own scrollbar. `hauteur ecran` (screen height) makes a block
458
+ fill the screen under the header, and `defilement interne` (inner scrolling)
459
+ gives a block its own scrolling without moving the rest. Together they make a
460
+ documentation page: the table of contents and the text each scroll on their own,
461
+ without a line of CSS.
462
+
463
+ ```humalis
464
+ entete avec fixe :
465
+ lien "Home" vers "guide"
466
+
467
+ page "guide" :
468
+ titre "The guide"
469
+ ligne avec hauteur ecran :
470
+ menu vertical avec defilement interne :
471
+ lien "Install" vers "guide"
472
+ lien "Pages" vers "guide"
473
+ colonne avec defilement interne :
474
+ texte "A long chapter that scrolls on its own."
475
+ ```
476
+
477
+ On the static target, the scrollbar stays the window's. On a phone the whole page
478
+ scrolls: `hauteur ecran` and `defilement interne` have no effect, and a warning
479
+ says so.
480
+
447
481
  ### Depending on the device
448
482
 
449
483
  ```humalis
@@ -608,12 +642,23 @@ page "menu" :
608
642
  ```
609
643
 
610
644
  `fil d'Ariane` is a breadcrumb. In a `menu`, the link to the current page is
611
- marked as active. The links of a menu wrap when space runs out. A menu that
612
- your CSS turns into a column (`flex-direction: column`) keeps that wrapping: add
613
- `flex-wrap: nowrap` so it scrolls instead of starting a second column.
645
+ marked as active. The links of a menu wrap when space runs out. A `menu
646
+ vertical :` stacks its links in a column and never starts a second column: a
647
+ table of contents with a limited height scrolls. It combines with `repliable`
648
+ (`menu vertical repliable :`).
614
649
 
615
- A `menu repliable :` (collapsible menu) folds behind a "Menu" button on a small
616
- screen (phone); choosing a link closes it. On a large screen it stays open:
650
+ ```humalis
651
+ page "guide" :
652
+ titre "The guide"
653
+ menu vertical :
654
+ lien "Install" vers "guide"
655
+ lien "Pages" vers "guide"
656
+ ```
657
+
658
+ A `menu repliable :` (collapsible menu) folds behind a burger button (named
659
+ "Menu" for screen readers) on a small screen (phone); the icon turns into a cross
660
+ while the menu is open. Choosing a link, tapping outside or pressing Escape
661
+ closes it. On a large screen it stays open:
617
662
 
618
663
  ```humalis
619
664
  page "home" :
@@ -1927,6 +1972,14 @@ For web and static, `--adresse https://…` gives the site's final address
1927
1972
  (sitemap, share links). For desktop, `--serveur https://…` gives the address
1928
1973
  of the server the application calls.
1929
1974
 
1975
+ With `humalis developper`, every element of the page carries its Humalis
1976
+ file and line: in the browser's inspector, a heading shows for example
1977
+ `data-humalis-source="pages/accueil.humalis:12"` (the path starts from the
1978
+ main file, the line is the one in that file). Hold the Alt key (Option on
1979
+ Mac) and hover over an element: a small label shows the same reference next
1980
+ to the cursor. `humalis compile` adds none of this: the published site stays
1981
+ identical.
1982
+
1930
1983
  ### Error messages
1931
1984
 
1932
1985
  Messages are in French, with the line number, and often suggest the fix:
package/docs/guide.md CHANGED
@@ -176,8 +176,10 @@ l'ordre des niveaux, sans en sauter : les lecteurs d'écran s'en servent pour
176
176
  parcourir la page.
177
177
 
178
178
  Dans un bloc placé sous un sous-titre, un `soustitre` descend d'un niveau tout
179
- seul : la carte d'une section devient un niveau 3. `avec niveau` reste possible
180
- pour choisir soi-même.
179
+ seul : la carte d'une section devient un niveau 3. Dans une `section`, le premier
180
+ sous-titre est le titre de la section, même rangé dans son propre bloc ; tous les
181
+ autres sous-titres de la section descendent d'un niveau. `avec niveau` reste
182
+ possible pour choisir soi-même.
181
183
 
182
184
  ```humalis
183
185
  page "accueil" :
@@ -387,6 +389,10 @@ Le CSS peut aussi venir d'un fichier : `utiliser style "./theme.css"` en haut du
387
389
  fichier. Le CSS s'applique au web, au statique et au desktop ; le mobile ne
388
390
  traduit que les styles nommés (couleur, fond, taille, position, bordure).
389
391
 
392
+ `humalis verifier` avertit si une classe donnée par `avec style` est absente du CSS
393
+ du projet et propose un nom proche. Il avertit aussi si une règle `!important`
394
+ cible une classe `.humalis-…` : retirez `!important` ou utilisez votre propre classe.
395
+
390
396
  ---
391
397
 
392
398
  ## 6. La mise en page
@@ -422,6 +428,32 @@ Les réglages des blocs : `largeur` (`etroite`, `large`), `remplissage` et
422
428
  `moyenne`), `bordure douce`, `repartition entre`, `alignement centre`,
423
429
  `degrade bleu vers violet`, `colle` (reste en haut pendant le défilement).
424
430
 
431
+ ### Une page qui tient dans l'écran
432
+
433
+ `entete avec fixe :` garde l'en-tête immobile : seule la zone en dessous défile,
434
+ avec sa barre. `hauteur ecran` fait remplir l'écran à un bloc, sous l'en-tête, et
435
+ `defilement interne` donne à un bloc son propre défilement, sans faire bouger le
436
+ reste. Ensemble, ils font une page de documentation : le sommaire et le texte
437
+ défilent chacun de leur côté, sans une ligne de CSS.
438
+
439
+ ```humalis
440
+ entete avec fixe :
441
+ lien "Accueil" vers "guide"
442
+
443
+ page "guide" :
444
+ titre "Le guide"
445
+ ligne avec hauteur ecran :
446
+ menu vertical avec defilement interne :
447
+ lien "Installer" vers "guide"
448
+ lien "Les pages" vers "guide"
449
+ colonne avec defilement interne :
450
+ texte "Un long chapitre, qui défile seul."
451
+ ```
452
+
453
+ Sur le statique, la barre de défilement reste celle de la fenêtre. Sur le
454
+ téléphone, toute la page défile : `hauteur ecran` et `defilement interne` n'ont
455
+ pas d'effet, et un avertissement le dit.
456
+
425
457
  ### Selon l'appareil
426
458
 
427
459
  ```humalis
@@ -573,13 +605,23 @@ page "carte" :
573
605
  ```
574
606
 
575
607
  Dans un `menu`, le lien de la page où l'on est est marqué comme actif.
576
- Les liens d'un menu passent à la ligne quand la place manque. Un menu que ton
577
- CSS met en colonne (`flex-direction: column`) garde ce retour à la ligne :
578
- ajoute `flex-wrap: nowrap` pour qu'il défile au lieu d'ouvrir une deuxième
579
- colonne.
608
+ Les liens d'un menu passent à la ligne quand la place manque. Un `menu
609
+ vertical :` range ses liens en colonne, sans jamais ouvrir une deuxième colonne :
610
+ un sommaire de hauteur limitée défile. Il se combine avec `repliable`
611
+ (`menu vertical repliable :`).
580
612
 
581
- Un `menu repliable :` se range derrière un bouton « Menu » sur un petit écran
582
- (téléphone) ; un lien choisi referme le menu. Sur grand écran, il reste ouvert :
613
+ ```humalis
614
+ page "guide" :
615
+ titre "Le guide"
616
+ menu vertical :
617
+ lien "Installer" vers "guide"
618
+ lien "Les pages" vers "guide"
619
+ ```
620
+
621
+ Un `menu repliable :` se range derrière un bouton burger (nommé « Menu » pour les
622
+ lecteurs d'écran) sur un petit écran (téléphone) ; l'icône devient une croix
623
+ quand le menu est ouvert. Un lien choisi, un toucher à côté ou la touche Échap
624
+ referment le menu. Sur grand écran, il reste ouvert :
583
625
 
584
626
  ```humalis
585
627
  page "accueil" :
@@ -1842,6 +1884,14 @@ Pour le web et le statique, `--adresse https://…` donne l'adresse finale du
1842
1884
  site (plan du site, liens de partage). Pour le desktop, `--serveur https://…`
1843
1885
  donne l'adresse du serveur que l'application appelle.
1844
1886
 
1887
+ Avec `humalis developper`, chaque élément de la page porte son fichier
1888
+ Humalis et sa ligne : dans l'inspecteur du navigateur, un titre montre par
1889
+ exemple `data-humalis-source="pages/accueil.humalis:12"` (le chemin part du
1890
+ fichier principal, la ligne est celle de ce fichier). Tiens la touche Alt
1891
+ (Option sur Mac) et survole un élément : une petite étiquette affiche ce
1892
+ même repère près du curseur. `humalis compile` n'ajoute rien de tout cela :
1893
+ le site publié reste identique.
1894
+
1845
1895
  ### Les messages d'erreur
1846
1896
 
1847
1897
  Les messages sont en français, avec la ligne, et proposent souvent la
package/llms.txt CHANGED
@@ -146,7 +146,8 @@ page "accueil" :
146
146
  - Un nom de classe CSS sans guillemets : il manque le nom de la classe CSS entre guillemets
147
147
  - « utiliser » sans « style » : il manque « style » après « utiliser »
148
148
  - Un chemin de fichier CSS sans guillemets : il manque le chemin du fichier CSS entre guillemets
149
- - Une classe CSS définie nulle part : n'est définie dans aucun bloc style
149
+ - Un style CSS introuvable : n'est défini dans aucun CSS du projet
150
+ - Une règle CSS écrase un style Humalis : le CSS écrase « humalis-page »
150
151
  - Une option de variable inconnue : n'est pas une option de variable
151
152
  - Un mot après « memorisee en » qui n’est pas « secret » : est attendu après « memorisee en »
152
153
  - Une variable en haut du fichier sans « avec partagee » : ajoutez « avec partagee »
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humalis",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Un seul langage, en français, pour créer une application complète : web, mobile, desktop et serveur",
5
5
  "type": "module",
6
6
  "bin": {