Editor de TOML
Abrir el editor

Integra el editor de TOML en tu sitio web

Para desarrolladores: cómo poner el editor de TOML en tu propia página (en un iframe, en una ventana propia o como visor de solo lectura) y cómo pasan los documentos, los guardados y los resultados de una IA entre el editor y tu servidor. No necesitas registro ni ninguna clave nuestra.

Cualquier página https (y localhost) puede integrar el editor con un <iframe> y ?embed=1, sin registro y sin clave. Integrado, el editor oculta el texto de la página y muestra un botón Guardar. Hay cuatro formas de meter y sacar el documento.

1. Abrir un archivo desde un enlace, guardar en tu servidor

src= abre un archivo (tu servidor debe permitir CORS), webhook= hace que «Guardar» (Ctrl+S) envíe el documento a esa dirección. Añade &auto para guardar después de cada cambio.

<iframe src="https://toml.giit.ch/es/?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>

El webhook recibe un POST con JSON { editor: "toml", event: "save", id, name, content, updated }. Un enlace no lleva token (acabaría en los registros del servidor y en el historial del navegador), por lo que un webhook en la dirección sirve para guardar en una dirección que no lo necesite, por ejemplo en localhost o en tu intranet. Para un servidor público, pasa el webhook con un token en tomleditor:load (hook: { url, token }) y compruébalo, como hace este pequeño 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. Tu página guarda el documento (postMessage)

El editor dice tomleditor:ready, tu página responde con tomleditor:load, el editor informa con tomleditor:loaded, con tomleditor:change en cada edición real y con tomleditor:save cuando el usuario guarda. Tu página puede enviar tomleditor:get (el estado actual) o tomleditor:save (pulsar Guardar). Así el guardado pasa por tu propio inicio de sesión y tu sesión.

<iframe id="ed" src="https://toml.giit.ch/es/?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') {              // el editor está listo: pásale el documento
    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';   // una edición real
  if (m.type === 'tomleditor:save') {               // el usuario hizo clic en «Guardar»
    await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: m.name, content: m.content }) });
    document.title = 'Saved';
  }
});

// Un botón de guardar en tu propia página: la respuesta llega como tomleditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'tomleditor:get' }, ORIGIN); }
</script>

Asistente de IA dentro de tu página: también funciona en el marco. Con allow="clipboard-write" en el iframe, «Copiar prompt» usa el portapapeles directamente (si no, el editor recurre al antiguo comando de copiar o muestra el prompt para copiarlo a mano). Un enlace que envía la IA no puede llegar a un editor dentro de otro sitio web, así que el editor indica a la IA y al usuario que peguen la respuesta completa de la IA en el editor, o que abran el editor en una ventana propia.

3. En una ventana propia

window.open("https://toml.giit.ch/es/?embed=window", "tomleditor-17") abre el editor completo en su propia ventana y usa los mismos mensajes con window.opener. Ahí sí le llegan los enlaces de una IA. tomleditor:load también puede llevar context: { host, title, rules } (el prompt de IA dice entonces a dónde pertenece el documento y sigue tus reglas) y hook: { url, token }, un webhook solo para este documento que nunca se guarda. Al cerrar la ventana con cambios sin guardar se envía tomleditor:closing.

4. IA a través de tu propio servidor

Pasa al editor tu propio prompt, una dirección en tu sitio y una clave para esta sesión del editor. El diálogo de IA muestra entonces «Conectado a» tu sitio, lo que le dices a la IA y la opción cambiar el documento actual o crear uno nuevo; Copiar prompt lo junta todo. La IA envía su resultado con la clave a tu servidor, y tu página lo abre en el editor: un documento nuevo se abre aparte y se queda en el navegador del usuario. Una clave por sesión del editor: sigue siendo válida mientras el editor esté abierto, y solo mientras no tenga ninguna el editor la pide con tomleditor:key-request. El editor nunca envía la clave por sí mismo ni la guarda; la dirección debe ser https en tu propio host o en un subdominio suyo.

const key = async () => (await fetch('/api/ai-key.php?doc=17')).json();   // una clave por sesión del editor
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);
// recuerda el id de tomleditor:loaded e ignora change/save/closing con otro id
// solo mientras el editor no tenga clave:
if (m.type === 'tomleditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'tomleditor:ai', key: k.key }, ORIGIN); }
// mientras el editor esté abierto, avisa a tu servidor cada minuto; cerrar el editor o la página anula la clave
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// tu servidor tiene un resultado (polling, SSE …): target es «current» o «new»
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content: draft, from: 'ai', target }, ORIGIN);

Crea claves con al menos 128 bits aleatorios, guarda solo su hash, vincula cada una al usuario con sesión iniciada, al documento y a esta sesión del editor, crea una por sesión, mantenla válida solo mientras tu página informe de que la sesión está abierta, y anúlala cuando se cierre el editor o la página.

El protocolo completo está en llms.txt: dáselo a una IA y construirá la conexión para tu sistema. Los demás editores de giit funcionan igual, cada uno con su propio prefijo: editors.giit.ch.

Mostrar un documento en solo lectura (visor)

?viewer=1 muestra solo el documento terminado: sin código, sin herramientas, no se puede cambiar nada y no se guarda nada en el navegador. El documento llega por un enlace (#b64=, ?src=), desde tu página con tomleditor:load o por POST como formulario normal a post.php: el campo content como texto o b64 como base64, opcionalmente name, 20 MB de texto como máximo.

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

Sin viewer=1 el documento se abre en el editor normal. En ambos casos se queda solo en memoria: no se guarda en el servidor ni pasa a "Mis documentos". Abierto directamente, el visor indica que el contenido procede de terceros.