mr-md 1.0.0

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.
@@ -0,0 +1,314 @@
1
+ function bkSimDoc(js, props, loop, dependencies) {
2
+ const scriptTags = (dependencies || []).map(url => '<script src="' + url.replace(/"/g, '&quot;') + '"></' + 'script>').join("\\n");
3
+ return (
4
+ "<!DOCTYPE html><html><head>" +
5
+ scriptTags +
6
+ "<style>" +
7
+ "html,body{height:100%;width:100%;margin:0;padding:0;overflow:hidden;background:transparent;display:flex;align-items:center;justify-content:center}" +
8
+ "canvas{display:block;touch-action:none;transform-origin:center center;flex-shrink:0}" +
9
+ "body{font-family:sans-serif}" +
10
+ '</style></head><body><canvas id="c" width="800" height="500"></canvas><script>' +
11
+ "window.__simProps=" +
12
+ JSON.stringify(props) +
13
+ ";window.__loop=" +
14
+ JSON.stringify(Boolean(loop)) +
15
+ ";window.bkSetupCalled=false;" +
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
+ '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");function l(){const fit=window.bkFitCanvas(c,w,h);if(window.innerWidth>=32&&window.innerHeight>=32){ctx.save();ctx.scale(fit.scale,fit.scale);f(ctx,fit.width,fit.height);ctx.restore()}requestAnimationFrame(l)}l()};' +
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}));});' +
20
+ "try{" +
21
+ js +
22
+ '}catch(e){console.error("Simulation Error:",e);document.body.innerHTML="<div style=\'padding: 20px; color: red; font-family: monospace;\'>Error: "+e.message+"</div>"}' +
23
+ "if(!window.bkSetupCalled){function fallbackScale(){window.bkFitCanvas(document.getElementById('c'),800,500,{bitmap:false});requestAnimationFrame(fallbackScale)}fallbackScale()}" +
24
+ "</" + "script></body></html>"
25
+ );
26
+ }
27
+
28
+ function bkReadSimProps(figure) {
29
+ const props = {};
30
+ figure.querySelectorAll("[data-bk-prop]").forEach((input) => {
31
+ if (input.type === "checkbox") {
32
+ props[input.dataset.bkProp] = input.checked;
33
+ } else if (input.type === "range" || input.type === "number") {
34
+ props[input.dataset.bkProp] = Number(input.value);
35
+ const output = input.parentElement?.querySelector("output");
36
+ if (output) output.textContent = input.value;
37
+ } else {
38
+ props[input.dataset.bkProp] = input.value;
39
+ }
40
+ });
41
+ return props;
42
+ }
43
+
44
+ function bkRestartSim(iframe, config, props) {
45
+ iframe.srcdoc = bkSimDoc(config.js, props, config.loop, config.dependencies);
46
+ }
47
+
48
+ function bkWireSimControls() {
49
+ document.querySelectorAll(".bk-object").forEach((figure) => {
50
+ const configEl = figure.querySelector(".bk-sim-config");
51
+ const iframe = figure.querySelector("iframe");
52
+ if (!configEl || !iframe) return;
53
+
54
+ let config;
55
+ try {
56
+ config = JSON.parse(configEl.textContent || "{}");
57
+ } catch {
58
+ return;
59
+ }
60
+
61
+ figure.querySelectorAll("[data-bk-prop]").forEach((input) => {
62
+ input.addEventListener("input", () => {
63
+ const props = bkReadSimProps(figure);
64
+ iframe.contentWindow?.postMessage({ type: "bk:set-props", props }, "*");
65
+ });
66
+ input.addEventListener("change", () => {
67
+ const props = bkReadSimProps(figure);
68
+ iframe.contentWindow?.postMessage({ type: "bk:set-props", props }, "*");
69
+ });
70
+ });
71
+ });
72
+ }
73
+
74
+ function bkWireMaximizeControls() {
75
+ document.querySelectorAll(".bk-object-maximize").forEach((btn) => {
76
+ btn.addEventListener("click", () => {
77
+ const obj = btn.closest(".bk-object");
78
+ if (!obj) return;
79
+ const isMax = obj.classList.toggle("bk-object--maximized");
80
+ if (isMax) {
81
+ document.body.style.overflow = "hidden";
82
+ btn.innerHTML =
83
+ '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/></svg>';
84
+ } else {
85
+ document.body.style.overflow = "";
86
+ btn.innerHTML =
87
+ '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>';
88
+ }
89
+ });
90
+ });
91
+ }
92
+
93
+ function bkWireInteractiveFrames() {
94
+ document.querySelectorAll(".bk-object").forEach((obj) => {
95
+ const activate = () => {
96
+ const frame = obj.querySelector(".bk-embed-interactive");
97
+ if (frame) frame.classList.add("is-interactive");
98
+ };
99
+ obj.addEventListener("pointerdown", activate, { passive: true });
100
+ obj.addEventListener("focusin", activate, { passive: true });
101
+ });
102
+
103
+ const exitInteractive = (e) => {
104
+ document
105
+ .querySelectorAll(".bk-embed-interactive.is-interactive")
106
+ .forEach((frame) => {
107
+ const container = frame.closest(".bk-object") || frame;
108
+ if (!container.contains(e.target)) {
109
+ frame.classList.remove("is-interactive");
110
+ }
111
+ });
112
+ };
113
+ document.addEventListener("pointerdown", exitInteractive, { passive: true });
114
+ document.addEventListener("focusin", exitInteractive, { passive: true });
115
+ }
116
+
117
+ function bkWireSidebarToggle() {
118
+ const shell = document.querySelector(".bk-shell");
119
+ const collapseBtn = document.getElementById("bk-sidebar-collapse");
120
+ const expandBtn = document.getElementById("bk-sidebar-expand");
121
+ if (collapseBtn)
122
+ collapseBtn.addEventListener("click", () =>
123
+ shell.setAttribute("data-collapsed", "true"),
124
+ );
125
+ if (expandBtn)
126
+ expandBtn.addEventListener("click", () =>
127
+ shell.removeAttribute("data-collapsed"),
128
+ );
129
+ }
130
+
131
+ function bkWireThemeControls() {
132
+ const root = document.documentElement;
133
+ const button = document.getElementById("bk-settings-button");
134
+ const panel = document.getElementById("bk-theme-panel");
135
+ const themeBtns = document.querySelectorAll("#bk-theme-icons button");
136
+ const paletteBtns = document.querySelectorAll("#bk-palette-icons button");
137
+ const savedTheme = localStorage.getItem("bk-theme");
138
+ const savedPalette = localStorage.getItem("bk-palette");
139
+
140
+ function updateThemeBtn(val) {
141
+ themeBtns.forEach(b => {
142
+ if(b.dataset.theme === val) b.classList.add("active");
143
+ else b.classList.remove("active");
144
+ });
145
+ }
146
+
147
+ function updatePaletteBtn(val) {
148
+ paletteBtns.forEach(b => {
149
+ if(b.dataset.palette === val) b.classList.add("active");
150
+ else b.classList.remove("active");
151
+ });
152
+ }
153
+
154
+ if (savedTheme) {
155
+ updateThemeBtn(savedTheme);
156
+ savedTheme === "auto"
157
+ ? root.removeAttribute("data-theme")
158
+ : root.setAttribute("data-theme", savedTheme);
159
+ }
160
+ if (savedPalette) {
161
+ const normalizedPalette = savedPalette === "green" ? "field" : savedPalette;
162
+ updatePaletteBtn(normalizedPalette);
163
+ root.setAttribute("data-palette", normalizedPalette);
164
+ }
165
+
166
+ button &&
167
+ panel &&
168
+ button.addEventListener("click", (event) => {
169
+ event.stopPropagation();
170
+ const open = panel.hasAttribute("hidden");
171
+ panel.hidden = !open;
172
+ button.setAttribute("aria-expanded", String(open));
173
+ });
174
+
175
+ panel?.addEventListener("click", (event) => event.stopPropagation());
176
+ document.addEventListener("click", () => {
177
+ if (!button || !panel || panel.hidden) return;
178
+ panel.hidden = true;
179
+ button.setAttribute("aria-expanded", "false");
180
+ });
181
+ document.addEventListener("keydown", (event) => {
182
+ if (event.key !== "Escape" || !button || !panel || panel.hidden) return;
183
+ panel.hidden = true;
184
+ button.setAttribute("aria-expanded", "false");
185
+ button.focus();
186
+ });
187
+
188
+ themeBtns.forEach(btn => {
189
+ btn.addEventListener("click", () => {
190
+ const val = btn.dataset.theme;
191
+ localStorage.setItem("bk-theme", val);
192
+ updateThemeBtn(val);
193
+ val === "auto"
194
+ ? root.removeAttribute("data-theme")
195
+ : root.setAttribute("data-theme", val);
196
+ });
197
+ });
198
+
199
+ paletteBtns.forEach(btn => {
200
+ btn.addEventListener("click", () => {
201
+ const val = btn.dataset.palette;
202
+ localStorage.setItem("bk-palette", val);
203
+ updatePaletteBtn(val);
204
+ root.setAttribute("data-palette", val);
205
+ });
206
+ });
207
+ }
208
+
209
+ // Quiz interaction
210
+ // biome-ignore lint/correctness/noUnusedVariables: Used in generated HTML
211
+ function bkAnswer(btn, qid) {
212
+ // biome-ignore lint/correctness/noUnusedVariables: Kept for clarity
213
+ const isCorrect = btn.dataset.correct === "true";
214
+ const question = document.getElementById(qid);
215
+ question.querySelectorAll(".bk-opt").forEach((b) => {
216
+ if (b.dataset.correct === "true") {
217
+ b.classList.add("correct");
218
+ } else if (b === btn) {
219
+ b.classList.add("wrong");
220
+ } else {
221
+ b.classList.add("disabled");
222
+ }
223
+ });
224
+ const exp = document.getElementById(`${qid}-exp`);
225
+ if (exp) exp.hidden = false;
226
+ }
227
+
228
+ // Code Block Copy Button
229
+ function bkWireCodeCopy() {
230
+ document.querySelectorAll("pre > code").forEach((code) => {
231
+ const pre = code.parentElement;
232
+ let container = pre.closest(".bk-code-block");
233
+
234
+ if (!container) {
235
+ container = document.createElement("div");
236
+ container.className = "bk-code-block";
237
+ const scroll = document.createElement("div");
238
+ scroll.className = "bk-code-scroll";
239
+
240
+ pre.parentNode.insertBefore(container, pre);
241
+ scroll.appendChild(pre);
242
+ container.appendChild(scroll);
243
+ }
244
+
245
+ if (getComputedStyle(container).position === "static") {
246
+ container.style.position = "relative";
247
+ }
248
+
249
+ const btn = document.createElement("button");
250
+ btn.className = "bk-copy-btn";
251
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
252
+ btn.setAttribute("aria-label", "Copy code");
253
+ btn.title = "Copy code";
254
+
255
+ btn.addEventListener("click", async () => {
256
+ try {
257
+ await navigator.clipboard.writeText(code.textContent || "");
258
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>';
259
+ btn.classList.add("copied");
260
+ setTimeout(() => {
261
+ btn.innerHTML = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>';
262
+ btn.classList.remove("copied");
263
+ }, 2000);
264
+ } catch (err) {
265
+ console.error("Failed to copy", err);
266
+ }
267
+ });
268
+
269
+ container.appendChild(btn);
270
+ });
271
+ }
272
+
273
+ // Active sidebar link on scroll
274
+ document.addEventListener("DOMContentLoaded", () => {
275
+ bkWireMaximizeControls();
276
+ bkWireSidebarToggle();
277
+ bkWireThemeControls();
278
+ bkWireSimControls();
279
+ bkWireInteractiveFrames();
280
+ bkWireCodeCopy();
281
+
282
+ const sections = document.querySelectorAll(
283
+ '[id^="heading-"], [id^="section-"], [id^="quiz-"]',
284
+ );
285
+ const navLinks = document.querySelectorAll(".bk-nav-item");
286
+
287
+ if (!sections.length || !navLinks.length) return;
288
+
289
+ const obs = new IntersectionObserver(
290
+ (entries) => {
291
+ let activeId = null;
292
+ entries.forEach((e) => {
293
+ if (e.isIntersecting) {
294
+ activeId = e.target.id;
295
+ }
296
+ });
297
+
298
+ if (activeId) {
299
+ navLinks.forEach((l) => {
300
+ if (l.dataset.id === activeId) {
301
+ l.classList.add("active");
302
+ } else {
303
+ l.classList.remove("active");
304
+ }
305
+ });
306
+ }
307
+ },
308
+ { rootMargin: "-20% 0px -60% 0px", threshold: 0 },
309
+ );
310
+
311
+ sections.forEach((s) => {
312
+ obs.observe(s);
313
+ });
314
+ });
package/src/index.ts ADDED
@@ -0,0 +1,34 @@
1
+ export { ChapterBuilder, chapter, LessonBuilder, lesson } from "./builder.js";
2
+ export { render } from "./renderer/index.js";
3
+ export type {
4
+ AnimationBlock,
5
+ AnimationOptions,
6
+ Block,
7
+ BlockAccent,
8
+ BlockType,
9
+ BuildOptions,
10
+ CalloutBlock,
11
+ CodeBlock,
12
+ ColumnItem,
13
+ ColumnsBlock,
14
+ ColumnsOptions,
15
+ HeadingBlock,
16
+ LatexBlock,
17
+ LatexOptions,
18
+ Lesson,
19
+ LessonMeta,
20
+ LessonPreset,
21
+ MarkdownBlock,
22
+ MediaBlock,
23
+ MediaKind,
24
+ MediaOptions,
25
+ QuizBlock,
26
+ QuizFile,
27
+ QuizQuestion,
28
+ SectionBlock,
29
+ SimulationBlock,
30
+ SimulationControl,
31
+ SimulationOptions,
32
+ YouTubeBlock,
33
+ YouTubeOptions,
34
+ } from "./types.js";