The Markdown Editor shows your code and the finished document side by side. Whatever you type appears in the preview right away - headings, lists, tables, task lists, footnotes and math - and a click on any text in the preview takes you to its place in the code. You can also edit the text right in the preview. That makes it quick to write READMEs, notes, documentation and articles.
Everything runs in your browser. Documents are not uploaded, there is no sign-up and no advertising.
Headings, lists, links, code, math and HTML in their own colors, line numbers, lists that continue on Enter, and a clear list of problems with their line.
CommonMark with tables, task lists, strikethrough, autolinks and footnotes. Headings get the same anchors as on GitHub, and links to them are checked.
Inline math in $…$ and displayed formulas in $$…$$, typeset with KaTeX right in the preview.
Click a sentence, a heading, a list item or a table cell in the preview and change it there - the Markdown follows, with special characters escaped.
Drag a picture into the editor and use it with . Pictures are kept with the document in your browser.
One HTML file with the pictures inside, PDF through the print dialog, plain text, or the Markdown with its pictures as ZIP.
Any https page (and localhost) can embed the editor with an <iframe> and ?embed=1 - no sign-up, no key. Embedded, the editor hides this text and shows a Save button. There are two ways to get the document in and out.
src= opens a file (your server must allow CORS), webhook= makes "Save" (Ctrl+S) send the document to that address. Add &token=… for an Authorization: Bearer … header and &auto to save after every change.
<iframe src="https://md.giit.ch/?embed=1&src=https://your-site.com/docs/notes.md&webhook=https://your-site.com/save.php"
allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
The webhook gets a POST with JSON { editor, event: "save", id, name, content, assets, updated }. A small PHP script that stores it:
<?php
header('Access-Control-Allow-Origin: https://md.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__ . "/docs/$name.md", $d['content']);
echo '{"ok":true}';
The editor says mdeditor:ready, your page answers with mdeditor:load, the editor reports mdeditor:loaded, mdeditor:change on real edits and mdeditor:save when the user saves. Your page may send mdeditor:get (the current state) or mdeditor:save (press Save). Saving then goes through your own login and session.
<iframe id="ed" src="https://md.giit.ch/?embed=1" allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
<script>
const ed = document.getElementById('ed');
const ORIGIN = 'https://md.giit.ch';
addEventListener('message', async e => {
if (e.source !== ed.contentWindow || e.origin !== ORIGIN) return;
const m = e.data;
if (m.type === 'mdeditor:ready') { // the editor is ready: hand it the document
ed.contentWindow.postMessage({
type: 'mdeditor:load',
name: 'Notes',
content: await (await fetch('/docs/notes.md')).text(),
assets: { 'logo.png': 'data:image/png;base64,…' } // optional pictures
}, ORIGIN);
}
if (m.type === 'mdeditor:change') document.title = '● not saved'; // a real edit
if (m.type === 'mdeditor:save') { // the user clicked "Save"
await fetch('/api/save', { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: m.name, content: m.content, assets: m.assets }) });
document.title = 'Saved';
}
});
// A save button on your own page: the answer comes as mdeditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'mdeditor:get' }, ORIGIN); }
</script>
AI Assistant inside your page: it works in the frame too. With allow="clipboard-write" on the iframe, "Copy prompt" uses the clipboard directly (otherwise the editor falls back to the old copy command, or shows the prompt to copy by hand). A link the AI sends cannot reach an editor inside another website, so the editor tells the AI and the user to paste the AI's whole answer into the editor instead - or open the editor in a window of its own.
window.open("https://md.giit.ch/?embed=window", "mdeditor-17") opens the full editor in its own window and speaks the same messages with window.opener. Links from an AI reach it there. mdeditor:load may also carry context: { host, title, rules } - the AI prompt then says where the document belongs and follows your rules - and hook: { url, token, assets: false }, a webhook for this document only that is never stored. Closing the window with unsaved changes sends mdeditor:closing.
Hand the editor your own prompt, an address on your site and a key for this editor session. The AI dialog then shows "Connected to" your site, what you tell the AI, and the choice change the current document or make a new one; Copy prompt puts it all together. The AI posts its result with the key to your server, and your page opens it in the editor - a new document opens separately and stays in the user's browser. One key per editor session: it stays valid while the editor is open, and only while it has none the editor asks for it with mdeditor:key-request. The editor never sends the key itself and never stores it; the address must be https on your own host or a subdomain of it.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // one key per editor session
const k = await key();
ed.contentWindow.postMessage({ type: 'mdeditor:load', name: 'Notes', content, ai: {
label: 'My Site', prompt: 'Keep the [[placeholders]]. Write in German.',
endpoint: 'https://your-site.com/api/ai-result.php', key: k.key } }, ORIGIN);
// remember the id from mdeditor:loaded and ignore change/save/closing with another id
// only while the editor has no key:
if (m.type === 'mdeditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'mdeditor:ai', key: k.key }, ORIGIN); }
// while the editor is open, tell your server every minute; closing the editor or the page voids the key
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// your server has a result (polling, SSE …): target is "current" or "new"
ed.contentWindow.postMessage({ type: 'mdeditor:load', name: 'Notes', content: draft, from: 'ai', target }, ORIGIN);
<?php // api/ai-key.php - the key of this editor session of the logged-in user (your page calls it)
session_start(); // check that the user may edit this document
$key = bin2hex(random_bytes(24));
$_SESSION['ai'][hash('sha256', $key)] = ['doc' => (int)$_GET['doc'], 'seen' => time()]; // or in your database; ?alive= sets seen again
echo json_encode(['key' => $key]);
<?php // api/ai-result.php - the AI posts here (no CORS headers: AIs call it from a server)
$key = preg_replace('/^Bearer\s+/', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
$row = find_key(hash('sha256', $key)); // your database: not voided, seen in the last 3 minutes
if (!$row) { http_response_code(401); exit('{"ok":false,"error":"key not valid"}'); }
$d = json_decode(file_get_contents('php://input', false, null, 0, 2000000), true);
save_draft($row['doc'], $d['content'], $d['target'] === 'new' ? 'new' : 'current'); // your page shows it with mdeditor:load
echo '{"ok":true}';
Make keys with at least 128 random bits, store only their hash, bind each to the logged-in user, the document and this editor session, make one per session, keep it valid only while your page reports the session as open, and void it when the editor or the page closes.
The whole protocol is in llms.txt - give it to an AI and it builds the connection for your system. The LaTeX Editor and the SVG Editor work the same way with the prefixes latexeditor: and svgeditor: - latex.giit.ch, svg.giit.ch.
No. The editor runs entirely in your browser. Your documents and pictures are never sent to a server, unless you set up a webhook to your own server yourself.
CommonMark with the GitHub extensions: tables, task lists, strikethrough, autolinks and footnotes, plus math in $…$ and $$…$$. Headings get the same anchors as on GitHub, so links like [see below](#my-heading) work.
Click "Export". Choose PDF and click "Print / Save as PDF" - in the print dialog choose "Save as PDF". Or choose HTML page for one file with the pictures and styles inside.
Drag or paste a picture into the editor, or add it in the Pictures tab on the right. It is stored with the document in your browser and used by .
Only in your browser. Click the folder button at the top left (My documents) to open, rename, duplicate, download or delete them. Clearing your browser's site data removes them, so download the ones you want to keep - or let the editor send them to your own server ("Save to server").
Yes, any AI - ChatGPT, Claude, Gemini, Copilot or the AI in your coding tool. Click "AI Assistant", then "Copy prompt", paste it into your AI and say what it should write. The AI answers with a link; click it and the document appears in your editor. "Undo" takes every change back.
Yes. Embed it with an iframe and exchange documents with postMessage, or let it send every saved version to your server with a webhook. See Use the editor on your own website for examples.
No. The editor is free, without ads and without sign-up.