Skip to main content

Markdown

Turn plain Markdown into a WLJS Notebook

Once exported to a .md file using Markdown, a notebook can be converted back into a regular notebook when opened in the WLJS Notebook app. Simply place the .md file in your project folder and open it.

For example, here’s a Markdown note that mixes JavaScript and Wolfram Language:

Test.md
# Connecting External JavaScript Library
## How to Make Interactive 3D Force-Directed Graphs

There is an amazing [JavaScript library](https://github.com/vasturiano/3d-force-graph) for building 3D force-directed graphs using THREE.js.

We can build it into a standalone module for our notebooks using ESM cells. But first, let's fetch this library using NPM:

```sh
npm i 3d-force-graph
```

Now define our function. WLJS Notebook has a system of shared JavaScript libraries, which already includes some dependencies.

```esm
import ForceGraph3D from '3d-force-graph';

core.ForceGraph3D = async (args, env) => {
await interpretate.shared.SpriteText.load();
const data = await interpretate(args[0], env);

const SpriteText = interpretate.shared.SpriteText.SpriteText;
const opts = await core._getRules(args, env);

const labels = (opts.VertexLabels || []).reduce((acc, { lhs, rhs }) => {
acc[lhs] = rhs;
return acc;
}, {});

const nodeIds = new Set();
const links = data.map(({ lhs, rhs }) => {
nodeIds.add(lhs);
nodeIds.add(rhs);
return { source: String(rhs), target: String(lhs) };
});

const nodes = Array.from(nodeIds).map(id => ({
id: String(id),
label: labels[id] || String(id),
}));

let imageSize = opts.ImageSize || 350;
if (!Array.isArray(imageSize)) {
imageSize = [imageSize, imageSize * 0.7];
}

const Graph = ForceGraph3D({})(env.element)
.width(imageSize[0])
.height(imageSize[1])
.cooldownTicks(100)
.graphData({ nodes, links })
.nodeThreeObject(node => {
const sprite = new SpriteText(node.label);
sprite.material.depthWrite = true;
sprite.color = 'white';
sprite.textHeight = 12;
return sprite;
})
.nodeThreeObjectExtend(false);

if ('Charge' in opts) {
Graph.d3Force('charge').strength(opts.Charge);
}

Graph.onEngineStop(() => Graph.zoomToFit(400));

env.local.Graph = Graph;
};

core.ForceGraph3D.destroy = () => {
console.warn('3D graph was removed');
};

core.ForceGraph3D.virtual = true;
```

Note: ==You don’t need to recompile or reevaluate the cell above== — an invisible output cell stores the JS module within the notebook.

Now we need to register this symbol in Wolfram Language:

```wolfram
ForceGraph3D /: MakeBoxes[f_ForceGraph3D, StandardForm] := With[{
o = CreateFrontEndObject[f]
},
ViewBox[o, o]
]
```

## Testing
Let’s create a simple graph:

```wolfram
ForceGraph3D[{
1 -> 2, 2 -> 3, 3 -> 4, 4 -> 2, 3 -> 5
}, "VertexLabels" -> {1 -> "one", 4 -> "four", 5 -> "five"}, ImageSize -> 500]
```

Try dragging the nodes around.

More nodes!

```wolfram
words = DictionaryLookup["pea*"];
Flatten[Map[(Thread[# -> DeleteCases[Nearest[words, #, 3], #]]) &, words]];

ForceGraph3D[%, ImageSize -> 500]
```

All code cells will be converted to Input cells, while plain text will be turned into Markdown cells with hidden input.

Let’s open this in WLJS Notebook: