Редактор Typst показывает ваш код и скомпилированные страницы рядом. Он запускает настоящий компилятор Typst в вашем браузере как WebAssembly, поэтому страницы выглядят точно так же, как в PDF, - заголовки, списки, таблицы, формулы, рисунки, сноски, ссылки и ваши собственные правила #set и #show. Щелчок по любому тексту на странице переносит вас к его месту в коде, а текст можно править прямо на странице.
Всё работает в вашем браузере. Документы никуда не загружаются, нет регистрации и нет рекламы.
Разметка, код, формулы, строки и комментарии своими цветами, номера строк, списки, которые продолжаются по Enter, и ошибки компилятора со строкой, столбцом и подсказкой.
Typst 0.14, скомпилированный в WebAssembly, работает в отдельном потоке рядом с редактором и компилирует через мгновение после каждого изменения - набор остаётся плавным.
Щёлкните по тексту на странице, чтобы изменить его там; изменение попадает в код. Щелчок по формуле или изображению переходит к его месту в коде.
Бумага, поля, колонки, шрифт, размер, язык, выравнивание по ширине, нумерованные заголовки и номера страниц - настройки правят правила #set вашего кода.
Добавляйте в документ изображения, CSV, JSON, YAML, библиографии, шрифты и другие файлы .typ и используйте их через image, csv, json или #import.
PDF от компилятора с настоящим выделяемым текстом, каждая страница как PNG, JPG, WebP или SVG или исходник .typ с его файлами в ZIP.
Любая страница на https (и localhost) может встроить редактор через <iframe> и ?embed=1 - без регистрации и без ключа. Во встроенном виде редактор скрывает этот текст и показывает кнопку «Сохранить». Есть два способа передать документ в редактор и получить его обратно.
src= открывает файл (ваш сервер должен разрешать CORS), webhook= заставляет «Сохранить» (Ctrl+S) отправлять документ на этот адрес. Добавьте &token=… для заголовка Authorization: Bearer … и &auto, чтобы сохранять после каждого изменения.
<iframe src="https://typst.giit.ch/?embed=1&src=https://your-site.com/docs/flyer.typ&webhook=https://your-site.com/save.php"
allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
Вебхук получает POST с JSON { editor, event: "save", id, name, content, assets, updated }. Небольшой PHP-скрипт, который его сохраняет:
<?php
header('Access-Control-Allow-Origin: https://typst.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.typ", $d['content']);
echo '{"ok":true}';
Редактор сообщает typsteditor:ready, ваша страница отвечает typsteditor:load, редактор сообщает typsteditor:loaded, typsteditor:change при настоящих правках и typsteditor:save, когда пользователь сохраняет. Ваша страница может отправить typsteditor:get (текущее состояние) или typsteditor:save (нажать «Сохранить»). Сохранение тогда идёт через ваш собственный вход и сеанс.
<iframe id="ed" src="https://typst.giit.ch/?embed=1" allow="clipboard-write" style="width:100%;height:800px;border:0"></iframe>
<script>
const ed = document.getElementById('ed');
const ORIGIN = 'https://typst.giit.ch';
addEventListener('message', async e => {
if (e.source !== ed.contentWindow || e.origin !== ORIGIN) return;
const m = e.data;
if (m.type === 'typsteditor:ready') { // редактор готов: передаём ему документ
ed.contentWindow.postMessage({
type: 'typsteditor:load',
name: 'Flyer',
content: await (await fetch('/docs/flyer.typ')).text(),
assets: { 'logo.png': 'data:image/png;base64,…' } // необязательные изображения и файлы данных
}, ORIGIN);
}
if (m.type === 'typsteditor:change') document.title = '● not saved'; // настоящая правка
if (m.type === 'typsteditor: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';
}
});
// Кнопка сохранения на вашей странице: ответ приходит как typsteditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'typsteditor:get' }, ORIGIN); }
</script>
ИИ-ассистент внутри вашей страницы: он работает и во фрейме. С allow="clipboard-write" на iframe кнопка «Копировать промпт» напрямую использует буфер обмена (иначе редактор переходит на старую команду копирования или показывает промпт для ручного копирования). Ссылка, которую присылает ИИ, не может попасть в редактор внутри другого сайта, поэтому редактор просит ИИ и пользователя вставить весь ответ ИИ в редактор - или открыть редактор в отдельном окне.
window.open("https://typst.giit.ch/?embed=window", "typsteditor-17") открывает полный редактор в отдельном окне и обменивается теми же сообщениями с window.opener. Ссылки от ИИ попадают туда. typsteditor:load может также содержать context: { host, title, rules } - тогда промпт для ИИ говорит, к чему относится документ, и следует вашим правилам, - и hook: { url, token, assets: false }, вебхук только для этого документа, который никогда не сохраняется. При закрытии окна с несохранёнными изменениями отправляется typsteditor:closing. typsteditor:pdf запрашивает PDF документа страницы и получает его обратно как data URI, созданный компилятором Typst с настоящим выделяемым текстом.
Передайте редактору свой промпт, адрес на вашем сайте и ключ для этого сеанса редактора. Тогда диалог ИИ показывает «Подключено к» вашему сайту, что вы сообщаете ИИ, и выбор изменить текущий документ или создать новый; Копировать промпт собирает всё вместе. ИИ отправляет свой результат с ключом на ваш сервер, а ваша страница открывает его в редакторе - новый документ открывается отдельно и остаётся в браузере пользователя. Один ключ на сеанс редактора: он действует, пока редактор открыт, и только пока ключа нет, редактор запрашивает его через typsteditor:key-request. Редактор никогда сам не отправляет ключ и никогда не сохраняет его; адрес должен быть https на вашем собственном хосте или его поддомене.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // один ключ на сеанс редактора
const k = await key();
ed.contentWindow.postMessage({ type: 'typsteditor:load', name: 'Flyer', 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);
// запомните id из typsteditor:loaded и игнорируйте change/save/closing с другим id
// только пока у редактора нет ключа:
if (m.type === 'typsteditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'typsteditor: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: 'typsteditor:load', name: 'Flyer', content: draft, from: 'ai', target }, ORIGIN);
<?php // api/ai-key.php - ключ этого сеанса редактора для вошедшего пользователя (его вызывает ваша страница)
session_start(); // проверьте, что пользователь может редактировать этот документ
$key = bin2hex(random_bytes(24));
$_SESSION['ai'][hash('sha256', $key)] = ['doc' => (int)$_GET['doc'], 'seen' => time()]; // или в вашей базе данных; ?alive= снова обновляет seen
echo json_encode(['key' => $key]);
<?php // api/ai-result.php - сюда отправляет ИИ (без заголовков CORS: ИИ вызывает его с сервера)
$key = preg_replace('/^Bearer\s+/', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
$row = find_key(hash('sha256', $key)); // ваша база данных: не аннулирован, seen за последние 3 минуты
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'); // ваша страница показывает его через typsteditor:load
echo '{"ok":true}';
Создавайте ключи минимум из 128 случайных бит, храните только их хеш, привязывайте каждый к вошедшему пользователю, документу и этому сеансу редактора, создавайте один ключ на сеанс, считайте его действительным, только пока ваша страница сообщает, что сеанс открыт, и аннулируйте его, когда редактор или страница закрываются.
Весь протокол описан в llms.txt - дайте его ИИ, и он построит подключение для вашей системы. Редактор LaTeX работает так же с префиксом latexeditor: - latex.giit.ch.
Нет. Редактор и компилятор Typst полностью работают в вашем браузере. Ваши документы, изображения и файлы никогда не отправляются на сервер, если только вы сами не настроите вебхук на свой собственный сервер.
Да. Редактор запускает Typst 0.14, скомпилированный в WebAssembly (typst.ts), поэтому страницы и PDF такие же, как из командной строки Typst. Он загружается один раз (около 11 МБ), а затем хранится в вашем браузере.
Нажмите «Экспорт», выберите PDF и нажмите «Скачать». PDF создаётся прямо компилятором Typst, с настоящим текстом, который можно выделять и искать.
Напрямую нет: редактор ничего не загружает с других серверов, поэтому #import "@preview/…" сообщает о проблеме с названием пакета. Можно добавить файлы .typ пакета в документ во вкладке «Изображения» и импортировать их по имени файла.
Шрифты, которые поставляются с Typst: Libertinus Serif (по умолчанию), New Computer Modern и его математический шрифт, а также DejaVu Sans Mono для кода. Добавьте в документ свои файлы .otf или .ttf и используйте их через #set text(font: "Name").
Перетащите или вставьте изображение в редактор или добавьте его во вкладке «Изображения» справа. Оно хранится вместе с документом в вашем браузере и используется через #image("name.png"). Файлы CSV, JSON, YAML и .bib работают так же.
Да, любой ИИ - ChatGPT, Claude, Gemini, Copilot или ИИ в вашем инструменте для программирования. Нажмите «ИИ-ассистент», затем «Копировать промпт», вставьте его в свой ИИ и скажите, что нужно написать. ИИ ответит ссылкой; щёлкните по ней, и документ появится в вашем редакторе. «Отменить» возвращает любое изменение.
Да. Встройте его через iframe и обменивайтесь документами через postMessage или пусть он отправляет каждую сохранённую версию на ваш сервер через вебхук. Примеры - в разделе Редактор на вашем сайте.
Нисколько. Редактор бесплатный, без рекламы и без регистрации.