mr-md 2.0.0-beta → 2.1.1-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +72 -33
- package/dist/cli/dev.d.ts.map +1 -1
- package/dist/cli/dev.js +16 -8
- package/dist/cli/generate.js +1 -1
- package/dist/client/app.js +55 -26
- package/dist/renderer/blocks.d.ts.map +1 -1
- package/dist/renderer/blocks.js +21 -12
- package/dist/renderer/html.d.ts +1 -0
- package/dist/renderer/html.d.ts.map +1 -1
- package/dist/renderer/html.js +25 -20
- package/dist/renderer/index.d.ts.map +1 -1
- package/dist/renderer/index.js +5 -91
- package/dist/renderer/markdown.d.ts.map +1 -1
- package/dist/renderer/markdown.js +49 -12
- package/dist/renderer/utils.d.ts.map +1 -1
- package/dist/renderer/utils.js +22 -7
- package/dist/styles/theme.css +239 -78
- package/dist/types.d.ts +4 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -5
- package/src/builder.ts +78 -36
- package/src/cli/dev.ts +17 -8
- package/src/cli/generate.ts +1 -1
- package/src/client/app.js +55 -26
- package/src/renderer/blocks.ts +21 -12
- package/src/renderer/html.ts +46 -23
- package/src/renderer/index.ts +13 -91
- package/src/renderer/markdown.ts +54 -14
- package/src/renderer/utils.ts +25 -7
- package/src/styles/theme.css +239 -78
- package/src/types.ts +4 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Mr Markdown
|
|
2
2
|
|
|
3
|
-
Mr Markdown is an opinionated TypeScript SDK for building interactive
|
|
3
|
+
Mr Markdown is an opinionated TypeScript SDK for building interactive learning pages. It is designed for lessons that mix prose, LaTeX, simulations, media, video, and quizzes without making every author build layout and interaction chrome from scratch.
|
|
4
4
|
|
|
5
5
|
## Documentation
|
|
6
6
|
|
package/dist/builder.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEX,gBAAgB,EAEhB,YAAY,EACZ,YAAY,EACZ,OAAO,EAGP,UAAU,EAEV,cAAc,EAId,YAAY,EACZ,MAAM,EACN,UAAU,EAGV,YAAY,EACZ,SAAS,EAIT,iBAAiB,EAEjB,cAAc,EACd,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"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"}
|
package/dist/builder.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { render, renderChapter } from "./renderer/index.js";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
4
7
|
function getCallerDir() {
|
|
5
8
|
const err = new Error();
|
|
6
9
|
const stack = err.stack?.split("\n");
|
|
@@ -16,7 +19,7 @@ function getCallerDir() {
|
|
|
16
19
|
p = p.replace(/^file:\/\//, "");
|
|
17
20
|
}
|
|
18
21
|
if (p.startsWith("/") && p[2] === ":") {
|
|
19
|
-
p = p.substring(1);
|
|
22
|
+
p = p.substring(1);
|
|
20
23
|
}
|
|
21
24
|
if (!builderFilePath) {
|
|
22
25
|
builderFilePath = p;
|
|
@@ -30,6 +33,60 @@ function getCallerDir() {
|
|
|
30
33
|
}
|
|
31
34
|
return undefined;
|
|
32
35
|
}
|
|
36
|
+
function copyAssets(outDir) {
|
|
37
|
+
const assetsDir = path.join(outDir, "assets");
|
|
38
|
+
if (!fs.existsSync(assetsDir))
|
|
39
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
40
|
+
const srcStylesDir = path.join(__dirname, "styles");
|
|
41
|
+
const srcClientDir = path.join(__dirname, "client");
|
|
42
|
+
const fallbackStylesDir = path.join(__dirname, "../src/styles");
|
|
43
|
+
const fallbackClientDir = path.join(__dirname, "../src/client");
|
|
44
|
+
const copyDir = (src, dest) => {
|
|
45
|
+
if (!fs.existsSync(src))
|
|
46
|
+
return;
|
|
47
|
+
if (!fs.existsSync(dest))
|
|
48
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
49
|
+
for (const file of fs.readdirSync(src)) {
|
|
50
|
+
const srcFile = path.join(src, file);
|
|
51
|
+
const destFile = path.join(dest, file);
|
|
52
|
+
if (fs.statSync(srcFile).isDirectory()) {
|
|
53
|
+
copyDir(srcFile, destFile);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
fs.copyFileSync(srcFile, destFile);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
if (fs.existsSync(srcStylesDir))
|
|
61
|
+
copyDir(srcStylesDir, assetsDir);
|
|
62
|
+
else
|
|
63
|
+
copyDir(fallbackStylesDir, assetsDir);
|
|
64
|
+
if (fs.existsSync(srcClientDir))
|
|
65
|
+
copyDir(srcClientDir, assetsDir);
|
|
66
|
+
else
|
|
67
|
+
copyDir(fallbackClientDir, assetsDir);
|
|
68
|
+
}
|
|
69
|
+
function mergeOptions(options, callerDir) {
|
|
70
|
+
const merged = {
|
|
71
|
+
outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : path.join(process.cwd(), "out")),
|
|
72
|
+
contentBase: options.contentBase ?? callerDir ?? process.cwd(),
|
|
73
|
+
theme: options.theme ?? "auto",
|
|
74
|
+
palette: options.palette ?? "ink",
|
|
75
|
+
strict: options.strict ?? process.env.NODE_ENV !== "development",
|
|
76
|
+
standalone: options.standalone ?? true,
|
|
77
|
+
};
|
|
78
|
+
for (const [k, v] of Object.entries(options)) {
|
|
79
|
+
if (v !== undefined && k !== "preset") {
|
|
80
|
+
merged[k] = v;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
merged.preset = {
|
|
84
|
+
layout: options.preset?.layout ?? "lesson",
|
|
85
|
+
density: options.preset?.density ?? "comfortable",
|
|
86
|
+
tone: options.preset?.tone ?? "scholarly",
|
|
87
|
+
};
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
33
90
|
// ─── LessonBuilder ────────────────────────────────────────────────────────────
|
|
34
91
|
export class LessonBuilder {
|
|
35
92
|
meta;
|
|
@@ -48,20 +105,7 @@ export class LessonBuilder {
|
|
|
48
105
|
title,
|
|
49
106
|
slug,
|
|
50
107
|
};
|
|
51
|
-
this.options =
|
|
52
|
-
outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
|
|
53
|
-
contentBase: options.contentBase ?? callerDir ?? ".",
|
|
54
|
-
theme: options.theme ?? "auto",
|
|
55
|
-
palette: options.palette ?? "ink",
|
|
56
|
-
strict: options.strict ?? process.env.NODE_ENV !== "development",
|
|
57
|
-
preset: {
|
|
58
|
-
layout: "lesson",
|
|
59
|
-
density: "comfortable",
|
|
60
|
-
tone: "scholarly",
|
|
61
|
-
...options.preset,
|
|
62
|
-
},
|
|
63
|
-
...options,
|
|
64
|
-
};
|
|
108
|
+
this.options = mergeOptions(options, callerDir);
|
|
65
109
|
}
|
|
66
110
|
// ── Meta setters ────────────────────────────────────────────────────────────
|
|
67
111
|
/**
|
|
@@ -373,10 +417,14 @@ export class LessonBuilder {
|
|
|
373
417
|
const lesson = { meta: this.meta, blocks: this.blocks };
|
|
374
418
|
const html = render(lesson, this.options);
|
|
375
419
|
const outDir = path.resolve(this.options.outDir);
|
|
376
|
-
if (!fs.existsSync(outDir))
|
|
377
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
378
420
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
421
|
+
const outPathDir = path.dirname(outPath);
|
|
422
|
+
if (!fs.existsSync(outPathDir))
|
|
423
|
+
fs.mkdirSync(outPathDir, { recursive: true });
|
|
379
424
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
425
|
+
if (this.options.standalone === false) {
|
|
426
|
+
copyAssets(outDir);
|
|
427
|
+
}
|
|
380
428
|
const relPath = path.relative(process.cwd(), outPath);
|
|
381
429
|
console.log(` ✓ Built lesson (${this.blocks.length} blocks) → ${relPath}`);
|
|
382
430
|
return outPath;
|
|
@@ -504,20 +552,7 @@ export class ChapterBuilder {
|
|
|
504
552
|
title,
|
|
505
553
|
slug,
|
|
506
554
|
};
|
|
507
|
-
this.options =
|
|
508
|
-
outDir: options.outDir ?? (callerDir ? path.join(callerDir, "out") : "./out"),
|
|
509
|
-
contentBase: options.contentBase ?? callerDir ?? ".",
|
|
510
|
-
theme: options.theme ?? "auto",
|
|
511
|
-
palette: options.palette ?? "ink",
|
|
512
|
-
strict: options.strict ?? process.env.NODE_ENV !== "development",
|
|
513
|
-
preset: {
|
|
514
|
-
layout: "lesson",
|
|
515
|
-
density: "comfortable",
|
|
516
|
-
tone: "scholarly",
|
|
517
|
-
...options.preset,
|
|
518
|
-
},
|
|
519
|
-
...options,
|
|
520
|
-
};
|
|
555
|
+
this.options = mergeOptions(options, callerDir);
|
|
521
556
|
}
|
|
522
557
|
slug(slug) {
|
|
523
558
|
this.meta.slug = slug;
|
|
@@ -561,10 +596,14 @@ export class ChapterBuilder {
|
|
|
561
596
|
const chapterData = { meta: this.meta, lessons };
|
|
562
597
|
const html = renderChapter(chapterData, this.options);
|
|
563
598
|
const outDir = path.resolve(this.options.outDir);
|
|
564
|
-
if (!fs.existsSync(outDir))
|
|
565
|
-
fs.mkdirSync(outDir, { recursive: true });
|
|
566
599
|
const outPath = path.join(outDir, `${this.meta.slug}.html`);
|
|
600
|
+
const outPathDir = path.dirname(outPath);
|
|
601
|
+
if (!fs.existsSync(outPathDir))
|
|
602
|
+
fs.mkdirSync(outPathDir, { recursive: true });
|
|
567
603
|
fs.writeFileSync(outPath, html, "utf-8");
|
|
604
|
+
if (this.options.standalone === false) {
|
|
605
|
+
copyAssets(outDir);
|
|
606
|
+
}
|
|
568
607
|
const relPath = path.relative(process.cwd(), outPath);
|
|
569
608
|
console.log(` ✓ Built chapter (${this.lessonBuilders.length} lessons) → ${relPath}`);
|
|
570
609
|
return outPath;
|
package/dist/cli/dev.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/cli/dev.ts"],"names":[],"mappings":"AAMA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,iBAwG1C"}
|
package/dist/cli/dev.js
CHANGED
|
@@ -12,7 +12,7 @@ export async function runDev(args) {
|
|
|
12
12
|
for (const entry of entryPoints) {
|
|
13
13
|
const entryPath = path.join(dir, entry);
|
|
14
14
|
if (fs.existsSync(entryPath)) {
|
|
15
|
-
exec(`NODE_ENV=development bun ${entryPath}`, (err, stdout, stderr) => {
|
|
15
|
+
exec(`NODE_ENV=development bun "${entryPath}"`, (err, stdout, stderr) => {
|
|
16
16
|
if (err)
|
|
17
17
|
console.error("Build failed:", stderr);
|
|
18
18
|
else {
|
|
@@ -45,8 +45,9 @@ export async function runDev(args) {
|
|
|
45
45
|
if (srv.upgrade(req))
|
|
46
46
|
return;
|
|
47
47
|
const url = new URL(req.url);
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
const decodedPath = decodeURIComponent(url.pathname);
|
|
49
|
+
let filePath = path.resolve(outDir, "." + decodedPath);
|
|
50
|
+
if (filePath.endsWith(path.sep)) {
|
|
50
51
|
const files = fs.existsSync(outDir) ? fs.readdirSync(outDir) : [];
|
|
51
52
|
const htmlFiles = files.filter(f => f.endsWith(".html"));
|
|
52
53
|
if (htmlFiles.includes("index.html")) {
|
|
@@ -61,24 +62,31 @@ export async function runDev(args) {
|
|
|
61
62
|
filePath = path.join(outDir, htmlFiles[0]);
|
|
62
63
|
}
|
|
63
64
|
else {
|
|
64
|
-
filePath
|
|
65
|
+
filePath = path.join(outDir, "index.html");
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
68
|
}
|
|
69
|
+
if (!filePath.startsWith(outDir + path.sep) && filePath !== outDir) {
|
|
70
|
+
return new Response("Forbidden", { status: 403 });
|
|
71
|
+
}
|
|
68
72
|
if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
|
|
69
73
|
if (filePath.endsWith(".html")) {
|
|
70
74
|
const file = Bun.file(filePath);
|
|
71
75
|
let text = await file.text();
|
|
72
|
-
|
|
76
|
+
const lastBodyIndex = text.lastIndexOf("</body>");
|
|
77
|
+
if (lastBodyIndex !== -1) {
|
|
78
|
+
text = text.slice(0, lastBodyIndex) + `<script>
|
|
73
79
|
const ws = new WebSocket("ws://localhost:3000/");
|
|
74
80
|
ws.onmessage = (e) => { if (e.data === "reload") location.reload(); };
|
|
75
|
-
</script></body>`);
|
|
81
|
+
</script></body>` + text.slice(lastBodyIndex + 7);
|
|
82
|
+
}
|
|
76
83
|
return new Response(text, { headers: { "Content-Type": "text/html" } });
|
|
77
84
|
}
|
|
78
85
|
return new Response(Bun.file(filePath));
|
|
79
86
|
}
|
|
80
|
-
const
|
|
81
|
-
|
|
87
|
+
const baseDir = path.resolve(process.cwd(), dir);
|
|
88
|
+
const srcPath = path.resolve(baseDir, "." + decodedPath);
|
|
89
|
+
if (srcPath.startsWith(baseDir + path.sep) && fs.existsSync(srcPath) && fs.statSync(srcPath).isFile()) {
|
|
82
90
|
return new Response(Bun.file(srcPath));
|
|
83
91
|
}
|
|
84
92
|
return new Response("Not found", { status: 404 });
|
package/dist/cli/generate.js
CHANGED
|
@@ -26,7 +26,7 @@ export async function runGenerate(args) {
|
|
|
26
26
|
console.error("Usage: md g <ch|le|qu|chapter|lesson|quiz> <name>");
|
|
27
27
|
process.exit(1);
|
|
28
28
|
}
|
|
29
|
-
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
29
|
+
const name = rawName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "item";
|
|
30
30
|
const cwd = process.cwd();
|
|
31
31
|
switch (type) {
|
|
32
32
|
case "ch":
|
package/dist/client/app.js
CHANGED
|
@@ -15,11 +15,10 @@ function bkSimDoc(js, props, loop, dependencies) {
|
|
|
15
15
|
";window.bkSetupCalled=false;" +
|
|
16
16
|
'window.bkCanvasPoint=function(e,c){const r=(c||e.currentTarget||e.target).getBoundingClientRect(),w=(c&&c.__bkLogicalW)||800,h=(c&&c.__bkLogicalH)||500;return{x:(e.clientX-r.left)*w/r.width,y:(e.clientY-r.top)*h/r.height}};' +
|
|
17
17
|
'window.bkFitCanvas=function(c,reqW,reqH,o){if(!c)return{scale:1,width:reqW,height:reqH,cssScale:1};const d=window.devicePixelRatio||1;const w=reqW;const h=reqH;c.__bkLogicalW=w;c.__bkLogicalH=h;c.style.width=w+"px";c.style.height=h+"px";c.style.position="relative";c.style.left="auto";c.style.top="auto";c.style.transformOrigin="center center";const sx=window.innerWidth/w,sy=window.innerHeight/h,cssS=Math.max(sx,sy);c.style.transform="scale("+cssS+")";const pw=Math.max(1,Math.round(w*d)),ph=Math.max(1,Math.round(h*d));if(!o||o.bitmap!==false){if(c.width!==pw||c.height!==ph){c.width=pw;c.height=ph}}return{scale:d,width:w,height:h,cssScale:cssS}};' +
|
|
18
|
-
'window.bkSetup=function(w,h,f){window.bkSetupCalled=true;const c=document.getElementById("c");if(!c)return;const ctx=c.getContext("2d");let loopId=null;let fit=window.bkFitCanvas(c,w,h);function l(){if(window.innerWidth>=32&&window.innerHeight>=32){ctx.save();ctx.scale(fit.scale,fit.scale);f(ctx,fit.width,fit.height);ctx.restore()}if(window.__loop){loopId=requestAnimationFrame(l)}else{loopId=null}}function i(){if(window.innerWidth>=32&&window.innerHeight>=32){fit=window.bkFitCanvas(c,w,h);l()}else{requestAnimationFrame(i)}}i();window.addEventListener("resize",function(){fit=window.bkFitCanvas(c,w,h);if(!window.__loop&&window.innerWidth>=32&&window.innerHeight>=32&&!loopId){ctx.save();ctx.scale(fit.scale,fit.scale);f(ctx,fit.width,fit.height);ctx.restore()}});window.addEventListener("message",function(event){if(!event.data)return;if(event.data.type==="bk:play"){window.__loop=true;if(!loopId)loopId=requestAnimationFrame(l)}else if(event.data.type==="bk:pause"){window.__loop=false}});};' +
|
|
19
|
-
'window.addEventListener("message",function(event){if(!event.data||event.data.type!=="bk:set-props")return;window.__simProps=Object.assign({},window.__simProps,event.data.props);window.dispatchEvent(new CustomEvent("bk:props",{detail:window.__simProps}));});' +
|
|
18
|
+
'window.bkSetup=function(w,h,f){window.bkSetupCalled=true;const c=document.getElementById("c");if(!c)return;const ctx=c.getContext("2d");let loopId=null;let fit=window.bkFitCanvas(c,w,h);function l(){if(window.innerWidth>=32&&window.innerHeight>=32){ctx.save();ctx.scale(fit.scale,fit.scale);f(ctx,fit.width,fit.height);ctx.restore()}if(window.__loop){loopId=requestAnimationFrame(l)}else{loopId=null}}function i(){if(window.innerWidth>=32&&window.innerHeight>=32){fit=window.bkFitCanvas(c,w,h);l()}else{requestAnimationFrame(i)}}i();window.addEventListener("resize",function(){fit=window.bkFitCanvas(c,w,h);if(!window.__loop&&window.innerWidth>=32&&window.innerHeight>=32&&!loopId){ctx.save();ctx.scale(fit.scale,fit.scale);f(ctx,fit.width,fit.height);ctx.restore()}});window.addEventListener("message",function(event){if(event.source!==window.parent)return;if(!event.data)return;if(event.data.type==="bk:play"){window.__loop=true;if(!loopId)loopId=requestAnimationFrame(l)}else if(event.data.type==="bk:pause"){window.__loop=false}else if(event.data.type==="bk:set-props"){window.__simProps=Object.assign({},window.__simProps,event.data.props);window.dispatchEvent(new CustomEvent("bk:props",{detail:window.__simProps}));}});};' +
|
|
20
19
|
"try{" +
|
|
21
20
|
js +
|
|
22
|
-
'}catch(e){console.error("Simulation Error:",e);document.
|
|
21
|
+
'}catch(e){console.error("Simulation Error:",e);const d=document.createElement("div");d.style.cssText="padding:20px;color:red;font-family:monospace";d.textContent="Error: "+e.message;document.body.innerHTML="";document.body.appendChild(d);}' +
|
|
23
22
|
"if(!window.bkSetupCalled){function fallbackScale(){window.bkFitCanvas(document.getElementById('c'),800,500,{bitmap:false});}fallbackScale();window.addEventListener('resize', fallbackScale);}" +
|
|
24
23
|
"</" + "script></body></html>"
|
|
25
24
|
);
|
|
@@ -76,27 +75,14 @@ function bkWireMaximizeControls() {
|
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
function bkWireInteractiveFrames() {
|
|
79
|
-
const
|
|
78
|
+
const interactiveHandler = (e) => {
|
|
80
79
|
const obj = e.target.closest?.(".bk-object");
|
|
81
|
-
|
|
82
|
-
const frame = obj.querySelector(".bk-embed-interactive");
|
|
83
|
-
if (frame) {
|
|
84
|
-
frame.classList.add("is-interactive");
|
|
85
|
-
const iframe = frame.querySelector("iframe");
|
|
86
|
-
if (iframe && iframe.contentWindow) {
|
|
87
|
-
iframe.contentWindow.postMessage({ type: "bk:play" }, "*");
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
document.addEventListener("pointerdown", activate, { passive: true });
|
|
92
|
-
document.addEventListener("focusin", activate, { passive: true });
|
|
80
|
+
const activateFrame = obj ? obj.querySelector(".bk-embed-interactive") : null;
|
|
93
81
|
|
|
94
|
-
const exitInteractive = (e) => {
|
|
95
82
|
document
|
|
96
83
|
.querySelectorAll(".bk-embed-interactive.is-interactive")
|
|
97
84
|
.forEach((frame) => {
|
|
98
|
-
|
|
99
|
-
if (!container.contains(e.target)) {
|
|
85
|
+
if (frame !== activateFrame) {
|
|
100
86
|
frame.classList.remove("is-interactive");
|
|
101
87
|
const iframe = frame.querySelector("iframe");
|
|
102
88
|
if (iframe && iframe.contentWindow) {
|
|
@@ -104,9 +90,17 @@ function bkWireInteractiveFrames() {
|
|
|
104
90
|
}
|
|
105
91
|
}
|
|
106
92
|
});
|
|
93
|
+
|
|
94
|
+
if (activateFrame && !activateFrame.classList.contains("is-interactive")) {
|
|
95
|
+
activateFrame.classList.add("is-interactive");
|
|
96
|
+
const iframe = activateFrame.querySelector("iframe");
|
|
97
|
+
if (iframe && iframe.contentWindow) {
|
|
98
|
+
iframe.contentWindow.postMessage({ type: "bk:play" }, "*");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
107
101
|
};
|
|
108
|
-
document.addEventListener("pointerdown",
|
|
109
|
-
document.addEventListener("focusin",
|
|
102
|
+
document.addEventListener("pointerdown", interactiveHandler, { passive: true });
|
|
103
|
+
document.addEventListener("focusin", interactiveHandler, { passive: true });
|
|
110
104
|
|
|
111
105
|
const obs = new IntersectionObserver((entries) => {
|
|
112
106
|
entries.forEach((e) => {
|
|
@@ -192,6 +186,9 @@ function bkBroadcastTheme(targetWindow) {
|
|
|
192
186
|
}
|
|
193
187
|
|
|
194
188
|
window.addEventListener("message", function(e) {
|
|
189
|
+
const isValidSource = Array.from(document.querySelectorAll("iframe")).some(f => f.contentWindow === e.source);
|
|
190
|
+
if (!isValidSource) return;
|
|
191
|
+
|
|
195
192
|
if (e.data && e.data.type === "bk:request-theme") {
|
|
196
193
|
// Small delay to ensure CSS has applied if this happens right on load
|
|
197
194
|
requestAnimationFrame(() => bkBroadcastTheme(e.source));
|
|
@@ -266,13 +263,16 @@ function bkWireThemeControls() {
|
|
|
266
263
|
|
|
267
264
|
if (savedTheme) {
|
|
268
265
|
updateThemeBtn(savedTheme);
|
|
266
|
+
root.setAttribute("data-theme", savedTheme);
|
|
269
267
|
}
|
|
270
268
|
if (savedPalette) {
|
|
271
269
|
const normalizedPalette = savedPalette === "green" ? "field" : savedPalette;
|
|
272
270
|
updatePaletteBtn(normalizedPalette);
|
|
271
|
+
root.setAttribute("data-palette", normalizedPalette);
|
|
273
272
|
}
|
|
274
273
|
if (savedUi) {
|
|
275
274
|
updateUiBtn(savedUi);
|
|
275
|
+
root.setAttribute("data-ui", savedUi);
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
button &&
|
|
@@ -344,15 +344,44 @@ function bkWireThemeControls() {
|
|
|
344
344
|
}
|
|
345
345
|
|
|
346
346
|
// Quiz interaction
|
|
347
|
-
// biome-ignore lint/correctness/noUnusedVariables: Used in generated HTML
|
|
348
347
|
function bkAnswer(btn, qid) {
|
|
349
|
-
// biome-ignore lint/correctness/noUnusedVariables: Kept for clarity
|
|
350
|
-
const isCorrect = btn.dataset.correct === "true";
|
|
351
348
|
const question = document.getElementById(qid);
|
|
349
|
+
if (!question) return;
|
|
350
|
+
const quiz = question.closest(".bk-quiz");
|
|
351
|
+
const dataEl = quiz ? quiz.querySelector(".bk-quiz-data") : null;
|
|
352
|
+
let isCorrect = false;
|
|
353
|
+
|
|
354
|
+
if (dataEl) {
|
|
355
|
+
try {
|
|
356
|
+
const answers = JSON.parse(dataEl.textContent || "[]");
|
|
357
|
+
// qid is format "quiz-IDX-qQI"
|
|
358
|
+
const match = qid.match(/-q(\d+)$/);
|
|
359
|
+
if (match) {
|
|
360
|
+
const qi = parseInt(match[1], 10);
|
|
361
|
+
const optIdx = parseInt(btn.dataset.optIdx, 10);
|
|
362
|
+
isCorrect = answers[qi] === optIdx;
|
|
363
|
+
}
|
|
364
|
+
} catch(e) {}
|
|
365
|
+
}
|
|
366
|
+
|
|
352
367
|
question.querySelectorAll(".bk-opt").forEach((b) => {
|
|
353
|
-
|
|
368
|
+
b.disabled = true; // Disable buttons for screen readers
|
|
369
|
+
const optIdx = parseInt(b.dataset.optIdx, 10);
|
|
370
|
+
// If we know the answer, highlight it
|
|
371
|
+
if (dataEl) {
|
|
372
|
+
try {
|
|
373
|
+
const answers = JSON.parse(dataEl.textContent || "[]");
|
|
374
|
+
const match = qid.match(/-q(\d+)$/);
|
|
375
|
+
if (match && answers[parseInt(match[1], 10)] === optIdx) {
|
|
376
|
+
b.classList.add("correct");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
} catch(e) {}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (b === btn && isCorrect) {
|
|
354
383
|
b.classList.add("correct");
|
|
355
|
-
} else if (b === btn) {
|
|
384
|
+
} else if (b === btn && !isCorrect) {
|
|
356
385
|
b.classList.add("wrong");
|
|
357
386
|
} else {
|
|
358
387
|
b.classList.add("disabled");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/renderer/blocks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAA0B,MAAM,aAAa,CAAC;AAC/E,OAAO,EACN,WAAW,EAKX,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,KAAK,OAAO,EAAmC,MAAM,YAAY,CAAC;AAG3E,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO3C;AACD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAID,iBAAS,WAAW,CACnB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CAuBxC;AAED,iBAAS,gBAAgB,CACxB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/renderer/blocks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAA0B,MAAM,aAAa,CAAC;AAC/E,OAAO,EACN,WAAW,EAKX,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,KAAK,OAAO,EAAmC,MAAM,YAAY,CAAC;AAG3E,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO3C;AACD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAID,iBAAS,WAAW,CACnB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CAuBxC;AAED,iBAAS,gBAAgB,CACxB,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,YAAY,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,CA4RxC;AAyMD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/renderer/blocks.js
CHANGED
|
@@ -176,7 +176,17 @@ function renderBlockInner(block, idx, options) {
|
|
|
176
176
|
loading="lazy"
|
|
177
177
|
style="width:100%;height:100%;border:none;display:block;">
|
|
178
178
|
</iframe>
|
|
179
|
-
</div
|
|
179
|
+
</div>
|
|
180
|
+
<script>
|
|
181
|
+
if (!window._bkYtBlurSetup) {
|
|
182
|
+
window._bkYtBlurSetup = true;
|
|
183
|
+
window.addEventListener('mousemove', function() {
|
|
184
|
+
if (document.activeElement && document.activeElement.tagName === 'IFRAME') {
|
|
185
|
+
document.activeElement.blur();
|
|
186
|
+
}
|
|
187
|
+
}, { passive: true });
|
|
188
|
+
}
|
|
189
|
+
</script>`, "neutral"),
|
|
180
190
|
};
|
|
181
191
|
}
|
|
182
192
|
case "latex": {
|
|
@@ -191,7 +201,7 @@ function renderBlockInner(block, idx, options) {
|
|
|
191
201
|
case "columns": {
|
|
192
202
|
return {
|
|
193
203
|
html: blockChrome("Columns", block.label, block.caption, `<div class="bk-columns" style="grid-template-columns:${block.columns
|
|
194
|
-
.map((column) => column.width ?? "minmax(0, 1fr)")
|
|
204
|
+
.map((column) => escAttr(column.width ?? "minmax(0, 1fr)"))
|
|
195
205
|
.join(" ")}">
|
|
196
206
|
${block.columns
|
|
197
207
|
.map((column) => {
|
|
@@ -236,6 +246,7 @@ function renderBlockInner(block, idx, options) {
|
|
|
236
246
|
<div class="bk-quiz-body">
|
|
237
247
|
${quiz.questions.map((q, qi) => renderQuestion(q, `quiz-${idx}`, qi)).join("\n")}
|
|
238
248
|
</div>
|
|
249
|
+
<script type="application/json" class="bk-quiz-data">${escapeScriptJson(quiz.questions.map(q => q.answer))}</script>
|
|
239
250
|
</div>`,
|
|
240
251
|
navItems: [{
|
|
241
252
|
id: `quiz-${idx}`,
|
|
@@ -254,7 +265,7 @@ function renderQuestion(q, quizId, qi) {
|
|
|
254
265
|
const qid = `${quizId}-q${qi}`;
|
|
255
266
|
const options = q.options
|
|
256
267
|
.map((opt, oi) => `
|
|
257
|
-
<button class="bk-opt" data-
|
|
268
|
+
<button class="bk-opt" data-opt-idx="${oi}" onclick="bkAnswer(this,'${escAttr(qid)}')">
|
|
258
269
|
<span class="bk-opt-dot"></span><span class="bk-opt-text">${mdInline(opt)}</span>
|
|
259
270
|
</button>`)
|
|
260
271
|
.join("");
|
|
@@ -423,10 +434,14 @@ window.addEventListener("message", (event) => {
|
|
|
423
434
|
window.dispatchEvent(new CustomEvent("bk:props", { detail: window.__simProps }));
|
|
424
435
|
});
|
|
425
436
|
try {
|
|
426
|
-
${js}
|
|
437
|
+
${js.replace(/<\/script>/gi, "<\\/script>")}
|
|
427
438
|
} catch (e) {
|
|
428
439
|
console.error("Simulation Error:", e);
|
|
429
|
-
|
|
440
|
+
const errDiv = document.createElement('div');
|
|
441
|
+
errDiv.style.cssText = "padding:20px;color:red;font-family:monospace";
|
|
442
|
+
errDiv.textContent = 'Error: ' + e.message;
|
|
443
|
+
document.body.innerHTML = '';
|
|
444
|
+
document.body.appendChild(errDiv);
|
|
430
445
|
}
|
|
431
446
|
if (!window.bkSetupCalled) {
|
|
432
447
|
function fallbackScale() {
|
|
@@ -437,12 +452,6 @@ if (!window.bkSetupCalled) {
|
|
|
437
452
|
}
|
|
438
453
|
</script>
|
|
439
454
|
</body></html>`;
|
|
440
|
-
|
|
441
|
-
return doc
|
|
442
|
-
.replace(/&/g, "&")
|
|
443
|
-
.replace(/"/g, """)
|
|
444
|
-
.replace(/'/g, "'")
|
|
445
|
-
.replace(/</g, "<")
|
|
446
|
-
.replace(/>/g, ">");
|
|
455
|
+
return escAttr(doc);
|
|
447
456
|
}
|
|
448
457
|
export { blockChrome, renderBlock, renderBlockInner };
|
package/dist/renderer/html.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BuildOptions, Lesson } from "../types.js";
|
|
2
2
|
import type { NavItem } from "./utils.js";
|
|
3
3
|
declare function renderNavItem(item: NavItem): string;
|
|
4
|
+
export declare function renderLayout(title: string, description: string | undefined, navHtml: string, contentHtml: string, opts: BuildOptions, extraSidebar?: string): string;
|
|
4
5
|
declare function renderPage(lesson: Lesson, navItems: NavItem[], bodyHtml: string, opts: BuildOptions): string;
|
|
5
6
|
declare function pageCSS(): string;
|
|
6
7
|
declare function clientScript(): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/renderer/html.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,iBAAS,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAU5C;AAsBD,iBAAS,UAAU,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,YAAY,GAChB,MAAM,
|
|
1
|
+
{"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/renderer/html.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1C,iBAAS,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAU5C;AAsBD,wBAAgB,YAAY,CAC3B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,YAAY,EAClB,YAAY,GAAE,MAAW,GACvB,MAAM,CA6GR;AAED,iBAAS,UAAU,CAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,YAAY,GAChB,MAAM,CA8BR;AAID,iBAAS,OAAO,IAAI,MAAM,CAKzB;AAID,iBAAS,YAAY,IAAI,MAAM,CAE9B;AAED,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC"}
|
package/dist/renderer/html.js
CHANGED
|
@@ -34,7 +34,7 @@ function renderEndNav(lesson) {
|
|
|
34
34
|
` : `<div class="bk-end-link" style="visibility:hidden"></div>`}
|
|
35
35
|
</nav>`;
|
|
36
36
|
}
|
|
37
|
-
function
|
|
37
|
+
export function renderLayout(title, description, navHtml, contentHtml, opts, extraSidebar = "") {
|
|
38
38
|
const theme = opts.theme ?? "light";
|
|
39
39
|
const schemeAttr = `data-theme="${theme}"`;
|
|
40
40
|
const preset = opts.preset ?? {};
|
|
@@ -43,8 +43,7 @@ function renderPage(lesson, navItems, bodyHtml, opts) {
|
|
|
43
43
|
const tone = preset.tone ?? "scholarly";
|
|
44
44
|
const palette = opts.palette ?? "ink";
|
|
45
45
|
const ui = opts.ui ?? "standard";
|
|
46
|
-
const
|
|
47
|
-
const endNavHtml = renderEndNav(lesson);
|
|
46
|
+
const safeFont = opts.font ? opts.font.replace(/[;{}<>\\]/g, "") : "";
|
|
48
47
|
return `<!DOCTYPE html>
|
|
49
48
|
<html lang="en" data-palette="${palette}" data-ui="${ui}" ${schemeAttr}>
|
|
50
49
|
<head>
|
|
@@ -61,26 +60,21 @@ function renderPage(lesson, navItems, bodyHtml, opts) {
|
|
|
61
60
|
</script>
|
|
62
61
|
<meta charset="UTF-8">
|
|
63
62
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
64
|
-
<title>${escHtml(
|
|
65
|
-
${
|
|
63
|
+
<title>${escHtml(title)}</title>
|
|
64
|
+
${description ? `<meta name="description" content="${escHtml(description)}">` : ""}
|
|
66
65
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.47/dist/katex.min.css">
|
|
67
66
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,650;9..144,760&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&family=Archivo:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&family=Syne:wght@600;700;800&family=Playfair+Display:ital,wght@0,400..700;1,400..700&family=Lora:ital,wght@0,400..700;1,400..700&display=swap" rel="stylesheet">
|
|
68
67
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/github-dark.min.css">
|
|
69
68
|
${opts.head ?? ""}
|
|
70
|
-
|
|
71
|
-
${opts.font ? `:root { --font-sans: ${opts.font}, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }` : ""}
|
|
72
|
-
${pageCSS()}
|
|
73
|
-
</style>
|
|
69
|
+
${opts.standalone === false ? `<link rel="stylesheet" href="assets/theme.css?v=${Date.now()}">` : `<style>\n${safeFont ? `:root { --font-sans: ${safeFont}, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }` : ""}\n${pageCSS()}\n</style>`}
|
|
74
70
|
</head>
|
|
75
71
|
<body class="bk-layout-${layout} bk-density-${density} bk-tone-${tone}">
|
|
76
72
|
<div class="bk-shell">
|
|
77
73
|
<aside class="bk-sidebar">
|
|
78
74
|
<div class="bk-sidebar-inner">
|
|
79
75
|
<div class="bk-sidebar-header">
|
|
80
|
-
${
|
|
81
|
-
<div class="bk-sidebar-title">${escHtml(
|
|
82
|
-
${lesson.meta.author ? `<div class="bk-sidebar-author">By ${escHtml(lesson.meta.author)}</div>` : ""}
|
|
83
|
-
${lesson.meta.tags?.length ? `<div class="bk-tag-row">${lesson.meta.tags.map((tag) => `<span>${escHtml(tag)}</span>`).join("")}</div>` : ""}
|
|
76
|
+
${extraSidebar}
|
|
77
|
+
<div class="bk-sidebar-title">${escHtml(title)}</div>
|
|
84
78
|
</div>
|
|
85
79
|
<nav class="bk-nav">${navHtml}</nav>
|
|
86
80
|
<div class="bk-sidebar-footer">
|
|
@@ -142,6 +136,22 @@ ${pageCSS()}
|
|
|
142
136
|
<button class="bk-sidebar-expand" id="bk-sidebar-expand" type="button" aria-label="Expand sidebar">
|
|
143
137
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 18l6-6-6-6"/></svg>
|
|
144
138
|
</button>
|
|
139
|
+
${contentHtml}
|
|
140
|
+
</main>
|
|
141
|
+
</div>
|
|
142
|
+
${opts.standalone === false ? `<script src="assets/app.js?v=${Date.now()}"></script>` : `<script>\n${clientScript()}\n</script>`}
|
|
143
|
+
</body>
|
|
144
|
+
</html>`;
|
|
145
|
+
}
|
|
146
|
+
function renderPage(lesson, navItems, bodyHtml, opts) {
|
|
147
|
+
const navHtml = navItems.map(renderNavItem).join("\n");
|
|
148
|
+
const endNavHtml = renderEndNav(lesson);
|
|
149
|
+
const extraSidebar = `
|
|
150
|
+
${lesson.meta.parentSlug ? `<div style="margin-top: 8px;"><a href="${lesson.meta.parentSlug}.html" class="bk-back-link" aria-label="Back to Chapter" style="margin-bottom: 12px;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg>Back to Chapter</a></div>` : `<div style="margin-top: 8px;"></div>`}
|
|
151
|
+
`;
|
|
152
|
+
const authorHtml = lesson.meta.author ? `<div class="bk-sidebar-author">By ${escHtml(lesson.meta.author)}</div>` : "";
|
|
153
|
+
const tagsHtml = lesson.meta.tags?.length ? `<div class="bk-tag-row">${lesson.meta.tags.map((tag) => `<span>${escHtml(tag)}</span>`).join("")}</div>` : "";
|
|
154
|
+
const contentHtml = `
|
|
145
155
|
<article class="bk-content">
|
|
146
156
|
<header class="bk-hero">
|
|
147
157
|
<p class="bk-eyebrow">Interactive Lesson</p>
|
|
@@ -151,13 +161,8 @@ ${pageCSS()}
|
|
|
151
161
|
${bodyHtml}
|
|
152
162
|
${endNavHtml}
|
|
153
163
|
</article>
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<script>
|
|
157
|
-
${clientScript()}
|
|
158
|
-
</script>
|
|
159
|
-
</body>
|
|
160
|
-
</html>`;
|
|
164
|
+
`;
|
|
165
|
+
return renderLayout(lesson.meta.title, lesson.meta.description, navHtml, contentHtml, opts, extraSidebar + authorHtml + tagsHtml);
|
|
161
166
|
}
|
|
162
167
|
// ─── CSS ──────────────────────────────────────────────────────────────────────
|
|
163
168
|
function pageCSS() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOjE,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,MAAM,CAatE;AAID,wBAAgB,aAAa,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,YAAiB,GACrB,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/renderer/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAOjE,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,MAAM,CAatE;AAID,wBAAgB,aAAa,CAC5B,OAAO,EAAE,OAAO,EAChB,IAAI,GAAE,YAAiB,GACrB,MAAM,CAuTR"}
|