TOML 편집기
편집기 열기

TOML 편집기를 내 웹사이트에 삽입하기

개발자를 위한 페이지입니다. TOML 편집기를 내 페이지에 넣는 방법(iframe, 별도 창, 읽기 전용 뷰어)과 문서, 저장, AI 결과가 편집기와 내 서버 사이를 오가는 방법을 설명합니다. 가입도, 저희가 발급하는 키도 필요 없습니다.

모든 https 페이지(와 localhost)는 <iframe>과 ?embed=1로 편집기를 삽입할 수 있습니다 - 가입도 키도 필요 없습니다. 삽입되면 편집기는 페이지 설명을 숨기고 저장 버튼을 보여 줍니다. 문서를 넣고 빼는 방법은 네 가지입니다.

1. 링크로 파일을 열고 내 서버에 저장하기

src=는 파일을 열고(서버가 CORS를 허용해야 합니다), webhook=은 "저장"(Ctrl+S) 시 문서를 그 주소로 보내게 합니다. 변경할 때마다 저장하려면 &auto를 추가하세요.

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

웹훅은 JSON { editor: "toml", event: "save", id, name, content, updated }이 담긴 POST를 받습니다. 링크에는 토큰을 넣지 않습니다. 서버 로그와 브라우저 방문 기록에 남기 때문입니다. 따라서 주소에 웹훅을 지정하는 방식은 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/ko/?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') {               // 사용자가 "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>

내 페이지 안의 AI 도우미: 프레임 안에서도 작동합니다. iframe에 allow="clipboard-write"가 있으면 "프롬프트 복사"가 클립보드를 직접 사용합니다(없으면 편집기가 예전 복사 명령을 쓰거나 손으로 복사할 프롬프트를 보여 줍니다). AI가 보낸 링크는 다른 웹사이트 안의 편집기에 닿을 수 없으므로, 편집기는 AI와 사용자에게 AI의 답변 전체를 편집기에 붙여 넣거나 편집기를 별도의 창으로 열라고 안내합니다.

3. 별도의 창에서

window.open("https://toml.giit.ch/ko/?embed=window", "tomleditor-17")은 전체 편집기를 별도의 창으로 열고 window.opener와 같은 메시지를 주고받습니다. AI의 링크는 그 창에 닿습니다. tomleditor:load에는 context: { host, title, rules }도 담을 수 있으며(그러면 AI 프롬프트가 문서의 소속을 알리고 내 규칙을 따릅니다), hook: { url, token }, 즉 저장되지 않는 이 문서 전용 웹훅도 담을 수 있습니다. 저장하지 않은 변경이 있는 채로 창을 닫으면 tomleditor:closing을 보냅니다.

4. 내 서버를 통한 AI

편집기에 내 프롬프트, 내 사이트의 주소, 이 편집기 세션용 키를 넘기세요. 그러면 AI 대화 상자에 내 사이트와 "연결됨", AI에게 전하는 내용, 현재 문서 변경 또는 새 문서 만들기 선택이 표시되고, 프롬프트 복사가 이를 모두 합칩니다. AI는 결과를 키와 함께 내 서버로 POST하고, 페이지가 이를 편집기에서 엽니다. 새 문서는 따로 열리며 사용자의 브라우저에 남습니다. 편집기 세션당 키 하나: 편집기가 열려 있는 동안 유효하며, 키가 없을 때만 편집기가 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);
// tomleditor:loaded의 id를 기억하고 id가 다른 change/save/closing은 무시
// 편집기에 키가 없을 때만:
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);
// 서버에 결과가 있음(폴링, SSE …): target은 "current" 또는 "new"
ed.contentWindow.postMessage({ type: 'tomleditor:load', name: 'Settings', content: draft, from: 'ai', target }, ORIGIN);

키는 최소 128비트의 무작위 값으로 만들고, 해시만 저장하고, 각각을 로그인한 사용자, 문서, 이 편집기 세션에 묶고, 세션마다 하나씩 만들고, 페이지가 세션이 열려 있다고 알리는 동안만 유효하게 하며, 편집기나 페이지가 닫히면 무효로 하세요.

전체 프로토콜은 llms.txt에 있습니다 - AI에게 주면 내 시스템에 맞는 연결을 만들어 줍니다. 다른 giit 편집기도 각자의 접두사로 똑같이 작동합니다 - editors.giit.ch.

문서를 읽기 전용으로 표시하기(뷰어)

?viewer=1은 완성된 문서만 보여 줍니다. 코드와 도구가 없고, 아무것도 변경할 수 없으며, 브라우저에 아무것도 저장되지 않습니다. 문서는 링크(#b64=, ?src=), 페이지의 tomleditor:load, 또는 일반 양식으로 post.php에 보내는 POST로 전달됩니다. 필드는 텍스트용 content 또는 base64용 b64, 선택 사항 name이며 텍스트는 최대 20MB입니다.

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

viewer=1이 없으면 문서는 일반 편집기에서 열립니다. 어느 경우든 메모리에만 유지되며 서버에 저장되지 않고 "내 문서"에도 들어가지 않습니다. 직접 열면 뷰어는 내용이 제3자로부터 왔음을 표시합니다.