From 23e58b17ee021708ffb11ec431290d93f102c4fa Mon Sep 17 00:00:00 2001 From: Pierre Martin Date: Fri, 17 Jul 2026 13:10:59 +0200 Subject: [PATCH] docs: record the prefill seam and the optional local config Co-Authored-By: Claude Opus 4.8 --- README.md | 31 ++++++++++++++++++++++++++ docs/DECISIONS.md | 55 +++++++++++++++++++++++++++++++++++++++++++++++ docs/RECHERCHE.md | 46 +++++++++++++++++++++++---------------- 3 files changed, 114 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index a7caec3..24ebda5 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,37 @@ runtime à installer) : > Pense aussi à **fermer les anciens onglets de la liste** avant de recharger : > une page orpheline rend les clics inertes. +### Configuration (optionnelle) + +Le formulaire de la mairie demande un **organisateur** et un **email** : ils sont +personnels, donc absents du dépôt. Pour qu'ils soient pré-remplis : + +```sh +cp extension/config.local.json.example extension/config.local.json +``` + +puis édite le fichier (il est gitignoré, il ne partira jamais sur le dépôt) : + +```json +{ + "organisateur": "L'Atelier du Huit", + "email": "contact@atelier-huit.fr" +} +``` + +Ce fichier est **facultatif**. Sans lui, tout le reste est pré-rempli (titre, +dates, heures, lieu, thème, tarifs) : seuls l'organisateur et l'email restent +vides, et le bandeau du formulaire te le dit. + +> ⚠️ Ce fichier ne surcharge que `organisateur` et `email`. Les autres valeurs +> par défaut (thème « Culture », tarifs « Gratuit », adresse **et coordonnées** +> du café) vont ensemble et vivent dans `extension/config.js` : c'est là qu'on +> les change. Y mettre un `cafe` partiel produirait des coordonnées invalides. + +> **Après avoir créé ou modifié `config.local.json`** : recharge le module dans +> `about:debugging`. L'extension lit le fichier au réveil de son arrière-plan, +> pas à chaque ouverture du formulaire. + ### Vérifier l'extension ```sh diff --git a/docs/DECISIONS.md b/docs/DECISIONS.md index a68cf6e..1d0c67c 100644 --- a/docs/DECISIONS.md +++ b/docs/DECISIONS.md @@ -126,3 +126,58 @@ deux avals du parseur ont chacun le leur : la liste affiche l'adresse pour ne pa montrer une ligne vide (cas majoritaire), le content script décide s'il pose **aussi** les coordonnées (`input_18`). Écrire l'adresse dans le payload détruirait le signal « pas de lieu » dont il a besoin. + +## 2026-07-17 — [T1] Pré-remplissage : content scripts classiques + `globalThis` + +Le formulaire mairie est pré-rempli par deux content scripts (`config.js`, +`formulaire-valeurs.js`, `formulaire-mairie.js`), **classiques — pas des modules +ESM** — qui communiquent par des namespaces `globalThis` (`EchoConfig`, +`EchoFormulaire`). + +**Pourquoi pas ESM :** un content script n'est pas un module (`import` y lève une +`SyntaxError`), et le `import()` dynamique y est cassé côté Firefox (bugs ouverts +1803950 / 1536094). Restait à convertir la couture `transfert.js`, figée le +2026-07-14 : non. + +⚠️ **`config.js` et `formulaire-valeurs.js` n'ont NI `import` NI `export`, et +c'est délibéré — ne pas « corriger ».** Un fichier sans les deux est valide **à la +fois** comme script classique (content script, background) et comme module ESM +(`import "./config.js"` depuis `presentation.js` et depuis `bun test`). C'est ce +qui permet à `presentation.js` de partager l'adresse du café **sans build**. Le +jour où quelqu'un y ajoute un `export`, les content scripts cassent +silencieusement. + +**Corollaire : l'ordre des tableaux `js` du manifest EST la dépendance** +(`config` → `valeurs` → `mairie`), testé dans `extension.test.ts`. + +**`CLE_EVENEMENT_EN_ATTENTE` est dupliquée** dans `formulaire-valeurs.js` +(l'original vit dans `transfert.js`, en ESM, inatteignable depuis un content +script classique). Un **test de dérive** garantit l'égalité des deux. + +**Config (a) : défauts committés + `config.local.json` optionnel.** Les défauts +(thème, tarifs, café) sont dans `config.js` ; seuls organisateur et email sont +personnels et vivent dans `config.local.json` (gitignoré). Le fichier n'est +**pas requis** : un clone frais doit marcher, il laisserait sinon l'extension +cassée par défaut. Absent ou invalide → défauts nus, manque **signalé** dans le +bandeau, jamais d'exception. + +**C'est le background qui lit `config.local.json`**, puis le passe par le +storage. Un content script ne pourrait le `fetch(runtime.getURL(...))` que si le +fichier était en `web_accessible_resources` — ce qui **exposerait l'email à la +page de la mairie**. Le fichier étant optionnel, il ne peut pas non plus être +listé dans un tableau `js` (Firefox refuse d'installer si un fichier déclaré +manque) : `fetch` + repli est la seule voie. + +**On ne remplit que ce qu'on sait ; le reste est dit, jamais inventé.** Pas +d'heure « 00:00 » pour une journée entière, pas de `lat|lng` deviné pour un lieu +hors café, pas d'organisateur par défaut. Ce qui manque part dans le bandeau +(`aCompleter`). En particulier, **`input_6_35` (accessibilité) est laissé +intact** : `RECHERCHE.md` le dit « fixe », mais la machine ne sait pas si le lieu +est accessible — cocher au hasard sur un formulaire **modéré** serait exactement +le « champ rempli faux » qu'on veut éviter. + +**Séparation calcul / DOM** : `formulaire-valeurs.js` est pur et testé (les dates +sont le vrai risque, invisible sous `TZ=Europe/Paris`) ; `formulaire-mairie.js` +est fin, impur et **non testé unitairement** — aucun harnais DOM dans le repo, et +happy-dom n'émulerait de toute façon pas Gravity Forms. C'est le test humain qui +tranche. diff --git a/docs/RECHERCHE.md b/docs/RECHERCHE.md index b41652d..3b6fd39 100644 --- a/docs/RECHERCHE.md +++ b/docs/RECHERCHE.md @@ -45,24 +45,34 @@ Content script qui **remplit le DOM par id**, puis **l'utilisateur soumet à la ### Mapping des champs (ids confirmés présents dans le HTML) -| id DOM | name | Contenu | Source | -|---|---|---|---| -| `#input_6_42` | `input_42` | Organisateur | config (« L'Atelier du Huit ») | -| `#input_6_41` | `input_41` | Email | config | -| `#input_6_1` | `input_1` | Titre | SUMMARY | -| `#input_6_3` | `input_3` | Description (textarea) | DESCRIPTION | -| `#input_6_20` | `input_20` | Thème (select) | mapping (voir §3) | -| `#input_6_31` | `input_31` | Date début `jj/mm/aaaa` | DTSTART | -| `#input_6_32` | `input_32` | Date fin `jj/mm/aaaa` | DTEND | -| `#input_6_38_1` / `#input_6_38_2` | `input_38[]` | Heure début [HH, MM] | DTSTART | -| `#input_6_39_1` / `#input_6_39_2` | `input_39[]` | Heure fin [HH, MM] | DTEND | -| `#input_6_35` (`_1`) | `input_35.1` | Accessibilité (case) | fixe | -| `#input_6_36` | `input_36` | Public ciblé | optionnel | -| `#input_6_40` | `input_40` | Tarifs | défaut « Gratuit » | -| `#nova_address` | `nova_address` | Adresse texte | défaut café | -| `#input_6_18` | `input_18` | `lat\|lng\|adresse` (hidden) | défaut café | -| `#input_6_34` | `input_34` | **Image (file)** | ⚠️ non remplissable par script (sécurité) → manuel | -| `#input_6_23`, `#input_6_43` | | Honeypot | laisser vide | +> ⚠️ **La colonne « balise réelle » a été payée par un bug** (2026-07-17). Le +> corps de la tâche T1 affirmait « Heures = 2 **selects** » : c'est **faux**, ce +> sont des ``. Le code l'a suivi et les 4 champs d'heures +> ont échoué (`champ.options` undefined → TypeError). **Ne pas déduire le type +> d'un champ de son intitulé : le HTML est la seule source de vérité.** Types +> ci-dessous vérifiés sur la page réelle le 2026-07-17. + +| id DOM | name | Balise réelle | Contenu | Source | +|---|---|---|---|---| +| `#input_6_42` | `input_42` | `input[type=text]` | Organisateur | config (« L'Atelier du Huit ») | +| `#input_6_41` | `input_41` | `input[type=email]` | Email | config | +| `#input_6_1` | `input_1` | `input[type=text]` | Titre | SUMMARY | +| `#input_6_3` | `input_3` | `textarea` | Description | DESCRIPTION | +| `#input_6_20` | `input_20` | **`select`** | Thème | mapping (voir §3) | +| `#input_6_31` | `input_31` | `input[type=text]` (datepicker) | Date début `jj/mm/aaaa` | DTSTART | +| `#input_6_32` | `input_32` | `input[type=text]` (datepicker) | Date fin `jj/mm/aaaa` | DTEND | +| `#input_6_38_1` / `#input_6_38_2` | `input_38[]` | **`input[type=number]`** (`min/max`, placeholder `HH`/`MM`) | Heure début [HH, MM] | DTSTART | +| `#input_6_39_1` / `#input_6_39_2` | `input_39[]` | **`input[type=number]`** | Heure fin [HH, MM] | DTEND | +| `#input_6_35` (`_1`) | `input_35.1` | `input[type=checkbox]` | Accessibilité | ⚠️ laissé **intact** : la machine ne sait pas si le lieu est accessible | +| `#input_6_36` | `input_36` | | Public ciblé | optionnel | +| `#input_6_40` | `input_40` | **`textarea`** | Tarifs | défaut « Gratuit » | +| `#nova_address` | `nova_address` | `input[type=text]` | Adresse texte | défaut café | +| `#input_6_18` | `input_18` | `input[type=hidden]` | `lat\|lng\|adresse` | défaut café | +| `#input_6_34` | `input_34` | `input[type=file]` | **Image** | ⚠️ non remplissable par script (sécurité) → manuel | +| `#input_6_23`, `#input_6_43` | | | Honeypot | laisser vide | + +Les 4 champs d'heures acceptent le **zéro-padding** (`"09"`, `"00"`) : une suite +de chiffres est un *valid floating-point number* au sens HTML. **Défaut café** : `8 Rue du Pré Vicinal 31270 Cugnaux` `input_18` = `43.53753132806989|1.342981890597508|8 Rue du Pré Vicinal 31270 Cugnaux`