Edytor HTML pokazuje kod i stronę obok siebie. Wszystko, co wpiszesz, od razu pojawia się w podglądzie - nagłówki, listy, tabele, obrazy i cały CSS - a kliknięcie czegokolwiek w podglądzie przenosi do jego miejsca w kodzie. Tekst można też edytować bezpośrednio w podglądzie. Dzięki temu szybko zbudujesz strony docelowe, newslettery, artykuły i formularze bez instalowania czegokolwiek.
Wszystko działa w przeglądarce. Strony nie są nigdzie wysyłane, nie ma rejestracji ani reklam.
Znaczniki, atrybuty, encje, komentarze oraz CSS i JavaScript wewnątrz strony we własnych kolorach, numery wierszy, znaczniki zamykające dodawane samoczynnie i porządne wcięcia jednym kliknięciem.
Strona tak, jak pokazuje ją przeglądarka, w ramce, w której skrypty i atrybuty on… nigdy się nie wykonują, a linki nie prowadzą dalej. Sprawdź ją w pełnej szerokości, w rozmiarze tabletu lub telefonu.
Kliknij nagłówek lub akapit w podglądzie i pisz: zmiana trafia do kodu, a &, < i > są zapisywane jako encje.
Niezamknięte lub źle zagnieżdżone znaczniki, nieznane elementy, obrazy bez alt, brakujące pliki i podwójnie użyte id - każdy z numerem wiersza.
Przeciągnij pliki do edytora i używaj ich po nazwie: <img src="../logo.png">, <link rel="stylesheet" href="../style.css">. Są przechowywane razem ze stroną w przeglądarce.
Plik .html, jeden plik HTML ze wszystkimi plikami w środku, ZIP z plikami, PDF przez okno drukowania albo zwykły tekst.
Każda strona https (i localhost) może osadzić edytor za pomocą <iframe> i ?embed=1 - bez rejestracji, bez klucza. Po osadzeniu edytor ukrywa ten tekst i pokazuje przycisk Zapisz. Dokument można przekazywać do edytora i z niego na dwa sposoby.
src= otwiera plik (twój serwer musi zezwalać na CORS), webhook= sprawia, że „Zapisz” (Ctrl+S) wysyła dokument pod ten adres. Dodaj &token=…, aby wysyłać nagłówek Authorization: Bearer …, i &auto, aby zapisywać po każdej zmianie.
<iframe src="https://html.giit.ch/?embed=1&src=https://your-site.com/pages/offer.html&webhook=https://your-site.com/save.php"
allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
Webhook otrzymuje żądanie POST z JSON { editor, event: "save", id, name, content, assets, updated }. Mały skrypt PHP, który go zapisuje:
<?php
header('Access-Control-Allow-Origin: https://html.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__ . "/pages/$name.html", $d['content']);
echo '{"ok":true}';
Edytor zgłasza htmleditor:ready, twoja strona odpowiada htmleditor:load, edytor zgłasza htmleditor:loaded, htmleditor:change przy rzeczywistych zmianach i htmleditor:save, gdy użytkownik zapisuje. Twoja strona może wysłać htmleditor:get (bieżący stan) lub htmleditor:save (naciśnięcie Zapisz). Zapis odbywa się wtedy przez twoje własne logowanie i sesję.
<iframe id="ed" src="https://html.giit.ch/?embed=1" allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
<script>
const ed = document.getElementById('ed');
const ORIGIN = 'https://html.giit.ch';
addEventListener('message', async e => {
if (e.source !== ed.contentWindow || e.origin !== ORIGIN) return;
const m = e.data;
if (m.type === 'htmleditor:ready') { // edytor jest gotowy: przekaż mu dokument
ed.contentWindow.postMessage({
type: 'htmleditor:load',
name: 'Offer',
content: await (await fetch('/pages/offer.html')).text(),
assets: { 'logo.png': 'data:image/png;base64,…' } // opcjonalne pliki
}, ORIGIN);
}
if (m.type === 'htmleditor:change') document.title = '● not saved'; // rzeczywista zmiana
if (m.type === 'htmleditor:save') { // użytkownik kliknął „Zapisz”
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';
}
});
// Przycisk zapisu na twojej stronie: odpowiedź przychodzi jako htmleditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'htmleditor:get' }, ORIGIN); }
</script>
Asystent AI na twojej stronie: działa także w ramce. Z allow="clipboard-write" w iframe „Kopiuj prompt” korzysta bezpośrednio ze schowka (w przeciwnym razie edytor używa starego polecenia kopiowania albo pokazuje prompt do ręcznego skopiowania). Link wysłany przez AI nie dotrze do edytora osadzonego w innej stronie, więc edytor prosi AI i użytkownika o wklejenie całej odpowiedzi AI do edytora - albo o otwarcie edytora we własnym oknie.
window.open("https://html.giit.ch/?embed=window", "htmleditor-17") otwiera pełny edytor we własnym oknie, który wymienia te same wiadomości z window.opener. Linki od AI docierają do niego tam. htmleditor:load może też zawierać context: { host, title, rules } - prompt dla AI mówi wtedy, gdzie należy dokument, i stosuje twoje zasady - oraz hook: { url, token, assets: false }, webhook tylko dla tego dokumentu, który nigdy nie jest zapisywany. Zamknięcie okna z niezapisanymi zmianami wysyła htmleditor:closing.
Przekaż edytorowi własny prompt, adres w twojej witrynie i klucz dla tej sesji edytora. Okno AI pokazuje wtedy „Połączono z” twoją witryną, to, co przekazujesz AI, oraz wybór: zmienić bieżący dokument lub utworzyć nowy; Kopiuj prompt łączy to wszystko. AI wysyła swój wynik z kluczem na twój serwer, a twoja strona otwiera go w edytorze - nowy dokument otwiera się osobno i zostaje w przeglądarce użytkownika. Jeden klucz na sesję edytora: pozostaje ważny, dopóki edytor jest otwarty, a edytor prosi o niego przez htmleditor:key-request tylko wtedy, gdy go nie ma. Edytor nigdy sam nie wysyła klucza i nigdy go nie zapisuje; adres musi być adresem https na twoim własnym hoście lub jego subdomenie.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // jeden klucz na sesję edytora
const k = await key();
ed.contentWindow.postMessage({ type: 'htmleditor:load', name: 'Offer', 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);
// zapamiętaj id z htmleditor:loaded i ignoruj change/save/closing z innym id
// tylko gdy edytor nie ma klucza:
if (m.type === 'htmleditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'htmleditor:ai', key: k.key }, ORIGIN); }
// dopóki edytor jest otwarty, informuj serwer co minutę; zamknięcie edytora lub strony unieważnia klucz
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// twój serwer ma wynik (polling, SSE …): target to "current" lub "new"
ed.contentWindow.postMessage({ type: 'htmleditor:load', name: 'Offer', content: draft, from: 'ai', target }, ORIGIN);
<?php // api/ai-key.php - klucz tej sesji edytora zalogowanego użytkownika (wywołuje go twoja strona)
session_start(); // sprawdź, czy użytkownik może edytować ten dokument
$key = bin2hex(random_bytes(24));
$_SESSION['ai'][hash('sha256', $key)] = ['doc' => (int)$_GET['doc'], 'seen' => time()]; // lub w twojej bazie danych; ?alive= ponownie ustawia seen
echo json_encode(['key' => $key]);
<?php // api/ai-result.php - tu AI wysyła wynik (bez nagłówków CORS: AI wywołują go z serwera)
$key = preg_replace('/^Bearer\s+/', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
$row = find_key(hash('sha256', $key)); // twoja baza danych: nieunieważniony, widziany w ciągu ostatnich 3 minut
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'); // twoja strona pokazuje go przez htmleditor:load
echo '{"ok":true}';
Twórz klucze z co najmniej 128 losowymi bitami, przechowuj tylko ich hash, wiąż każdy z zalogowanym użytkownikiem, dokumentem i tą sesją edytora, twórz jeden na sesję, utrzymuj go ważnym tylko wtedy, gdy twoja strona zgłasza sesję jako otwartą, i unieważniaj go, gdy edytor lub strona zostaną zamknięte.
Cały protokół jest opisany w llms.txt - przekaż go AI, a zbuduje połączenie dla twojego systemu. Edytor LaTeX i Edytor SVG działają tak samo z prefiksami latexeditor: i svgeditor: - latex.giit.ch, svg.giit.ch.
Nie. Edytor działa w całości w przeglądarce. Strony i ich pliki nigdy nie są wysyłane na serwer, chyba że sam skonfigurujesz webhook do własnego serwera.
Nie. Podgląd pokazuje HTML i CSS strony, ale skrypty i atrybuty on… nigdy się nie wykonują, a linki nie prowadzą dalej. Dzięki temu edytor jest bezpieczny przy każdej otwartej stronie. Kod zachowuje swoje skrypty, więc wyeksportowana strona działa tak jak wcześniej.
Przeciągnij lub wklej je do edytora albo dodaj je w karcie Pliki po prawej. Są przechowywane razem ze stroną w przeglądarce i używane po nazwie, na przykład <img src="../logo.png"> lub <link rel="stylesheet" href="../style.css">.
Kliknij „Eksportuj”, wybierz PDF i kliknij „Drukuj / zapisz jako PDF”. W oknie drukowania wybierz jako drukarkę „Zapisz jako PDF”.
Tylko w przeglądarce. Kliknij przycisk folderu w lewym górnym rogu (Moje dokumenty), aby je otwierać, zmieniać ich nazwy, duplikować, pobierać lub usuwać. Wyczyszczenie danych witryny w przeglądarce je usuwa, więc pobierz te, które chcesz zachować - albo pozwól edytorowi wysyłać je na własny serwer („Zapis na serwerze”).
Tak, każda AI - ChatGPT, Claude, Gemini, Copilot lub AI w twoim narzędziu programistycznym. Kliknij „Asystent AI”, potem „Kopiuj prompt”, wklej go do swojej AI i napisz, co ma napisać. AI odpowiada linkiem; kliknij go, a strona pojawi się w edytorze. „Cofnij” cofa każdą zmianę.
Tak. Osadź go w iframe i wymieniaj dokumenty przez postMessage albo pozwól mu wysyłać każdą zapisaną wersję na twój serwer przez webhook. Przykłady znajdziesz w sekcji Edytor na własnej stronie.
Nie. Edytor jest darmowy, bez reklam i bez rejestracji.