Как встроить редактор TOML в свой сайт
Для разработчиков: как разместить редактор TOML на своей странице - в iframe, в отдельном окне или в режиме только для чтения - и как документы, сохранения и результаты ИИ передаются между редактором и вашим сервером. Ни регистрация, ни ключ от нас не нужны.
Любая страница на https (и localhost) может встроить редактор через <iframe> и ?embed=1 - без регистрации и без ключа. Во встроенном виде редактор скрывает текст страницы и показывает кнопку «Сохранить». Есть четыре способа передать документ в редактор и получить его обратно.
1. Открыть файл по ссылке, сохранять на ваш сервер
src= открывает файл (ваш сервер должен разрешать CORS), webhook= заставляет «Сохранить» (Ctrl+S) отправлять документ на этот адрес. Добавьте &auto, чтобы сохранять после каждого изменения.
<iframe src="https://toml.giit.ch/ru/?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/ru/?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/ru/?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="ru">
<input type="hidden" name="name" value="Report">
<input type="hidden" name="b64" value="…">
<button>Open</button>
</form>
Без viewer=1 документ открывается в обычном редакторе. В обоих случаях он остаётся только в памяти: не сохраняется на сервере и не попадает в «Мои документы». При прямом открытии просмотрщик сообщает, что содержимое получено от третьих лиц.