REST
API is provided by a core extension wljs-api
. All communication is done via HTTP POST request in JSON format. In a nutshell it allows to
- create transactions to evaluate Wolfram, WLX, any other support languages (see Cell Types) code and get back the result
- fetch Frontend Objects used for 3D, 2D graphics automatically
In principle public REST API is rich enough to write your own small notebook interface with full support of all available cell types, 3D graphics, sound and etc. However, this is a natural limitation - dynamics or event-based expressions such as InputRange or Manipulate are not going to work.
A basic example of a single HTML file REPL interface made using this API See at the bottom
WLJS System due to its architecture requires to have a few objects / interfaces to be exposed to the global window
scope.
WLJS packages includes both Wolfram Language and Javascript code, therefore for the simplicity we inject JS modules to the runtime using sort of IIFE approach.
Setting up server
You need to have running WLJS server (via docker or directly) or WLJS Notebook desktop app. See instruction
Steps to set up REPL
To make a sort of interactive evaluation environment using REST API and WLJS Notebook runtime you need to
- Configure server link (optional)
- Wait for the connection
- Find available kernel
- Make a transaction for evaluation
- Wait for the result
- Print the result using view-component
Required scripts and styles
WLJS Notebook consists of multiple modules and extensions including WLJS Interpreter (a tiny Wolfram Language interpreter written in Javascript), Graphics, Graphics3D, sound libraries and view components for various cell types (optional). For styling we use Tailwind, however we do recommend to use JIT-version like Twind
for the simplify.
Here is a full set of scripts and styles required
<script type="module" src="https://cdn.skypack.dev/twind/shim"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/JerryI/wljs-api@base/assets/minimal.css">
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/dist/interpreter.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/src/core.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/src/settings.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-cells@base/src/module.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-mk@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/boxes.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/objects.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/autocomplete.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/frontsubmit.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/dir.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/metamarkers.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/misc.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-d3@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-three@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/Mathematica-ThreeJS-graphics-engine@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-html-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-js-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-markdown-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-mermaid-support@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-plotly@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-graphics-d3@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-wlx-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-inputs@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sound@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-revealjs@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-manipulate@base/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-print-redirect-fix@base/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-wxf-accelerator@base/override.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-api@base/assets/polyfill.js"></script>
Most of them support lazy loading. Feel free to remove some of the dependencies (cell types or IO libraries) if you are not going to use them.
Javascript server API
We provide a helper interface via polyfill.js
script (included above) to manage evaluation, and links automatically.
The interface is exposed via global object server
.
server.configure(opts)
server.configure({
address = 'http://127.0.0.1:20560',
fetchOptions = {},
polingDelay = 300
}) : null
Here fetchOptions
will be used globally for all POST requests to the server.
server.waitForConnection
server.waitForConnection() : Promise
Async function, that waits for a server link to be ready
server.findKernel
server.findKernel() : Promise<String>
Async function. It finds and returns string
uuid of the first available evaluation kernel
server.createTransaction
server.createTransaction(
kernel : String,
data : String
) : Promise<String>
Async function. Submits raw data
(as string) for the evaluation to kernel
and returns a transaction uuid as a string. See also Transactions
server.getResult
server.getResult(
kernel : String | null,
transaction: String
) : Promise<Array>
Async function. Requests the results of the transaction provided by uuid. Kernel parameter is optional. It returns the results in a form of Array
[
{Display: <String>, Data: <String>},
...
]
where Display
can be codemirror
, html
, markdown
and etc describing a view-component (see View components).
server.abortKernel
server.abortKernel(kernel : String) : Promise<String>
aborts the evaluation on the given kernel
server.cachingFunction
Is a global async function definition used internally to fetch Frontend Objects from the server. You can alter it in order to cache these object. By the default the definition is
server.cachingFunction = async (objectId) => {
//here you can do caching if needed
//for offline work
const res = await server.requestObject(null, objectId);
return res;
}
server.requestCDNExtensionsList
server.requestCDNExtensionsList() : Promise<Array<String>>
Returns a list of enabled extensions / modules used on the server as Array
of String
[
"wljs-interpreter",
...
]
server.requestCDNJavascript
server.requestCDNJavascript(
list : Array<String>
) : Promise<Array<String>>
requests a list of URLs to CDN for provided list of extensions
server.requestCDNStyles
server.requestCDNStyles() : Promise<Array<String>>
requests a list of URLs to CDN for CSS files
Transactions
Transactions work in the same way as input cells in the normal notebook. Any expression has to be provided as a string in InputForm
const expression = `Plot[x, {x,0,1}]`
The expression will be automatically split into multiple transactions (only internally) if separated by a top-level line break
const expression = `Plot[x, {x,0,1}]
1+1
`
As a result server.getResult
will return an array of two output expressions.
Cell types
WLJS Notebook supports multiple cell types (see Cell types), it is specified in the first line of a transaction text
const expression = `.md
# Hello World
`
The view component will be specified in the object of the first element of array returned by server.getResult
. See View components
View components
To render the results you need to operate with view-components provided by a global Javascript object SupportedCells
new window.SupportedCells[Display: String].view(
{element: HTMLElement}, Data: String
) : CellTypeLike
where Display
and Data
can be acquired from the result object in array returned by server.getResult
. element
is DOM element where a component will be mounted.
CellTypeLike
instance has the following methods:
.dispose()
removes a view component from DOM and frees all requested objects
Minimal example
Here is a basic HTML page template, which can perform evaluation using a WLJS server running on a default port
Details
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="module" src="https://cdn.skypack.dev/twind/shim"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/JerryI/wljs-api@base/assets/minimal.css">
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/dist/interpreter.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/src/core.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-interpreter@base/src/settings.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-cells@base/src/module.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-mk@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/boxes.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/objects.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/autocomplete.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/frontsubmit.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/dir.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/metamarkers.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-editor@base/src/misc.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-d3@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sharedlib-three@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/Mathematica-ThreeJS-graphics-engine@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-html-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-js-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-markdown-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-mermaid-support@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-plotly@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-graphics-d3@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-wlx-support@base/src/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-inputs@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-sound@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-revealjs@base/dist/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-manipulate@base/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-print-redirect-fix@base/kernel.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-wxf-accelerator@base/override.js"></script>
<script type="module" src="https://cdn.jsdelivr.net/gh/JerryI/wljs-api@base/assets/polyfill.js"></script>
<title>Textarea Template</title>
</head>
<body>
<div class="max-w-2xl mx-auto p-6 space-y-4 bg-white shadow-md rounded-2xl">
<!-- State Display -->
<div class="text-gray-700 text-sm">
<strong class="font-semibold">State:</strong>
<span id="stateField" class="ml-1 text-blue-600">Waiting for wljs connection</span>
</div>
<!-- Code Area -->
<textarea
id="code_area"
placeholder="Type your text here..."
class="w-full min-h-[150px] p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 resize-y text-sm font-mono"
>Plot[{x, x^2}, {x,0,1}, PlotLegends->Automatic]</textarea>
<!-- Results Div -->
<div id="resultsDiv" class="p-4 bg-gray-100 rounded-lg min-h-[100px] text-sm"></div>
<!-- Submit Button -->
<button
id="submit_button"
type="button"
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Submit
</button>
</div>
<script type="module">
const stateField = document.getElementById("stateField");
const submitButton = document.getElementById("submit_button");
const codeArea = document.getElementById("code_area");
const resultsDiv = document.getElementById('resultsDiv');
async function start() {
console.warn('Initialization complete!');
stateField.innerText = 'Connecting to Kernel';
const kernel = await server.findKernel();
console.warn('Obtained Kernel '+kernel);
stateField.innerText = 'Ready!';
submitButton.addEventListener('click', async () => {
const transaction = await server.createTransaction(kernel, codeArea.value.trim());
stateField.innerText = 'Evaluation...';
console.log(transaction);
const results = await server.getResult(kernel, transaction);
console.warn('Ready');
console.log(results);
stateField.innerText = 'Ready!';
results.forEach((data) => {
const display = data.Display || 'codemirror';
const parentelement = document.createElement('div');
resultsDiv.appendChild(parentelement);
const origin = {
element: parentelement
};
const cell = new window.SupportedCells[display].view(origin, data.Data);
//to remove cell use
//cell.dispose()
})
})
}
(async () => {
await server.waitForConnection();
start();
})();
</script>
</body>
</html>