Créer un template avec le SDK v0.1.3
Workflow du développeur — de l'initialisation au bundle importable, avec React, Vue ou HTML/CSS/JS. L'API décrite est celle du SDK 0.1.0 livré.
Installez le SDK avec npm install @onirtech/template-sdk (ou npm install -D @onirtech/template-sdk pour le build), puis créez un projet avec npx @onirtech/create-onirtech-template aurora --stack react. ADR-0024 est Accepté.
Le SDK supprime le squelette de Custom Element : vous écrivez l'interface (React, Vue ou DOM natif) et le SDK gère le Shadow DOM, la propriété onirtechContext, le rendu à chaque mise à jour, les styles, les erreurs et le nettoyage.
1. À qui s'adresse cette page
| Profil | Ce que vous écrivez | Build |
|---|---|---|
| React | Composants .jsx + useTemplate() | Vite requis |
| Vue | SFC .vue + useTemplate() | Vite requis |
| Débutant | HTML / CSS / JavaScript avec h() | Aucun |
Il faut Node.js 18 ou plus et npm. Aucune connaissance des Custom Elements, du Shadow DOM ou du protocole hôte n'est nécessaire.
2. Le workflow en un coup d'œil
# SDK 0.1.2 — générateur inclus :
npx @onirtech/create-onirtech-template aurora --stack react # ou vue | vanilla
cd aurora
npm install
npm run dev # développement local (hôte simulé)
npm test # tests unitaires du template
npm run build # React / Vue / TypeScript uniquement
npm run validate # manifeste + entrée + intégrité
npm run pack # archive .onirtech-template| Étape | Commande | Fonctions |
|---|---|---|
| Initialiser | npx @onirtech/create-onirtech-template … | échafaudage (manifeste, entrée, styles, tests) |
| Déclarer | — | defineManifest |
| Coder | — | defineReactTemplate + useTemplate / defineVueTemplate + useTemplate / defineTemplate + h + param + hasCapability |
| Développer | npm run dev | hôte simulé (harness) |
| Tester | npm test | createMockContext + mountTemplate |
| Construire | npm run build | defineTemplateConfig |
| Valider / empaqueter | npm run validate puis npm run pack | kit (manifeste, entrée, SRI) |
| Importer / rattacher | — | panneau Templates visuels + liens |
3. Initialiser le projet
Commande : npx @onirtech/create-onirtech-template aurora --stack reactFonctions : —
Remplacez react par vue ou vanilla. Le générateur crée un projet complet, avec le nom d'élément déjà cohérent entre le manifeste, l'entrée et les tests.
aurora/
├─ src/
│ ├─ index.js entrée : enregistre le Custom Element
│ ├─ Aurora.jsx votre interface (React)
│ ├─ styles.css styles (inlinés dans le bundle)
│ └─ manifest.ts manifeste typé (defineManifest)
├─ tests/
│ └─ aurora.test.js tests unitaires du template
├─ vite.config.ts build correct pour un template
├─ package.json
└─ onirtech.template.json généré au build (React / Vue / TypeScript)Version débutant (--stack vanilla) : pas de vite.config.ts, aucun build, des styles en src/styles.js (constante CSS) et un onirtech.template.json à la place de manifest.ts.
aurora/
├─ src/
│ ├─ index.js entrée : defineTemplate(...)
│ ├─ ui.js fonctions de rendu (h, param)
│ └─ styles.js styles : export const CSS (chaîne, aucun build)
├─ tests/
│ └─ aurora.test.js
├─ onirtech.template.json manifeste (JSON, pas de build)
└─ package.json4. Déclarer le manifeste
Commande : —Fonctions : defineManifest
Le manifeste décrit le template : identité, version, nom du Custom Element, capacités (données reçues) et paramètres (variables réglables par lien). defineManifest remplit les valeurs fixes (schemaVersion, kind, network) et vérifie le reste à la compilation.
// src/manifest.ts
import { defineManifest } from "@onirtech/template-sdk";
export default defineManifest({
id: "aurora",
name: "Aurora",
description: "Chat et formulaire clairs, orientés conversion.",
version: "1.0.0",
publisher: { id: "onirtech", name: "ONIRTECH", trustLevel: "official" },
ui: {
mode: "web-component",
entry: "./dist/index.js",
element: "onirtech-template-aurora",
shadowDom: "open",
},
capabilities: ["messages", "theme"],
params: [
{
key: "titre",
label: "Titre",
description: "Titre affiché en haut du chat",
type: "string",
default: "Discutons de votre projet",
maxLength: 80,
},
],
});Version débutant : le même contenu, en JSON, dans onirtech.template.json (avec schemaVersion, kind, compatibility et network explicites).
{
"schemaVersion": 1,
"kind": "template",
"id": "aurora",
"name": "Aurora",
"description": "Chat et formulaire clairs, orientés conversion.",
"version": "1.0.0",
"publisher": { "id": "onirtech", "name": "ONIRTECH", "trustLevel": "official" },
"compatibility": { "hostProtocol": "onirtech.template-host.v1" },
"ui": {
"mode": "web-component",
"entry": "./index.js",
"element": "onirtech-template-aurora",
"shadowDom": "open"
},
"capabilities": ["messages", "theme"],
"network": [],
"params": [
{
"key": "titre",
"label": "Titre",
"description": "Titre affiché en haut du chat",
"type": "string",
"default": "Discutons de votre projet",
"maxLength": 80
}
]
}ui.elementdoit être identique au nom passé àdefineTemplate/defineReactTemplate/defineVueTemplate.params[].defaultest obligatoire : c'est le repli quand l'exploitant ne renseigne rien.- Une capacité non déclarée ⇒ la donnée correspondante est absente/ vide.
5. Construire l'interface
Commande : —Fonctions : defineTemplate / useTemplate
Les trois stacks lisent le même contexte. En React et Vue, useTemplate() renvoie phase, messages, params, capabilities et actions ; en vanilla, le contexte complet arrive dans la fonction de rendu.
React
Commande : aucune (édition) · Fonctions : defineReactTemplate, useTemplate
// src/Aurora.jsx
import { useState } from "react";
import { useTemplate } from "@onirtech/template-sdk/react";
export function Aurora() {
const { phase, messages, params, actions } = useTemplate();
const [draft, setDraft] = useState("");
if (phase === "identity") {
return (
<form
onSubmit={(event) => {
event.preventDefault();
const data = new FormData(event.currentTarget);
actions.submitIdentity({
nom: String(data.get("nom") ?? ""),
email: String(data.get("email") ?? ""),
consentement: data.get("consentement") === "on",
});
}}
>
<h2>{params.titre}</h2>
<input name="nom" placeholder="Votre nom" aria-label="Nom" required />
<input name="email" type="email" placeholder="vous@exemple.mg" aria-label="Email" required />
<label>
<input name="consentement" type="checkbox" /> J'accepte le traitement de mes données
</label>
<button type="submit">Démarrer la discussion</button>
</form>
);
}
return (
<section>
<h2>{params.titre}</h2>
<ul>
{messages.map((message) => (
<li key={message.id} className={message.role}>{message.content}</li>
))}
</ul>
<form
onSubmit={(event) => {
event.preventDefault();
if (draft.trim() === "") return;
actions.sendMessage(draft.trim());
setDraft("");
}}
>
<input
value={draft}
onChange={(event) => setDraft(event.target.value)}
placeholder="Votre message…"
aria-label="Message"
/>
<button type="submit">Envoyer</button>
</form>
</section>
);
}// src/index.js
import css from "./styles.css?inline";
import { defineReactTemplate } from "@onirtech/template-sdk/react";
import { Aurora } from "./Aurora";
defineReactTemplate("onirtech-template-aurora", Aurora, { css });import css from "./styles.css?inline" est indispensable : le CSS doit être une chaîne injectée dans le Shadow DOM (le build Vite l'inlinera).
Vue
Commande : aucune (édition) · Fonctions : defineVueTemplate, useTemplate
<!-- src/Aurora.vue -->
<script setup>
import { ref } from "vue";
import { useTemplate } from "@onirtech/template-sdk/vue";
const { phase, messages, params, actions } = useTemplate();
const draft = ref("");
function submitIdentity(event) {
const data = new FormData(event.currentTarget);
actions.submitIdentity({
nom: String(data.get("nom") ?? ""),
email: String(data.get("email") ?? ""),
consentement: data.get("consentement") === "on",
});
}
function sendMessage() {
if (draft.value.trim() === "") return;
actions.sendMessage(draft.value.trim());
draft.value = "";
}
</script>
<template>
<h2>{{ params.titre }}</h2>
<form v-if="phase === 'identity'" @submit.prevent="submitIdentity">
<input name="nom" placeholder="Votre nom" aria-label="Nom" required />
<input name="email" type="email" placeholder="vous@exemple.mg" aria-label="Email" required />
<label>
<input name="consentement" type="checkbox" /> J'accepte le traitement de mes données
</label>
<button type="submit">Démarrer la discussion</button>
</form>
<section v-else>
<ul>
<li v-for="message in messages" :key="message.id" :class="message.role">
{{ message.content }}
</li>
</ul>
<form @submit.prevent="sendMessage">
<input v-model="draft" placeholder="Votre message…" aria-label="Message" />
<button type="submit">Envoyer</button>
</form>
</section>
</template>// src/index.js
import css from "./styles.css?inline";
import { defineVueTemplate } from "@onirtech/template-sdk/vue";
import Aurora from "./Aurora.vue";
defineVueTemplate("onirtech-template-aurora", Aurora, { css });Débutant — HTML / CSS / JavaScript
Commande : aucune (édition) · Fonctions : defineTemplate, h, param, hasCapability
// src/index.js — aucun build
import { defineTemplate, h, param, hasCapability } from "@onirtech/template-sdk";
function identity(ctx) {
const nom = h("input", { name: "nom", placeholder: "Votre nom", "aria-label": "Nom" });
const email = h("input", { name: "email", type: "email", placeholder: "vous@exemple.mg", "aria-label": "Email" });
const consentement = h("input", { name: "consentement", type: "checkbox" });
return h("form", {
onsubmit: (event) => {
event.preventDefault();
ctx.actions.submitIdentity({
nom: nom.value.trim(),
email: email.value.trim(),
consentement: consentement.checked,
});
},
},
h("h2", { text: param(ctx, "titre", "Discutons de votre projet") }),
nom,
email,
h("label", {}, consentement, " J'accepte le traitement de mes données"),
h("button", { type: "submit", text: "Démarrer la discussion" }),
);
}
function chat(ctx) {
const champ = h("input", { placeholder: "Votre message…", "aria-label": "Message" });
const envoyer = () => {
if (champ.value.trim() === "") return;
ctx.actions.sendMessage(champ.value.trim());
champ.value = "";
};
return h("section", {},
h("h2", { text: param(ctx, "titre", "Discutons de votre projet") }),
hasCapability(ctx, "media")
? h("p", { text: "Photos et liens disponibles" })
: null,
h("ul", {}, ...ctx.state.messages.map((message) =>
h("li", { class: message.role, text: message.content }))),
ctx.state.typing ? h("p", { text: "…" }) : null,
h("div", {}, champ, h("button", { type: "button", text: "Envoyer", onclick: envoyer })),
);
}
defineTemplate(
"onirtech-template-aurora",
(ctx) => (ctx.state.phase === "identity" ? identity(ctx) : chat(ctx)),
{ css: `
:host { display: block; font-family: system-ui, sans-serif; }
ul { list-style: none; padding: 0; }
li.user { background: #e2e8f0; }
` },
);En vanilla, les styles vivent dans src/styles.js : le module exporte une constante CSS passée à l'option css. Aucun build n'intervient pour transformer un fichier .css.
h(tag, props, ...children): crée un nœud.text= texte (jamais de HTML),onclick= écouteur, le reste = attributs.param(ctx, "titre", défaut): lit une variable avec repli.hasCapability(ctx, "media"): teste une capacité avant de lire la donnée.
6. Développer en local
Commande : npm run devFonctions : hôte simulé (harness)
Le harness implémente le côté hôte avec des données MOCK : aucune base, aucun lien, tout est hors-ligne. Pour React/Vue/TypeScript, il lance aussi le build en --watch.
http://localhost:4500— le template dans un faux lien.?phase=chat— saute le formulaire, va directement à la discussion.?consent=0— lien sans consentement.- Après un rebuild, rechargez la page (pas de live-reload).
Ouvrez les DevTools et inspectez le Shadow DOM pour vérifier le rendu. Les actions sont simulées : submitIdentity passe en phase chat, une réponse mock arrive,finish clôt la discussion.
7. Tester
Commande : npm testFonctions : createMockContext, mountTemplate
Le scaffolder génère Vitest + happy-dom. Les deux helpers évitent de lancer le harness dans les tests.
// tests/aurora.test.js
import { expect, it } from "vitest";
import { createMockContext, mountTemplate } from "@onirtech/template-sdk/testing";
import "../src/index.js";
it("affiche le titre du paramètre", () => {
const ctx = createMockContext({ params: { titre: "Album souvenirs" } });
const { el } = mountTemplate("onirtech-template-aurora", ctx);
expect(el.shadowRoot.textContent).toContain("Album souvenirs");
});
it("passe du formulaire au chat", () => {
const ctx = createMockContext();
const { el, update } = mountTemplate("onirtech-template-aurora", ctx);
update(createMockContext({
state: { phase: "chat", messages: [{ role: "assistant", content: "Bonjour" }] },
}));
expect(el.shadowRoot.textContent).toContain("Bonjour");
});createMockContext(overrides)produit un contexte complet et conforme (identité ou chat, messages, médias, params).mountTemplate(element, ctx)monte le vrai élément et exposeupdate(ctx)pour simuler une nouvelle assignation du contexte.
Vérifiez au minimum : les deux phases, le consentement, les messages vides, un texte long et chaque paramètre.
8. Construire le bundle
Commande : npm run buildFonctions : defineTemplateConfig
Étape nécessaire pour React, Vue et TypeScript ; inutile en vanilla. La configuration est générée par le scaffolder :
// vite.config.ts
import { defineTemplateConfig } from "@onirtech/template-sdk/vite";
export default defineTemplateConfig({
entry: "src/index.ts",
element: "onirtech-template-aurora",
manifest: "src/manifest.ts",
});React et Vue passent leurs plugins de compilation via l'option additive plugins (@vitejs/plugin-react, @vitejs/plugin-vue) : defineTemplateConfig({ entry, element, manifest, plugins: [react()] }). vite reste une peer dependency optionnelle.
| Ce que la configuration impose | Pourquoi |
|---|---|
| un seul fichier ESM | le manifeste pointe une entrée unique |
CSS non séparé (cssCodeSplit: false) | les styles voyagent dans le JS du Shadow DOM |
| assets inlinés | aucune requête externe non déclarée |
target: es2022 | Custom Elements et syntaxes modernes |
| React/Vue embarqués une seule fois | pas de dépendance fournie par l'hôte |
Résultat : dist/index.js (+ onirtech.template.json émis depuismanifest.ts). Vérifiez le poids : gzip -c dist/index.js | wc -c.
9. Valider, empaqueter, importer
Commande : npm run validate puis npm run packFonctions : kit (manifeste, entrée, intégrité SRI)
npm run validatevérifie le manifeste, l'entrée, le nom d'élément et les paramètres.npm run packproduitrelease/aurora-1.0.0.onirtech-template(intégrité incluse).- Dans le dashboard : Templates visuels → importer l'archive.
- Sur le lien : choisir le template et la version, renseigner les paramètres, puis Aperçu avec ces valeurs.
- Pour une mise à jour : incrémenter
version, revalider, repackager, réimporter et sélectionner la nouvelle version sur le lien.
10. Récapitulatif
| # | Processus | Commande | Fonctions | Résultat |
|---|---|---|---|---|
| 1 | Initialiser | create-onirtech-templatelocal ; npx après publication npm | — | projet complet |
| 2 | Déclarer | — | defineManifest | manifeste typé |
| 3 | Coder | — | defineReactTemplate + useTemplatedefineVueTemplate + useTemplatedefineTemplate + h + param + hasCapability | interface |
| 4 | Développer | npm run dev | harness | aperçu local |
| 5 | Tester | npm test | createMockContext + mountTemplate | tests verts |
| 6 | Construire | npm run build | defineTemplateConfig | dist/index.js |
| 7 | Valider | npm run validate | kit | conformité |
| 8 | Empaqueter | npm run pack | kit | archive |
| 9 | Importer | dashboard | — | version installée |
| 10 | Rattacher | dashboard | — | template publié |
11. Dépannage
- L'élément ne s'affiche pas.
- Vérifiez que
ui.elementest exactement le nom passé àdefineTemplate(même kebab-case), et que l'entrée est biendist/index.jsaprès un build. - Mes styles ne s'appliquent pas.
- Le CSS doit être importé en
?inlinepuis passé à l'optioncss; un fichier CSS séparé n'est pas chargé dans le Shadow DOM. params.titreestundefined.- Le paramètre n'est pas déclaré dans le manifeste, ou le harness ne fournit pas de valeurs : utilisez
param(ctx, "titre", défaut). - React lève « Invalid hook call ».
- Deux copies de React sont chargées : vérifiez que React n'est pas externalisé et qu'une seule version est installée.
- Le template change mais le lien ne change pas.
- Une version publiée est immuable : incrémentez
version, repackagez, réimportez et sélectionnez la nouvelle version sur le lien. - Une donnée du contexte est vide.
- Déclarez la capacité correspondante (
messages,media,modelForm,theme,userState,knowledge,attachments).
12. Références
- ADR-0024 — SDK JavaScript des templates visuels (API complète).
- Contrat des templates visuels — manifeste, contexte, actions, paramètres.
- ADR-0019 (packages UI-only), ADR-0023 (variables paramétrables).
- Norme
docs/templates-visuels/norme-v1.md.
Le guide Your first package vous accompagne de la création du projet à l'import dans le panneau, avec du code vérifié à chaque étape.
Les actions d'un template détaillent l'identité, l'envoi, la fin et les ressources : ce que l'hôte exécute à chaque appel, en mock et en production.
Le skill onirtech-template-sdk fournit à l'agent les règles, l'API réelle, les vérifications et les pièges connus pour créer un template de bout en bout.