Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion backend/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion backend/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mdbook-repl"
version = "0.2.8"
version = "0.2.9"
edition = "2021"
license = "MIT"
readme = "../README.md"
Expand Down
9 changes: 8 additions & 1 deletion backend/assets/repl.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
<div class="repl" data-id="{id}" data-readonly="{readonly}" data-lang="{lang}">
<div
class="repl"
data-id="{id}"
data-readonly="{readonly}"
data-lang="{lang}"
data-editor-theme="{editor.theme}"
data-editor-dark-theme="{editor.darkTheme}"
>
<iframe
class="hide"
loading="{loading}"
Expand Down
4 changes: 3 additions & 1 deletion backend/assets/script.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
const lang = replElement.getAttribute("data-lang");
const code = getCode(replElement.nextElementSibling);
const readonly = replElement.getAttribute("data-readonly") === "true";
const editorTheme = replElement.getAttribute("data-editor-theme") ?? "";
const editorDarkTheme = replElement.getAttribute("data-editor-dark-theme") ?? "";
let theme = mapTheme(localStorage.getItem("mdbook-theme") || document.querySelector(".theme.theme-selected")?.id);
const postmessage = (msg) => iframeElement.contentWindow.postMessage({ repl: msg }, "*");
function mapTheme(bookTheme) {
Expand All @@ -22,7 +24,7 @@
if (event.source === window || !repl) return;
// if the id is empty, then it's the first time the iframe is loaded
if (repl.id === "") {
postmessage({ id, editor: { theme, lang, code, readonly, defaultCode: code } });
postmessage({ id, editor: { theme, editorTheme, editorDarkTheme, lang, code, readonly, defaultCode: code } });
return;
}
if (repl.id !== id) return;
Expand Down
27 changes: 24 additions & 3 deletions backend/src/repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,26 @@ fn map_lang(raw_lang: &str) -> &str {
}
}

fn get_langs_regex(extensions: &[&str]) -> String {
extensions
.iter()
.map(|lang| format!("\\b{}\\b", lang))
.collect::<Vec<_>>()
.join("|")
}

fn render_repls(content: &str, config: &Config) -> (bool, String) {
// \r? is for windows line endings
let langs = r"\bpy\b|\bpython\b|\bts\b|\btypescript\b|\bjs\b|\bjavascript\b|\blua\b|\blua\b";
let extensions = [
"py",
"python",
"ts",
"typescript",
"js",
"javascript",
"lua",
];
let langs = get_langs_regex(&extensions);
let re = Regex::new(&format!(r"(?s)```({}),?(.*?)\r?\n(.*?)```", langs)).unwrap();

// if there are no matches, return the content as is
Expand All @@ -70,11 +87,13 @@ fn render_repls(content: &str, config: &Config) -> (bool, String) {

// get the config options
let enable = cfg::get_config_bool(config, &format!("{}.enable", lang), false);
let loading = cfg::get_config_string(config, &format!("{}.lazy", lang), "lazy");
let loading = cfg::get_config_string(config, &format!("{}.loading", lang), "lazy");
let editor_theme = cfg::get_config_string(config, "editor.theme", "");
let editor_dark_theme = cfg::get_config_string(config, "editor.darkTheme", "");
let src = cfg::get_config_string(
config,
"src",
"https://mr-addict.github.io/mdbook-repl/embed/",
"https://mr-addict.github.io/mdbook-repl/embed",
);

// if norepl is in the options, return the code block as is
Expand All @@ -89,6 +108,8 @@ fn render_repls(content: &str, config: &Config) -> (bool, String) {
.replace("{loading}", &loading)
.replace("{codeblock}", &codeblock)
.replace("{readonly}", if readonly { "true" } else { "false" })
.replace("{editor.theme}", &editor_theme)
.replace("{editor.darkTheme}", &editor_dark_theme)
})
.to_string();

Expand Down
10 changes: 6 additions & 4 deletions docs/book.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[book]
authors = ["MR-Addict"]
language = "en"
multilingual = false
src = "src"
title = "mdbook-repl"

Expand Down Expand Up @@ -49,8 +48,10 @@ copy-js = true
[preprocessor.repl]
command = "backend/target/release/mdbook-repl"

# iframe url, default is https://mr-addict.github.io/mdbook-repl/embed/
src = "https://mr-addict.github.io/mdbook-repl/embed/"
# iframe url and editor theme settings
src = "https://mr-addict.github.io/mdbook-repl/embed"
editor.theme = "textmate"
editor.darkTheme = "monokai"

# python is disabled by default and loading is lazy
python.enable = true
Expand All @@ -64,5 +65,6 @@ typescript.loading = "lazy"
javascript.enable = true
javascript.loading = "lazy"

# lua is disabled by default and loading is lazy
lua.enable = true
lua.loading = "lazy"
lua.loading = "lazy"
2 changes: 1 addition & 1 deletion docs/src/for-developers.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Actually, you can use repl in your own web project other than **mdbook**. What *

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/](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](https://github.com/MR-Addict/mdbook-repl/tree/gh-pages/embed).
The iframe url is [https://mr-addict.github.io/mdbook-repl/embed](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](https://github.com/MR-Addict/mdbook-repl/tree/gh-pages/embed).

## API

Expand Down
19 changes: 14 additions & 5 deletions docs/src/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ After installation, you need to add some configurations to your **book.toml** fi

```toml
[preprocessor.repl]
# iframe url, default is https://mr-addict.github.io/mdbook-repl/embed/
src = "https://mr-addict.github.io/mdbook-repl/embed/"
# iframe url and editor theme settings
src = "https://mr-addict.github.io/mdbook-repl/embed"
editor.theme = "textmate"
editor.darkTheme = "monokai"

# python is disabled by default and loading is lazy
python.enable = true
Expand All @@ -40,11 +42,17 @@ typescript.loading = "lazy"
# javascript is disabled by default and loading is lazy
javascript.enable = true
javascript.loading = "lazy"

# lua is disabled by default and loading is lazy
lua.enable = true
lua.loading = "lazy"
```

- **src**: The url of the repl iframe, the default value is [https://mr-addict.github.io/mdbook-repl/embed/](https://mr-addict.github.io/mdbook-repl/embed/). You can also deploy your own repl server for better performance, see [For Developers](for-developers.md) 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**.
- **src**: The url of the repl iframe. You can also deploy your own repl server for better performance, see [For Developers](for-developers.md) section.
- **editor.theme**: The theme of the code editor, the default value is **textmate**.
- **editor.darkTheme**: The theme of the code editor in dark mode, if not specified, it will use the value of **editor.theme**, otherwise the default value is **monokai**.
- **[language].enable**: Enable the language for the repl, the default value is **false**.
- **[language].loading**: The loading of the language, can be **eager** or **lazy**, the default value is **lazy**.

For example if you only care about python codeblock, you can only enable python and disable the others:

Expand Down Expand Up @@ -111,6 +119,7 @@ Here is the full list of extensions:
| Python | python, py |
| TypeScript | typescript, ts |
| JavaScript | javascript, js |
| Lua | lua |

## Performance

Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mdbook-repl",
"private": true,
"version": "0.2.8",
"version": "0.2.9",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
59 changes: 56 additions & 3 deletions frontend/src/components/Editor/Editor.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,59 @@
import AceEditor from "react-ace";
import { useEffect } from "react";
import { useEffect, useMemo } from "react";

import "ace-builds/src-noconflict/mode-lua";
import "ace-builds/src-noconflict/mode-python";
import "ace-builds/src-noconflict/mode-typescript";
import "ace-builds/src-noconflict/mode-javascript";
import "ace-builds/src-noconflict/mode-lua";

import "ace-builds/src-noconflict/theme-ambiance";
import "ace-builds/src-noconflict/theme-chaos";
import "ace-builds/src-noconflict/theme-chrome";
import "ace-builds/src-noconflict/theme-cloud9_day";
import "ace-builds/src-noconflict/theme-cloud9_night";
Comment thread
MR-Addict marked this conversation as resolved.
import "ace-builds/src-noconflict/theme-cloud9_night_low_color";
import "ace-builds/src-noconflict/theme-cloud_editor";
import "ace-builds/src-noconflict/theme-cloud_editor_dark";
import "ace-builds/src-noconflict/theme-clouds";
import "ace-builds/src-noconflict/theme-clouds_midnight";
import "ace-builds/src-noconflict/theme-cobalt";
import "ace-builds/src-noconflict/theme-crimson_editor";
import "ace-builds/src-noconflict/theme-dawn";
import "ace-builds/src-noconflict/theme-dracula";
import "ace-builds/src-noconflict/theme-dreamweaver";
import "ace-builds/src-noconflict/theme-eclipse";
import "ace-builds/src-noconflict/theme-github";
import "ace-builds/src-noconflict/theme-github_dark";
import "ace-builds/src-noconflict/theme-github_light_default";
import "ace-builds/src-noconflict/theme-gob";
import "ace-builds/src-noconflict/theme-gruvbox";
import "ace-builds/src-noconflict/theme-gruvbox_dark_hard";
import "ace-builds/src-noconflict/theme-gruvbox_light_hard";
import "ace-builds/src-noconflict/theme-idle_fingers";
import "ace-builds/src-noconflict/theme-iplastic";
import "ace-builds/src-noconflict/theme-katzenmilch";
import "ace-builds/src-noconflict/theme-kr_theme";
import "ace-builds/src-noconflict/theme-kuroir";
import "ace-builds/src-noconflict/theme-merbivore";
import "ace-builds/src-noconflict/theme-merbivore_soft";
import "ace-builds/src-noconflict/theme-mono_industrial";
import "ace-builds/src-noconflict/theme-monokai";
import "ace-builds/src-noconflict/theme-nord_dark";
import "ace-builds/src-noconflict/theme-one_dark";
import "ace-builds/src-noconflict/theme-pastel_on_dark";
import "ace-builds/src-noconflict/theme-solarized_dark";
import "ace-builds/src-noconflict/theme-solarized_light";
import "ace-builds/src-noconflict/theme-sqlserver";
import "ace-builds/src-noconflict/theme-terminal";
import "ace-builds/src-noconflict/theme-textmate";
import "ace-builds/src-noconflict/theme-tomorrow";
import "ace-builds/src-noconflict/theme-tomorrow_night";
import "ace-builds/src-noconflict/theme-tomorrow_night_blue";
import "ace-builds/src-noconflict/theme-tomorrow_night_bright";
import "ace-builds/src-noconflict/theme-tomorrow_night_eighties";
import "ace-builds/src-noconflict/theme-twilight";
import "ace-builds/src-noconflict/theme-vibrant_ink";
import "ace-builds/src-noconflict/theme-xcode";

import style from "./Editor.module.css";
import Buttons from "./Buttons/Buttons";
Expand All @@ -27,6 +75,11 @@ const defaultOptions = {
export default function Editor() {
const { outputs, editor, setEditor, execuateCode, clearOutput } = useAppContext();

const theme = useMemo(() => {
if (editor.theme === "light") return editor.editorTheme || "textmate";
return editor.editorDarkTheme || editor.editorTheme || "monokai";
}, [editor.theme, editor.editorTheme, editor.editorDarkTheme]);

// listen ctrl + r
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
Expand All @@ -50,6 +103,7 @@ export default function Editor() {
<Buttons />
<AceEditor
width="100%"
theme={theme}
name={editor.lang}
mode={editor.lang}
value={editor.code}
Expand All @@ -58,7 +112,6 @@ export default function Editor() {
defaultValue={editor.defaultCode}
setOptions={{ useWorker: false }}
tabSize={editor.lang === "python" ? 4 : 2}
theme={editor.theme === "light" ? "textmate" : "monokai"}
onChange={(code) => setEditor({ ...editor, code })}
{...defaultOptions}
/>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/contexts/AppProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const workers = {
python: new URL("../workers/python-worker.js", import.meta.url).toString(),
typescript: new URL("../workers/typescript-worker.js", import.meta.url).toString(),
javascript: new URL("../workers/javascript-worker.js", import.meta.url).toString(),
lua: new URL("../workers/lua-worker.js", import.meta.url).toString(),
lua: new URL("../workers/lua-worker.js", import.meta.url).toString()
};

const defaultOutputs: OutputsType = {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/types/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ const Editor = z.object({
code: z.string(),
defaultCode: z.string(),
theme: z.enum(["dark", "light"]),
editorTheme: z.string().optional(),
editorDarkTheme: z.string().optional(),
readonly: z.boolean()
});

Expand Down
44 changes: 25 additions & 19 deletions frontend/src/workers/lua-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ const factory = new wasmoon.LuaFactory();
const postmessage = (status, msg) => self.postMessage({ lang: "lua", output: { status, data: msg } });

const stderr = (...msg) => postmessage("running", [{ color: "red", msg }]);
const stdout = (...msg) => { if (msg.length) postmessage("running", [{ color: "normal", msg }]);};
const stdout = (...msg) => {
if (msg.length) postmessage("running", [{ color: "normal", msg }]);
};

async function waitWasmoonReady() {
postmessage("idle", []);
Expand All @@ -21,37 +23,41 @@ self.onmessage = async (event) => {
// add all the context to the worker
for (const key of Object.keys(context)) self[key] = context[key];

const lua = await factory.createEngine()
try{
lua.global.set('print', wasmoon.decorateFunction((thread, argsQuantity) => {
const values = [];
for (let i = 1; i <= argsQuantity; i++) {
const lua = await factory.createEngine();
try {
lua.global.set(
"print",
wasmoon.decorateFunction(
(thread, argsQuantity) => {
const values = [];
for (let i = 1; i <= argsQuantity; i++) {
values.push(thread.indexToString(i));
}
stdout(...values.map(String));
},
{
receiveArgsQuantity: true,
receiveThread: true
}
stdout(...values.map(String));
}, {
receiveArgsQuantity: true,
receiveThread: true
}));
)
);

await lua.global.set('error', (...args) => {
await lua.global.set("error", (...args) => {
stderr(...args.map(String));
});

if (!code) postmessage("finished", [{ color: "red", msg: "lua code is empty" }]);
else {
try {
await lua.doString(code);
postmessage("finished", []);
await lua.doString(code);
postmessage("finished", []);
} catch (err) {
postmessage("finished", [{ color: "red", msg: [err.message] }]);
postmessage("finished", [{ color: "red", msg: [err.message] }]);
}
}
}
catch(e){
} catch (e) {
stderr(e.toString());
}
finally{
} finally {
lua.global.close();
postmessage("finished", []);
}
Comment thread
MR-Addict marked this conversation as resolved.
Expand Down
11 changes: 11 additions & 0 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@ export default defineConfig({
return `_${name}_${hash}_${lineNumber}`;
}
}
},
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes("node_modules/react-ace")) return "react-ace";
else if (id.includes("node_modules/ace-builds/src-noconflict/mode")) return "ace-modes";
else if (id.includes("node_modules/ace-builds/src-noconflict/theme")) return "ace-themes";
}
}
}
}
});
Loading