diff --git a/core/package-lock.json b/core/package-lock.json index 1ba6ccb3e68..aaf34cbee24 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pyscript/core", - "version": "0.7.18", + "version": "0.7.24", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pyscript/core", - "version": "0.7.18", + "version": "0.7.24", "license": "APACHE-2.0", "dependencies": { "@ungap/with-resolvers": "^0.1.0", diff --git a/core/package.json b/core/package.json index e6844e85633..5139b6b4072 100644 --- a/core/package.json +++ b/core/package.json @@ -1,6 +1,6 @@ { "name": "@pyscript/core", - "version": "0.7.18", + "version": "0.7.24", "type": "module", "description": "PyScript", "module": "./index.js", diff --git a/core/src/plugins/py-editor.js b/core/src/plugins/py-editor.js index 9fc326a21a5..71c15f31904 100644 --- a/core/src/plugins/py-editor.js +++ b/core/src/plugins/py-editor.js @@ -38,7 +38,7 @@ const getRelatedScript = (target, type) => { return editor?.parentNode?.previousElementSibling; }; -async function execute({ currentTarget, script }) { +async function execute({ currentTarget, script }, onBeforeRun = "") { const { env, pySrc, outDiv } = this; const hasRunButton = !!currentTarget; @@ -152,7 +152,8 @@ async function execute({ currentTarget, script }) { console.error(str); } }; - sync.runAsync(pySrc).then(enable, enable); + if (onBeforeRun) onBeforeRun += ";"; + sync.runAsync(onBeforeRun + pySrc).then(enable, enable); }); } @@ -176,11 +177,11 @@ const makeRunButton = (handler, type) => { runButton.innerHTML = RUN_BUTTON; runButton.setAttribute("aria-label", "Python Script Run Button"); runButton.addEventListener("click", async (event) => { + const script = getRelatedScript(runButton, type); if ( runButton.classList.contains("running") && confirm("Stop evaluating this code?") ) { - const script = getRelatedScript(runButton, type); if (script) { const env = script.getAttribute("env"); // remove the bootstrapped env which could be one or shared @@ -205,7 +206,10 @@ const makeRunButton = (handler, type) => { return; } runButton.blur(); - await handler.handleEvent(event); + await handler.handleEvent( + event, + script?.getAttribute("onbeforerun") || "", + ); }); return runButton; }; @@ -235,12 +239,13 @@ const makeOutDiv = (type) => { return outDiv; }; -const makeBoxDiv = (handler, type) => { +const makeBoxDiv = (handler, type, output) => { const boxDiv = document.createElement("div"); boxDiv.className = `${type}-editor-box`; const editorDiv = makeEditorDiv(handler, type); - const outDiv = makeOutDiv(type); + const outDiv = output ? document.getElementById(output) : makeOutDiv(type); + if (output) outDiv.classList.add(`${type}-editor-output`); boxDiv.append(editorDiv, outDiv); return [boxDiv, outDiv, editorDiv.querySelector("button")]; @@ -276,12 +281,20 @@ const init = async (script, type, interpreter) => { }).onmessage = ({ target }) => target.terminate(); } + // allow bootstrap with same env for repeated editor creation + // only if `env-override` is explicitly set as attribute if (hasConfig && configs.has(env)) { - throw new SyntaxError( - configs.get(env) - ? `duplicated config for env: ${env}` - : `unable to add a config to the env: ${env}`, - ); + if (script.hasAttribute("env-override")) { + // in this case we need to bootstrap the env again + // because otherwise each env would leak + envs.delete(env); + } else { + throw new SyntaxError( + configs.get(env) + ? `duplicated config for env: ${env}` + : `unable to add a config to the env: ${env}`, + ); + } } configs.set(env, hasConfig); @@ -328,7 +341,7 @@ const init = async (script, type, interpreter) => { // in every other case be sure that if the listener override returned // `false` nothing happens, otherwise keep doing what it always did else { - context.handleEvent = async (event) => { + context.handleEvent = async (event, onBeforeRun) => { // trap the currentTarget ASAP (if any) // otherwise it gets lost asynchronously const { currentTarget } = event; @@ -338,7 +351,11 @@ const init = async (script, type, interpreter) => { }); // avoid executing the default handler if the override returned `false` if ((await callback(event)) !== false) - await execute.call(context, { currentTarget }); + await execute.call( + context, + { currentTarget }, + onBeforeRun, + ); }; } }, @@ -388,8 +405,7 @@ const init = async (script, type, interpreter) => { }; if (isSetup) { - await context.handleEvent({ currentTarget: null, script }); - notifyEditor(); + context.handleEvent({ currentTarget: null, script }).then(notifyEditor); return; } @@ -411,13 +427,23 @@ const init = async (script, type, interpreter) => { if (!target.hasAttribute("root")) target.setAttribute("root", target.id); // @see https://github.com/JeffersGlass/mkdocs-pyscript/blob/main/mkdocs_pyscript/js/makeblocks.js - const [boxDiv, outDiv, runButton] = makeBoxDiv(context, type); + const [boxDiv, outDiv, runButton] = makeBoxDiv( + context, + type, + script.getAttribute("output"), + ); boxDiv.dataset.env = script.hasAttribute("env") ? env : interpreter; const inputChild = boxDiv.querySelector(`.${type}-editor-input > div`); const parent = inputChild.attachShadow({ mode: "open" }); // avoid inheriting styles from the outer component - parent.innerHTML = ``; + const styles = [":host { all: initial; }"]; + const rows = script.getAttribute("rows"); + if (rows) { + const maxHeight = Math.floor(parseInt(rows) * 18.5) + "px"; + styles.push(`.cm-editor { height: auto; max-height: ${maxHeight}; }`); + } + parent.innerHTML = ``; target.appendChild(boxDiv);