De TOML-editor in je eigen website insluiten
Voor ontwikkelaars: zo zet je de TOML-editor in je eigen pagina - in een iframe, in een eigen venster of als viewer die alleen leest - en zo gaan documenten, opgeslagen versies en resultaten van een AI heen en weer tussen de editor en je server. Je hebt geen registratie en geen sleutel van ons nodig.
Elke https-pagina (en localhost) kan de editor insluiten met een <iframe> en ?embed=1 - geen registratie, geen sleutel. Ingesloten verbergt de editor de paginatekst en toont hij een knop Opslaan. Er zijn vier manieren om het document erin en eruit te krijgen.
1. Een bestand openen via een link, opslaan op je server
src= opent een bestand (je server moet CORS toestaan), webhook= zorgt dat "Opslaan" (Ctrl+S) het document naar dat adres stuurt. Voeg &auto toe om na elke wijziging op te slaan.
<iframe src="https://toml.giit.ch/nl/?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>
De webhook krijgt een POST met JSON { editor: "toml", event: "save", id, name, content, updated }. Een link bevat geen token (dat zou in de serverlogs en de browsergeschiedenis terechtkomen). Een webhook in het adres is daarom geschikt voor een opslagadres dat geen token nodig heeft, bijvoorbeeld op localhost of in je intranet. Geef voor een openbare server de webhook met een token door in tomleditor:load (hook: { url, token }) en controleer dat token, zoals dit kleine PHP-script doet:
<?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. Je pagina beheert het document (postMessage)
De editor zegt tomleditor:ready, je pagina antwoordt met tomleditor:load, de editor meldt tomleditor:loaded, tomleditor:change bij echte bewerkingen en tomleditor:save wanneer de gebruiker opslaat. Je pagina mag tomleditor:get (de huidige toestand) of tomleditor:save (op Opslaan drukken) sturen. Opslaan loopt dan via je eigen login en sessie.
<iframe id="ed" src="https://toml.giit.ch/nl/?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') { // de editor is klaar: geef hem het 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'; // een echte bewerking
if (m.type === 'tomleditor:save') { // de gebruiker klikte op "Opslaan"
await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: m.name, content: m.content }) });
document.title = 'Saved';
}
});
// Een opslaanknop op je eigen pagina: het antwoord komt als tomleditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'tomleditor:get' }, ORIGIN); }
</script>
AI-assistent binnen je pagina: die werkt ook in het frame. Met allow="clipboard-write" op het iframe gebruikt "Prompt kopiëren" direct het klembord (anders valt de editor terug op het oude kopieercommando, of toont hij de prompt om met de hand te kopiëren). Een link die de AI stuurt, kan een editor binnen een andere website niet bereiken, dus vraagt de editor de AI en de gebruiker om in plaats daarvan het hele antwoord van de AI in de editor te plakken - of de editor in een eigen venster te openen.
3. In een eigen venster
window.open("https://toml.giit.ch/nl/?embed=window", "tomleditor-17") opent de volledige editor in een eigen venster en spreekt dezelfde berichten met window.opener. Links van een AI bereiken hem daar. tomleditor:load kan ook context: { host, title, rules } meegeven - de AI-prompt zegt dan waar het document thuishoort en volgt je regels - en hook: { url, token }, een webhook alleen voor dit document die nooit wordt opgeslagen. Als het venster met niet-opgeslagen wijzigingen wordt gesloten, stuurt het tomleditor:closing.
4. AI via je eigen server
Geef de editor je eigen prompt, een adres op je site en een sleutel voor deze editorsessie. Het AI-venster toont dan "Verbonden met" je site, wat je de AI vertelt, en de keuze het huidige document wijzigen of een nieuw maken; Prompt kopiëren zet alles bij elkaar. De AI post het resultaat met de sleutel naar je server, en je pagina opent het in de editor - een nieuw document opent apart en blijft in de browser van de gebruiker. Eén sleutel per editorsessie: hij blijft geldig zolang de editor open is, en alleen zolang de editor er geen heeft, vraagt hij erom met tomleditor:key-request. De editor verstuurt de sleutel nooit zelf en slaat hem nooit op; het adres moet https zijn op je eigen host of een subdomein ervan.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // één sleutel per editorsessie
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);
// onthoud de id uit tomleditor:loaded en negeer change/save/closing met een andere id
// alleen zolang de editor geen sleutel heeft:
if (m.type === 'tomleditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'tomleditor:ai', key: k.key }, ORIGIN); }
// zolang de editor open is, meld het elke minuut aan je server; het sluiten van de editor of de pagina maakt de sleutel ongeldig
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// je server heeft een resultaat (polling, SSE …): target is "current" of "new"
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content: draft, from: 'ai', target }, ORIGIN);
Maak sleutels met minstens 128 willekeurige bits, sla alleen hun hash op, koppel elke sleutel aan de ingelogde gebruiker, het document en deze editorsessie, maak er één per sessie, houd hem alleen geldig zolang je pagina de sessie als open meldt, en maak hem ongeldig wanneer de editor of de pagina sluit.
Het hele protocol staat in llms.txt - geef het aan een AI en die bouwt de koppeling voor je systeem. De andere giit-editors werken op dezelfde manier met hun eigen voorvoegsel - editors.giit.ch.
Een document alleen-lezen tonen (viewer)
?viewer=1 toont alleen het afgewerkte document: geen code, geen gereedschap, er kan niets worden gewijzigd en er wordt niets in de browser opgeslagen. Het document komt via een link (#b64=, ?src=), van je pagina met tomleditor:load of via POST als normaal formulier naar post.php: het veld content als tekst of b64 als base64, optioneel name, maximaal 20 MB tekst.
<form method="post" action="https://toml.giit.ch/post.php?viewer=1" target="_blank">
<input type="hidden" name="lang" value="nl">
<input type="hidden" name="name" value="Report">
<input type="hidden" name="b64" value="…">
<button>Open</button>
</form>
Zonder viewer=1 opent het document in de normale editor. In beide gevallen blijft het alleen in het geheugen: het wordt niet op de server opgeslagen en komt niet bij "Mijn documenten". Direct geopend meldt de viewer dat de inhoud van derden komt.