Skip to content

Instantly share code, notes, and snippets.

@Kreijstal
Last active June 17, 2025 14:46
Show Gist options
  • Save Kreijstal/e5dc70889e0c41ddb73a35f69af0abce to your computer and use it in GitHub Desktop.
Save Kreijstal/e5dc70889e0c41ddb73a35f69af0abce to your computer and use it in GitHub Desktop.
typst playground
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Typst.ts Live Editor (Modern API)</title>
<style>
body { font-family: sans-serif; display: flex; gap: 1rem; margin: 1rem; height: 95vh; background-color: #f4f4f4; }
.panel { flex: 1; display: flex; flex-direction: column; border: 1px solid #ccc; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
h3 { margin: 0; padding: 0.75rem; background-color: #e9ecef; border-bottom: 1px solid #ccc; font-size: 1rem; font-weight: 600; }
textarea { flex: 1; border: none; padding: 1rem; font-family: 'Courier New', Courier, monospace; font-size: 14px; resize: none; outline: none; background-color: #fff; }
#output-container { flex: 1; padding: 1rem; overflow-y: auto; background: white; }
#diagnostics-container { height: 100px; overflow-y: auto; background-color: #212529; color: #f8f9fa; font-family: 'Courier New', Courier, monospace; white-space: pre-wrap; padding: 1rem; font-size: 0.85em; }
</style>
</head>
<body>
<div class="panel">
<h3>Typst Code (Live Editor)</h3>
<textarea id="editor">
#set page(width: auto, height: auto)
#set text(font: "Libertinus Serif")
= Incremental Compilation Demo
This is a demonstration of the
*incremental compiler* working
with a live editor.
Type here to see updates in real-time!
You can also use Typst functions:
#let x = 5
The value of x is #x.
Or include data from a shadow file:
#let data = json("/assets/data.json")
Hello, #(data.name)! You are
from #(data.city).
</textarea>
</div>
<div class="panel">
<h3>Rendered Output (SVG)</h3>
<div id="output-container">Loading...</div>
<h3>Compiler Diagnostics</h3>
<div id="diagnostics-container"></div>
</div>
<script type="module">
async function main() {
const editorEl = document.getElementById('editor');
const outputEl = document.getElementById('output-container');
const diagnosticsEl = document.getElementById('diagnostics-container');
// --- Helper for Debouncing ---
let debounceTimer;
function debounce(func, delay) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(func, delay);
}
// --- Initialize Compiler and Renderer ---
console.log('Initializing...');
const typst = await import("https://esm.sh/@myriaddreamin/[email protected]");
const compiler = typst.createTypstCompiler();
const renderer = typst.createTypstRenderer();
await Promise.all([
compiler.init({
getModule: () => "https://cdn.jsdelivr.net/npm/@myriaddreamin/[email protected]/pkg/typst_ts_web_compiler_bg.wasm",
beforeBuild: [
typst.preloadRemoteFonts([
// This is crucial for the text to render correctly!
'https://raw.githubusercontent.com/Myriad-Dreamin/typst.ts/main/assets/data/LibertinusSerif-Regular-subset.otf',
]),
]
}),
renderer.init({
getModule: () => 'https://cdn.jsdelivr.net/npm/@myriaddreamin/[email protected]/pkg/typst_ts_renderer_bg.wasm',
})
]);
console.log('Initialized.');
// --- Set up Shadow Files ---
const jsonData = JSON.stringify({ name: 'Typst', city: 'the Web' });
compiler.mapShadow('/assets/data.json', new TextEncoder().encode(jsonData));
// --- Core Render Logic ---
const updateAndRender = async () => {
const sourceCode = editorEl.value;
diagnosticsEl.textContent = 'Compiling...';
// Add the main source file to the VFS. This is the key to making it work.
compiler.addSource("/main.typ", sourceCode);
try {
// Use the same compiler instance to get incremental benefits
const artifact = await compiler.compile({
mainFilePath: "/main.typ",
});
// Diagnostics are now part of the compiler's state, not the result.
// We'll just assume for this demo that it compiles. A real app
// would handle the `world.diagnostics` part.
diagnosticsEl.textContent = 'No errors or warnings.';
if (artifact && artifact.result) {
// Use the stable SVG renderer
const svg = await renderer.renderSvg({
artifactContent: artifact.result,
});
outputEl.innerHTML = svg;
console.log('SVG Rendered!');
} else {
console.warn('Compilation did not produce an artifact.');
outputEl.innerHTML = '<p style="color: red;">Compilation failed. Check console.</p>';
}
} catch (err) {
console.error("Compilation or Rendering failed:", err);
diagnosticsEl.textContent = `CRITICAL ERROR: ${err.message}`;
}
};
// --- Event Listener ---
editorEl.addEventListener('input', () => {
debounce(updateAndRender, 300); // Debounce for 300ms
});
// --- Initial Render ---
await updateAndRender();
}
main();
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment