Mdbook REPL
This is a mdbook real time playground for some programming languages which you can directly execute them in the browser without any server. It's fast and easy to use.
This is mostly inspired by mdbook rust playground, but it's only limited to rust and it's using https://play.rust-lang.org as its backend compiler server. So I want to make a playground for other languages based on webassembly.
Below is an example of a python code block that can be executed directly in the browser:
# Python codeblock
print("Hello, world!")
All the code is editable and runnable. You can change the code and run it again. The execution is really fast.
Attention 💥
This playground is still in development and not many languages are supported yet. If you have any ideas or suggestions, please let me know.
Usage
This preprocessor is designed to be used with mdbook. If you want to use this repl in your own web project, you can have a look at For Developers section.
Installation
There two ways to install this preprocessor.
You can install it with cargo if you have rust installed:
cargo install mdbook-repl
Or you can download the pre built binary from github release page. You should put the binary in your machine's PATH after installation.
You can check your installation by running:
mdbook-repl --version
Configuration
After installation, you need to add some configurations to your book.toml file. Below is an example of the full configuration options for this preprocessor:
[preprocessor.repl]
# iframe url, default is https://mr-addict.github.io/mdbook-repl/embed/
src = "https://mr-addict.github.io/mdbook-repl/embed/"
# python is disabled by default and loading is lazy
python.enable = true
python.loading = "lazy"
# typescript is disabled by default and loading is lazy
typescript.enable = true
typescript.loading = "lazy"
# javascript is disabled by default and loading is lazy
javascript.enable = true
javascript.loading = "lazy"
- src: The url of the repl iframe, the default value is https://mr-addict.github.io/mdbook-repl/embed/. You can also deploy your own repl server for better performance, see For Developers section.
- language.enable: Enable the language for the repl, default value is false.
- language.loading: The loading of the language, can be eager or lazy, default value is lazy.
For example if you only care about python codeblock, you can only enable python and disable the others:
[preprocessor.repl]
python.enable = true
Options
You can also specific some options for the each codeblock.
readonly
readonly option will make the codeblock not editable. You can use this option if you want to show some code examples that should not be changed.
```javascript,readonly
// javascript codeblock
console.log("Hello, world!");
```
And the codeblock will not be eidtable:
// javascript codeblock
console.log("Hello, world!");
norepl
norepl option will make the codeblock not rendered by the preprocessor. You can use this option if you want to show some code examples that should not be executed.
```javascript,norepl
// javascript codeblock
console.log("Hello, world!");
```
And it will not be rendered by this preprocessor:
// javascript codeblock
console.log("Hello, world!");
Shortcuts
You can use CTRL + R to run the codeblock. This is useful when you want to run the codeblock without clicking the run button.
Language Extensions
This preprocessor only recongnizes specific extensions for sepecific language. For example, you can only use python or py codeblock for python code.
Here is the full list of extensions:
Language | Extensions |
---|---|
Python | python, py |
TypeScript | typescript, ts |
JavaScript | javascript, js |
Performance
There is no doubt that the execution of the code is really fast compared with backend server playgrounds. However, the bottleneck is the loading time of the codeblock. The loading time is depending on language.
The smallest one is javascrpt, because javascipt is natively supported by the browser. However others languages like python and typescript need some complied webassembly runtime to be loaded first.
So the loading option is lazy and all lanugages are disabled by default. Also, most of the examples in this docs are using javascript by default to make the loading time faster.
Below is the relative size of the extra needed runtime for each language:
Language | Size |
---|---|
Python | 5.5MB |
TypeScript | 718kb |
JavaScript | 0 |
Limitations
There are some limitations when using this playground, because of the nature of the technologies used in this playground. So, don't expect to use this playground as a full-featured IDE.
The best usage of this playground is to test and learn programming languages for educational purposes, not for production.
If you find any other issues, don't hesitate to report it to the GitHub repository.
User input
For example, you can not use the input function to get user input in python. Though it is possible to play arround to make user input work, but it's not worth it. The following code won't work:
# This will not work
input('Enter your name: ')
Interrupt
And now there is also no easy way for interrupt your code, so don't write bad code!!
For Developers
Actually, you can use repl in your own web project other than mdbook. What mdbook-repl does is to preprocess your markdown code blocks and replace them with some js and css.
The core of mdbook-repl is the iframe. The iframe is used to display the output of the code. The js and css are used to communicate with the iframe.
The iframe url is https://mr-addict.github.io/mdbook-repl/embed/. You can also deploy the iframe in your own server. You can find the source code of the iframe in the github repository.
API
When the iframe is loaded, it will send a message to the parent window. The message is a json string. The json object has the following properties:
{
"repl": {
"id": "",
"dimensions": {
"width": 800,
"height": 600
},
"editor": {
"readonly": false,
"theme": "light",
"language": "python",
"code": "# This is a default python code\n\nprint('Hello world')",
"defaultCode": "# This is a default python code\n\nprint('Hello world')"
},
"output": {
"data": [],
"status": "loading"
}
}
}
What you should do fist is to send some basic information to the iframe including id and editor data. The id is used to identify the editor if you have more that one iframes in you page. The id is empty at first. When new information updated, id will be sent with it. The editor information is used to initialize the editor.
The dimensions is used to set the width and height of the iframe. The output data is used to display the output of the code. The status can be idle, loading, running or finished. The data is an array of objects. Each object has a color and msg. The color is used to set the color of the message which can be normal or red. The msg is used to display the message.
Example
Here is an example of how to use the mdbook-repl in your own project:
<style>
iframe {
border: none;
width: 100%;
}
</style>
<iframe src="http://localhost:4173/" width="100%" allow="clipboard-write"></iframe>
<script>
const id = "ac2f5a2";
const lang = "python";
const theme = "light";
const readonly = false;
const code = "# Python\n\nprint('Hello world')";
const iframe = document.querySelector("iframe");
const postmessage = (msg) => iframe.contentWindow.postMessage({ repl: msg }, "*");
window.addEventListener("message", (event) => {
const replData = event.data.repl;
if (event.source === window || !replData) return;
// if the id is empty, then it's the first time the iframe is loaded
if (replData.id === "") {
postmessage({ id, editor: { theme, lang, code, readonly, defaultCode: code } });
return;
}
if (replData.id !== id) return;
// update the iframe height
iframe.style.height = replData.dimensions.height + "px";
});
</script>
💥 Attention
You need to add
allow="clipboard-write"
to the iframe to make the clipboard work.
Python
Python is a programming language that lets you work quickly and integrate systems more effectively . It's easy to learn and use.
This playground uses pyodide to run python code in the browser. Currently, it's using pyodide 0.25.0 which is using python 3.11.3. You can check the version by running the following code:
import sys
print(sys.version)
Examples
Here are some basic examples of using Python that you can do in this playground.
Basic Example
message = "Hello, world!"
print(message)
Example with Function
def greet(name):
return "Hello, " + name
print(greet("world"))
Example with Class
class Greeter:
def __init__(self, name):
self.name = name
def greet(self):
return "Hello, " + self.name
g = Greeter("world")
print(g.greet())
Example with List Comprehension
squares = [x * x for x in range(10)]
print(squares)
Example with async/await:
import asyncio
async def delay(ms):
await asyncio.sleep(ms / 1000)
async def main():
print("Start")
await delay(1000)
print("End")
await main()
Packages
There are many built-in packages available in pyodide. You should have a look at the list of packages before you use them.
For example, you can use regex pakcage to match a pattern in a string:
import regex
pattern = r'(\d{4})-(\d{2})-(\d{2})'
match = regex.match(pattern, '2022-12-31')
print(match.groups())
It may take some time when you first import a package, but after that, it should be faster.
And here is another example of using numpy package to calculate the mean of a list of numbers:
import numpy as np
numbers = [1, 2, 3, 4, 5]
mean = np.mean(numbers)
print(mean)
Attention 💥
Some of the packages may not work as expected due to the limitations of browser environment.
Typescript
Typescript is a superset of JavaScript that adds static typing to the language. Though you can run javascript code in the browser, but you need to compile typescript to javascript first. Then you can run the compiled javascript code in the browser.
This playground uses babel/standalone in service worker to compile typescript code to javascript and then execute compiled javascript code to get the result.
let message: string = "Hello, world!";
console.log(message);
Examples
Here are some basic examples of using TypeScript that you can do in this playground.
Basic Example
let message: string = "Hello, world!";
console.log(message);
Example with Function
function greet(name: string) {
return "Hello, " + name;
}
console.log(greet("world"));
Example with Interface
interface Person {
first: string;
last: string;
}
function greeter(person: Person) {
return "Hello, " + person.first + " " + person.last;
}
console.log(greeter({ first: "Jane", last: "Doe" }));
async/await:
async function delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function main() {
console.log("Start");
await delay(1000);
console.log("End");
}
main();
Javascript
Javascript is a programming language that is used to make web pages interactive. Browser is able to run javascript code directly. But the repl code still exected in service worker to prevent the page from freezing.
let message = "Hello, world!";
console.log(message);
Examples
Here are some basic examples of using JavaScript that you can do in this playground.
Basic Example
let message = "Hello, world!";
console.log(message);
Example with Function
function greet(name) {
return "Hello, " + name;
}
console.log(greet("world"));
Example with Class
class Greeter {
constructor(name) {
this.name = name;
}
greet() {
return "Hello, " + this.name;
}
}
let g = new Greeter("world");
console.log(g.greet());
Example with Array
let squares = Array.from({ length: 10 }, (_, i) => i * i);
console.log(squares);
Example with async/await:
async function delay(ms) {
await new Promise((resolve) => setTimeout(resolve, ms));
}
async function main() {
console.log("Start");
await delay(1000);
console.log("End");
}
main();