Skip to main content

Headless Mode

The LiveCodes SDK can be used to create playgrounds in headless mode. In this mode, no visible output is displayed in the embedding web page. However, all SDK methods are accessible (e.g. for updating code, getting compiled code, console output, result HTML, shareable URLs, formatting code, running tests, etc).

This provides the power of leveraging the wide range of features and language support offered by LiveCodes, while retaining full control over the UI.

Usage

To create a headless playground, set the embed option view to "headless".

Please note that in headless mode, the first parameter (container) of the function createPlayground is optional and can be omitted.

Example:

import { createPlayground } from 'livecodes';

createPlayground({
view: 'headless',
config: {
markup: {
language: 'markdown',
content: '# Hello World!',
},
},
}).then(async (playground) => {
const code = await playground.getCode();
console.log(code.markup.compiled); // "<h1>Hello World!</h1>"
console.log(code.result); // (result page HTML)
});

Examples

The following examples show how to use the headless mode to make a Markdown editor, an MDX editor and a Python interpreter.

tip

You may want to view the following playgrounds in full screen (using the full screen button in the top right of each playground).

Markdown Editor

In this demo, code changes are watched using the SDK method watch('code', callback). The callback function accepts an argument which is an object with the properties code and config (see getCode and getConfig). The compiled code is obtained as code.markup.compiled.

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"markup": {
"language": "html",
"content": "<textarea id=\"editor\" style=\"display: none;\"></textarea>\n<div id=\"output\">Loading...</div>\n\n<script type=\"module\">\n import { createPlayground } from \"https://cdn.jsdelivr.net/npm/[email protected]\";\n import debounce from \"https://jspm.dev/debounce\";\n\n const initialCode = \"# Hello, LiveCodes!\\n\\n\";\n\n // the code editor\n const editor = CodeMirror.fromTextArea(document.getElementById(\"editor\"), {\n lineNumbers: true,\n mode: \"markdown\",\n });\n editor.setSize(\"100%\", 200);\n editor.setValue(initialCode);\n\n // the playground\n const options = {\n view: \"headless\",\n };\n\n const livecodes = await createPlayground(options);\n await livecodes.load();\n\n const compile = async () => {\n await livecodes.setConfig({\n autoupdate: false,\n markup: {\n language: \"markdown\",\n content: editor.doc.getValue(),\n },\n });\n };\n\n // watch for changes\n editor.on(\"change\", debounce(compile, 1000));\n livecodes.watch(\"code\", ({ code, config }) => {\n createSandbox(document.querySelector(\"#output\"), code.markup.compiled);\n });\n\n await compile();\n\n // create a sandbox for safe execution of compiled code\n function createSandbox (container, html) {\n const iframe = document.createElement(\"iframe\");\n iframe.src = \"https://livecodes-sandbox.pages.dev/v7/\";\n iframe.sandbox =\n \"allow-same-origin allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-presentation allow-scripts\";\n iframe.onload = () => {\n iframe.contentWindow.postMessage({ html }, \"*\");\n };\n container.innerHTML = \"\";\n container.appendChild(iframe);\n return iframe;\n };\n</script>\n\n<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/lib/codemirror.css\" />\n<script src=\"https://unpkg.com/[email protected]/lib/codemirror.js\"></script>\n<script src=\"https://unpkg.com/[email protected]/mode/markdown/markdown.js\"></script>\n\n<style>\n * {\n margin: 0;\n padding: 0;\n }\n body {\n display: flex;\n flex-direction: column;\n height: 100vh;\n overflow: hidden;\n }\n #output {\n flex: 1;\n }\n #output iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n</style>\n"
}
}
};
createPlayground('#container', options);

MDX Editor

In this demo, code changes are watched using the SDK method watch('code', callback). The callback function accepts an argument which is an object with the properties code and config (see getCode and getConfig). The result HTML is obtained as code.result.

tip

If you do not want to run the result page in the headless playground and only want to get the generated result HTML, you can set the configuration option `autoupdate to false.

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"markup": {
"language": "html",
"content": "<textarea id=\"editor\" style=\"display: none;\"></textarea>\n<div id=\"output\">Loading...</div>\n\n<script type=\"module\">\n import { createPlayground } from \"https://cdn.jsdelivr.net/npm/[email protected]\";\n import debounce from \"https://jspm.dev/debounce\";\n\n const initialCode = `import { useState, useEffect } from 'react';\n\nexport const Hello = ({name}) => {\n const [count, setCount] = useState(0);\n return (\n <>\n <h1>Hello, {name}!</h1>\n <p>You clicked {count} times.</p>\n <button onClick={() => setCount(count + 1)}>Click me</button>\n </>\n );\n};\n\n<Hello name=\"LiveCodes\"></Hello>\n\n## MDX in short\n\n- ❤️ Powerful\n- 💻 Everything is a component\n- 🔧 Customizable\n- 📚 Markdown-based\n- 🔥 Blazingly blazing fast\n\n> from [mdxjs.com](https://mdxjs.com/)\n`;\n\n // the code editor\n const editor = CodeMirror.fromTextArea(document.getElementById(\"editor\"), {\n lineNumbers: true,\n mode: \"markdown\",\n });\n editor.setSize(\"100%\", 200);\n editor.setValue(initialCode);\n\n // the playground\n const options = {\n view: \"headless\",\n config: { autoupdate: false },\n };\n\n const livecodes = await createPlayground(options);\n await livecodes.load();\n\n const compile = async () => {\n await livecodes.setConfig({\n autoupdate: false,\n markup: {\n language: \"mdx\",\n content: editor.doc.getValue(),\n },\n });\n };\n\n // watch for changes\n editor.on(\"change\", debounce(compile, 1000));\n livecodes.watch(\"code\", ({ code, config }) => {\n createSandbox(document.querySelector(\"#output\"), code.result);\n });\n\n await compile();\n\n // create a sandbox for safe execution of compiled code\n function createSandbox (container, html) {\n const iframe = document.createElement(\"iframe\");\n iframe.src = \"https://livecodes-sandbox.pages.dev/v7/\";\n iframe.sandbox =\n \"allow-same-origin allow-downloads allow-forms allow-modals allow-orientation-lock allow-pointer-lock allow-popups allow-presentation allow-scripts\";\n iframe.onload = () => {\n iframe.contentWindow.postMessage({ html }, \"*\");\n };\n container.innerHTML = \"\";\n container.appendChild(iframe);\n return iframe;\n };\n</script>\n\n<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/lib/codemirror.css\" />\n<script src=\"https://unpkg.com/[email protected]/lib/codemirror.js\"></script>\n<script src=\"https://unpkg.com/[email protected]/mode/markdown/markdown.js\"></script>\n\n<style>\n * {\n margin: 0;\n padding: 0;\n }\n body {\n display: flex;\n flex-direction: column;\n height: 100vh;\n overflow: hidden;\n }\n #output {\n flex: 1;\n }\n #output iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n</style>\n"
}
}
};
createPlayground('#container', options);

Python Interpreter

In this demo, console output is obtained using the SDK method watch('code', callback). The callback function accepts an argument which is an object with the properties method and args indicating the console method and the arguments that were passed (as an array).

show code
import { createPlayground } from 'livecodes';

const options = {
"config": {
"markup": {
"language": "html",
"content": "<textarea id=\"editor\" style=\"display: none\"></textarea>\n<div id=\"output\">Loading...</div>\n\n<script type=\"module\">\n import { createPlayground } from \"https://cdn.jsdelivr.net/npm/[email protected]\";\n import debounce from \"https://jspm.dev/debounce\";\n\n const initialCode = `def say_hello(name):\n return f\"Hello, {name}!\"\n\nprint(say_hello(\"LiveCodes\"))\n`;\n\n // the code editor\n const editor = CodeMirror.fromTextArea(document.getElementById(\"editor\"), {\n lineNumbers: true,\n mode: \"python\",\n });\n editor.setSize(\"100%\", 250);\n editor.setValue(initialCode);\n\n // the playground\n const options = {\n view: \"headless\",\n };\n\n const livecodes = await createPlayground(options);\n await livecodes.load();\n\n const run = async () => {\n await livecodes.setConfig({\n autoupdate: true,\n script: {\n language: \"python\",\n content: editor.doc.getValue(),\n },\n });\n };\n\n // watch for changes\n editor.on(\"change\", debounce(run, 1000));\n livecodes.watch(\"console\", ({ method, args }) => {\n const output = document.querySelector(\"#output\");\n output.innerHTML = args.join(\"\\n\");\n if (method === \"error\") {\n output.style.color = \"red\";\n } else {\n output.style.color = \"unset\";\n }\n });\n\n await run();\n</script>\n\n<link rel=\"stylesheet\" href=\"https://unpkg.com/[email protected]/lib/codemirror.css\" />\n<script src=\"https://unpkg.com/[email protected]/lib/codemirror.js\"></script>\n<script src=\"https://unpkg.com/[email protected]/mode/python/python.js\"></script>\n\n<style>\n * {\n margin: 0;\n padding: 0;\n }\n body {\n display: flex;\n flex-direction: column;\n height: 100vh;\n overflow: hidden;\n }\n #output {\n flex: 1;\n margin: 1em;\n white-space: pre;\n font-family: monospace;\n }\n #output iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n</style>\n"
}
}
};
createPlayground('#container', options);