Редактор TOML
Відкрити редактор

Як вбудувати редактор TOML у свій сайт

Для розробників: як розмістити редактор TOML на своїй сторінці - в iframe, в окремому вікні або в режимі лише для читання - і як документи, збереження та результати ШІ передаються між редактором і вашим сервером. Ні реєстрація, ні ключ від нас не потрібні.

Будь-яка сторінка https (і localhost) може вбудувати редактор за допомогою <iframe> і ?embed=1 - без реєстрації, без ключа. У вбудованому вигляді редактор приховує текст сторінки і показує кнопку «Зберегти». Передати документ у редактор і отримати його назад можна чотирма способами.

1. Відкрити файл за посиланням, зберегти на ваш сервер

src= відкриває файл (ваш сервер має дозволяти CORS), webhook= змушує «Зберегти» (Ctrl+S) надсилати документ на цю адресу. Додайте &auto, щоб зберігати після кожної зміни.

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

Вебхук отримує POST із JSON { editor: "toml", event: "save", id, name, content, updated }. Посилання не містить токена: він потрапив би до журналів сервера та історії браузера. Тому вебхук в адресі підходить для збереження на сервері, якому токен не потрібен, наприклад на localhost або у вашій інтрамережі. Для публічного сервера передайте вебхук із токеном у tomleditor:load (hook: { url, token }) і перевірте токен, як це робить невеликий 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. Документ зберігає ваша сторінка (postMessage)

Редактор повідомляє tomleditor:ready, ваша сторінка відповідає tomleditor:load, редактор повідомляє tomleditor:loaded, tomleditor:change при справжніх змінах і tomleditor:save, коли користувач зберігає. Ваша сторінка може надіслати tomleditor:get (поточний стан) або tomleditor:save (натиснути «Зберегти»). Тоді збереження проходить через ваш власний вхід і сеанс.

<iframe id="ed" src="https://toml.giit.ch/uk/?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') {              // редактор готовий: передайте йому документ
    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';   // справжня зміна
  if (m.type === 'tomleditor:save') {               // користувач натиснув «Зберегти»
    await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: m.name, content: m.content }) });
    document.title = 'Saved';
  }
});

// Кнопка збереження на вашій сторінці: відповідь надходить як tomleditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'tomleditor:get' }, ORIGIN); }
</script>

ШІ-асистент усередині вашої сторінки: він працює і у фреймі. З allow="clipboard-write" на iframe «Копіювати промпт» використовує буфер обміну напряму (інакше редактор вдається до старої команди копіювання або показує промпт, щоб скопіювати його вручну). Посилання, яке надсилає ШІ, не може дістатися до редактора всередині іншого сайту, тому редактор просить ШІ та користувача натомість вставити всю відповідь ШІ в редактор - або відкрити редактор в окремому вікні.

3. В окремому вікні

window.open("https://toml.giit.ch/uk/?embed=window", "tomleditor-17") відкриває повний редактор в окремому вікні, і він обмінюється тими самими повідомленнями з window.opener. Посилання від ШІ доходять до нього туди. tomleditor:load може також містити context: { host, title, rules } - тоді промпт для ШІ каже, де розміщено документ, і дотримується ваших правил - і hook: { url, token }, вебхук лише для цього документа, який ніколи не зберігається. Якщо закрити вікно з незбереженими змінами, надсилається tomleditor:closing.

4. ШІ через ваш власний сервер

Передайте редактору власний промпт, адресу на вашому сайті та ключ для цього сеансу редактора. Тоді діалог ШІ показує «Під’єднано до» вашого сайту, що ви повідомляєте ШІ, і вибір змінити поточний документ або створити новий; Копіювати промпт збирає все разом. ШІ надсилає свій результат із ключем на ваш сервер, а ваша сторінка відкриває його в редакторі - новий документ відкривається окремо й залишається в браузері користувача. Один ключ на сеанс редактора: він дійсний, поки редактор відкрито, і лише поки ключа немає, редактор запитує його через tomleditor:key-request. Редактор ніколи сам не надсилає ключ і ніколи його не зберігає; адреса має бути https на вашому власному хості або його піддомені.

const key = async () => (await fetch('/api/ai-key.php?doc=17')).json();   // один ключ на сеанс редактора
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);
// запам’ятайте id з tomleditor:loaded та ігноруйте change/save/closing з іншим id
// лише поки редактор не має ключа:
if (m.type === 'tomleditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'tomleditor:ai', key: k.key }, ORIGIN); }
// поки редактор відкрито, повідомляйте свій сервер щохвилини; закриття редактора або сторінки анулює ключ
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// ваш сервер має результат (polling, SSE …): target - "current" або "new"
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content: draft, from: 'ai', target }, ORIGIN);

Створюйте ключі щонайменше зі 128 випадкових бітів, зберігайте лише їхній хеш, прив’язуйте кожен до користувача, що увійшов, до документа й до цього сеансу редактора, створюйте один на сеанс, тримайте його дійсним лише поки ваша сторінка повідомляє, що сеанс відкритий, і анулюйте його, коли редактор або сторінка закриваються.

Увесь протокол - у llms.txt: дайте його ШІ, і він побудує під’єднання для вашої системи. Інші редактори giit працюють так само, кожен зі своїм префіксом, - editors.giit.ch.

Показ документа лише для читання (переглядач)

?viewer=1 показує лише готовий документ: без коду, без інструментів, нічого не можна змінити, і в браузері нічого не зберігається. Документ надходить за посиланням (#b64=, ?src=), з вашої сторінки через tomleditor:load або через POST звичайною формою на post.php: поле content як текст або b64 як base64, за бажанням name, не більше 20 МБ тексту.

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

Без viewer=1 документ відкривається у звичайному редакторі. В обох випадках він залишається лише в пам’яті: не зберігається на сервері й не потрапляє до «Моїх документів». За прямого відкриття переглядач повідомляє, що вміст отримано від третіх осіб.