# Flowora Atelier — kit source de nœuds personnalisés

Un exemple réutilisable **MIT**, destiné à **`@newvexa/flowora@0.1.0-beta.1`**. Il ajoute trois presets de données et trois composants Angular standalone. Le style est original : ivoire `#f7f4ed`, orange `#c45c35`, graphite `#272b2b`, sauge `#3e7268`.

**Ces composants ne sont pas exportés par le paquet npm Flowora beta.1.** Ce dossier est un **kit d’exemple à copier depuis les sources**, pas une nouvelle bibliothèque publiée. Le manifeste `private: true` sert uniquement à compiler et tester l’exemple. Aucun paquet Flowora, site ou autre produit n’est modifié par ce kit.

## Les trois presets

| Type | Composant Angular | Champs | Ports |
| --- | --- | --- | --- |
| `atelier-note` | `FloworaNoteNodeComponent` | `text` : texte brut | Aucun |
| `atelier-http` | `FloworaHttpNodeComponent` | `method`, `url` | Entrée `in`, sortie `out`, type `flow` |
| `atelier-decision` | `FloworaDecisionNodeComponent` | `expression` : chaîne stockée | Entrée `in`, sorties `true` / `false`, type `flow` |

Les cartes sont conçues pour les dimensions stock **220 × 112**. Le type et le nom sont à l’intérieur de la carte ; la zone supérieure droite reste libre pour le bouton de propriétés de Flowora. Les noms longs sont tronqués visuellement, pas dans le document.

**Il n’y a aucun moteur d’exécution.** Aucune requête HTTP, aucun traitement d’expression, aucun rendu HTML du texte, aucun champ d’authentification ou injection de secret. Les exemples utilisent uniquement des valeurs fictives et `https://example.invalid/atelier`.

## Copier et intégrer les sources

Votre application Angular doit déjà consommer `@newvexa/flowora@0.1.0-beta.1`, avec ses dépendances Angular compatibles. Copiez `src/` dans un dossier de l’application, en conservant les fichiers CSS. Copiez éventuellement `demo/` à côté de ce dossier ; ajustez les imports et chemins relatifs si vous le renommez.

- **Entrée Angular** : `src/index.ts`, composants + directive de contexte + helpers.
- **Entrée portable** : `src/catalog.ts`, sans import Angular, DOM ou API Node.
- **Intégration complète compilée** : `demo/atelier-demo.component.ts`, `.html`, `.css`.

Les imports relatifs se terminent par `.js`, comme l’API publiée ; TypeScript les résout vers les sources `.ts` lors de la compilation. L’application hôte compile ces sources avec sa chaîne Angular habituelle. Les artefacts partiels produits par la validation locale ne constituent **pas** un paquet Angular distributable ; l’hôte effectue sa compilation / son linking Angular normal.

Exemple portable, à adapter au chemin du dossier copié :

```ts
import { Editor, NODE_CATALOG } from '@newvexa/flowora';
import {
  combineAtelierCatalog, createCustomDemoDocument, createAtelierEditor,
} from './atelier-nodes/catalog.js';

// La fabrique combine par défaut NODE_CATALOG et les trois presets Atelier.
const editor = createAtelierEditor();

// Équivalent explicite, utile pour votre chaîne restauration/import :
const catalog = combineAtelierCatalog(NODE_CATALOG);
const restoredEditor = new Editor(createCustomDemoDocument(), { catalog });
restoredEditor.loadJSON(editor.toJSON());
```

Créer **une instance stable d’`Editor` par document**, pas une instance à chaque rendu. Le composant hôte de démonstration appelle `dispose()` lorsqu’il est détruit. Il faut donner une hauteur explicite au conteneur du renderer.

Pour ajouter des presets propres à l’hôte, passez-les comme catalogue de base :

```ts
const editor = createAtelierEditor(document, { historyLimit: 64 }, hostBaseCatalog);
```

`hostBaseCatalog` doit contenir les builtins dont vous avez besoin, mais pas une seconde copie des trois types Atelier. `combineAtelierCatalog(base)` refuse les types dupliqués au lieu d’écraser silencieusement un preset.

## Template Angular et écriture par le store

Le fichier `demo/atelier-demo.component.html` montre un `@switch` explicite, les trois composants et une branche `@default`. La directive `FloworaNodeTemplateDirective` fournit le contexte `NodeTemplateContext` au vérificateur `strictTemplates` : `node` est un `EditorNode`, `selected` un `boolean`, `preset` un `NodePreset | undefined`.

```html
<flowora-editor [editor]="editor" [nodeTemplate]="face.template" />
<ng-template floworaNodeTemplate #face="floworaNodeTemplate"
  let-node let-selected="selected">
  @switch (node.type) {
    @case ('atelier-note') {
      <flowora-note-node [node]="node" [selected]="selected"
        (patch)="editor.updateNode(node.id, $event)" />
    }
    @case ('atelier-http') {
      <flowora-http-node [node]="node" [selected]="selected"
        (patch)="editor.updateNode(node.id, $event)" />
    }
    @case ('atelier-decision') {
      <flowora-decision-node [node]="node" [selected]="selected"
        (patch)="editor.updateNode(node.id, $event)" />
    }
    @default {
      <div class="votre-face-de-repli">{{ node.name }}</div>
    }
  }
</ng-template>
```

Ce fragment montre la liaison minimale. **Préférez la démo complète** : son repli affiche le nom, les libellés du preset, le type, les branches et `builtinConfigPreview(node)` sous forme de texte court. Le helper ne sérialise jamais une configuration complète ; il choisit quelques champs textuels connus des builtins et renvoie un message générique pour les types inconnus. Son aperçu est limité à 80 caractères. Le nom et l’aperçu passent uniquement par l’interpolation Angular, jamais par `innerHTML`.

La démo utilise un callback hôte `updateNode(node, patch)` qui appelle exactement `editor.updateNode(node.id, patch)` et affiche les erreurs du store, plutôt que de laisser une exception remonter au template.

### Piège important : remplacement global des faces

`[nodeTemplate]` remplace **toutes les faces non-groupes**. Flowora beta.1 ne fournit pas une fonction pour retomber depuis votre `@default` sur son template interne. Une branche de repli vide rendrait les nœuds standard vides. Le repli de la démo recrée une présentation lisible ; il ne prétend pas reproduire exactement leurs icônes ou leur HTML interne.

Les ports, les connexions, la sélection, le bouton de propriétés, le déplacement et le double-clic restent gérés par `FloworaEditorComponent`. **Ne dupliquez pas les ports dans vos composants.** Les groupes conservent le renderer spécifique de Flowora.

Seuls les wrappers des contrôles portent `data-flowora-no-drag` et arrêtent `pointerdown`, `keydown` et `dblclick`. Le double-clic dans un champ sélectionne son texte sans ouvrir l’inspecteur du canvas. Le fond et l’en-tête ne bloquent aucun événement : ils restent déplaçables et leur double-clic ouvre les propriétés. Les contrôles ne bloquent pas les actions par défaut du navigateur ; la saisie, la sélection de texte et Tab restent disponibles. Les touches saisies dans un contrôle ne déclenchent pas la suppression ou le déplacement du nœud par le canevas.

## Contrat des patches

Les trois composants exposent les mêmes propriétés explicites :

```ts
@Input({ required: true }) node!: EditorNode;
@Input() selected: boolean = false;
@Output() readonly patch: EventEmitter<NodePatch> = new EventEmitter<NodePatch>();
```

Ils sont standalone et utilisent `ChangeDetectionStrategy.OnPush`. Ils n’ont pas accès à une instance de store et n’écrivent jamais dans un snapshot. La validation et l’émission ont lieu sur `change` : une édition validée est une commande annulable, pas une requête réseau à chaque caractère.

Dans Flowora beta.1, `updateNode(id, { config })` **remplace** la configuration : il ne fusionne pas automatiquement ses champs. `noteTextPatch`, `httpConfigPatch` et `decisionExpressionPatch` renvoient donc une configuration complète `{ ...node.config, champModifié }`. Ils préservent notamment les champs JSON inconnus appartenant à l’hôte.

Une erreur de validation survient avant l’émission / l’écriture. L’hôte doit appliquer immédiatement le patch au nœud actuel, via `updateNode`. Un patch préparé depuis un snapshot obsolète n’est pas un mécanisme de fusion concurrente : pour des écritures asynchrones, reconstruisez le patch à partir du nœud courant.

### URL et méthodes HTTP

- Méthodes autorisées : `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, en majuscules. Le type public `HttpMethod` et le contrôle runtime suivent cette liste.
- L’entrée portable utilise `normalizeHttpUrlText` : politique textuelle conservative HTTP(S) absolue, longueur maximale 2048, sans espace interne, caractère de contrôle, antislash ni identifiants dans l’autorité. **Ce n’est pas un parseur URL complet.**
- Le fichier Angular `src/angular/http-url.ts` ajoute une analyse syntaxique avec `new URL`, vérifie le protocole `http:` / `https:`, le hostname et l’absence de username/password. Il n’utilise jamais `fetch`, DNS ou un transport réseau.
- Le composant conserve le brouillon d’URL localement. Une URL invalide déclenche un statut accessible et **aucun patch** : le document reste intact. À la réception d’un nœud restauré, les valeurs sont initialisées depuis sa configuration et vérifiées sans modifier le store.
- La validation ne prouve ni l’accessibilité ni la sécurité d’un service. L’application hôte est responsable de toute politique réseau, de l’authentification hors document, de l’anti-SSRF et de l’exécution éventuelle. Ne stockez pas de secrets ou jetons dans les URL du document.

La note et l’expression sont des chaînes, au maximum 2000 caractères. Une expression incomplète ou ressemblant à du code reste du texte : elle n’est ni compilée ni évaluée.

## Restauration, import et inspecteur

Les documents JSON conservent leurs types, ports et configurations personnalisés. Utilisez le **même catalogue étendu** pour l’instance initiale, la restauration et toute fonction qui remplace une instance `Editor` après import. `loadJSON()` sur l’instance configurée conserve son catalogue.

Le stock `new Editor()` accepte un type inconnu dans un document JSON validé. Cela ne l’ajoute **pas** à sa palette : `addFromCatalog('atelier-http', ...)` nécessite le catalogue Atelier. L’acceptation JSON ne garantit pas non plus une validation sémantique de votre configuration métier.

La démo **ne fournit pas `inspectorTemplate`**, afin de conserver l’inspecteur intégré et son comportement pour les builtins. Cet inspecteur peut éditer des valeurs JSON génériques : ses modifications et les imports peuvent donc contourner les règles métier des contrôles HTTP. Le composant HTTP signale une configuration restaurée invalide ; l’hôte doit valider les données selon ses besoins avant toute exécution. Le kit n’ajoute volontairement aucun moteur et aucun inspecteur global incomplet.

## Validation locale

Environnement vérifié : Angular **22.2.4**, TypeScript **6**, Node **22** et la vraie dépendance registry `@newvexa/flowora@0.1.0-beta.1`. La chaîne Angular installée utilise également Babel 8 ; le kit n’ajoute pas une étape Babel ou un bundle applicatif.

Dans le répertoire de développement de cette livraison, `node_modules` est uniquement un lien vers les dépendances déjà installées. Il n’est pas livré dans le ZIP. Pour reproduire la validation, utilisez vos dépendances équivalentes, puis :

```sh
npm run test
```

Cette commande effectue :

1. `ngc -p tsconfig.angular.json` : vrais composants et démo, `strictTemplates`, contrôles TypeScript stricts, émission partial-Ivy et déclarations `.d.ts`.
2. `tsc6 -p tsconfig.portable.json` : catalogue seul, **sans bibliothèque DOM**, sortie ESM séparée.
3. `node --test tests/*.test.mjs` : catalogue réel, helpers, store publié, préservation des champs, invalidité atomique, transactions, undo/redo historiques, import et roundtrip JSON.

Les tests Node importent le catalogue ESM portable et le helper URL sans décorateur, jamais les composants Angular partiellement compilés. Aucun contournement Angular JIT n’est utilisé. Les artefacts partial-Ivy et leurs types sont inspectés comme fichiers. Les assertions de structure sur les événements ne sont **pas** des essais interactifs navigateur ; le drag, le double-clic, le focus et le rendu final doivent être vérifiés dans l’application hôte. Aucun packaging npm, publication, déploiement ou résultat réseau n’est revendiqué.

Les sorties temporaires sont dans `.build/`. Le ZIP source exclut `.build/`, `node_modules`, les journaux et les secrets. Il contient les sources, les fichiers d’intégration, les tests, les tsconfigs, le manifeste privé et la licence MIT.
