|
| 1 | +/** Reusable direct MicroPython runner for first-party Read the Docs embeds. */ |
| 2 | +(function () { |
| 3 | + const RUNTIME_URL = "https://PyDevices.github.io/vendor/micropython/micropython.mjs"; |
| 4 | + const state = { phase: "idle", errors: [], mp: null, activeOutput: null }; |
| 5 | + window.__pydevicesDocsRuntime = state; |
| 6 | + |
| 7 | + function setAll(demos, text) { |
| 8 | + demos.forEach((demo) => { |
| 9 | + const status = demo.querySelector(".demo-status"); |
| 10 | + if (status) status.textContent = text; |
| 11 | + }); |
| 12 | + } |
| 13 | + |
| 14 | + async function runtime(demos) { |
| 15 | + if (state.mp) return state.mp; |
| 16 | + state.phase = "loading"; setAll(demos, "Downloading direct MicroPython…"); |
| 17 | + const { loadMicroPython } = await import(RUNTIME_URL); |
| 18 | + state.mp = await loadMicroPython({ |
| 19 | + stdout: (line) => { if (state.activeOutput) state.activeOutput.textContent += `${line}\n`; }, |
| 20 | + stderr: (line) => { if (state.activeOutput) state.activeOutput.textContent += `${line}\n`; }, |
| 21 | + heapsize: 16 * 1024 * 1024 |
| 22 | + }); |
| 23 | + state.phase = "ready"; setAll(demos, "Ready"); |
| 24 | + window.dispatchEvent(new CustomEvent("pydevices-docs-ready")); |
| 25 | + return state.mp; |
| 26 | + } |
| 27 | + |
| 28 | + async function execute(demo, demos) { |
| 29 | + const editor = demo.querySelector(".code-editor"); |
| 30 | + const canvas = demo.querySelector("canvas"); |
| 31 | + const output = demo.querySelector(".demo-output"); |
| 32 | + const status = demo.querySelector(".demo-status"); |
| 33 | + const button = demo.querySelector(".run-btn"); |
| 34 | + if (!editor || !canvas) return; |
| 35 | + if (button) button.disabled = true; |
| 36 | + if (output) output.textContent = ""; |
| 37 | + if (status) status.textContent = "Running…"; |
| 38 | + try { |
| 39 | + const mp = await runtime(demos); |
| 40 | + state.activeOutput = output; |
| 41 | + const source = JSON.stringify(editor.value); |
| 42 | + const canvasId = JSON.stringify(canvas.id); |
| 43 | + await mp.runPythonAsync(` |
| 44 | +from displaydev import env_set |
| 45 | +env_set("PYDEVICES_CANVAS_ID", ${canvasId}) |
| 46 | +CANVAS_ID = ${canvasId} |
| 47 | +_source = ${source} |
| 48 | +exec(compile(_source, "<docs-demo>", "exec"), {"__name__": "__main__", "CANVAS_ID": CANVAS_ID}) |
| 49 | +`); |
| 50 | + if (status) status.textContent = "Active"; |
| 51 | + } catch (error) { |
| 52 | + const detail = String(error && (error.stack || error)); |
| 53 | + state.errors.push(detail); state.phase = "failed"; |
| 54 | + if (output) output.textContent += `${detail}\n`; |
| 55 | + if (status) status.textContent = "Error"; |
| 56 | + console.error("Direct documentation demo failed:", error); |
| 57 | + window.dispatchEvent(new CustomEvent("pydevices-docs-failed", { detail })); |
| 58 | + } finally { |
| 59 | + state.activeOutput = null; |
| 60 | + if (button) button.disabled = false; |
| 61 | + } |
| 62 | + } |
| 63 | + |
| 64 | + function init() { |
| 65 | + const demos = [...document.querySelectorAll(".pydevices-live-demo")]; |
| 66 | + demos.forEach((demo, index) => { |
| 67 | + const canvas = demo.querySelector("canvas"); |
| 68 | + const editor = demo.querySelector(".code-editor"); |
| 69 | + const run = demo.querySelector(".run-btn"); |
| 70 | + const reset = demo.querySelector(".reset-btn"); |
| 71 | + if (canvas && !canvas.id) canvas.id = `live_canvas_${index}`; |
| 72 | + if (editor) demo._initialCode = editor.value; |
| 73 | + run?.addEventListener("click", () => execute(demo, demos)); |
| 74 | + reset?.addEventListener("click", () => { |
| 75 | + if (editor) editor.value = demo._initialCode || ""; |
| 76 | + execute(demo, demos); |
| 77 | + }); |
| 78 | + }); |
| 79 | + if (!demos.length) return; |
| 80 | + runtime(demos).then(() => { |
| 81 | + demos.forEach((demo) => execute(demo, demos)); |
| 82 | + }).catch((error) => { |
| 83 | + state.phase = "failed"; state.errors.push(String(error)); |
| 84 | + setAll(demos, "Load Failed"); console.error(error); |
| 85 | + }); |
| 86 | + } |
| 87 | + if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", init); |
| 88 | + else init(); |
| 89 | +})(); |
0 commit comments