# Format de fichier Tabler (.table.md)
> Documentation technique — ProAppli Tabler

---

## Vue d'ensemble

Un fichier `.table.md` est un fichier Markdown standard contenant un tableau, précédé d'un **front matter YAML** qui définit les propriétés de chaque colonne.

```
---
[Front Matter : métadonnées + définitions de champs]
---

| colonne1 | colonne2 | colonne3 |
| -------- | -------- | -------- |
| valeur   | valeur   | valeur   |
```

Le fichier est lisible et éditable avec n'importe quel éditeur de texte. Les IA (ChatGPT, Claude, Grok…) comprennent et génèrent nativement ce format.

---

## 1. Front Matter (entête YAML)

Délimité par `---` en début de fichier. Contient une ligne `#meta` optionnelle et une définition par champ.

```yaml
---
#meta title=Contacts icon=👤
name: type=text label=Nom
email: type=email label=Email
age: type=number label=Âge
---
```

### 1.1 Ligne `#meta` (optionnelle)

Métadonnées globales du fichier. Utilisée principalement par le système de modèles.

```
#meta title=Contacts icon=👤
```

| Propriété | Description |
|-----------|-------------|
| `title`   | Nom affiché dans le sélecteur de modèles |
| `icon`    | Emoji affiché comme icône du modèle |

### 1.2 Définition d'un champ

Chaque ligne définit une colonne du tableau :

```
nom_technique: propriété1=valeur1 propriété2=valeur2
```

Le **nom technique** (avant le `:`) est le nom de la colonne dans le tableau Markdown. Il sert d'identifiant interne et est utilisé dans les formules. Il doit être en caractères simples, sans espaces (utiliser des underscores).

Le **label** (propriété optionnelle) est le nom affiché dans l'interface. Il peut contenir des espaces s'il est entre guillemets.

```
postal_code: type=text label="Code postal"
```

---

## 2. Propriétés des champs

### 2.1 Type de données (`type=`)

Définit la nature de la donnée. Contrôle la validation et le formatage.

| Type | Description | Exemple de valeur |
|------|-------------|-------------------|
| `text` | Texte libre (défaut) | `Dupont` |
| `number` | Nombre entier ou décimal | `42`, `3.14` |
| `currency` | Montant monétaire | `1500.00` |
| `percent` | Pourcentage | `75` (affiché 75%) |
| `date` | Date | `2025-01-15` |
| `time` | Heure | `14:30` |
| `datetime` | Date et heure | `2025-01-15T14:30` |
| `email` | Adresse email | `jean@exemple.fr` |
| `url` | Lien web | `https://exemple.fr` |
| `phone` | Numéro de téléphone | `+33 6 12 34 56 78` |
| `boolean` | Vrai/Faux | `true`, `false` |
| `color` | Couleur hexadécimale | `#ff6600` |
| `image` | URL d'image | `https://exemple.fr/photo.jpg` |
| `file` | Chemin de fichier | `documents/rapport.pdf` |

### 2.2 Mode de saisie (`input=`)

Définit le widget d'édition affiché dans l'interface.

| Input | Description | Propriétés associées |
|-------|-------------|---------------------|
| `input` | Champ texte simple (défaut) | — |
| `select` | Liste déroulante | `values=opt1,opt2,opt3` |
| `checkbox` | Case à cocher | — |
| `radio` | Boutons radio | `values=opt1,opt2` |
| `textarea` | Zone de texte multiligne | — |
| `formula` | Champ calculé (non éditable) | `formula=expression` |
| `rating` | Étoiles cliquables (1 à N) | `max=5` (défaut : 5) |

### 2.3 Propriétés complémentaires

| Propriété | Description | Exemple |
|-----------|-------------|---------|
| `label` | Libellé affiché (si différent du nom technique) | `label="Code postal"` |
| `values` | Valeurs possibles (pour select/radio), séparées par des virgules | `values=faible,moyen,fort` |
| `formula` | Expression de calcul référençant d'autres colonnes | `formula=price_ht*(1+vat/100)` |
| `default` | Valeur par défaut pour les nouvelles lignes | `default=0` |
| `currency` | Symbole monétaire | `currency=€`, `currency=$` |
| `max` | Nombre max d'étoiles (pour rating) | `max=5` |
| `required` | Champ obligatoire (flag sans valeur) | `required` |
| `aggregations` | Calculs affichés en pied de tableau, séparés par des virgules | `aggregations=sum,average` |

### 2.4 Agrégations disponibles

Les agrégations s'affichent automatiquement dans une ligne de pied de tableau.

| Agrégation | Symbole | Description |
|------------|---------|-------------|
| `sum` | Σ | Somme de la colonne |
| `average` | μ | Moyenne de la colonne |
| `min` | ↓ | Valeur minimale |
| `max` | ↑ | Valeur maximale |
| `count` | # | Nombre de valeurs non vides |

Exemple : `aggregations=sum,average` affiche la somme et la moyenne en bas de la colonne.

---

## 3. Corps du tableau (Markdown)

Le tableau suit le standard Markdown :

```markdown
| name   | email            | age |
| ------ | ---------------- | --- |
| Dupont | jean@exemple.fr  | 42  |
| Martin | anne@exemple.fr   | 35  |
|        |                  |     |
```

### Règles

- La première ligne contient les **noms techniques** des colonnes (ceux définis dans le front matter).
- La deuxième ligne est le **séparateur** composé de tirets (`---`).
- Les lignes suivantes sont les **données**.
- Les lignes vides (`|  |  |  |`) sont des lignes sans données, elles sont préservées.
- Les cellules sont séparées par des pipes `|`.
- Les espaces autour des valeurs sont ignorés.

---

## 4. Formules

Les formules référencent les autres colonnes par leur **nom technique**. Moteur type Excel
(`tabler-formulas.js`) mais qui **travaille par colonne, pas par cellule** : pas de notation
`A1`/`B2` ni de plage `A1:B10`. Une fonction d'agrégation (`SUM`, `AVERAGE`…) reçoit directement
toute la colonne. Le séparateur d'arguments est le **point-virgule** `;` (convention Excel FR),
pas la virgule.

### Opérateurs supportés

| Opérateur | Description |
|-----------|-------------|
| `+` `-` `*` `/` `%` `**` `^` | Arithmétique (`**`/`^` = puissance, `%` = modulo) |
| `( )` | Groupement |
| `< > <= >= = == != <>` | Comparaisons (`=`/`==` et `!=`/`<>` équivalents) |
| `&& \|\|` | ET / OU logique |
| `&` | Concaténation de texte (façon Excel) |
| `? :` | Ternaire |

### Fonctions disponibles

| Catégorie | Fonctions |
|-----------|-----------|
| Agrégation (colonne entière) | `SUM` `AVERAGE`/`AVG` `COUNT` `COUNTA` `MIN` `MAX` |
| Math | `ROUND` `FLOOR` `CEIL` `ABS` `SQRT` `POWER` `MOD` `INT` |
| Logique | `IF` `AND` `OR` `NOT` `IFERROR` `ISBLANK` `ISNUMBER` |
| Texte | `CONCAT` `LEFT` `RIGHT` `MID` `LEN` `LOWER` `UPPER` `TRIM` `SUBSTITUTE` `VALUE` `TEXT` |
| Date | `TODAY` `NOW` `DATE` `YEAR` `MONTH` `DAY` `DATEDIF` `DAYS` |
| Recherche inter-tableaux | `LOOKUP(valeur; table; colonne_clé; colonne_résultat)`, `VLOOKUP` (alias) |

`LOOKUP`/`VLOOKUP` ne prennent **pas** de plage de cellules + numéro de colonne comme dans Excel :
`table` est le nom d'un autre `.table.md` lié, `colonne_clé`/`colonne_résultat` sont des noms de
colonnes de ce tableau — cohérent avec le modèle par colonne de Tabler.

Sécurité : DSL parsé (tokenizer + AST), aucun `eval`, aucun accès à `window`/`document`/réseau
depuis une formule.

### Exemples

```
# Prix TTC calculé depuis HT et TVA, arrondi à 2 décimales
price_ttc: type=currency input=formula formula=ROUND(price_ht*(1+vat/100);2) currency=€

# Reste à dépenser
remaining: type=currency input=formula formula=budget-spent currency=€

# Marge en pourcentage
margin: type=percent input=formula formula=(revenue-cost)/revenue*100

# Somme de toute la colonne price_ht
total_ht: type=currency input=formula formula=SUM(price_ht) currency=€

# Statut conditionnel
statut: type=formula formula=IF(stock>0;"En stock";"Épuisé")

# Recherche du taux de TVA dans un autre tableau
taux: type=formula formula=VLOOKUP(reference;"taux_tva";reference;taux)
```

Les formules sont recalculées automatiquement à chaque modification d'une cellule.

---

## 5. Guillemets dans le front matter

Les valeurs contenant des **espaces** doivent être entre guillemets doubles :

```
postal_code: type=text label="Code postal"
full_name: type=text label="Nom complet"
status: type=text input=select values=todo,in_progress,done label="État"
```

Les valeurs sans espaces n'ont pas besoin de guillemets :

```
name: type=text label=Nom
age: type=number label=Âge
```

---

## 6. Exemples complets

### 6.1 Gestion de contacts

```markdown
---
#meta title=Contacts icon=👤
name: type=text label=Nom
first_name: type=text label="Prénom"
title: type=text input=select values=M.,Mme,Dr,Me,Pr label="Civilité"
company: type=text label="Société"
email: type=email label=Email
phone: type=phone label="Téléphone"
category: type=text input=select values=client,prospect,fournisseur,partenaire label="Catégorie"
city: type=text label=Ville
notes: type=text input=textarea label=Notes
active: type=boolean input=checkbox label=Actif
---

| name    | first_name | title | company     | email               | phone          | category | city       | notes           | active |
| ------- | ---------- | ----- | ----------- | ------------------- | -------------- | -------- | ---------- | --------------- | ------ |
| Dupont  | Marie      | Mme   | Acme SA     | marie@acme.fr       | 01 23 45 67 89 | client   | Paris      | Compte premium  | true   |
| Martin  | Pierre     | M.    | Tech Corp   | p.martin@tech.fr    | 06 12 34 56 78 | prospect | Lyon       |                 | true   |
| Durand  | Sophie     | Dr    | MedLab      | s.durand@medlab.fr  | 04 56 78 90 12 | partenaire | Marseille |                 | false  |
```

### 6.2 Catalogue produits avec formules

```markdown
---
#meta title=Produits icon=📦
reference: type=text label="Référence"
name: type=text label=Nom
price_ht: type=currency currency=€ label="Prix HT" aggregations=sum
vat: type=percent label=TVA default=20
price_ttc: type=currency input=formula formula=price_ht*(1+vat/100) currency=€ label="Prix TTC"
stock: type=number label=Stock aggregations=sum
status: type=text input=select values=actif,épuisé,abandonné label=Statut
---

| reference | name              | price_ht | vat | price_ttc | stock | status |
| --------- | ----------------- | -------- | --- | --------- | ----- | ------ |
| PRD-001   | Licence Standard  | 49.00    | 20  |           | 500   | actif  |
| PRD-002   | Licence Pro       | 99.00    | 20  |           | 200   | actif  |
| PRD-003   | Formation 1 jour  | 450.00   | 20  |           | 10    | actif  |
```

> Les cellules `price_ttc` sont vides dans le fichier source : elles sont calculées automatiquement à l'affichage.

### 6.3 Suivi de projet

```markdown
---
#meta title=Projets icon=📊
project: type=text label=Projet
owner: type=text label=Responsable
status: type=text input=select values=planifié,en_cours,en_pause,terminé,annulé label=Statut
start_date: type=date label=Début
end_date: type=date label=Fin
budget: type=currency currency=€ label=Budget aggregations=sum
spent: type=currency currency=€ label=Dépensé aggregations=sum
remaining: type=currency input=formula formula=budget-spent currency=€ label=Reste aggregations=sum
progress: type=percent label=Avancement aggregations=average
risk: type=text input=select values=faible,moyen,élevé,critique label=Risque
---

| project         | owner   | status   | start_date | end_date   | budget   | spent    | remaining | progress | risk   |
| --------------- | ------- | -------- | ---------- | ---------- | -------- | -------- | --------- | -------- | ------ |
| Refonte site    | Marie   | en_cours | 2025-01-15 | 2025-06-30 | 15000.00 | 8500.00  |           | 55       | moyen  |
| App mobile      | Pierre  | planifié | 2025-03-01 | 2025-09-30 | 25000.00 | 0        |           | 0        | élevé  |
| Migration cloud | Sophie  | en_cours | 2025-02-01 | 2025-04-30 | 8000.00  | 6200.00  |           | 80       | faible |
```

### 6.4 Quiz interactif — `type: quiz`

⚠️ Ne pas confondre avec un tableau *à propos de* questions (ça, c'est juste un `.table.md`
normal avec des colonnes `type=text`/`type=select`). Le widget quiz **interactif** (correction
immédiate, glisser-déposer pour l'ordre, enregistrement des résultats) est déclenché par
`type: quiz` dans le front matter, avec un schéma de colonnes précis — c'est ce schéma-ci qui
est lu par `quiz_preview.php`, `quiz_submit.php`, `presenter_view.php` et l'aperçu Writer
(harmonisé le 2026-09-23, voir §&nbsp;8 de `formats-ouverts.html`) :

```markdown
---
title: Quiz — La photosynthèse
type: quiz
---

| id | type             | question                          | options                            | correcte | explication                   |
| -- | ---------------- | ---------------------------------- | ----------------------------------- | -------- | ------------------------------ |
| 1  | qcm_simple       | Quel gaz est produit ?             | Azote,Oxygène,CO2,Argon             | Oxygène  | La photosynthèse produit O₂.  |
| 2  | tri_sequence     | Remettre les étapes dans l'ordre   | Absorption,Réaction,Libération O2   | Absorption,Réaction,Libération O2 |  |
| 3  | question_ouverte | Rôle de la chlorophylle ?          |                                      |          | Pigment absorbant la lumière. |
```

| Colonne | Rôle |
|---|---|
| `id` | Identifiant de la question |
| `type` | `qcm_simple` \| `tri_sequence` \| `question_ouverte` |
| `question` | Texte de la question |
| `options` | Options séparées par virgule (vide pour `question_ouverte`) |
| `correcte` | Réponse correcte (alias accepté : `answer`) |
| `explication` | Explication affichée après réponse (alias accepté : `explanation`) |
| `seuil` | Optionnel, `question_ouverte` uniquement — seuil de validation IA (0-100) |

**Mapping `role=`** — si les colonnes n'ont pas ces noms exacts, chaque colonne du front matter
peut déclarer `role=id`, `role=type`, `role=question`, `role=options`, `role=answer` ou
`role=explanation` ; sans mapping, résolution par nom littéral (comportement ci-dessus).

```markdown
---
title: Quiz — La photosynthèse
type: quiz
qid: role=id
qtype: role=type
libelle: role=question
choix: role=options
bonne_reponse: role=answer
---
```

---

## 7. Modèles (templates)

Les modèles sont des fichiers `.table.md` standard placés dans le dossier `_templates/` à côté de `tabler.php`.

```
tabler.php
tabler.js
tabler.css
_templates/
  ├── contacts.table.md
  ├── products.table.md
  ├── tasks.table.md
  └── ...
```

Pour créer un nouveau modèle : créez un fichier `.table.md` avec le front matter et quelques lignes d'exemple, placez-le dans `_templates/`. Il apparaîtra automatiquement dans le sélecteur de modèles.

La ligne `#meta` est utilisée pour l'affichage dans le sélecteur :

```
#meta title="Mon modèle" icon=🎯
```

---

## 8. Compatibilité et interopérabilité

| Outil | Import | Export |
|-------|--------|--------|
| **ProAppli Tabler** | ✅ Natif | ✅ Natif |
| **Excel / LibreOffice** | ✅ Copier-coller | ✅ Export .xlsx |
| **Éditeur de texte** (VS Code, Notepad…) | ✅ Lecture/écriture directe | ✅ Fichier texte standard |
| **Git** | ✅ Versionnable (diff lisible) | ✅ Texte brut |
| **IA** (ChatGPT, Claude, Grok…) | ✅ Comprend le format nativement | ✅ Génère du Markdown table |
| **Marp** | ⚠️ Front matter différent | ⚠️ Tableau affiché sans types |

---

## 9. Règles importantes

- Le front matter est **optionnel**. Un fichier `.table.md` sans front matter est simplement un tableau Markdown classique — il fonctionnera mais sans typage ni formules.
- Les noms de colonnes dans le front matter **doivent correspondre exactement** aux en-têtes du tableau Markdown.
- Les colonnes présentes dans le tableau mais absentes du front matter sont traitées comme `type=text`.
- Les formules référencent les colonnes par leur **nom technique** (celui du front matter), pas par leur label.
- Les valeurs de cellules `formula` peuvent être laissées vides dans le fichier source : elles sont calculées à l'affichage.
- Le séparateur `| --- | --- |` doit contenir au moins un tiret par colonne.
