O Editor Typst mostra o seu código e as páginas compiladas lado a lado. Ele roda o compilador Typst de verdade no seu navegador como WebAssembly, então as páginas ficam exatamente como ficarão no PDF - títulos, listas, tabelas, fórmulas, figuras, notas de rodapé, referências e as suas próprias regras #set e #show. Um clique em qualquer texto da página leva você ao lugar dele no código, e você pode editar o texto direto na página.
Tudo roda no seu navegador. Os documentos não são enviados, não há cadastro nem publicidade.
Marcação, código, fórmulas, strings e comentários em cores próprias, números de linha, listas que continuam com Enter e os erros do compilador com linha, coluna e dica.
O Typst 0.14 compilado para WebAssembly roda em um worker ao lado do editor e compila um instante depois de cada alteração - a digitação continua fluida.
Clique em um texto na página para alterá-lo ali; a alteração vai para o código. Um clique em uma fórmula ou imagem leva ao lugar dela no código.
Papel, margens, colunas, fonte, tamanho, idioma, texto justificado, títulos numerados e números de página - as configurações editam as regras #set do seu código.
Adicione imagens, CSV, JSON, YAML, bibliografias, fontes e outros arquivos .typ a um documento e use-os com image, csv, json ou #import.
Um PDF do compilador com texto de verdade e selecionável, cada página como PNG, JPG, WebP ou SVG, ou o código-fonte .typ com os arquivos em ZIP.
Qualquer página https (e localhost) pode incorporar o editor com um <iframe> e ?embed=1 - sem cadastro, sem chave. Incorporado, o editor oculta este texto e mostra um botão Salvar. Há dois jeitos de levar o documento para dentro e para fora.
src= abre um arquivo (seu servidor precisa permitir CORS), webhook= faz "Salvar" (Ctrl+S) enviar o documento para esse endereço. Acrescente &token=… para um cabeçalho Authorization: Bearer … e &auto para salvar após cada alteração.
<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>
O webhook recebe um POST com JSON { editor, event: "save", id, name, content, assets, updated }. Um pequeno script PHP que o guarda:
<?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}';
O editor diz typsteditor:ready, a sua página responde com typsteditor:load, o editor informa typsteditor:loaded, typsteditor:change em edições reais e typsteditor:save quando o usuário salva. A sua página pode enviar typsteditor:get (o estado atual) ou typsteditor:save (apertar Salvar). Salvar passa então pelo seu próprio login e sessão.
<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') { // o editor está pronto: entregue o documento a ele
ed.contentWindow.postMessage({
type: 'typsteditor:load',
name: 'Flyer',
content: await (await fetch('/docs/flyer.typ')).text(),
assets: { 'logo.png': 'data:image/png;base64,…' } // imagens e arquivos de dados opcionais
}, ORIGIN);
}
if (m.type === 'typsteditor:change') document.title = '● not saved'; // uma edição real
if (m.type === 'typsteditor:save') { // o usuário clicou em "Salvar"
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';
}
});
// Um botão de salvar na sua própria página: a resposta chega como typsteditor:save
function saveNow() { ed.contentWindow.postMessage({ type: 'typsteditor:get' }, ORIGIN); }
</script>
Assistente de IA dentro da sua página: ele também funciona no frame. Com allow="clipboard-write" no iframe, "Copiar prompt" usa a área de transferência diretamente (senão o editor recorre ao antigo comando de copiar, ou mostra o prompt para copiar à mão). Um link enviado pela IA não alcança um editor dentro de outro site, então o editor pede à IA e ao usuário que colem a resposta inteira da IA no editor - ou abram o editor em uma janela própria.
window.open("https://typst.giit.ch/?embed=window", "typsteditor-17") abre o editor completo em uma janela própria e troca as mesmas mensagens com window.opener. Os links de uma IA chegam até ele ali. typsteditor:load também pode levar context: { host, title, rules } - o prompt da IA então diz a que lugar o documento pertence e segue as suas regras - e hook: { url, token, assets: false }, um webhook só para este documento que nunca é guardado. Fechar a janela com alterações não salvas envia typsteditor:closing. typsteditor:pdf pede o PDF do documento da página e o recebe de volta como data URI, gerado pelo compilador Typst com texto de verdade e selecionável.
Entregue ao editor o seu próprio prompt, um endereço no seu site e uma chave para esta sessão do editor. A janela de IA mostra então "Conectado a" o seu site, o que você diz à IA, e a escolha alterar o documento atual ou criar um novo; Copiar prompt junta tudo. A IA envia o resultado com a chave para o seu servidor, e a sua página o abre no editor - um novo documento abre separado e fica no navegador do usuário. Uma chave por sessão do editor: ela vale enquanto o editor estiver aberto, e só enquanto não tiver nenhuma o editor a pede com typsteditor:key-request. O editor nunca envia a chave por conta própria e nunca a guarda; o endereço precisa ser https no seu próprio host ou em um subdomínio dele.
const key = async () => (await fetch('/api/ai-key.php?doc=17')).json(); // uma chave por sessão do editor
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);
// guarde o id de typsteditor:loaded e ignore change/save/closing com outro id
// só enquanto o editor não tiver chave:
if (m.type === 'typsteditor:key-request') { const k = await key(); ed.contentWindow.postMessage({ type: 'typsteditor:ai', key: k.key }, ORIGIN); }
// enquanto o editor estiver aberto, avise o seu servidor a cada minuto; fechar o editor ou a página invalida a chave
setInterval(() => fetch('/api/ai-key.php?alive=' + k.key.slice(0, 8)), 60000);
// o seu servidor tem um resultado (polling, SSE …): target é "current" ou "new"
ed.contentWindow.postMessage({ type: 'typsteditor:load', name: 'Flyer', content: draft, from: 'ai', target }, ORIGIN);
<?php // api/ai-key.php - a chave desta sessão do editor do usuário logado (a sua página a chama)
session_start(); // verifique se o usuário pode editar este documento
$key = bin2hex(random_bytes(24));
$_SESSION['ai'][hash('sha256', $key)] = ['doc' => (int)$_GET['doc'], 'seen' => time()]; // ou no seu banco de dados; ?alive= atualiza seen
echo json_encode(['key' => $key]);
<?php // api/ai-result.php - a IA envia para cá (sem cabeçalhos CORS: as IAs chamam a partir de um servidor)
$key = preg_replace('/^Bearer\s+/', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
$row = find_key(hash('sha256', $key)); // o seu banco de dados: não invalidada, vista nos últimos 3 minutos
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'); // a sua página o mostra com typsteditor:load
echo '{"ok":true}';
Crie chaves com pelo menos 128 bits aleatórios, guarde só o hash delas, vincule cada uma ao usuário logado, ao documento e a esta sessão do editor, crie uma por sessão, mantenha-a válida só enquanto a sua página informar que a sessão está aberta, e invalide-a quando o editor ou a página fechar.
O protocolo completo está em llms.txt - entregue-o a uma IA e ela monta a conexão para o seu sistema. O Editor LaTeX funciona do mesmo jeito com o prefixo latexeditor: - latex.giit.ch.
Não. O editor e o compilador Typst rodam inteiramente no seu navegador. Seus documentos, imagens e arquivos nunca são enviados a um servidor, a não ser que você mesmo configure um webhook para o seu próprio servidor.
Sim. O editor roda o Typst 0.14 compilado para WebAssembly (typst.ts), então as páginas e o PDF são os mesmos da linha de comando do Typst. Ele é carregado uma vez (cerca de 11 MB) e depois fica guardado no seu navegador.
Clique em "Exportar", escolha PDF e clique em "Baixar". O PDF vem direto do compilador Typst, com texto de verdade que pode ser selecionado e pesquisado.
Não diretamente: o editor não carrega nada de outros servidores, então #import "@preview/…" aponta um problema com o nome do pacote. Você pode adicionar os arquivos .typ de um pacote ao documento na aba Imagens e importá-los pelo nome do arquivo.
As fontes que vêm com o Typst: Libertinus Serif (a padrão), New Computer Modern e a sua fonte de fórmulas, e DejaVu Sans Mono para código. Adicione os seus próprios arquivos .otf ou .ttf a um documento e use-os com #set text(font: "Name").
Arraste ou cole uma imagem no editor, ou adicione-a na aba Imagens, à direita. Ela fica guardada com o documento no seu navegador e é usada por #image("name.png"). Arquivos CSV, JSON, YAML e .bib funcionam do mesmo jeito.
Sim, qualquer IA - ChatGPT, Claude, Gemini, Copilot ou a IA da sua ferramenta de programação. Clique em "Assistente de IA", depois em "Copiar prompt", cole na sua IA e diga o que ela deve escrever. A IA responde com um link; clique nele e o documento aparece no seu editor. "Desfazer" desfaz qualquer alteração.
Sim. Incorpore-o com um iframe e troque documentos com postMessage, ou deixe-o enviar cada versão salva ao seu servidor com um webhook. Veja exemplos em Use o editor no seu próprio site.
Não. O editor é gratuito, sem anúncios e sem cadastro.