# Editor — Face DSL Editor

Editeur visuel de cadrans (faces) pour l'app OBD Dashboard. Interface web Vue 3 + Canvas 2D.

## Stack

- **Vue 3** (CDN, pas de build step) — reactive data binding
- **Canvas 2D** — rendu temps reel du cadran
- **Tailwind CSS** (CDN) + `css/styles.css` custom
- **Pas de bundler** : tous les scripts sont charges directement dans `index.html`

## Structure des fichiers

```
editor/
├── index.html              # App Vue 3 (template + layout)
├── css/styles.css          # Styles custom (pas de Tailwind pour les composants internes)
├── js/
│   ├── dsl/
│   │   ├── parser.js       # Parsing DSL <-> objets JS (parseDSL, serializeFace)
│   │   ├── expr.js         # Evaluateur d'expressions (math, variables, conditions)
│   │   ├── presets.js      # Presets built-in (PRESETS array)
│   │   └── svg-import.js   # Import SVG → elements DSL (SVGImport.parse)
│   ├── renderer.js         # Rendu Canvas (renderFace, drawElement, getElementBounds)
│   └── editor/
│       ├── app.js          # App Vue : data, computed, canvas, preview, mount
│       ├── elements.js     # ElementMethods : CRUD elements, groupes, drag-drop calques
│       ├── sync.js         # SyncMethods : undo/redo, sync code, import/export, QR, localStorage
│       ├── props.js        # PropMethods : editeur de proprietes, @var, autocomplete
│       ├── types.js        # TYPE_DEFS : definitions des 21 primitives + categories
│       └── interactions.js # Events canvas : drag, click, clavier, context menu
└── docs/
    └── dsl-reference.md    # Reference complete du langage DSL
```

## Architecture

### Flux de donnees

```
Code DSL (textarea) <--bidi--> face object (Vue state) --> Canvas render
                                    |
                                    +--> Element thumbnails (32x32)
                                    +--> Preset thumbnails (80x80)
```

La synchronisation est bidirectionnelle :
- Modification visuelle (drag, props) → `syncCodeFromFace()` → met a jour le code
- Modification code → `syncFaceFromCode()` → met a jour le face object
- Flag `_syncing` empeche les boucles infinies

### Organisation des methodes Vue

Les methodes de l'app Vue sont reparties dans 4 fichiers (pattern "method bundles") :
- **app.js** : squelette Vue (data, computed, watch, mounted) + rendu canvas + preview + menu
- **elements.js** → `ElementMethods` : ajout/suppression/deplacement d'elements, groupes, drag-drop calques, arbre
- **sync.js** → `SyncMethods` : undo/redo, synchronisation code↔face, import/export, QR, localStorage
- **props.js** → `PropMethods` : editeur de proprietes, variables @var, autocomplete, saisie

Les bundles sont merges dans l'app via `methods: { ...ElementMethods, ...SyncMethods, ...PropMethods, ... }`.

### Objet face

```js
{
  name: 'string',
  author: 'string',
  bg: '#hex',
  vars: { '$name': 'expression', ... },
  elements: [
    { type: 'circle', args: ['50','50','40'], props: { fill: '#FF0000' }, _id: 1, _depth: 0 },
    { isConditional: true, condition: '$zone==danger', children: [...], _id: 2, _depth: 0 },
    { isGroup: true, name: 'Mon groupe', children: [...], _id: 3, _depth: 0 }
  ]
}
```

Proprietes internes (prefixees `_`) : `_id`, `_depth`, `_hidden` — pas serialisees.

### Undo/Redo

- `pushUndo()` : snapshot immediat (avant action destructive)
- `pushUndoDebounced()` : groupe les modifications rapides (props, vars) en un seul step
- Stack limitee a 50 entrees
- Ctrl+Z / Ctrl+Shift+Z (geres dans `interactions.js`)

### Rendu Canvas

Le renderer parcourt `face.elements`, resout les `@var` via `resolveCustomVars()`, puis dessine chaque element. Les groupes sont traverses recursivement (pass-through). Les elements avec `_hidden: true` sont ignores. Le systeme de coordonnees est en pourcentage (0-100) du canvas.

### Animation ($time)

La variable `$time` fournit le temps en secondes (float, modulo 3600). Elle est calculee dans `substituteVars()` via `performance.now()` (JS) / `System.nanoTime()` (Kotlin).

Quand un face utilise `$time`, une boucle d'animation 60fps est activee automatiquement :
- **JS** : `_faceUsesTime()` detecte `$time` dans le JSON, `_animLoop` via `requestAnimationFrame`
- **Kotlin** : `hasTimeVar` dans `GaugeView`, `postDelayed` Runnable a 16ms

### Primitives a args variables

Le type `shape` accepte un nombre variable de coordonnees (paires x y). Le parser DSL pousse generiquement les tokens non-prop dans `args`, sans limite. Dans l'editeur, `argDefs` (app.js) genere dynamiquement les champs pour les sommets supplementaires, et des boutons "+ Sommet" / "- Sommet" permettent l'ajout/suppression.

### Deplacement groupe / multi-selection

Le drag canvas et les fleches directionnelles fonctionnent pour :
- **Element unique** : comportement standard
- **Groupe selectionne** (via le panneau calques) : tous les enfants du groupe bougent ensemble
- **Multi-selection** (Ctrl/Shift+clic dans les calques) : tous les elements selectionnes bougent
Le code utilise `_collectDragTargets()` dans interactions.js pour resoudre les cibles (recursif dans les groupes).

### Import SVG

`svg-import.js` (SVGImport.parse) convertit un SVG en elements DSL :
- circle, ellipse, rect, line, polygon/polyline → primitives DSL directes
- path → linearisation des beziers/arcs en `shape` (sommets)
- g → `group`
- Gere les transforms (translate, rotate, scale, matrix)
- Coordonnees SVG normalisees dans le systeme 0-100% via viewBox

## Conventions

- **Coordonnees** : toujours en % du canvas (0-100), converties en pixels par `pct(val, size, ctx)`
- **Couleurs** : `#hex`, noms (`white`, `muted`, `$accent`, `$zone_color`)
- **Angles** : en degres, 0 = droite, sens horaire
- **Props** : cle:valeur sans espaces (`stroke:#FF0000`), sauf `bold`/`italic` (flags seuls)
- **Variables** : prefixees `$` (`$value`, `$progress`, `$my_var`)
- **IDs internes** : `_nextId` global, increment monotone
- **Arbre d'elements** : les conditionnels (`isConditional`) et groupes (`isGroup`) ont un tableau `children` — toute traversee doit gerer les deux

## Points d'attention

- **Pas de build step** : tout changement JS est immediat au reload
- **Ordre de chargement des scripts** dans index.html est critique : presets → parser → expr → renderer → types → interactions → elements → sync → props → app
- `TYPE_DEFS` dans types.js definit les props de chaque primitive — c'est la source de verite pour le panneau de proprietes
- Le renderer JS (`renderer.js`) doit rester en miroir exact de `FaceRenderer.kt` (Kotlin) — tout ajout de primitive ou de propriete doit etre fait dans les deux
- Les presets dans `presets.js` sont aussi utilises par l'app Android (`FaceStore.kt`) — garder la compatibilite
- `serializeFace()` / `parseDSL()` dans parser.js doivent rester en miroir de `FaceParser.kt`
- **Pas d'instruction `if` dans les expressions Vue** : utiliser des ternaires ou `&&` dans les `@event` handlers du template

## Ajout d'une nouvelle primitive

1. Ajouter l'entree dans `TYPE_DEFS` (types.js) : `cat`, `desc`, `icon`, `args`, `props`, `defaults`
2. Ajouter la fonction `drawXxx()` dans `renderer.js`
3. Ajouter le case dans `drawElement()` (renderer.js)
4. Ajouter dans `getElementBounds()` et `getPositionArgs()` (renderer.js)
5. Ajouter le case dans `FaceRenderer.kt` (Kotlin)
6. Tester dans l'editeur ET dans l'app Android
7. **Mettre a jour la documentation** : `editor/CLAUDE.md` et `editor/docs/dsl-reference.md`

## Regle de documentation

A chaque changement touchant le DSL (nouvelle primitive, nouvelle propriete, nouvelle variable, nouvelle fonction), **mettre a jour** :
- `editor/CLAUDE.md` — structure, conventions, points d'attention
- `editor/docs/dsl-reference.md` — reference complete du langage (syntaxe, proprietes, exemples)

Cela inclut : ajout/modification de primitives, de proprietes, de variables integrees ($xxx), de fonctions mathematiques, de couleurs nommees, ou de mecanismes (blink, animation, etc.).
