TOML エディター
エディターを開く

TOML エディターを自分の Web サイトに埋め込む

開発者向けのページです。TOML エディターを自分のページに組み込む方法 (iframe 内、専用ウィンドウ、読み取り専用のビューアー) と、ドキュメント、保存、AI の結果をエディターと自分のサーバーの間でやり取りする方法を説明します。登録も当サイトのキーも不要です。

https のページ (および localhost) なら、<iframe> と ?embed=1 でエディターを埋め込めます。登録もキーも不要です。埋め込まれたエディターはページの説明文を隠し、保存ボタンを表示します。ドキュメントを受け渡す方法は 4 つあります。

1. リンクからファイルを開き、自分のサーバーに保存する

src= でファイルを開き (サーバーで CORS の許可が必要)、webhook= を指定すると「保存」(Ctrl+S) でドキュメントがそのアドレスに送信されます。変更のたびに保存するには &auto を追加します。

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

Webhook は JSON { editor: "toml", event: "save", id, name, content, updated } を含む POST を受け取ります。リンクにはトークンを含めません。サーバーのログやブラウザーの履歴に残ってしまうためです。そのため、URL に指定する Webhook は、localhost やイントラネットなど、トークンを必要としない保存先に適しています。公開サーバーでは、tomleditor:load の hook: { url, token } で Webhook とトークンを渡し、次の短い 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/ja/?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>

ページ内の AI アシスタント: フレーム内でも動作します。iframe に allow="clipboard-write" を付けると、「プロンプトをコピー」がクリップボードを直接使います (付けない場合は古いコピーコマンドを使うか、手動でコピーできるようプロンプトを表示します)。AI が送るリンクはほかの Web サイト内のエディターには届かないため、エディターは AI とユーザーに、代わりに AI の回答全体をエディターに貼り付けるか、エディターを専用のウィンドウで開くよう案内します。

3. 専用のウィンドウで開く

window.open("https://toml.giit.ch/ja/?embed=window", "tomleditor-17") は完全なエディターを専用のウィンドウで開き、window.opener と同じメッセージをやり取りします。AI からのリンクもそこに届きます。tomleditor:load には context: { host, title, rules } (AI プロンプトがドキュメントの所属先を伝え、あなたのルールに従います) と hook: { url, token } (このドキュメント専用で保存されない Webhook) も含められます。未保存の変更があるままウィンドウを閉じると tomleditor:closing が送られます。

4. 自分のサーバー経由の AI

エディターに独自のプロンプト、サイト上のアドレス、このエディターセッション用のキーを渡します。すると AI ダイアログには、あなたのサイトへの「接続先」、AI に伝える内容、そして 現在のドキュメントを変更 か 新しいドキュメントを作成 の選択肢が表示され、プロンプトをコピー ですべてがまとめられます。AI はキーを付けて結果をあなたのサーバーに送り、ページがそれをエディターで開きます。新しいドキュメントは別に開き、ユーザーのブラウザー内に残ります。キーはエディターセッションごとに 1 つです。エディターを開いている間は有効で、キーがないときだけエディターが tomleditor:key-request で要求します。エディターがキーを自分で送ったり保存したりすることはありません。アドレスはあなた自身のホストまたはそのサブドメイン上の https である必要があります。

const key = async () => (await fetch('/api/ai-key.php?doc=17')).json();   // エディターセッションごとにキーを 1 つ
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 ビット以上のランダム値で作り、ハッシュだけを保存し、それぞれをログイン中のユーザー、ドキュメント、このエディターセッションに結び付けてください。キーはセッションごとに 1 つ作り、ページがセッションを開いていると報告している間だけ有効にし、エディターまたはページが閉じたら無効にしてください。

プロトコルの全体は llms.txt にあります。AI に渡せば、あなたのシステム用の接続を作ってくれます。ほかの giit エディターも、それぞれの接頭辞で同じように動作します - editors.giit.ch。

文書を読み取り専用で表示する(ビューアー)

?viewer=1 は完成した文書だけを表示します。コードもツールもなく、何も変更できず、ブラウザーにも何も保存されません。文書はリンク(#b64=、?src=)、ページからの tomleditor:load、または通常のフォームによる post.php への POST で届きます。フィールドは、テキストなら content、base64 なら b64、任意で name、テキストは最大 20 MB です。

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

viewer=1 がなければ、文書は通常のエディターで開きます。どちらの場合もメモリ内にだけ保持され、サーバーには保存されず「マイドキュメント」にも入りません。直接開いた場合、ビューアーは内容が第三者からのものであることを示します。