Skip to content

Commit 9e06dad

Browse files
committed
first attempt to use iframe for p5
1 parent 0d24014 commit 9e06dad

File tree

16 files changed

+299
-37
lines changed

16 files changed

+299
-37
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ vdirectory.public.json
66
vfiles.json
77

88
packages/markdown/index.html
9+
packages/markdown/public
910

1011
*.zip
1112

packages/markdown/assets/directive-p5/client.js

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.directive-p5 {
2+
display: flex;
3+
justify-content: center;
4+
align-items: center;
5+
margin-bottom: 16px;
6+
overflow: scroll;
7+
}

packages/markdown/dev.md

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,3 @@
11
# Test Site
22

3-
4-
```mermaid
5-
classDiagram
6-
Kryptomat <|-- Caesar
7-
Kryptomat <|-- Vigenere
8-
<<Abstract>> Kryptomat
9-
Kryptomat: -String gt
10-
Kryptomat: -String kt
11-
Kryptomat: +Kryptomat()
12-
Kryptomat: +verschluesseln()*
13-
Kryptomat: +entschlüsseln()*
14-
Kryptomat: -zahlenZuBuchstaben(int pWert) char
15-
Kryptomat: -buchstabenZuZahlen(char pWert) int
16-
Kryptomat: +getGt() String
17-
Kryptomat: +setGt(String pGt) void
18-
Kryptomat: +getKt() String
19-
Kryptomat: +setKt(String pKt) void
20-
21-
class Caesar{
22-
-int schluessel
23-
+Caesar()
24-
+getSchluessel() int
25-
+setSchluessel(int pSchluessel) void
26-
+verschluesseln() void
27-
+entschluesseln() void
28-
}
29-
class Vigenere{
30-
-String schluessel
31-
+Vigenere()
32-
+getSchluessel() String
33-
+setSchluessel(String pSchluessel) void
34-
+verschluesseln() void
35-
+entschluesseln() void
36-
}
37-
```
3+
::p5{src="koch.js"}

packages/markdown/devBuild.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,10 @@ const result = await process(markdown, {
6868
if (typeof p === "string") {
6969
p = [p];
7070
}
71-
if (base === "public" || base === "book") {
72-
return path.posix.join("", ...p);
71+
if (base === "public") {
72+
return path.posix.join("/public", ...p);
73+
} else if (base === "book") {
74+
return path.posix.join("/", ...p);
7375
}
7476
if (base === "assets") {
7577
return path.posix.join("/dist", base, ...p);

packages/markdown/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
"live-server": "^1.2.2",
8282
"mermaid": "11.3.0",
8383
"ncp": "^2.0.0",
84+
"p5": "^1.11.1",
8485
"scratchblocks": "^3.6.4",
8586
"vfile": "^6.0.3",
8687
"vitest": "^1.6.0",

packages/markdown/postbuild.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,20 @@ async function postbuild() {
7979
"hyperbook-excalidraw.umd.js",
8080
),
8181
},
82+
{
83+
src: path.join("./node_modules", "p5", "lib", "p5.min.js"),
84+
dst: path.join("./dist", "assets", "directive-p5", "p5.min.js"),
85+
},
86+
{
87+
src: path.join(
88+
"./node_modules",
89+
"p5",
90+
"lib",
91+
"addons",
92+
"p5.sound.min.js",
93+
),
94+
dst: path.join("./dist", "assets", "directive-p5", "p5.sound.min.js"),
95+
},
8296
];
8397

8498
for (let asset of assets) {

packages/markdown/src/process.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import remarkDirectiveTerm from "./remarkDirectiveTerm";
4646
import remarkLink from "./remarkLink";
4747
import remarkDirectivePagelist from "./remarkDirectivePagelist";
4848
import rehypeQrCode from "./rehypeQrCode";
49+
import rehypeDirectiveP5 from "./rehypeDirectiveP5";
4950

5051
const remark = (ctx: HyperbookContext) => {
5152
const remarkPlugins: PluggableList = [
@@ -107,6 +108,7 @@ const remark = (ctx: HyperbookContext) => {
107108
},
108109
},
109110
],
111+
rehypeDirectiveP5(ctx),
110112
rehypeFormat,
111113
];
112114

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Register directive nodes in mdast:
2+
/// <reference types="mdast-util-directive" />
3+
//
4+
import { HyperbookContext } from "@hyperbook/types";
5+
import { Root } from "mdast";
6+
import fs from "fs";
7+
import path from "path";
8+
import { visit } from "unist-util-visit";
9+
import { VFile } from "vfile";
10+
import {
11+
expectContainerDirective,
12+
isDirective,
13+
registerDirective,
14+
} from "./remarkHelper";
15+
16+
interface CodeBundle {
17+
js?: string;
18+
scripts?: string[];
19+
}
20+
21+
export default (ctx: HyperbookContext) => () => {
22+
const name = "p5";
23+
const cdnLibraryUrl = ctx.makeUrl(
24+
path.posix.join("directive-p5", "p5.min.js"),
25+
"assets",
26+
);
27+
28+
const wrapSketch = (sketchCode?: string) => {
29+
if (sketchCode !== "" && !sketchCode?.includes("setup")) {
30+
return `
31+
function setup() {
32+
createCanvas(100, 100);
33+
background(200);
34+
${sketchCode}
35+
}`;
36+
}
37+
return sketchCode;
38+
};
39+
40+
// see https://github.com/processing/p5.js-website/blob/main/src/components/CodeEmbed/frame.tsx
41+
const wrapInMarkup = (code: CodeBundle) =>
42+
`<!DOCTYPE html>
43+
<meta charset="utf8" />
44+
<base href="${ctx.makeUrl("", "public")}" />
45+
<style type='text/css'>
46+
html, body {
47+
margin: 0;
48+
padding: 0;
49+
}
50+
canvas {
51+
display: block;
52+
}
53+
</style>
54+
${(code.scripts ? [cdnLibraryUrl, ...code.scripts] : []).map((src) => `<script type="text/javascript" src="${src}"></script>`).join("\n")}
55+
<body>Hallo</body>
56+
<script id="code" type="text/javascript">${wrapSketch(code.js) || ""}</script>
57+
${
58+
(code.scripts?.length ?? 0) > 0
59+
? ""
60+
: `
61+
<script type="text/javascript">
62+
// Listen for p5.min.js text content and include in iframe's head as script
63+
window.addEventListener("message", event => {
64+
// Include check to prevent p5.min.js from being loaded twice
65+
const scriptExists = !!document.getElementById("p5ScriptTagInIframe");
66+
if (!scriptExists && event.data?.sender === '${cdnLibraryUrl}') {
67+
const p5ScriptElement = document.createElement('script');
68+
p5ScriptElement.id = "p5ScriptTagInIframe";
69+
p5ScriptElement.type = 'text/javascript';
70+
p5ScriptElement.textContent = event.data.message;
71+
document.head.appendChild(p5ScriptElement);
72+
}
73+
})
74+
</script>
75+
`
76+
}
77+
`.replace(/\u00A0/g, " ");
78+
79+
return (tree: Root, file: VFile) => {
80+
visit(tree, function (node) {
81+
if (node.type === "element" && node.tagName === "p5") {
82+
const { src = "" } = node.properties || {};
83+
84+
expectContainerDirective(node, file, name);
85+
registerDirective(file, name, [], ["style.css"]);
86+
87+
const srcFile = fs.readFileSync(
88+
path.join(ctx.root, "public", String(src)),
89+
"utf8",
90+
);
91+
92+
const srcdoc = wrapInMarkup({
93+
js: srcFile,
94+
scripts: [
95+
ctx.makeUrl(
96+
path.posix.join("directive-p5", "p5.sound.min.js"),
97+
"assets",
98+
),
99+
],
100+
});
101+
node.tagName = "div";
102+
node.properties = {
103+
class: "directive-p5",
104+
};
105+
node.children = [
106+
{
107+
type: "element",
108+
tagName: "iframe",
109+
properties: {
110+
srcdoc: srcdoc,
111+
loading: "eager",
112+
sandbox:
113+
"allow-scripts allow-popups allow-modals allow-forms allow-same-origin",
114+
"aria-label": "Code Preview",
115+
title: "Code Preview",
116+
},
117+
children: [],
118+
},
119+
];
120+
}
121+
});
122+
};
123+
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`remarkDirectiveEmbed > should transform 1`] = `
4+
"
5+
<div class="directive-p5">
6+
<iframe srcdoc="<!DOCTYPE html>
7+
<meta charset=&#x22;utf8&#x22; />
8+
<base href=&#x22;/public&#x22; />
9+
<style type='text/css'>
10+
html, body {
11+
margin: 0;
12+
padding: 0;
13+
}
14+
canvas {
15+
display: block;
16+
}
17+
</style>
18+
<script type=&#x22;text/javascript&#x22; src=&#x22;/assets/directive-p5/p5.min.js&#x22;></script>
19+
<script type=&#x22;text/javascript&#x22; src=&#x22;/assets/directive-p5/p5.sound.min.js&#x22;></script>
20+
<body>Hallo</body>
21+
<script id=&#x22;code&#x22; type=&#x22;text/javascript&#x22;>function setup() {
22+
createCanvas(720, 200);
23+
background(0);
24+
}
25+
function draw() {
26+
circle(mouseX, mouseY, 50);
27+
}
28+
</script>
29+
30+
" loading="eager" sandbox="allow-scripts allow-popups allow-modals allow-forms allow-same-origin" aria-label="Code Preview" title="Code Preview"></iframe>
31+
</div>
32+
"
33+
`;

0 commit comments

Comments
 (0)