mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
add base component file with components base classe and adapt pyrepl to new base class
This commit is contained in:
145
pyscriptjs/src/components/base.ts
Normal file
145
pyscriptjs/src/components/base.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, mode } from '../stores';
|
||||
|
||||
// Premise used to connect to the first available pyodide interpreter
|
||||
let pyodideReadyPromise;
|
||||
let environments;
|
||||
let currentMode;
|
||||
let Element;
|
||||
|
||||
pyodideLoaded.subscribe(value => {
|
||||
pyodideReadyPromise = value;
|
||||
});
|
||||
loadedEnvironments.subscribe(value => {
|
||||
environments = value;
|
||||
});
|
||||
|
||||
let propertiesNavOpen;
|
||||
componentDetailsNavOpen.subscribe(value => {
|
||||
propertiesNavOpen = value;
|
||||
});
|
||||
|
||||
mode.subscribe(value => {
|
||||
currentMode = value;
|
||||
});
|
||||
|
||||
// TODO: use type declaractions
|
||||
type PyodideInterface = {
|
||||
registerJsModule(name: string, module: object): void
|
||||
}
|
||||
|
||||
|
||||
export class BaseEvalElement extends HTMLElement {
|
||||
shadow: ShadowRoot;
|
||||
wrapper: HTMLElement;
|
||||
code: string;
|
||||
btnConfig: HTMLElement;
|
||||
btnRun: HTMLElement;
|
||||
outputElement: HTMLElement; //HTMLTextAreaElement;
|
||||
theme: string;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// attach shadow so we can preserve the element original innerHtml content
|
||||
this.shadow = this.attachShadow({ mode: 'open'});
|
||||
this.wrapper = document.createElement('slot');
|
||||
this.shadow.appendChild(this.wrapper);
|
||||
}
|
||||
|
||||
addToOutput(s: string) {
|
||||
this.outputElement.innerHTML += "<div>"+s+"</div>";
|
||||
this.outputElement.hidden = false;
|
||||
}
|
||||
|
||||
postEvaluate(){
|
||||
|
||||
}
|
||||
|
||||
getSource(): string{
|
||||
return "";
|
||||
}
|
||||
|
||||
protected async _register_esm(pyodide: PyodideInterface): Promise<void> {
|
||||
const imports: {[key: string]: unknown} = {}
|
||||
|
||||
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
||||
const importmap = (() => {
|
||||
try {
|
||||
return JSON.parse(node.textContent)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
})()
|
||||
|
||||
if (importmap?.imports == null)
|
||||
continue
|
||||
|
||||
for (const [name, url] of Object.entries(importmap.imports)) {
|
||||
if (typeof name != "string" || typeof url != "string")
|
||||
continue
|
||||
|
||||
try {
|
||||
// XXX: pyodide doesn't like Module(), failing with
|
||||
// "can't read 'name' of undefined" at import time
|
||||
imports[name] = {...await import(url)}
|
||||
} catch {
|
||||
console.error(`failed to fetch '${url}' for '${name}'`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pyodide.registerJsModule("esm", imports)
|
||||
}
|
||||
|
||||
async evaluate() {
|
||||
console.log('evaluate');
|
||||
let pyodide = await pyodideReadyPromise;
|
||||
|
||||
try {
|
||||
// @ts-ignore
|
||||
const source = this.getSource();
|
||||
await this._register_esm(pyodide)
|
||||
|
||||
let output;
|
||||
|
||||
if (source.includes("asyncio")){
|
||||
output = await pyodide.runPythonAsync(source);
|
||||
await pyodide.runPythonAsync(`output_manager.revert()`)
|
||||
}else{
|
||||
output = pyodide.runPython(source);
|
||||
pyodide.runPython(`output_manager.revert()`)
|
||||
}
|
||||
|
||||
if (output !== undefined){
|
||||
if (Element === undefined){
|
||||
Element = pyodide.globals.get('Element');
|
||||
}
|
||||
const out = Element(this.outputElement.id);
|
||||
// @ts-ignore
|
||||
out.write.callKwargs(output, { append : true});
|
||||
|
||||
if (!this.hasAttribute('target')) {
|
||||
this.outputElement.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
this.postEvaluate()
|
||||
|
||||
// if (this.hasAttribute('auto-generate')) {
|
||||
// let nextExecId = parseInt(this.getAttribute('exec-id')) + 1;
|
||||
// const newPyRepl = document.createElement("py-repl");
|
||||
// newPyRepl.setAttribute('root', this.getAttribute('root'));
|
||||
// newPyRepl.id = this.getAttribute('root') + "-" + nextExecId.toString();
|
||||
// newPyRepl.setAttribute('auto-generate', null);
|
||||
// if (this.hasAttribute('target')){
|
||||
// newPyRepl.setAttribute('target', this.getAttribute('target'));
|
||||
// }
|
||||
|
||||
// newPyRepl.setAttribute('exec-id', nextExecId.toString());
|
||||
// this.parentElement.appendChild(newPyRepl);
|
||||
// }
|
||||
} catch (err) {
|
||||
this.addToOutput(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { oneDarkTheme } from "@codemirror/theme-one-dark";
|
||||
|
||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode } from '../stores';
|
||||
import { addClasses } from '../utils';
|
||||
import { BaseEvalElement } from './base';
|
||||
|
||||
// Premise used to connect to the first available pyodide interpreter
|
||||
let pyodideReadyPromise;
|
||||
@@ -43,27 +44,15 @@ function createCmdHandler(el){
|
||||
}
|
||||
|
||||
|
||||
export class PyRepl extends HTMLElement {
|
||||
shadow: ShadowRoot;
|
||||
wrapper: HTMLElement;
|
||||
export class PyRepl extends BaseEvalElement {
|
||||
editor: EditorView;
|
||||
editorNode: HTMLElement;
|
||||
code: string;
|
||||
cm: any;
|
||||
btnConfig: HTMLElement;
|
||||
btnRun: HTMLElement;
|
||||
editorOut: HTMLElement; //HTMLTextAreaElement;
|
||||
theme: string;
|
||||
// editorState: EditorState;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
// attach shadow so we can preserve the element original innerHtml content
|
||||
this.shadow = this.attachShadow({ mode: 'open'});
|
||||
|
||||
this.wrapper = document.createElement('slot');
|
||||
|
||||
// add an extra div where we can attach the codemirror editor
|
||||
this.editorNode = document.createElement('div');
|
||||
addClasses(this.editorNode, ["editor-box"])
|
||||
@@ -172,7 +161,7 @@ export class PyRepl extends HTMLElement {
|
||||
}
|
||||
|
||||
if (this.hasAttribute('target')) {
|
||||
this.editorOut = document.getElementById(this.getAttribute('target'));
|
||||
this.outputElement = document.getElementById(this.getAttribute('target'));
|
||||
|
||||
// in this case, the default output-mode is append, if hasn't been specified
|
||||
if (!this.hasAttribute('output-mode')) {
|
||||
@@ -180,13 +169,13 @@ export class PyRepl extends HTMLElement {
|
||||
}
|
||||
}else{
|
||||
// Editor Output Div
|
||||
this.editorOut = document.createElement('div');
|
||||
this.editorOut.classList.add("output");
|
||||
this.editorOut.hidden = true;
|
||||
this.editorOut.id = this.id + "-" + this.getAttribute("exec-id");
|
||||
this.outputElement = document.createElement('div');
|
||||
this.outputElement.classList.add("output");
|
||||
this.outputElement.hidden = true;
|
||||
this.outputElement.id = this.id + "-" + this.getAttribute("exec-id");
|
||||
|
||||
// add the output div id there's not target
|
||||
mainDiv.appendChild(this.editorOut);
|
||||
mainDiv.appendChild(this.outputElement);
|
||||
}
|
||||
|
||||
this.appendChild(mainDiv);
|
||||
@@ -195,63 +184,35 @@ export class PyRepl extends HTMLElement {
|
||||
}
|
||||
|
||||
addToOutput(s: string) {
|
||||
this.editorOut.innerHTML += "<div>"+s+"</div>";
|
||||
this.editorOut.hidden = false;
|
||||
this.outputElement.innerHTML += "<div>"+s+"</div>";
|
||||
this.outputElement.hidden = false;
|
||||
}
|
||||
|
||||
async evaluate() {
|
||||
console.log('evaluate');
|
||||
let pyodide = await pyodideReadyPromise;
|
||||
postEvaluate(): void {
|
||||
if (this.hasAttribute('auto-generate')) {
|
||||
let nextExecId = parseInt(this.getAttribute('exec-id')) + 1;
|
||||
const newPyRepl = document.createElement("py-repl");
|
||||
newPyRepl.setAttribute('root', this.getAttribute('root'));
|
||||
newPyRepl.id = this.getAttribute('root') + "-" + nextExecId.toString();
|
||||
newPyRepl.setAttribute('auto-generate', null);
|
||||
if (this.hasAttribute('target')){
|
||||
newPyRepl.setAttribute('target', this.getAttribute('target'));
|
||||
}
|
||||
|
||||
newPyRepl.setAttribute('exec-id', nextExecId.toString());
|
||||
this.parentElement.appendChild(newPyRepl);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// @ts-ignore
|
||||
const sourceStrings = [`output_manager.change("`+this.editorOut.id+`")`,
|
||||
getSource(): string {
|
||||
const sourceStrings = [`output_manager.change("`+this.outputElement.id+`")`,
|
||||
...this.editor.state.doc.toString().split("\n")];
|
||||
const source = sourceStrings.join('\n')
|
||||
let output;
|
||||
return sourceStrings.join('\n')
|
||||
}
|
||||
|
||||
if (source.includes("asyncio")){
|
||||
output = await pyodide.runPythonAsync(source);
|
||||
await pyodide.runPythonAsync(`output_manager.revert()`)
|
||||
}else{
|
||||
output = pyodide.runPython(source);
|
||||
pyodide.runPython(`output_manager.revert()`)
|
||||
}
|
||||
|
||||
if (output !== undefined){
|
||||
let Element = pyodide.globals.get('Element');
|
||||
let out = Element(this.editorOut.id);
|
||||
|
||||
// @ts-ignore
|
||||
out.write.callKwargs(output, { append : true});
|
||||
|
||||
if (!this.hasAttribute('target')) {
|
||||
this.editorOut.hidden = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasAttribute('auto-generate')) {
|
||||
let nextExecId = parseInt(this.getAttribute('exec-id')) + 1;
|
||||
const newPyRepl = document.createElement("py-repl");
|
||||
newPyRepl.setAttribute('root', this.getAttribute('root'));
|
||||
newPyRepl.id = this.getAttribute('root') + "-" + nextExecId.toString();
|
||||
newPyRepl.setAttribute('auto-generate', null);
|
||||
if (this.hasAttribute('target')){
|
||||
newPyRepl.setAttribute('target', this.getAttribute('target'));
|
||||
}
|
||||
|
||||
newPyRepl.setAttribute('exec-id', nextExecId.toString());
|
||||
this.parentElement.appendChild(newPyRepl);
|
||||
}
|
||||
} catch (err) {
|
||||
this.addToOutput(err);
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
console.log('rendered');
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user