Éditeur TOML
Ouvrir l’éditeur

Intégrer l’éditeur TOML à votre site web

Pour les développeurs : comment placer l’éditeur TOML dans votre propre page - dans une iframe, dans une fenêtre à part ou en lecture seule - et comment les documents, les enregistrements et les résultats d’une IA circulent entre l’éditeur et votre serveur. Aucune inscription ni clé de notre part n’est nécessaire.

Toute page https (et localhost) peut intégrer l’éditeur avec une <iframe> et ?embed=1 - sans inscription, sans clé. Intégré, l’éditeur masque le texte de la page et affiche un bouton Enregistrer. Il y a quatre façons de faire entrer et sortir le document.

1. Ouvrir un fichier depuis un lien, enregistrer sur votre serveur

src= ouvre un fichier (votre serveur doit autoriser CORS), webhook= fait envoyer le document à cette adresse par « Enregistrer » (Ctrl+S). Ajoutez &auto pour enregistrer après chaque modification.

<iframe src="https://toml.giit.ch/fr/?embed=1&src=https://your-site.com/config/app.toml&webhook=https://your-site.com/save.php"
        allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>

Le webhook reçoit un POST avec du JSON { editor: "toml", event: "save", id, name, content, updated }. Un lien ne contient pas de jeton (il se retrouverait dans les journaux du serveur et l’historique du navigateur). Un webhook dans l’adresse convient donc à une adresse de sauvegarde qui n’en exige pas, par exemple sur localhost ou dans votre intranet. Pour un serveur public, transmettez le webhook avec un jeton dans tomleditor:load (hook: { url, token }) et vérifiez ce jeton, comme le fait ce petit script PHP :

<?php
header('Access-Control-Allow-Origin: https://toml.giit.ch');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
header('Access-Control-Allow-Methods: POST, OPTIONS');
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit;
if (($_SERVER['HTTP_AUTHORIZATION'] ?? '') !== 'Bearer SECRET') { http_response_code(403); exit; }
$d = json_decode(file_get_contents('php://input'), true);
$name = preg_replace('/[^\w\- ]/u', '_', $d['name']);
file_put_contents(__DIR__ . "/config/$name.toml", $d['content']);
echo '{"ok":true}';

2. Votre page détient le document (postMessage)

L’éditeur annonce tomleditor:ready, votre page répond par tomleditor:load, l’éditeur signale tomleditor:loaded, tomleditor:change lors de vraies modifications et tomleditor:save quand l’utilisateur enregistre. Votre page peut envoyer tomleditor:get (l’état actuel) ou tomleditor:save (appuyer sur Enregistrer). L’enregistrement passe alors par votre propre connexion et votre propre session.

<iframe id="ed" src="https://toml.giit.ch/fr/?embed=1" allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
<script>
const ed = document.getElementById('ed');
const ORIGIN = 'https://toml.giit.ch';

addEventListener('message', async e => {
  if (e.source !== ed.contentWindow || e.origin !== ORIGIN) return;
  const m = e.data;
  if (m.type === 'tomleditor:ready') {              // l’éditeur est prêt : on lui transmet le document
    ed.contentWindow.postMessage({
      type: 'tomleditor:load',
      name: 'Settings',
      content: await (await fetch('/config/app.toml')).text()
    }, ORIGIN);
  }
  if (m.type === 'tomleditor:change') document.title = '● not saved';   // une vraie modification
  if (m.type === 'tomleditor:save') {               // l’utilisateur a cliqué sur "Save"
    await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: m.name, content: m.content }) });
    document.title = 'Saved';
  }
});

// Un bouton d’enregistrement sur votre propre page : la réponse arrive sous forme de tomleditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'tomleditor:get' }, ORIGIN); }
</script>

L’assistant IA dans votre page : il fonctionne aussi dans le cadre. Avec allow="clipboard-write" sur l’iframe, « Copier le prompt » utilise directement le presse-papiers (sinon l’éditeur se rabat sur l’ancienne commande de copie, ou affiche le prompt à copier à la main). Un lien envoyé par l’IA ne peut pas atteindre un éditeur intégré dans un autre site web : l’éditeur demande donc à l’IA et à l’utilisateur de coller toute la réponse de l’IA dans l’éditeur - ou d’ouvrir l’éditeur dans sa propre fenêtre.

3. Dans sa propre fenêtre

window.open("https://toml.giit.ch/fr/?embed=window", "tomleditor-17") ouvre l’éditeur complet dans sa propre fenêtre et échange les mêmes messages avec window.opener. Les liens d’une IA l’y atteignent. tomleditor:load peut aussi transporter context: { host, title, rules } - le prompt de l’IA indique alors où se trouve le document et suit vos règles - et hook: { url, token }, un webhook pour ce seul document, jamais stocké. Fermer la fenêtre avec des modifications non enregistrées envoie tomleditor:closing.

4. L’IA via votre propre serveur

Transmettez à l’éditeur votre propre prompt, une adresse sur votre site et une clé pour cette session de l’éditeur. La boîte de dialogue de l’IA affiche alors « Connecté à » votre site, ce que vous indiquez à l’IA, et le choix modifier le document actuel ou en créer un nouveau ; Copier le prompt assemble le tout. L’IA envoie son résultat avec la clé à votre serveur, et votre page l’ouvre dans l’éditeur - un nouveau document s’ouvre séparément et reste dans le navigateur de l’utilisateur. Une clé par session de l’éditeur : elle reste valable tant que l’éditeur est ouvert, et l’éditeur ne la demande avec tomleditor:key-request que tant qu’il n’en a pas. L’éditeur n’envoie jamais la clé lui-même et ne la stocke jamais ; l’adresse doit être en https sur votre propre hôte ou sur un de ses sous-domaines.

const key = async () => (await fetch('/api/ai-key.php?doc=17')).json();   // une clé par session de l’éditeur
const k = await key();
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content, ai: {
  label: 'My Site', prompt: 'Keep every key and every comment.',
  endpoint: 'https://your-site.com/api/ai-result.php', key: k.key } }, ORIGIN);
// retenez l’id de tomleditor:loaded et ignorez change/save/closing portant un autre id
// seulement tant que l’éditeur n’a pas de clé :
if (m.type === 'tomleditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'tomleditor:ai', key: k.key }, ORIGIN); }
// tant que l’éditeur est ouvert, prévenez votre serveur chaque minute ; fermer l’éditeur ou la page annule la clé
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// votre serveur a un résultat (polling, SSE …) : target vaut "current" ou "new"
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content: draft, from: 'ai', target }, ORIGIN);

Créez des clés d’au moins 128 bits aléatoires, ne stockez que leur hash, liez chacune à l’utilisateur connecté, au document et à cette session de l’éditeur, créez-en une par session, ne la gardez valable que tant que votre page signale la session comme ouverte, et annulez-la quand l’éditeur ou la page se ferme.

Le protocole complet se trouve dans llms.txt - donnez-le à une IA et elle construit la connexion pour votre système. Les autres éditeurs giit fonctionnent de la même façon, chacun avec son propre préfixe - editors.giit.ch.

Afficher un document en lecture seule (visionneuse)

?viewer=1 affiche uniquement le document fini : pas de code, pas d’outils, rien ne peut être modifié et rien n’est enregistré dans le navigateur. Le document vient d’un lien (#b64=, ?src=), de votre page avec tomleditor:load, ou par POST sous forme de formulaire normal vers post.php : le champ content en texte ou b64 en base64, éventuellement name, 20 Mo de texte au maximum.

<form method="post" action="https://toml.giit.ch/post.php?viewer=1" target="_blank">
  <input type="hidden" name="lang" value="fr">
  <input type="hidden" name="name" value="Report">
  <input type="hidden" name="b64" value="…">
  <button>Open</button>
</form>

Sans viewer=1, le document s’ouvre dans l’éditeur normal. Dans les deux cas, il reste uniquement en mémoire : il n’est pas enregistré sur le serveur et ne rejoint pas « Mes documents ». Ouverte directement, la visionneuse indique que le contenu provient d’un tiers.