Skip to main content

JavaScript/TypeScript SDK

This is the core SDK on which others (React, Vue, and Svelte SDKs) are build on top. It is a lightweight library (less than 5kb gzipped) that allows creating, embedding and communication with LiveCodes playgrounds. It also allows easily creating links to playgrounds.

Installation

Please refer to the SDK installation section.

info

In the full standalone app, the JavaScript SDK is accessible via the global variable livecodes, which can be interacted with in the browser console.

TypeScript Types

TypeScript types are documented here and can be imported from the library.

import type { EmbedOptions, Playground } from 'livecodes';

The following 2 functions are exported by the library:

createPlayground

Type: (container: string | Element, options?: EmbedOptions) => Promise<Playground>

The library exports the function createPlayground which has 2 parameters:

  • container (required): HTMLElement or a string representing a CSS selector.
    If not found, an error is thrown (except in headless mode, in which this parameter is optional and can be omitted).
  • options (optional): an object with embed options (EmbedOptions).

The createPlayground function returns a promise which resolves to an object that exposes the SDK methods (Playground).

import { createPlayground, type EmbedOptions } from 'livecodes';

const options: EmbedOptions = {
// appUrl: ...
// config: ...
// import: ...
// lite: ...
// loading: ...
// params: ...
// template: ...
// view: ...
};

createPlayground('#container', options).then((playground) => {
// the `playground` object exposes the SDK methods
// e.g. playground.run()
});
Throws

The createPlayground function throws an error (promise is rejected) in any of the following conditions:

  • The first parameter (container) is not an element or not found (by CSS selector), except in headless mode.
  • The embed option appUrl is supplied and is not a valid URL.
  • The embed option config is supplied and is not an object or a valid URL.
  • Any of the SDK methods was called after calling the destroy method.

getPlaygroundUrl

Type: (options?: EmbedOptions) => string

Gets the URL to playground (as a string) from the provided embed options. This is useful for easily providing links to run code in playgrounds.

Example:

<pre><code class="language-markdown"># Hello World!</code></pre>
<a href="#" id="playground-link" target="_blank">Open in playground</a>
<script type="module">
import { getPlaygroundUrl } from 'livecodes';
const config = {
markup: {
language: 'markdown',
content: '# Hello World!',
},
};
const url = getPlaygroundUrl({ config });
document.querySelector('#playground-link').href = url;
</script>

Embed Options

Type: EmbedOptions

The createPlayground and getPlaygroundUrl functions accept an optional object with the following optional properties:

appUrl

Type: string

Default: "https://livecodes.io/"

Allows the library to load the playground from a custom URL (e.g. self-hosted app, permanent URL).

If supplied with an invalid URL, an error is thrown.

config

Type: string | Partial<Config>

Default: {}

A configuration object or a URL to a JSON file representing a configuration object to load.

If supplied and is not an object or a valid URL, an error is thrown.

import

Type: string

A resource to import (from any of the supported sources).

lite

Type: boolean

Default: false

If true, the playground is loaded in lite mode.

loading

Type: "eager" | "lazy" | "click"

Default: "lazy"

"eager": The playground loads immediately.

"lazy": A playground embedded low down in the page will not load until the user scrolls so that it approaches the viewport.

"click": The playground does not load automatically. Instead, a "Click-to-load" screen is shown.

params

Type: UrlQueryParams

An object that represents URL Query parameters.

These 2 snippets produce similar output:

import { createPlayground } from 'livecodes';

// use config
createPlayground('#container1', {
config: {
markup: {
language: 'markdown',
content: '# Hello World!',
},
},
});

// use params
createPlayground('#container2', { params: { md: '# Hello World!' } });

template

Type: TemplateName

A starter template to load.

view

Type: "editor" | "result" | "split" | "headless"

Default: "split"

The default view for the playground.

When set to "headless", the playground is loaded in headless mode.

SDK Methods

The createPlayground function returns a promise which resolves to an object (Playground) that exposes some useful SDK methods that can be used to interact with the playground. These methods include:

load

Type: () => Promise<void>

When the embed option loading is set to click, the playground is not loaded automatically. Instead, a screen is shown with "Click to load" button. Calling the SDK method load() allows loading the playground.

If the playground was not loaded, calling any other method will load the playground first before executing.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
await playground.load();
// playground loaded
});

run

Type: () => Promise<void>

Runs the result page (after any required compilation for code).

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
await playground.run();
// new result page is displayed
});

format

Type: (allEditors?: boolean) => Promise<void>

Formats the code.

By default, the code in all editors (markup, style and script) is formatted. If you wish to format only the active editor, pass the value false as an argument.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
await playground.format();
// code in editors is formatted
});

getShareUrl

Type: (shortUrl?: boolean) => Promise<string>

Gets a share url.

By default, the url is has a long query string representing the compressed config object. If the argument shortUrl was set to true, a short url is generated.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const longUrl = await playground.getShareUrl();
const shortUrl = await playground.getShareUrl(true);
});

getConfig

Type: (contentOnly?: boolean) => Promise<Config>

Gets a config object representing the playground state. This can be used to restore state if passed as embed option property on creating playground, or can be manipulated and loaded in run-time using setConfig method.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const config = await playground.getConfig();
});

setConfig

Type: (config: Partial<Config>) => Promise<Config>

Loads a new project using the passed configuration object.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const config = {
markup: {
language: 'html',
content: 'Hello World!',
},
};
const newConfig = await playground.setConfig(config);
// new project loaded
});

getCode

Type: () => Promise<Code>

Gets the playground code (including source code, source language and compiled code) for each editor (markup, style, script), in addition to result page HTML.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const code = await playground.getCode();

// source code, language and compiled code are available
const { content, language, compiled } = code.script;

// result page HTML
const result = code.result;
});

show

Type: (panel: EditorId | Lowercase<Tool['title']> | 'result', options?: { full?: boolean; line?: number; column?: number; zoom?: 1 | 0.5 | 0.25 }) => Promise<void>

Shows the selected panel, which is either:

  • Editor: markup, style or script
  • Tool: console, compiled or tests
  • Result page: result

The second optional argument is an object:

  • It may have the boolean property full, which If true, selected editor or result page will take the full vertical and horizontal space of the playground, while tools will take the full vertical and half the horizontal space, leaving some space for the active editor.

  • The optional properties line and column allow scrolling to line/column number in the shown editor.

  • The optional property zoom sets the result page zoom level (the selected panel must be result).

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const delay = (duration) =>
new Promise((resolve) => {
setTimeout(resolve, duration);
});

await playground.show('style');
await delay(2000);
await playground.show('result', { full: true });
await delay(2000);
await playground.show('script');
await delay(2000);
await playground.show('result', { zoom: 0.5 });
await delay(2000);
await playground.show('console', { full: true });
});

runTests

Type: () => Promise<{ results: TestResult[] }>

Runs project tests (if present) and gets test results.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
const { results } = await playground.runTests();
});

watch

Type: docs

((event: 'load', fn: () => void) => { remove: () => void }) &
((event: 'ready', fn: (data: { config: Config }) => void) => { remove: () => void }) &
((event: 'code', fn: (data: { code: Code; config: Config }) => void) => { remove: () => void }) &
((event: 'console', fn: (data: { method: string; args: any[] }) => void) => { remove: () => void }) &
((event: 'tests', fn: (data: { results: TestResult[]; error?: string }) => void) => { remove: () => void }) &
((event: 'destroy', fn: () => void) => { remove: () => void });

Allows to watch for various playground events. It takes 2 arguments: event name and a callback function that will be called on every event.

In some events, the callback function will be called with an object that supplies relevant data to the callback function (e.g. code, console output, test results).

The watch method returns an object with a single method remove, which when called will remove the callback from watching further events.

import { createPlayground } from 'livecodes';

createPlayground('#container').then((playground) => {
const codeWatcher = playground.watch('code', ({ code, config }) => {
// this will run on every code change
console.log('code:', code);
console.log('config:', config);
});

const consoleWatcher = playground.watch('console', ({ method, args }) => {
// this will run on every console output
console[method](...args);
});

const testsWatcher = playground.watch('tests', ({ results }) => {
// this will run when tests run
results.forEach((testResult) => {
console.log('status:', testResult.status); // "pass", "fail" or "skip"
console.log(testResult.errors); // array of errors as strings
});
});

// then later
codeWatcher.remove();
consoleWatcher.remove();
testsWatcher.remove();
// events are no longer watched
});

These are the events that can be watched and the description of their callback functions:

  • "load": Called when the playground first loads.

    (
    event: "load",
    fn: () => void
    ) => { remove: () => void }
  • "ready": Called when a new project is loaded (including when imported) and the playground is ready to run.

    (
    event: "ready",
    fn: (data: { config: Config }) => void
    ) => { remove: () => void }
  • "code": Called when the playground "content" is changed (see getCode and getConfig). This include changes in:

    (
    event: "code",
    fn: (data: { code: Code; config: Config }) => void
    ) => { remove: () => void }
  • "console": Called when the playground console gets new outputs. The callback method is passed an object with 2 properties: "method" (e.g. "log", "error", etc.) and "args" (the arguments passed to the method, as an array).

    (
    event: "console",
    fn: (data: { method: string; args: any[] }) => void
    ) => { remove: () => void }
  • "tests": Called when tests run and test results are available (see runTests).

    (
    event: "tests",
    fn: (data: { results: TestResult[]; error?: string }) => void
    ) => { remove: () => void }
  • "destroy": Called when the playground is destroyed.

    (
    event: "destroy",
    fn: () => void
    ) => { remove: () => void }

onChange

Deprecated: Use watch method instead.

Type: (fn: ChangeHandler) => { remove: () => void }

Allows to watch the playground for changes. It takes a callback function that will be called on every change.

The callback function will be called with an object that has 2 properties: code and config, representing the current codes and configuration objects (see getCode and getConfig).

The onChange method returns an object with a single method remove, which when called will remove the callback from watching changes.

import { createPlayground } from 'livecodes';

createPlayground('#container').then((playground) => {
const watcher = playground.onChange(({ code, config }) => {
// this will run on every code change
console.log('code:', code);
console.log('config:', config);
});

// then later
watcher.remove();
// changes are no longer watched
});

exec

Type: (command: APICommands, ...args: any[]) => Promise<{ output: any } | { error: string }>

Execute custom commands, including:

// in browser console of full app (e.g. https://livecodes.io)
await livecodes.exec('setBroadcastToken', 'my-token');
  • "showVersion": Logs the current LiveCodes app version, SDK version, git commit SHA, permanent app URL and SDK URL in the browser console.
// in browser console of full app (e.g. https://livecodes.io)
await livecodes.exec('showVersion');

destroy

Type: () => Promise<void>

Destroys the playground instance, and removes event listeners. Further call to any SDK methods throws an error.

import { createPlayground } from 'livecodes';

createPlayground('#container').then(async (playground) => {
await playground.destroy();
// playground destroyed
// any further SDK call throws an error
});

Styles

Default Styles

By default, the container element is styled when the SDK is initialized (including width, height, border, etc.). To disable default styles, set the container element attribute data-default-styles to "false" before initializing.

Example:

<div id="container" data-default-styles="false" class="custom"></div>
<script type="module">
import { createPlayground } from 'livecodes';
createPlayground('#container');
</script>

Height

By default, the playground container height is set to "300px". To change the height, either disable the default styles and override them, or simply set the data-height attribute to a number (in pixels) or any valid CSS value (e.g. "100%" to take the full height of its parent element).

Example:

<div id="container" data-height="500"></div>
<script type="module">
import { createPlayground } from 'livecodes';
createPlayground('#container');
</script>

Demo

show code
import { createPlayground } from 'livecodes';

const options = {
"params": {
"js": "import { createPlayground } from \"livecodes\";\n\nconst params = {\n html: \"<h1>Hello World!</h1>\",\n css: \"h1 {color: blue;}\",\n js: 'console.log(\"Hello, LiveCodes!\")',\n console: \"open\",\n};\n\ncreatePlayground('#container', { params });\n",
"html": "<div id=\"container\"></div>"
}
};
createPlayground('#container', options);