diff --git a/pyscriptjs/src/App.svelte b/pyscriptjs/src/App.svelte index 9e817ce..3f67fbf 100644 --- a/pyscriptjs/src/App.svelte +++ b/pyscriptjs/src/App.svelte @@ -4,7 +4,7 @@ import { faPlusCircle } from '@fortawesome/free-solid-svg-icons' import Tailwind from "./Tailwind.svelte"; import { loadInterpreter } from './interpreter'; - import { pyodideLoaded, loadedEnvironments, navBarOpen, componentsNavOpen, mode, scriptsQueue } from './stores'; + import { pyodideLoaded, loadedEnvironments, navBarOpen, componentsNavOpen, mode, scriptsQueue, initializers } from './stores'; import Main from "./Main.svelte"; import Header from "./Header.svelte"; import SideNav from "./SideNav.svelte"; @@ -47,6 +47,12 @@ for (let script of $scriptsQueue) { script.evaluate(); } + scriptsQueue.set([]) + } + + // now we call all initializers AFTER we actually executed all page scripts + for (let initializer of $initializers){ + initializer(); } } diff --git a/pyscriptjs/src/components/pyrepl.ts b/pyscriptjs/src/components/pyrepl.ts index e6208f0..5c38e29 100644 --- a/pyscriptjs/src/components/pyrepl.ts +++ b/pyscriptjs/src/components/pyrepl.ts @@ -6,7 +6,7 @@ import { keymap, ViewUpdate } from "@codemirror/view"; import { defaultKeymap } from "@codemirror/commands"; import { oneDarkTheme } from "@codemirror/theme-one-dark"; -import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue } from '../stores'; +import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode } from '../stores'; import { addClasses } from '../utils'; // Premise used to connect to the first available pyodide interpreter @@ -208,7 +208,7 @@ export class PyRepl extends HTMLElement { let source = this.editor.state.doc.toString(); let output; if (source.includes("asyncio")){ - output = pyodide.runPythonAsync(source); + output = await pyodide.runPythonAsync(source); }else{ output = pyodide.runPython(source); } diff --git a/pyscriptjs/src/components/pyscript.ts b/pyscriptjs/src/components/pyscript.ts index d9b2a5c..b3864d1 100644 --- a/pyscriptjs/src/components/pyscript.ts +++ b/pyscriptjs/src/components/pyscript.ts @@ -6,13 +6,14 @@ import { keymap, ViewUpdate } from "@codemirror/view"; import { defaultKeymap } from "@codemirror/commands"; import { oneDarkTheme } from "@codemirror/theme-one-dark"; -import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue } from '../stores'; +import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue, addInitializer } from '../stores'; import { addClasses } from '../utils'; // Premise used to connect to the first available pyodide interpreter let pyodideReadyPromise; let environments; let currentMode; +let handlersCollected = false; pyodideLoaded.subscribe(value => { pyodideReadyPromise = value; @@ -40,6 +41,46 @@ function createCmdHandler(el){ } +class Script { + source: string; + state: string; + target: string; + + constructor(source: string, target: string) { + this.target = target; + this.source = source; + this.state = 'waiting'; + } + + async evaluate() { + console.log('evaluate'); + let pyodide = await pyodideReadyPromise; + // debugger + try { + // @ts-ignore + // let source = this.editor.state.doc.toString(); + let output; + if (this.source.includes("asyncio")){ + output = await pyodide.runPythonAsync(this.source); + }else{ + output = pyodide.runPython(this.source); + } + + if (this.target){ + // this.editorOut.innerHTML = s; + } + // if (output !== undefined){ + // this.addToOutput(output); + // } + + + } catch (err) { + console.log("OOOPS, this happened: " + err); + // this.addToOutput(err); + } + } +} + export class PyScript extends HTMLElement { shadow: ShadowRoot; wrapper: HTMLElement; @@ -176,11 +217,10 @@ export class PyScript extends HTMLElement { let source = this.editor.state.doc.toString(); let output; if (source.includes("asyncio")){ - output = pyodide.runPythonAsync(source); + output = await pyodide.runPythonAsync(source); }else{ output = pyodide.runPython(source); } - if (output !== undefined){ this.addToOutput(output); } @@ -200,3 +240,47 @@ export class PyScript extends HTMLElement { } } + +async function initHandlers() { + if( handlersCollected == true ) return; + + console.log('Collecting nodes...'); + let pyodide = await pyodideReadyPromise; + let matches : NodeListOf = document.querySelectorAll('[pys-onClick]'); + let output; + let source; + for (var el of matches) { + let handlerCode = el.getAttribute('pys-onClick'); + source = `Element("${ el.id }").element.onclick = ${ handlerCode }`; + output = await pyodide.runPythonAsync(source); + + // el.onclick = (evt: any) => { + // console.log("click"); + // new Promise((resolve, reject) => { + // setTimeout(() => { + // console.log('Inside') + // }, 300); + // }).then(() => { + // console.log("resolved") + // }); + // // let handlerCode = el.getAttribute('pys-onClick'); + // // pyodide.runPython(handlerCode); + // } + } + handlersCollected = true; + + matches = document.querySelectorAll('[pys-onKeyDown]'); + for (var el of matches) { + let handlerCode = el.getAttribute('pys-onKeyDown'); + source = `Element("${ el.id }").element.addEventListener("keydown", ${ handlerCode })`; + output = await pyodide.runPythonAsync(source); + } +} +addInitializer(initHandlers) + +// if( document.readyState === 'loading' ) { +// document.addEventListener( 'DOMContentLoaded', initHandlers ); +// } +// else if( document.readyState === 'interactive' || document.readyState === 'complete' ) { +// initHandlers(); +// } diff --git a/pyscriptjs/src/interpreter.ts b/pyscriptjs/src/interpreter.ts index 5efe747..0e07036 100644 --- a/pyscriptjs/src/interpreter.ts +++ b/pyscriptjs/src/interpreter.ts @@ -47,30 +47,54 @@ pyscript = PyScript() class Element: - def __init__(self, element_id): + def __init__(self, element_id, element=None): self._id = element_id + self._element = element @property def element(self): """Return the dom element""" - return document.querySelector(f'#{self._id}'); + if not self._element: + self._element = document.querySelector(f'#{self._id}'); + return self._element def write(self, value, append=False): console.log(f"Element.write: {value} --> {append}") + # TODO: it should be the opposite... pyscript.write should use the Element.write + # so we can consolidate on how we write depending on the element type pyscript.write(self._id, value, append=append) def clear(self): - self.write("", append=False) + if hasattr(self.element, 'value'): + self.element.value = '' + else: + self.write("", append=False) - def clone(self, new_id=None): + def select(self, query, from_content=False): + el = self.element + if from_content: + el = el.content + + _el = el.querySelector(query) + if _el: + return Element(_el.id, _el) + else: + console.log(f"WARNING: can't find element matching query {query}") + + def clone(self, new_id=None, to=None): if new_id is None: new_id = self.element.id - clone = self.element.cloneNode(true); + clone = self.element.cloneNode(True); clone.id = new_id; + if to: + to.element.appendChild(clone) + # Inject it into the DOM self.element.after(clone); + + return Element(clone.id, clone) ` @@ -81,13 +105,13 @@ let loadInterpreter = async function(): any { }); // now that we loaded, add additional convenience fuctions - pyodide.loadPackage(['matplotlib', 'numpy']) + // pyodide.loadPackage(['matplotlib', 'numpy']) await pyodide.loadPackage("micropip"); - await pyodide.runPythonAsync(` - import micropip - await micropip.install("ipython") - `); + // await pyodide.runPythonAsync(` + // import micropip + // await micropip.install("ipython") + // `); let output = pyodide.runPython(additional_definitions); diff --git a/pyscriptjs/src/stores.ts b/pyscriptjs/src/stores.ts index 36f5587..a795531 100644 --- a/pyscriptjs/src/stores.ts +++ b/pyscriptjs/src/stores.ts @@ -23,8 +23,10 @@ export const mainDiv = writable(null); export const currentComponentDetails = writable([]); export const mode = writable(DEFAULT_MODE) export const scriptsQueue = writable([]) +export const initializers = writable([]) -let scriptsQueue_ = [] +let scriptsQueue_ = []; +let initializers_ = []; scriptsQueue.subscribe(value => { scriptsQueue_ = value; @@ -33,3 +35,11 @@ scriptsQueue.subscribe(value => { export const addToScriptsQueue = (script) => { scriptsQueue.set([...scriptsQueue_, script]); }; + +scriptsQueue.subscribe(value => { + scriptsQueue_ = value; +}); + +export const addInitializer = (initializer) => { + initializers.set([...initializers_, initializer]); +};