mirror of
https://github.com/pyscript/pyscript.git
synced 2022-05-01 19:47:48 +03:00
Merge pull request #44 from anaconda/rb/prettier
Added prettier and eslint
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -118,8 +118,9 @@ venv.bak/
|
|||||||
# Rope project settings
|
# Rope project settings
|
||||||
.ropeproject
|
.ropeproject
|
||||||
|
|
||||||
# VS Code Files
|
# IDE Files
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
|
||||||
# mkdocs documentation
|
# mkdocs documentation
|
||||||
/site
|
/site
|
||||||
|
|||||||
46
pyscriptjs/.eslintrc.js
Normal file
46
pyscriptjs/.eslintrc.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||||
|
],
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module',
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
project: ['./tsconfig.json'],
|
||||||
|
extraFileExtensions: ['.svelte'],
|
||||||
|
},
|
||||||
|
env: {
|
||||||
|
es6: true,
|
||||||
|
browser: true,
|
||||||
|
},
|
||||||
|
overrides: [
|
||||||
|
{
|
||||||
|
files: ['*.svelte'],
|
||||||
|
processor: 'svelte3/svelte3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
settings: {
|
||||||
|
'svelte3/typescript': require('typescript'),
|
||||||
|
// ignore style tags in Svelte because of Tailwind CSS
|
||||||
|
// See https://github.com/sveltejs/eslint-plugin-svelte3/issues/70
|
||||||
|
'svelte3/ignore-styles': () => true,
|
||||||
|
},
|
||||||
|
plugins: ['svelte3', '@typescript-eslint'],
|
||||||
|
ignorePatterns: ['node_modules'],
|
||||||
|
rules: {
|
||||||
|
'no-prototype-builtins': 'warn',
|
||||||
|
'@typescript-eslint/no-unused-vars': 'warn',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-assignment': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-call': 'warn',
|
||||||
|
'@typescript-eslint/no-unsafe-return': 'warn',
|
||||||
|
'@typescript-eslint/no-floating-promises': 'warn',
|
||||||
|
'@typescript-eslint/restrict-plus-operands': 'warn',
|
||||||
|
'@typescript-eslint/no-empty-function': 'warn',
|
||||||
|
},
|
||||||
|
};
|
||||||
6
pyscriptjs/.prettierignore
Normal file
6
pyscriptjs/.prettierignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
build
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore all HTML files
|
||||||
|
*.html
|
||||||
13
pyscriptjs/.prettierrc.js
Normal file
13
pyscriptjs/.prettierrc.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
arrowParens: 'avoid',
|
||||||
|
bracketSameLine: true,
|
||||||
|
singleQuote: true,
|
||||||
|
printWidth: 120,
|
||||||
|
plugins: ['prettier-plugin-svelte'],
|
||||||
|
semi: true,
|
||||||
|
svelteSortOrder: 'options-styles-scripts-markup',
|
||||||
|
svelteStrictMode: false,
|
||||||
|
svelteIndentScriptAndStyle: true,
|
||||||
|
tabWidth: 4,
|
||||||
|
trailingComma: 'all',
|
||||||
|
}
|
||||||
743
pyscriptjs/package-lock.json
generated
743
pyscriptjs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,15 +5,26 @@
|
|||||||
"build": "NODE_ENV=production rollup -c",
|
"build": "NODE_ENV=production rollup -c",
|
||||||
"dev": "rollup -c -w",
|
"dev": "rollup -c -w",
|
||||||
"start": "sirv public --no-clear --port 8080",
|
"start": "sirv public --no-clear --port 8080",
|
||||||
"validate": "svelte-check"
|
"validate": "svelte-check",
|
||||||
|
"format:check": "prettier --check './src/**/*.{js,svelte,html,ts}'",
|
||||||
|
"format": "prettier --write './src/**/*.{js,svelte,html,ts}'",
|
||||||
|
"lint": "eslint './src/**/*.{js,svelte,html,ts}'",
|
||||||
|
"lint:fix": "eslint --fix './src/**/*.{js,svelte,html,ts}'",
|
||||||
|
"xprelint": "npm run format"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.0.0",
|
"@rollup/plugin-node-resolve": "^11.0.0",
|
||||||
"@rollup/plugin-typescript": "^8.1.0",
|
"@rollup/plugin-typescript": "^8.1.0",
|
||||||
"@tsconfig/svelte": "^1.0.0",
|
"@tsconfig/svelte": "^1.0.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||||
|
"@typescript-eslint/parser": "^5.20.0",
|
||||||
"autoprefixer": "^10.2.3",
|
"autoprefixer": "^10.2.3",
|
||||||
|
"eslint": "^8.14.0",
|
||||||
|
"eslint-plugin-svelte3": "^3.4.1",
|
||||||
"postcss": "^8.2.4",
|
"postcss": "^8.2.4",
|
||||||
|
"prettier": "^2.6.2",
|
||||||
|
"prettier-plugin-svelte": "^2.7.0",
|
||||||
"rollup": "^2.3.4",
|
"rollup": "^2.3.4",
|
||||||
"rollup-plugin-css-only": "^3.1.0",
|
"rollup-plugin-css-only": "^3.1.0",
|
||||||
"rollup-plugin-livereload": "^2.0.0",
|
"rollup-plugin-livereload": "^2.0.0",
|
||||||
|
|||||||
@@ -1,81 +1,84 @@
|
|||||||
<script lang="ts">
|
|
||||||
|
|
||||||
import Tailwind from "./Tailwind.svelte";
|
|
||||||
import { loadInterpreter } from './interpreter';
|
|
||||||
import { pyodideLoaded, loadedEnvironments, navBarOpen, componentsNavOpen, mode, scriptsQueue, initializers, postInitializers } from './stores';
|
|
||||||
|
|
||||||
let iconSize = 2;
|
|
||||||
let pyodideReadyPromise
|
|
||||||
|
|
||||||
function bumpSize(evt){
|
|
||||||
iconSize = 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
function downSize(evt){
|
|
||||||
iconSize = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initializePyodide = async () =>{
|
|
||||||
// @ts-ignore
|
|
||||||
pyodideReadyPromise = loadInterpreter();
|
|
||||||
// @ts-ignore
|
|
||||||
let newEnv = {
|
|
||||||
'id': 'a',
|
|
||||||
'promise': pyodideReadyPromise,
|
|
||||||
'state': 'loading',
|
|
||||||
}
|
|
||||||
pyodideLoaded.set(pyodideReadyPromise);
|
|
||||||
loadedEnvironments.update((value: any): any => {
|
|
||||||
value[newEnv['id']] = newEnv;
|
|
||||||
});
|
|
||||||
|
|
||||||
let showNavBar = false;
|
|
||||||
let main = document.querySelector("#main");
|
|
||||||
navBarOpen.subscribe(value => {
|
|
||||||
showNavBar = value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// now we call all initializers before we actually executed all page scripts
|
|
||||||
for (let initializer of $initializers){
|
|
||||||
await initializer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we can actually execute the page scripts if we are in play mode
|
|
||||||
if ($mode == "play"){
|
|
||||||
for (let script of $scriptsQueue) {
|
|
||||||
script.evaluate();
|
|
||||||
}
|
|
||||||
scriptsQueue.set([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we call all post initializers AFTER we actually executed all page scripts
|
|
||||||
setTimeout(() => {
|
|
||||||
for (let initializer of $postInitializers){
|
|
||||||
initializer();
|
|
||||||
}
|
|
||||||
}, 3000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleComponentsNavBar(evt){
|
|
||||||
componentsNavOpen.set(!$componentsNavOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
:global(div.buttons-box) {
|
:global(div.buttons-box) {
|
||||||
margin-top: -25px;
|
margin-top: -25px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.parentBox:hover .buttons-box) {
|
|
||||||
visibility: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
:global(.parentBox:hover .buttons-box) {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Tailwind from './Tailwind.svelte';
|
||||||
|
import { loadInterpreter } from './interpreter';
|
||||||
|
import {
|
||||||
|
componentsNavOpen,
|
||||||
|
initializers,
|
||||||
|
loadedEnvironments,
|
||||||
|
mode,
|
||||||
|
navBarOpen,
|
||||||
|
postInitializers,
|
||||||
|
pyodideLoaded,
|
||||||
|
scriptsQueue,
|
||||||
|
} from './stores';
|
||||||
|
|
||||||
|
let iconSize = 2;
|
||||||
|
let pyodideReadyPromise;
|
||||||
|
|
||||||
|
function bumpSize(evt) {
|
||||||
|
iconSize = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function downSize(evt) {
|
||||||
|
iconSize = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initializePyodide = async () => {
|
||||||
|
pyodideReadyPromise = loadInterpreter();
|
||||||
|
let newEnv = {
|
||||||
|
id: 'a',
|
||||||
|
promise: pyodideReadyPromise,
|
||||||
|
state: 'loading',
|
||||||
|
};
|
||||||
|
pyodideLoaded.set(pyodideReadyPromise);
|
||||||
|
loadedEnvironments.update((value: any): any => {
|
||||||
|
value[newEnv['id']] = newEnv;
|
||||||
|
});
|
||||||
|
|
||||||
|
let showNavBar = false;
|
||||||
|
let main = document.querySelector('#main');
|
||||||
|
navBarOpen.subscribe(value => {
|
||||||
|
showNavBar = value;
|
||||||
|
});
|
||||||
|
|
||||||
|
// now we call all initializers before we actually executed all page scripts
|
||||||
|
for (let initializer of $initializers) {
|
||||||
|
await initializer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we can actually execute the page scripts if we are in play mode
|
||||||
|
if ($mode == 'play') {
|
||||||
|
for (let script of $scriptsQueue) {
|
||||||
|
script.evaluate();
|
||||||
|
}
|
||||||
|
scriptsQueue.set([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we call all post initializers AFTER we actually executed all page scripts
|
||||||
|
setTimeout(() => {
|
||||||
|
for (let initializer of $postInitializers) {
|
||||||
|
initializer();
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggleComponentsNavBar(evt) {
|
||||||
|
componentsNavOpen.set(!$componentsNavOpen);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
<script src="https://cdn.jsdelivr.net/pyodide/v0.20.0/full/pyodide.js" on:load={initializePyodide}></script>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<Tailwind />
|
<Tailwind />
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, mode } from '../stores';
|
import { componentDetailsNavOpen, loadedEnvironments, mode, pyodideLoaded } from '../stores';
|
||||||
import { guidGenerator } from '../utils';
|
import { guidGenerator } from '../utils';
|
||||||
// Premise used to connect to the first available pyodide interpreter
|
// Premise used to connect to the first available pyodide interpreter
|
||||||
let pyodideReadyPromise;
|
let pyodideReadyPromise;
|
||||||
@@ -7,7 +7,7 @@ let currentMode;
|
|||||||
let Element;
|
let Element;
|
||||||
|
|
||||||
pyodideLoaded.subscribe(value => {
|
pyodideLoaded.subscribe(value => {
|
||||||
pyodideReadyPromise = value;
|
pyodideReadyPromise = value;
|
||||||
});
|
});
|
||||||
loadedEnvironments.subscribe(value => {
|
loadedEnvironments.subscribe(value => {
|
||||||
environments = value;
|
environments = value;
|
||||||
@@ -15,17 +15,17 @@ loadedEnvironments.subscribe(value => {
|
|||||||
|
|
||||||
let propertiesNavOpen;
|
let propertiesNavOpen;
|
||||||
componentDetailsNavOpen.subscribe(value => {
|
componentDetailsNavOpen.subscribe(value => {
|
||||||
propertiesNavOpen = value;
|
propertiesNavOpen = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
mode.subscribe(value => {
|
mode.subscribe(value => {
|
||||||
currentMode = value;
|
currentMode = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: use type declaractions
|
// TODO: use type declaractions
|
||||||
type PyodideInterface = {
|
type PyodideInterface = {
|
||||||
registerJsModule(name: string, module: object): void
|
registerJsModule(name: string, module: object): void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class BaseEvalElement extends HTMLElement {
|
export class BaseEvalElement extends HTMLElement {
|
||||||
shadow: ShadowRoot;
|
shadow: ShadowRoot;
|
||||||
@@ -37,142 +37,136 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
outputElement: HTMLElement;
|
outputElement: HTMLElement;
|
||||||
errorElement: HTMLElement;
|
errorElement: HTMLElement;
|
||||||
theme: string;
|
theme: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// attach shadow so we can preserve the element original innerHtml content
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
this.shadow = this.attachShadow({ mode: 'open'});
|
this.shadow = this.attachShadow({ mode: 'open' });
|
||||||
this.wrapper = document.createElement('slot');
|
this.wrapper = document.createElement('slot');
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
addToOutput(s: string) {
|
addToOutput(s: string) {
|
||||||
this.outputElement.innerHTML += "<div>"+s+"</div>";
|
this.outputElement.innerHTML += '<div>' + s + '</div>';
|
||||||
this.outputElement.hidden = false;
|
this.outputElement.hidden = false;
|
||||||
}
|
|
||||||
|
|
||||||
postEvaluate(){
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkId(){
|
postEvaluate() {}
|
||||||
if (!this.id)
|
|
||||||
this.id = this.constructor.name+"-"+guidGenerator();
|
checkId() {
|
||||||
|
if (!this.id) this.id = this.constructor.name + '-' + guidGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFromElement(): string{
|
getSourceFromElement(): string {
|
||||||
return "";
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSourceFromFile(s: string): Promise<string>{
|
async getSourceFromFile(s: string): Promise<string> {
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let response = await fetch(s);
|
const response = await fetch(s);
|
||||||
this.code = await response.text();
|
this.code = await response.text();
|
||||||
return this.code;
|
return this.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _register_esm(pyodide: PyodideInterface): Promise<void> {
|
protected async _register_esm(pyodide: PyodideInterface): Promise<void> {
|
||||||
const imports: {[key: string]: unknown} = {}
|
const imports: { [key: string]: unknown } = {};
|
||||||
|
|
||||||
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
||||||
const importmap = (() => {
|
const importmap = (() => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(node.textContent)
|
return JSON.parse(node.textContent);
|
||||||
} catch {
|
} catch {
|
||||||
return null
|
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}'`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})()
|
|
||||||
|
|
||||||
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)
|
pyodide.registerJsModule('esm', imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(): Promise<void> {
|
async evaluate(): Promise<void> {
|
||||||
console.log('evaluate');
|
console.log('evaluate');
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let source: string;
|
let source: string;
|
||||||
let output;
|
let output;
|
||||||
try {
|
try {
|
||||||
// @ts-ignore
|
if (this.source) {
|
||||||
if (this.source){
|
|
||||||
source = await this.getSourceFromFile(this.source);
|
source = await this.getSourceFromFile(this.source);
|
||||||
}else{
|
} else {
|
||||||
source = this.getSourceFromElement();
|
source = this.getSourceFromElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
await this._register_esm(pyodide);
|
await this._register_esm(pyodide);
|
||||||
|
|
||||||
if (source.includes("asyncio")){
|
if (source.includes('asyncio')) {
|
||||||
await pyodide.runPythonAsync(`output_manager.change("`+this.outputElement.id+`", "`+this.errorElement.id+`")`);
|
await pyodide.runPythonAsync(
|
||||||
|
`output_manager.change("` + this.outputElement.id + `", "` + this.errorElement.id + `")`,
|
||||||
|
);
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
await pyodide.runPythonAsync(`output_manager.revert()`)
|
await pyodide.runPythonAsync(`output_manager.revert()`);
|
||||||
}else{
|
} else {
|
||||||
output = pyodide.runPython(`output_manager.change("`+this.outputElement.id+`", "`+this.errorElement.id+`")`);
|
output = pyodide.runPython(
|
||||||
|
`output_manager.change("` + this.outputElement.id + `", "` + this.errorElement.id + `")`,
|
||||||
|
);
|
||||||
output = pyodide.runPython(source);
|
output = pyodide.runPython(source);
|
||||||
pyodide.runPython(`output_manager.revert()`)
|
pyodide.runPython(`output_manager.revert()`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output !== undefined){
|
if (output !== undefined) {
|
||||||
if (Element === undefined){
|
if (Element === undefined) {
|
||||||
Element = pyodide.globals.get('Element');
|
Element = pyodide.globals.get('Element');
|
||||||
}
|
}
|
||||||
const out = Element(this.outputElement.id);
|
const out = Element(this.outputElement.id);
|
||||||
// @ts-ignore
|
out.write.callKwargs(output, { append: true });
|
||||||
out.write.callKwargs(output, { append : true});
|
|
||||||
|
|
||||||
this.outputElement.hidden = false;
|
this.outputElement.hidden = false;
|
||||||
this.outputElement.style.display = 'block';
|
this.outputElement.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.postEvaluate()
|
this.postEvaluate();
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (Element === undefined){
|
if (Element === undefined) {
|
||||||
Element = pyodide.globals.get('Element');
|
Element = pyodide.globals.get('Element');
|
||||||
}
|
}
|
||||||
const out = Element(this.errorElement.id);
|
const out = Element(this.errorElement.id);
|
||||||
// @ts-ignore
|
out.write.callKwargs(err, { append: true });
|
||||||
out.write.callKwargs(err, { append : true});
|
|
||||||
this.errorElement.hidden = false;
|
this.errorElement.hidden = false;
|
||||||
this.errorElement.style.display = 'block';
|
this.errorElement.style.display = 'block';
|
||||||
}
|
}
|
||||||
} // end evaluate
|
} // end evaluate
|
||||||
|
|
||||||
async eval(source: string): Promise<void> {
|
async eval(source: string): Promise<void> {
|
||||||
let output;
|
let output;
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
|
|
||||||
try{
|
try {
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
if (output !== undefined){ console.log(output); }
|
if (output !== undefined) {
|
||||||
|
console.log(output);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
} // end eval
|
} // end eval
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createWidget(name: string, code: string, klass: string) {
|
||||||
|
class CustomWidget extends HTMLElement {
|
||||||
function createWidget(name: string, code: string, klass: string){
|
|
||||||
|
|
||||||
class CustomWidget extends HTMLElement{
|
|
||||||
shadow: ShadowRoot;
|
shadow: ShadowRoot;
|
||||||
wrapper: HTMLElement;
|
wrapper: HTMLElement;
|
||||||
|
|
||||||
@@ -183,13 +177,13 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
proxyClass: any;
|
proxyClass: any;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// attach shadow so we can preserve the element original innerHtml content
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
this.shadow = this.attachShadow({ mode: 'open'});
|
this.shadow = this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
this.wrapper = document.createElement('slot');
|
this.wrapper = document.createElement('slot');
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
@@ -207,19 +201,19 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
}, 2000);
|
}, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerWidget(){
|
async registerWidget() {
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
console.log('new widget registered:', this.name);
|
console.log('new widget registered:', this.name);
|
||||||
pyodide.globals.set(this.id, this.proxy);
|
pyodide.globals.set(this.id, this.proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
async eval(source: string): Promise<void> {
|
async eval(source: string): Promise<void> {
|
||||||
let output;
|
let output;
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
try{
|
try {
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
this.proxyClass = pyodide.globals.get(this.klass);
|
this.proxyClass = pyodide.globals.get(this.klass);
|
||||||
if (output !== undefined){
|
if (output !== undefined) {
|
||||||
console.log(output);
|
console.log(output);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -227,10 +221,10 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let xPyWidget = customElements.define(name, CustomWidget);
|
const xPyWidget = customElements.define(name, CustomWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PyWidget extends HTMLElement {
|
export class PyWidget extends HTMLElement {
|
||||||
shadow: ShadowRoot;
|
shadow: ShadowRoot;
|
||||||
name: string;
|
name: string;
|
||||||
klass: string;
|
klass: string;
|
||||||
@@ -240,43 +234,45 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
theme: string;
|
theme: string;
|
||||||
source: string;
|
source: string;
|
||||||
code: string;
|
code: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// attach shadow so we can preserve the element original innerHtml content
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
this.shadow = this.attachShadow({ mode: 'open'});
|
this.shadow = this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
this.wrapper = document.createElement('slot');
|
this.wrapper = document.createElement('slot');
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
|
|
||||||
if (this.hasAttribute('src')) {
|
if (this.hasAttribute('src')) {
|
||||||
this.source = this.getAttribute('src');
|
this.source = this.getAttribute('src');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('name')) {
|
if (this.hasAttribute('name')) {
|
||||||
this.name = this.getAttribute('name');
|
this.name = this.getAttribute('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('klass')) {
|
if (this.hasAttribute('klass')) {
|
||||||
this.klass = this.getAttribute('klass');
|
this.klass = this.getAttribute('klass');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
if (this.id === undefined){
|
if (this.id === undefined) {
|
||||||
throw new ReferenceError(`No id specified for component. Components must have an explicit id. Please use id="" to specify your component id.`)
|
throw new ReferenceError(
|
||||||
return;
|
`No id specified for component. Components must have an explicit id. Please use id="" to specify your component id.`,
|
||||||
}
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mainDiv = document.createElement('div');
|
const mainDiv = document.createElement('div');
|
||||||
mainDiv.id = this.id + '-main';
|
mainDiv.id = this.id + '-main';
|
||||||
this.appendChild(mainDiv);
|
this.appendChild(mainDiv);
|
||||||
console.log('reading source')
|
console.log('reading source');
|
||||||
this.getSourceFromFile(this.source).then((code:string) => {
|
this.getSourceFromFile(this.source).then((code: string) => {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
createWidget(this.name, code, this.klass);
|
createWidget(this.name, code, this.klass);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initOutErr(): void {
|
initOutErr(): void {
|
||||||
@@ -287,42 +283,42 @@ export class BaseEvalElement extends HTMLElement {
|
|||||||
if (!this.hasAttribute('output-mode')) {
|
if (!this.hasAttribute('output-mode')) {
|
||||||
this.setAttribute('output-mode', 'append');
|
this.setAttribute('output-mode', 'append');
|
||||||
}
|
}
|
||||||
}else{
|
} else {
|
||||||
if (this.hasAttribute('std-out')){
|
if (this.hasAttribute('std-out')) {
|
||||||
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
||||||
}else{
|
} else {
|
||||||
// In this case neither output or std-out have been provided so we need
|
// In this case neither output or std-out have been provided so we need
|
||||||
// to create a new output div to output to
|
// to create a new output div to output to
|
||||||
this.outputElement = document.createElement('div');
|
this.outputElement = document.createElement('div');
|
||||||
this.outputElement.classList.add("output");
|
this.outputElement.classList.add('output');
|
||||||
this.outputElement.hidden = true;
|
this.outputElement.hidden = true;
|
||||||
this.outputElement.id = this.id + "-" + this.getAttribute("exec-id");
|
this.outputElement.id = this.id + '-' + this.getAttribute('exec-id');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('std-err')){
|
if (this.hasAttribute('std-err')) {
|
||||||
this.outputElement = document.getElementById(this.getAttribute('std-err'));
|
this.outputElement = document.getElementById(this.getAttribute('std-err'));
|
||||||
}else{
|
} else {
|
||||||
this.errorElement = this.outputElement;
|
this.errorElement = this.outputElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getSourceFromFile(s: string): Promise<string>{
|
async getSourceFromFile(s: string): Promise<string> {
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let response = await fetch(s);
|
const response = await fetch(s);
|
||||||
return await response.text();
|
return await response.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
async eval(source: string): Promise<void> {
|
async eval(source: string): Promise<void> {
|
||||||
let output;
|
let output;
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
try{
|
try {
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
if (output !== undefined){
|
if (output !== undefined) {
|
||||||
console.log(output);
|
console.log(output);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,65 +5,62 @@ export class PyBox extends HTMLElement {
|
|||||||
wrapper: HTMLElement;
|
wrapper: HTMLElement;
|
||||||
theme: string;
|
theme: string;
|
||||||
widths: Array<string>;
|
widths: Array<string>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// attach shadow so we can preserve the element original innerHtml content
|
// attach shadow so we can preserve the element original innerHtml content
|
||||||
this.shadow = this.attachShadow({ mode: 'open'});
|
this.shadow = this.attachShadow({ mode: 'open' });
|
||||||
|
|
||||||
this.wrapper = document.createElement('slot');
|
this.wrapper = document.createElement('slot');
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
let mainDiv = document.createElement('div');
|
const mainDiv = document.createElement('div');
|
||||||
addClasses(mainDiv, ["flex", "mx-8"])
|
addClasses(mainDiv, ['flex', 'mx-8']);
|
||||||
|
|
||||||
// Hack: for some reason when moving children, the editor box duplicates children
|
|
||||||
// meaning that we end up with 2 editors, if there's a <py-repl> inside the <py-box>
|
|
||||||
// so, if we have more than 2 children with the cm-editor class, we remove one of them
|
|
||||||
while (this.childNodes.length > 0) {
|
|
||||||
console.log(this.firstChild);
|
|
||||||
if ( this.firstChild.nodeName == "PY-REPL" ){
|
|
||||||
// in this case we need to remove the child and craete a new one from scratch
|
|
||||||
let replDiv = document.createElement('div');
|
|
||||||
// we need to put the new repl inside a div so that if the repl has auto-generate true
|
|
||||||
// it can replicate itself inside that constrained div
|
|
||||||
replDiv.appendChild(this.firstChild.cloneNode());
|
|
||||||
mainDiv.appendChild(replDiv);
|
|
||||||
this.firstChild.remove();
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
if ( this.firstChild.nodeName != "#text" ){
|
|
||||||
mainDiv.appendChild(this.firstChild);
|
|
||||||
}else{
|
|
||||||
this.firstChild.remove()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now we need to set widths
|
// Hack: for some reason when moving children, the editor box duplicates children
|
||||||
this.widths = []
|
// meaning that we end up with 2 editors, if there's a <py-repl> inside the <py-box>
|
||||||
if (this.hasAttribute('widths')) {
|
// so, if we have more than 2 children with the cm-editor class, we remove one of them
|
||||||
for (let w of this.getAttribute('widths').split(";")) {
|
while (this.childNodes.length > 0) {
|
||||||
this.widths.push(`w-${w}`);
|
console.log(this.firstChild);
|
||||||
|
if (this.firstChild.nodeName == 'PY-REPL') {
|
||||||
|
// in this case we need to remove the child and craete a new one from scratch
|
||||||
|
const replDiv = document.createElement('div');
|
||||||
|
// we need to put the new repl inside a div so that if the repl has auto-generate true
|
||||||
|
// it can replicate itself inside that constrained div
|
||||||
|
replDiv.appendChild(this.firstChild.cloneNode());
|
||||||
|
mainDiv.appendChild(replDiv);
|
||||||
|
this.firstChild.remove();
|
||||||
|
} else {
|
||||||
|
if (this.firstChild.nodeName != '#text') {
|
||||||
|
mainDiv.appendChild(this.firstChild);
|
||||||
|
} else {
|
||||||
|
this.firstChild.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
for (let el of mainDiv.childNodes) {
|
// now we need to set widths
|
||||||
this.widths.push(`w-1/${mainDiv.childNodes.length}`);
|
this.widths = [];
|
||||||
|
if (this.hasAttribute('widths')) {
|
||||||
|
for (const w of this.getAttribute('widths').split(';')) {
|
||||||
|
this.widths.push(`w-${w}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const el of mainDiv.childNodes) {
|
||||||
|
this.widths.push(`w-1/${mainDiv.childNodes.length}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for (let i in this.widths) {
|
this.widths.forEach((width, index)=>{
|
||||||
// @ts-ignore
|
const node: ChildNode = mainDiv.childNodes[index];
|
||||||
addClasses(mainDiv.childNodes[parseInt(i)], [this.widths[i], 'mx-4']);
|
addClasses(node, [width, 'mx-4'])
|
||||||
}
|
|
||||||
|
|
||||||
this.appendChild(mainDiv);
|
})
|
||||||
console.log('py-box connected');
|
|
||||||
|
this.appendChild(mainDiv);
|
||||||
|
console.log('py-box connected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,47 +10,47 @@ export class PyButton extends BaseEvalElement {
|
|||||||
mount_name: string;
|
mount_name: string;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (this.hasAttribute('label')) {
|
if (this.hasAttribute('label')) {
|
||||||
this.label = this.getAttribute('label');
|
this.label = this.getAttribute('label');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.code = htmlDecode(this.innerHTML);
|
this.code = htmlDecode(this.innerHTML);
|
||||||
this.mount_name = this.id.split("-").join("_");
|
this.mount_name = this.id.split('-').join('_');
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
let mainDiv = document.createElement('button');
|
|
||||||
mainDiv.innerHTML = this.label;
|
|
||||||
addClasses(mainDiv, ["p-2", "text-white", "bg-blue-600", "border", "border-blue-600", "rounded"]);
|
|
||||||
|
|
||||||
mainDiv.id = this.id;
|
|
||||||
this.id = `${this.id}-container`;
|
|
||||||
|
|
||||||
this.appendChild(mainDiv);
|
const mainDiv = document.createElement('button');
|
||||||
this.code = this.code.split("self").join(this.mount_name);
|
mainDiv.innerHTML = this.label;
|
||||||
let registrationCode = `${this.mount_name} = Element("${ mainDiv.id }")`;
|
addClasses(mainDiv, ['p-2', 'text-white', 'bg-blue-600', 'border', 'border-blue-600', 'rounded']);
|
||||||
if (this.code.includes("def on_focus")){
|
|
||||||
this.code = this.code.replace("def on_focus", `def on_focus_${this.mount_name}`);
|
|
||||||
registrationCode += `\n${this.mount_name}.element.onfocus = on_focus_${this.mount_name}`
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.code.includes("def on_click")){
|
mainDiv.id = this.id;
|
||||||
this.code = this.code.replace("def on_click", `def on_click_${this.mount_name}`);
|
this.id = `${this.id}-container`;
|
||||||
registrationCode += `\n${this.mount_name}.element.onclick = on_click_${this.mount_name}`
|
|
||||||
}
|
this.appendChild(mainDiv);
|
||||||
|
this.code = this.code.split('self').join(this.mount_name);
|
||||||
// now that we appended and the element is attached, lets connect with the event handlers
|
let registrationCode = `${this.mount_name} = Element("${mainDiv.id}")`;
|
||||||
// defined for this widget
|
if (this.code.includes('def on_focus')) {
|
||||||
setTimeout(() => {
|
this.code = this.code.replace('def on_focus', `def on_focus_${this.mount_name}`);
|
||||||
this.eval(this.code).then(() => {
|
registrationCode += `\n${this.mount_name}.element.onfocus = on_focus_${this.mount_name}`;
|
||||||
this.eval(registrationCode).then(() => {
|
}
|
||||||
console.log('registered handlers');
|
|
||||||
});
|
if (this.code.includes('def on_click')) {
|
||||||
});
|
this.code = this.code.replace('def on_click', `def on_click_${this.mount_name}`);
|
||||||
}, 4000);
|
registrationCode += `\n${this.mount_name}.element.onclick = on_click_${this.mount_name}`;
|
||||||
|
}
|
||||||
console.log('py-button connected');
|
|
||||||
|
// now that we appended and the element is attached, lets connect with the event handlers
|
||||||
|
// defined for this widget
|
||||||
|
setTimeout(() => {
|
||||||
|
this.eval(this.code).then(() => {
|
||||||
|
this.eval(registrationCode).then(() => {
|
||||||
|
console.log('registered handlers');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 4000);
|
||||||
|
|
||||||
|
console.log('py-button connected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,68 +9,65 @@ let environments;
|
|||||||
let currentMode;
|
let currentMode;
|
||||||
|
|
||||||
pyodideLoaded.subscribe(value => {
|
pyodideLoaded.subscribe(value => {
|
||||||
pyodideReadyPromise = value;
|
pyodideReadyPromise = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
loadedEnvironments.subscribe(value => {
|
loadedEnvironments.subscribe(value => {
|
||||||
environments = value;
|
environments = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
mode.subscribe(value => {
|
mode.subscribe(value => {
|
||||||
currentMode = value;
|
currentMode = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
export class PyEnv extends HTMLElement {
|
export class PyEnv extends HTMLElement {
|
||||||
shadow: ShadowRoot;
|
shadow: ShadowRoot;
|
||||||
wrapper: HTMLElement;
|
wrapper: HTMLElement;
|
||||||
code: string;
|
code: string;
|
||||||
environment: any;
|
environment: any;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.shadow = this.attachShadow({ mode: 'open'});
|
this.shadow = this.attachShadow({ mode: 'open' });
|
||||||
this.wrapper = document.createElement('slot');
|
this.wrapper = document.createElement('slot');
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.code = this.innerHTML;
|
this.code = this.innerHTML;
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
let env = [];
|
const env = [];
|
||||||
let paths = [];
|
const paths = [];
|
||||||
|
|
||||||
this.environment = jsyaml.load(this.code);
|
this.environment = jsyaml.load(this.code);
|
||||||
if (this.environment === undefined)
|
if (this.environment === undefined) return;
|
||||||
return
|
|
||||||
|
|
||||||
for (let entry of this.environment) {
|
for (const entry of this.environment) {
|
||||||
if (typeof entry == "string" ){
|
if (typeof entry == 'string') {
|
||||||
env.push(entry);
|
env.push(entry);
|
||||||
}
|
} else if (entry.hasOwnProperty('paths')) {
|
||||||
else if (entry.hasOwnProperty('paths')){
|
for (const path of entry.paths) {
|
||||||
for (let path of entry.paths) {
|
paths.push(path);
|
||||||
paths.push(path);
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadEnv() {
|
async function loadEnv() {
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
await loadPackage(env, pyodide);
|
await loadPackage(env, pyodide);
|
||||||
console.log("enviroment loaded")
|
console.log('enviroment loaded');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadPaths() {
|
async function loadPaths() {
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
for (let singleFile of paths) {
|
for (const singleFile of paths) {
|
||||||
await loadFromFile(singleFile, pyodide);
|
await loadFromFile(singleFile, pyodide);
|
||||||
}
|
}
|
||||||
console.log("paths loaded")
|
console.log('paths loaded');
|
||||||
|
}
|
||||||
|
addInitializer(loadEnv);
|
||||||
|
addInitializer(loadPaths);
|
||||||
|
console.log('enviroment loading...', env);
|
||||||
}
|
}
|
||||||
addInitializer(loadEnv);
|
|
||||||
addInitializer(loadPaths);
|
|
||||||
console.log("enviroment loading...", env)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,46 +9,44 @@ export class PyInputBox extends BaseEvalElement {
|
|||||||
label: string;
|
label: string;
|
||||||
mount_name: string;
|
mount_name: string;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (this.hasAttribute('label')) {
|
if (this.hasAttribute('label')) {
|
||||||
this.label = this.getAttribute('label');
|
this.label = this.getAttribute('label');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.code = htmlDecode(this.innerHTML);
|
this.code = htmlDecode(this.innerHTML);
|
||||||
this.mount_name = this.id.split("-").join("_");
|
this.mount_name = this.id.split('-').join('_');
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
let mainDiv = document.createElement('input');
|
|
||||||
mainDiv.type = "text";
|
|
||||||
addClasses(mainDiv, ["border", "flex-1", "w-full", "mr-3", "border-gray-300", "p-2", "rounded"]);
|
|
||||||
|
|
||||||
mainDiv.id = this.id;
|
const mainDiv = document.createElement('input');
|
||||||
this.id = `${this.id}-container`;
|
mainDiv.type = 'text';
|
||||||
this.appendChild(mainDiv);
|
addClasses(mainDiv, ['border', 'flex-1', 'w-full', 'mr-3', 'border-gray-300', 'p-2', 'rounded']);
|
||||||
|
|
||||||
// now that we appended and the element is attached, lets connect with the event handlers
|
mainDiv.id = this.id;
|
||||||
// defined for this widget
|
this.id = `${this.id}-container`;
|
||||||
this.appendChild(mainDiv);
|
this.appendChild(mainDiv);
|
||||||
this.code = this.code.split("self").join(this.mount_name);
|
|
||||||
let registrationCode = `${this.mount_name} = Element("${ mainDiv.id }")`;
|
|
||||||
if (this.code.includes("def on_keypress")){
|
|
||||||
this.code = this.code.replace("def on_keypress", `def on_keypress_${this.mount_name}`);
|
|
||||||
registrationCode += `\n${this.mount_name}.element.onkeypress = on_keypress_${this.mount_name}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: For now we delay execution to allow pyodide to load but in the future this
|
// now that we appended and the element is attached, lets connect with the event handlers
|
||||||
// should really wait for it to load..
|
// defined for this widget
|
||||||
setTimeout(() => {
|
this.appendChild(mainDiv);
|
||||||
this.eval(this.code).then(() => {
|
this.code = this.code.split('self').join(this.mount_name);
|
||||||
this.eval(registrationCode).then(() => {
|
let registrationCode = `${this.mount_name} = Element("${mainDiv.id}")`;
|
||||||
console.log('registered handlers');
|
if (this.code.includes('def on_keypress')) {
|
||||||
});
|
this.code = this.code.replace('def on_keypress', `def on_keypress_${this.mount_name}`);
|
||||||
});
|
registrationCode += `\n${this.mount_name}.element.onkeypress = on_keypress_${this.mount_name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: For now we delay execution to allow pyodide to load but in the future this
|
||||||
|
// should really wait for it to load..
|
||||||
|
setTimeout(() => {
|
||||||
|
this.eval(this.code).then(() => {
|
||||||
|
this.eval(registrationCode).then(() => {
|
||||||
|
console.log('registered handlers');
|
||||||
|
});
|
||||||
|
});
|
||||||
}, 4000);
|
}, 4000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
|
import { basicSetup, EditorState, EditorView } from '@codemirror/basic-setup';
|
||||||
import { python } from "@codemirror/lang-python"
|
import { python } from '@codemirror/lang-python';
|
||||||
// @ts-ignore
|
import { Compartment, StateCommand } from '@codemirror/state';
|
||||||
import { StateCommand, Compartment } from '@codemirror/state';
|
import { keymap } from '@codemirror/view';
|
||||||
import { keymap, ViewUpdate } from "@codemirror/view";
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { defaultKeymap } from "@codemirror/commands";
|
import { oneDarkTheme } from '@codemirror/theme-one-dark';
|
||||||
import { oneDarkTheme } from "@codemirror/theme-one-dark";
|
|
||||||
|
|
||||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode } from '../stores';
|
import { componentDetailsNavOpen, currentComponentDetails, loadedEnvironments, mode, pyodideLoaded } from '../stores';
|
||||||
import { addClasses } from '../utils';
|
import { addClasses } from '../utils';
|
||||||
import { BaseEvalElement } from './base';
|
import { BaseEvalElement } from './base';
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ let environments;
|
|||||||
let currentMode;
|
let currentMode;
|
||||||
|
|
||||||
pyodideLoaded.subscribe(value => {
|
pyodideLoaded.subscribe(value => {
|
||||||
pyodideReadyPromise = value;
|
pyodideReadyPromise = value;
|
||||||
});
|
});
|
||||||
loadedEnvironments.subscribe(value => {
|
loadedEnvironments.subscribe(value => {
|
||||||
environments = value;
|
environments = value;
|
||||||
@@ -24,216 +23,230 @@ loadedEnvironments.subscribe(value => {
|
|||||||
|
|
||||||
let propertiesNavOpen;
|
let propertiesNavOpen;
|
||||||
componentDetailsNavOpen.subscribe(value => {
|
componentDetailsNavOpen.subscribe(value => {
|
||||||
propertiesNavOpen = value;
|
propertiesNavOpen = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
mode.subscribe(value => {
|
mode.subscribe(value => {
|
||||||
currentMode = value;
|
currentMode = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const languageConf = new Compartment();
|
||||||
|
|
||||||
const languageConf = new Compartment
|
function createCmdHandler(el) {
|
||||||
|
|
||||||
function createCmdHandler(el){
|
|
||||||
// Creates a codemirror cmd handler that calls the el.evaluate when an event
|
// Creates a codemirror cmd handler that calls the el.evaluate when an event
|
||||||
// triggers that specific cmd
|
// triggers that specific cmd
|
||||||
const toggleCheckbox:StateCommand = ({ state, dispatch }) => {
|
const toggleCheckbox: StateCommand = ({ state, dispatch }) => {
|
||||||
return el.evaluate(state)
|
return el.evaluate(state);
|
||||||
}
|
};
|
||||||
return toggleCheckbox
|
return toggleCheckbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class PyRepl extends BaseEvalElement {
|
export class PyRepl extends BaseEvalElement {
|
||||||
editor: EditorView;
|
editor: EditorView;
|
||||||
editorNode: HTMLElement;
|
editorNode: HTMLElement;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// add an extra div where we can attach the codemirror editor
|
// add an extra div where we can attach the codemirror editor
|
||||||
this.editorNode = document.createElement('div');
|
this.editorNode = document.createElement('div');
|
||||||
addClasses(this.editorNode, ["editor-box"])
|
addClasses(this.editorNode, ['editor-box']);
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.checkId()
|
this.checkId();
|
||||||
this.code = this.innerHTML;
|
this.code = this.innerHTML;
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
let extensions = [
|
const extensions = [
|
||||||
basicSetup,
|
basicSetup,
|
||||||
languageConf.of(python()),
|
languageConf.of(python()),
|
||||||
keymap.of([
|
keymap.of([
|
||||||
...defaultKeymap,
|
...defaultKeymap,
|
||||||
{ key: "Ctrl-Enter", run: createCmdHandler(this) },
|
{ key: 'Ctrl-Enter', run: createCmdHandler(this) },
|
||||||
{ key: "Shift-Enter", run: createCmdHandler(this) }
|
{ key: 'Shift-Enter', run: createCmdHandler(this) },
|
||||||
]),
|
]),
|
||||||
|
|
||||||
// Event listener function that is called every time an user types something on this editor
|
// Event listener function that is called every time an user types something on this editor
|
||||||
// EditorView.updateListener.of((v:ViewUpdate) => {
|
// EditorView.updateListener.of((v:ViewUpdate) => {
|
||||||
// if (v.docChanged) {
|
// if (v.docChanged) {
|
||||||
// console.log(v.changes);
|
// console.log(v.changes);
|
||||||
|
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
];
|
||||||
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!this.hasAttribute('theme')) {
|
if (!this.hasAttribute('theme')) {
|
||||||
this.theme = this.getAttribute('theme');
|
this.theme = this.getAttribute('theme');
|
||||||
if (this.theme == 'dark'){
|
if (this.theme == 'dark') {
|
||||||
extensions.push(oneDarkTheme);
|
extensions.push(oneDarkTheme);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let startState = EditorState.create({
|
|
||||||
doc: this.code.trim(),
|
|
||||||
extensions: extensions
|
|
||||||
})
|
|
||||||
|
|
||||||
this.editor = new EditorView({
|
|
||||||
state: startState,
|
|
||||||
parent: this.editorNode
|
|
||||||
})
|
|
||||||
|
|
||||||
let mainDiv = document.createElement('div');
|
|
||||||
addClasses(mainDiv, ["parentBox", "group", "flex", "flex-col", "mt-2", "border-2", "border-gray-200", "rounded-lg", "mx-8"])
|
|
||||||
// add Editor to main PyScript div
|
|
||||||
|
|
||||||
// Butons DIV
|
|
||||||
var eDiv = document.createElement('div');
|
|
||||||
addClasses(eDiv, "buttons-box opacity-0 group-hover:opacity-100 relative top-0 right-0 flex flex-row-reverse space-x-reverse space-x-4 font-mono text-white text-sm font-bold leading-6 dev-buttons-group".split(" "))
|
|
||||||
eDiv.setAttribute("role", "group");
|
|
||||||
|
|
||||||
// Play Button
|
|
||||||
this.btnRun = document.createElement('button');
|
|
||||||
this.btnRun.innerHTML = '<svg id="" class="svelte-fa svelte-ps5qeg" style="height:1em;vertical-align:-.125em;transform-origin:center;overflow:visible" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>';
|
|
||||||
let buttonClasses = ["mr-2", "block", "py-2", "px-4", "rounded-full"];
|
|
||||||
addClasses(this.btnRun, buttonClasses);
|
|
||||||
addClasses(this.btnRun, ["bg-green-500"])
|
|
||||||
eDiv.appendChild(this.btnRun);
|
|
||||||
|
|
||||||
this.btnRun.onclick = wrap(this);
|
|
||||||
|
|
||||||
function wrap(el: any){
|
|
||||||
async function evaluatePython() {
|
|
||||||
el.evaluate()
|
|
||||||
}
|
|
||||||
return evaluatePython;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Settings button
|
|
||||||
this.btnConfig = document.createElement('button');
|
|
||||||
this.btnConfig.innerHTML = '<svg id="" class="svelte-fa svelte-ps5qeg" style="height:1em;vertical-align:-.125em;transform-origin:center;overflow:visible" viewBox="0 0 512 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(256 256)" transform-origin="128 0"><g transform="translate(0,0) scale(1,1)"><path d="M495.9 166.6C499.2 175.2 496.4 184.9 489.6 191.2L446.3 230.6C447.4 238.9 448 247.4 448 256C448 264.6 447.4 273.1 446.3 281.4L489.6 320.8C496.4 327.1 499.2 336.8 495.9 345.4C491.5 357.3 486.2 368.8 480.2 379.7L475.5 387.8C468.9 398.8 461.5 409.2 453.4 419.1C447.4 426.2 437.7 428.7 428.9 425.9L373.2 408.1C359.8 418.4 344.1 427 329.2 433.6L316.7 490.7C314.7 499.7 307.7 506.1 298.5 508.5C284.7 510.8 270.5 512 255.1 512C241.5 512 227.3 510.8 213.5 508.5C204.3 506.1 197.3 499.7 195.3 490.7L182.8 433.6C167 427 152.2 418.4 138.8 408.1L83.14 425.9C74.3 428.7 64.55 426.2 58.63 419.1C50.52 409.2 43.12 398.8 36.52 387.8L31.84 379.7C25.77 368.8 20.49 357.3 16.06 345.4C12.82 336.8 15.55 327.1 22.41 320.8L65.67 281.4C64.57 273.1 64 264.6 64 256C64 247.4 64.57 238.9 65.67 230.6L22.41 191.2C15.55 184.9 12.82 175.3 16.06 166.6C20.49 154.7 25.78 143.2 31.84 132.3L36.51 124.2C43.12 113.2 50.52 102.8 58.63 92.95C64.55 85.8 74.3 83.32 83.14 86.14L138.8 103.9C152.2 93.56 167 84.96 182.8 78.43L195.3 21.33C197.3 12.25 204.3 5.04 213.5 3.51C227.3 1.201 241.5 0 256 0C270.5 0 284.7 1.201 298.5 3.51C307.7 5.04 314.7 12.25 316.7 21.33L329.2 78.43C344.1 84.96 359.8 93.56 373.2 103.9L428.9 86.14C437.7 83.32 447.4 85.8 453.4 92.95C461.5 102.8 468.9 113.2 475.5 124.2L480.2 132.3C486.2 143.2 491.5 154.7 495.9 166.6V166.6zM256 336C300.2 336 336 300.2 336 255.1C336 211.8 300.2 175.1 256 175.1C211.8 175.1 176 211.8 176 255.1C176 300.2 211.8 336 256 336z" fill="currentColor" transform="translate(-256 -256)"></path></g></g></svg>';
|
|
||||||
this.btnConfig.onclick = function toggleNavBar(evt){
|
|
||||||
console.log('clicked');
|
|
||||||
componentDetailsNavOpen.set(!propertiesNavOpen);
|
|
||||||
|
|
||||||
currentComponentDetails.set([
|
|
||||||
{key: "auto-generate", value: true},
|
|
||||||
{key:"output", value: "default"},
|
|
||||||
{key: "source", value: "self"},
|
|
||||||
{key: "output-mode", value: "clear"}
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
addClasses(this.btnConfig, buttonClasses);
|
|
||||||
addClasses(this.btnConfig, ["bg-blue-500"])
|
|
||||||
eDiv.appendChild(this.btnConfig);
|
|
||||||
|
|
||||||
|
|
||||||
mainDiv.appendChild(eDiv);
|
|
||||||
mainDiv.appendChild(this.editorNode);
|
|
||||||
|
|
||||||
if (!this.id){
|
|
||||||
console.log("WARNING: <pyrepl> define with an id. <pyrepl> should always have an id. More than one <pyrepl> on a page won't work otherwise!")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.hasAttribute('exec-id')) {
|
|
||||||
this.setAttribute("exec-id", "1");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.hasAttribute('root')) {
|
|
||||||
this.setAttribute("root", this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.hasAttribute('output')) {
|
|
||||||
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
|
|
||||||
|
|
||||||
// in this case, the default output-mode is append, if hasn't been specified
|
|
||||||
if (!this.hasAttribute('output-mode')) {
|
|
||||||
this.setAttribute('output-mode', 'append');
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (this.hasAttribute('std-out')){
|
|
||||||
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
|
||||||
}else{
|
|
||||||
// In this case neither output or std-out have been provided so we need
|
|
||||||
// to create a new output div to output to
|
|
||||||
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 if there's not output pre-defined
|
|
||||||
mainDiv.appendChild(this.outputElement);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('std-err')){
|
const startState = EditorState.create({
|
||||||
this.errorElement = document.getElementById(this.getAttribute('std-err'));
|
doc: this.code.trim(),
|
||||||
}else{
|
extensions: extensions,
|
||||||
this.errorElement = this.outputElement;
|
});
|
||||||
|
|
||||||
|
this.editor = new EditorView({
|
||||||
|
state: startState,
|
||||||
|
parent: this.editorNode,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainDiv = document.createElement('div');
|
||||||
|
addClasses(mainDiv, [
|
||||||
|
'parentBox',
|
||||||
|
'group',
|
||||||
|
'flex',
|
||||||
|
'flex-col',
|
||||||
|
'mt-2',
|
||||||
|
'border-2',
|
||||||
|
'border-gray-200',
|
||||||
|
'rounded-lg',
|
||||||
|
'mx-8',
|
||||||
|
]);
|
||||||
|
// add Editor to main PyScript div
|
||||||
|
|
||||||
|
// Butons DIV
|
||||||
|
const eDiv = document.createElement('div');
|
||||||
|
addClasses(
|
||||||
|
eDiv,
|
||||||
|
'buttons-box opacity-0 group-hover:opacity-100 relative top-0 right-0 flex flex-row-reverse space-x-reverse space-x-4 font-mono text-white text-sm font-bold leading-6 dev-buttons-group'.split(
|
||||||
|
' ',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
eDiv.setAttribute('role', 'group');
|
||||||
|
|
||||||
|
// Play Button
|
||||||
|
this.btnRun = document.createElement('button');
|
||||||
|
this.btnRun.innerHTML =
|
||||||
|
'<svg id="" class="svelte-fa svelte-ps5qeg" style="height:1em;vertical-align:-.125em;transform-origin:center;overflow:visible" viewBox="0 0 384 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(192 256)" transform-origin="96 0"><g transform="translate(0,0) scale(1,1)"><path d="M361 215C375.3 223.8 384 239.3 384 256C384 272.7 375.3 288.2 361 296.1L73.03 472.1C58.21 482 39.66 482.4 24.52 473.9C9.377 465.4 0 449.4 0 432V80C0 62.64 9.377 46.63 24.52 38.13C39.66 29.64 58.21 29.99 73.03 39.04L361 215z" fill="currentColor" transform="translate(-192 -256)"></path></g></g></svg>';
|
||||||
|
const buttonClasses = ['mr-2', 'block', 'py-2', 'px-4', 'rounded-full'];
|
||||||
|
addClasses(this.btnRun, buttonClasses);
|
||||||
|
addClasses(this.btnRun, ['bg-green-500']);
|
||||||
|
eDiv.appendChild(this.btnRun);
|
||||||
|
|
||||||
|
this.btnRun.onclick = wrap(this);
|
||||||
|
|
||||||
|
function wrap(el: any) {
|
||||||
|
async function evaluatePython() {
|
||||||
|
await el.evaluate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return evaluatePython;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Settings button
|
||||||
|
this.btnConfig = document.createElement('button');
|
||||||
|
this.btnConfig.innerHTML =
|
||||||
|
'<svg id="" class="svelte-fa svelte-ps5qeg" style="height:1em;vertical-align:-.125em;transform-origin:center;overflow:visible" viewBox="0 0 512 512" aria-hidden="true" role="img" xmlns="http://www.w3.org/2000/svg"><g transform="translate(256 256)" transform-origin="128 0"><g transform="translate(0,0) scale(1,1)"><path d="M495.9 166.6C499.2 175.2 496.4 184.9 489.6 191.2L446.3 230.6C447.4 238.9 448 247.4 448 256C448 264.6 447.4 273.1 446.3 281.4L489.6 320.8C496.4 327.1 499.2 336.8 495.9 345.4C491.5 357.3 486.2 368.8 480.2 379.7L475.5 387.8C468.9 398.8 461.5 409.2 453.4 419.1C447.4 426.2 437.7 428.7 428.9 425.9L373.2 408.1C359.8 418.4 344.1 427 329.2 433.6L316.7 490.7C314.7 499.7 307.7 506.1 298.5 508.5C284.7 510.8 270.5 512 255.1 512C241.5 512 227.3 510.8 213.5 508.5C204.3 506.1 197.3 499.7 195.3 490.7L182.8 433.6C167 427 152.2 418.4 138.8 408.1L83.14 425.9C74.3 428.7 64.55 426.2 58.63 419.1C50.52 409.2 43.12 398.8 36.52 387.8L31.84 379.7C25.77 368.8 20.49 357.3 16.06 345.4C12.82 336.8 15.55 327.1 22.41 320.8L65.67 281.4C64.57 273.1 64 264.6 64 256C64 247.4 64.57 238.9 65.67 230.6L22.41 191.2C15.55 184.9 12.82 175.3 16.06 166.6C20.49 154.7 25.78 143.2 31.84 132.3L36.51 124.2C43.12 113.2 50.52 102.8 58.63 92.95C64.55 85.8 74.3 83.32 83.14 86.14L138.8 103.9C152.2 93.56 167 84.96 182.8 78.43L195.3 21.33C197.3 12.25 204.3 5.04 213.5 3.51C227.3 1.201 241.5 0 256 0C270.5 0 284.7 1.201 298.5 3.51C307.7 5.04 314.7 12.25 316.7 21.33L329.2 78.43C344.1 84.96 359.8 93.56 373.2 103.9L428.9 86.14C437.7 83.32 447.4 85.8 453.4 92.95C461.5 102.8 468.9 113.2 475.5 124.2L480.2 132.3C486.2 143.2 491.5 154.7 495.9 166.6V166.6zM256 336C300.2 336 336 300.2 336 255.1C336 211.8 300.2 175.1 256 175.1C211.8 175.1 176 211.8 176 255.1C176 300.2 211.8 336 256 336z" fill="currentColor" transform="translate(-256 -256)"></path></g></g></svg>';
|
||||||
|
this.btnConfig.onclick = function toggleNavBar(evt) {
|
||||||
|
console.log('clicked');
|
||||||
|
componentDetailsNavOpen.set(!propertiesNavOpen);
|
||||||
|
|
||||||
this.appendChild(mainDiv);
|
currentComponentDetails.set([
|
||||||
this.editor.focus();
|
{ key: 'auto-generate', value: true },
|
||||||
console.log('connected');
|
{ key: 'output', value: 'default' },
|
||||||
|
{ key: 'source', value: 'self' },
|
||||||
|
{ key: 'output-mode', value: 'clear' },
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
|
||||||
|
addClasses(this.btnConfig, buttonClasses);
|
||||||
|
addClasses(this.btnConfig, ['bg-blue-500']);
|
||||||
|
eDiv.appendChild(this.btnConfig);
|
||||||
|
|
||||||
|
mainDiv.appendChild(eDiv);
|
||||||
|
mainDiv.appendChild(this.editorNode);
|
||||||
|
|
||||||
|
if (!this.id) {
|
||||||
|
console.log(
|
||||||
|
'WARNING: <pyrepl> define with an id. <pyrepl> should always have an id. More than one <pyrepl> on a page won\'t work otherwise!',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasAttribute('exec-id')) {
|
||||||
|
this.setAttribute('exec-id', '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.hasAttribute('root')) {
|
||||||
|
this.setAttribute('root', this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('output')) {
|
||||||
|
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
|
||||||
|
|
||||||
|
// in this case, the default output-mode is append, if hasn't been specified
|
||||||
|
if (!this.hasAttribute('output-mode')) {
|
||||||
|
this.setAttribute('output-mode', 'append');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.hasAttribute('std-out')) {
|
||||||
|
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
||||||
|
} else {
|
||||||
|
// In this case neither output or std-out have been provided so we need
|
||||||
|
// to create a new output div to output to
|
||||||
|
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 if there's not output pre-defined
|
||||||
|
mainDiv.appendChild(this.outputElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('std-err')) {
|
||||||
|
this.errorElement = document.getElementById(this.getAttribute('std-err'));
|
||||||
|
} else {
|
||||||
|
this.errorElement = this.outputElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.appendChild(mainDiv);
|
||||||
|
this.editor.focus();
|
||||||
|
console.log('connected');
|
||||||
}
|
}
|
||||||
|
|
||||||
addToOutput(s: string) {
|
addToOutput(s: string) {
|
||||||
this.outputElement.innerHTML += "<div>"+s+"</div>";
|
this.outputElement.innerHTML += '<div>' + s + '</div>';
|
||||||
this.outputElement.hidden = false;
|
this.outputElement.hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
postEvaluate(): void {
|
postEvaluate(): void {
|
||||||
|
this.outputElement.hidden = false;
|
||||||
|
this.outputElement.style.display = 'block';
|
||||||
|
|
||||||
this.outputElement.hidden = false;
|
if (this.hasAttribute('auto-generate')) {
|
||||||
this.outputElement.style.display = 'block';
|
const 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('output')) {
|
||||||
|
newPyRepl.setAttribute('output', this.getAttribute('output'));
|
||||||
|
}
|
||||||
|
if (this.hasAttribute('std-out')) {
|
||||||
|
newPyRepl.setAttribute('std-out', this.getAttribute('std-out'));
|
||||||
|
}
|
||||||
|
if (this.hasAttribute('std-err')) {
|
||||||
|
newPyRepl.setAttribute('std-err', this.getAttribute('std-err'));
|
||||||
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('auto-generate')) {
|
newPyRepl.setAttribute('exec-id', nextExecId.toString());
|
||||||
let nextExecId = parseInt(this.getAttribute('exec-id')) + 1;
|
this.parentElement.appendChild(newPyRepl);
|
||||||
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('output')){
|
|
||||||
newPyRepl.setAttribute('output', this.getAttribute('output'));
|
|
||||||
}
|
}
|
||||||
if (this.hasAttribute('std-out')){
|
|
||||||
newPyRepl.setAttribute('std-out', this.getAttribute('std-out'));
|
|
||||||
}
|
|
||||||
if (this.hasAttribute('std-err')){
|
|
||||||
newPyRepl.setAttribute('std-err', this.getAttribute('std-err'));
|
|
||||||
}
|
|
||||||
|
|
||||||
newPyRepl.setAttribute('exec-id', nextExecId.toString());
|
|
||||||
this.parentElement.appendChild(newPyRepl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFromElement(): string {
|
getSourceFromElement(): string {
|
||||||
const sourceStrings = [`output_manager.change("`+this.outputElement.id+`")`,
|
const sourceStrings = [
|
||||||
...this.editor.state.doc.toString().split("\n")];
|
`output_manager.change("` + this.outputElement.id + `")`,
|
||||||
return sourceStrings.join('\n')
|
...this.editor.state.doc.toString().split('\n'),
|
||||||
|
];
|
||||||
|
return sourceStrings.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
render(){
|
render() {
|
||||||
console.log('rendered');
|
console.log('rendered');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
|
import { EditorState } from '@codemirror/basic-setup';
|
||||||
import { python } from "@codemirror/lang-python"
|
import { python } from '@codemirror/lang-python';
|
||||||
// @ts-ignore
|
|
||||||
import { StateCommand } from '@codemirror/state';
|
import { StateCommand } from '@codemirror/state';
|
||||||
import { keymap, ViewUpdate } from "@codemirror/view";
|
import { keymap } from '@codemirror/view';
|
||||||
import { defaultKeymap } from "@codemirror/commands";
|
import { defaultKeymap } from '@codemirror/commands';
|
||||||
import { oneDarkTheme } from "@codemirror/theme-one-dark";
|
import { oneDarkTheme } from '@codemirror/theme-one-dark';
|
||||||
|
|
||||||
import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue, addInitializer, addPostInitializer } from '../stores';
|
import {
|
||||||
|
addInitializer,
|
||||||
|
addPostInitializer,
|
||||||
|
addToScriptsQueue,
|
||||||
|
componentDetailsNavOpen,
|
||||||
|
loadedEnvironments,
|
||||||
|
mode,
|
||||||
|
pyodideLoaded,
|
||||||
|
} from '../stores';
|
||||||
import { addClasses, htmlDecode } from '../utils';
|
import { addClasses, htmlDecode } from '../utils';
|
||||||
import { BaseEvalElement } from './base';
|
import { BaseEvalElement } from './base';
|
||||||
|
|
||||||
@@ -17,7 +24,7 @@ let currentMode;
|
|||||||
let handlersCollected = false;
|
let handlersCollected = false;
|
||||||
|
|
||||||
pyodideLoaded.subscribe(value => {
|
pyodideLoaded.subscribe(value => {
|
||||||
pyodideReadyPromise = value;
|
pyodideReadyPromise = value;
|
||||||
});
|
});
|
||||||
loadedEnvironments.subscribe(value => {
|
loadedEnvironments.subscribe(value => {
|
||||||
environments = value;
|
environments = value;
|
||||||
@@ -25,240 +32,233 @@ loadedEnvironments.subscribe(value => {
|
|||||||
|
|
||||||
let propertiesNavOpen;
|
let propertiesNavOpen;
|
||||||
componentDetailsNavOpen.subscribe(value => {
|
componentDetailsNavOpen.subscribe(value => {
|
||||||
propertiesNavOpen = value;
|
propertiesNavOpen = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
mode.subscribe(value => {
|
mode.subscribe(value => {
|
||||||
currentMode = value;
|
currentMode = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
function createCmdHandler(el){
|
function createCmdHandler(el) {
|
||||||
// Creates a codemirror cmd handler that calls the el.evaluate when an event
|
// Creates a codemirror cmd handler that calls the el.evaluate when an event
|
||||||
// triggers that specific cmd
|
// triggers that specific cmd
|
||||||
const toggleCheckbox:StateCommand = ({ state, dispatch }) => {
|
const toggleCheckbox: StateCommand = ({ state, dispatch }) => {
|
||||||
return el.evaluate(state)
|
return el.evaluate(state);
|
||||||
}
|
};
|
||||||
return toggleCheckbox
|
return toggleCheckbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use type declaractions
|
// TODO: use type declaractions
|
||||||
type PyodideInterface = {
|
type PyodideInterface = {
|
||||||
registerJsModule(name: string, module: object): void
|
registerJsModule(name: string, module: object): void;
|
||||||
}
|
};
|
||||||
|
|
||||||
// TODO: This should be used as base for generic scripts that need exectutoin
|
// TODO: This should be used as base for generic scripts that need exectutoin
|
||||||
// from PyScript to initializers, etc...
|
// from PyScript to initializers, etc...
|
||||||
class Script {
|
class Script {
|
||||||
source: string;
|
source: string;
|
||||||
state: string;
|
state: string;
|
||||||
output: string;
|
output: string;
|
||||||
|
|
||||||
constructor(source: string, output: string) {
|
|
||||||
this.output = output;
|
|
||||||
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.output){
|
constructor(source: string, output: string) {
|
||||||
// this.editorOut.innerHTML = s;
|
this.output = output;
|
||||||
}
|
this.source = source;
|
||||||
// if (output !== undefined){
|
this.state = 'waiting';
|
||||||
// this.addToOutput(output);
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
|
async evaluate() {
|
||||||
} catch (err) {
|
console.log('evaluate');
|
||||||
console.log("OOOPS, this happened: " + err);
|
const pyodide = await pyodideReadyPromise;
|
||||||
// this.addToOutput(err);
|
// debugger
|
||||||
}
|
try {
|
||||||
}
|
// 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.output) {
|
||||||
|
// 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 BaseEvalElement {
|
export class PyScript extends BaseEvalElement {
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// add an extra div where we can attach the codemirror editor
|
// add an extra div where we can attach the codemirror editor
|
||||||
this.shadow.appendChild(this.wrapper);
|
this.shadow.appendChild(this.wrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.checkId()
|
this.checkId();
|
||||||
this.code = this.innerHTML;
|
this.code = this.innerHTML;
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
let startState = EditorState.create({
|
const startState = EditorState.create({
|
||||||
doc: this.code,
|
doc: this.code,
|
||||||
extensions: [
|
extensions: [
|
||||||
keymap.of([
|
keymap.of([
|
||||||
...defaultKeymap,
|
...defaultKeymap,
|
||||||
{ key: "Ctrl-Enter", run: createCmdHandler(this) },
|
{ key: 'Ctrl-Enter', run: createCmdHandler(this) },
|
||||||
{ key: "Shift-Enter", run: createCmdHandler(this) }
|
{ key: 'Shift-Enter', run: createCmdHandler(this) },
|
||||||
]),
|
]),
|
||||||
oneDarkTheme,
|
oneDarkTheme,
|
||||||
python(),
|
python(),
|
||||||
// Event listener function that is called every time an user types something on this editor
|
// Event listener function that is called every time an user types something on this editor
|
||||||
// EditorView.updateListener.of((v:ViewUpdate) => {
|
// EditorView.updateListener.of((v:ViewUpdate) => {
|
||||||
// if (v.docChanged) {
|
// if (v.docChanged) {
|
||||||
// console.log(v.changes);
|
// console.log(v.changes);
|
||||||
|
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
]
|
],
|
||||||
})
|
});
|
||||||
|
|
||||||
let mainDiv = document.createElement('div');
|
|
||||||
addClasses(mainDiv, ["parentBox", "flex", "flex-col", 'mx-8'])
|
|
||||||
// add Editor to main PyScript div
|
|
||||||
|
|
||||||
|
|
||||||
if (this.hasAttribute('output')) {
|
const mainDiv = document.createElement('div');
|
||||||
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
|
addClasses(mainDiv, ['parentBox', 'flex', 'flex-col', 'mx-8']);
|
||||||
|
// add Editor to main PyScript div
|
||||||
|
|
||||||
// in this case, the default output-mode is append, if hasn't been specified
|
if (this.hasAttribute('output')) {
|
||||||
if (!this.hasAttribute('output-mode')) {
|
this.errorElement = this.outputElement = document.getElementById(this.getAttribute('output'));
|
||||||
this.setAttribute('output-mode', 'append');
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
if (this.hasAttribute('std-out')){
|
|
||||||
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
|
||||||
}else{
|
|
||||||
// In this case neither output or std-out have been provided so we need
|
|
||||||
// to create a new output div to output to
|
|
||||||
|
|
||||||
// Let's check if we have an id first and create one if not
|
// in this case, the default output-mode is append, if hasn't been specified
|
||||||
this.outputElement = document.createElement('div');
|
if (!this.hasAttribute('output-mode')) {
|
||||||
const exec_id = this.getAttribute("exec-id");
|
this.setAttribute('output-mode', 'append');
|
||||||
this.outputElement.id = this.id + (exec_id ? "-"+exec_id : "");
|
}
|
||||||
|
} else {
|
||||||
|
if (this.hasAttribute('std-out')) {
|
||||||
|
this.outputElement = document.getElementById(this.getAttribute('std-out'));
|
||||||
|
} else {
|
||||||
|
// In this case neither output or std-out have been provided so we need
|
||||||
|
// to create a new output div to output to
|
||||||
|
|
||||||
// add the output div id if there's not output pre-defined
|
// Let's check if we have an id first and create one if not
|
||||||
mainDiv.appendChild(this.outputElement);
|
this.outputElement = document.createElement('div');
|
||||||
|
const exec_id = this.getAttribute('exec-id');
|
||||||
|
this.outputElement.id = this.id + (exec_id ? '-' + exec_id : '');
|
||||||
|
|
||||||
|
// add the output div id if there's not output pre-defined
|
||||||
|
mainDiv.appendChild(this.outputElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasAttribute('std-err')) {
|
||||||
|
this.outputElement = document.getElementById(this.getAttribute('std-err'));
|
||||||
|
} else {
|
||||||
|
this.errorElement = this.outputElement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.hasAttribute('std-err')){
|
if (currentMode == 'edit') {
|
||||||
this.outputElement = document.getElementById(this.getAttribute('std-err'));
|
// TODO: We need to build a plan for this
|
||||||
}else{
|
this.appendChild(mainDiv);
|
||||||
this.errorElement = this.outputElement;
|
} else {
|
||||||
|
this.appendChild(mainDiv);
|
||||||
|
addToScriptsQueue(this);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (currentMode=="edit"){
|
console.log('connected');
|
||||||
// TODO: We need to build a plan for this
|
|
||||||
this.appendChild(mainDiv);
|
|
||||||
}else{
|
|
||||||
this.appendChild(mainDiv);
|
|
||||||
addToScriptsQueue(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('connected');
|
if (this.hasAttribute('src')) {
|
||||||
|
this.source = this.getAttribute('src');
|
||||||
if (this.hasAttribute('src')) {
|
}
|
||||||
this.source = this.getAttribute('src');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _register_esm(pyodide: PyodideInterface): Promise<void> {
|
protected async _register_esm(pyodide: PyodideInterface): Promise<void> {
|
||||||
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
for (const node of document.querySelectorAll("script[type='importmap']")) {
|
||||||
const importmap = (() => {
|
const importmap = (() => {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(node.textContent)
|
return JSON.parse(node.textContent);
|
||||||
} catch {
|
} catch {
|
||||||
return null
|
return null;
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
|
|
||||||
if (importmap?.imports == null)
|
if (importmap?.imports == null) continue;
|
||||||
continue
|
|
||||||
|
|
||||||
for (const [name, url] of Object.entries(importmap.imports)) {
|
for (const [name, url] of Object.entries(importmap.imports)) {
|
||||||
if (typeof name != "string" || typeof url != "string")
|
if (typeof name != 'string' || typeof url != 'string') continue;
|
||||||
continue
|
|
||||||
|
|
||||||
let exports: object
|
let exports: object;
|
||||||
try {
|
try {
|
||||||
// XXX: pyodide doesn't like Module(), failing with
|
// XXX: pyodide doesn't like Module(), failing with
|
||||||
// "can't read 'name' of undefined" at import time
|
// "can't read 'name' of undefined" at import time
|
||||||
exports = {...await import(url)}
|
exports = { ...(await import(url)) };
|
||||||
} catch {
|
} catch {
|
||||||
console.warn(`failed to fetch '${url}' for '${name}'`)
|
console.warn(`failed to fetch '${url}' for '${name}'`);
|
||||||
continue
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
pyodide.registerJsModule(name, exports)
|
pyodide.registerJsModule(name, exports);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSourceFromElement(): string {
|
getSourceFromElement(): string {
|
||||||
return htmlDecode(this.code);
|
return htmlDecode(this.code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initialize all elements with py-onClick handlers attributes */
|
/** Initialize all elements with py-onClick handlers attributes */
|
||||||
async function initHandlers() {
|
async function initHandlers() {
|
||||||
console.log('Collecting nodes...');
|
console.log('Collecting nodes...');
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[pys-onClick]');
|
let matches: NodeListOf<HTMLElement> = document.querySelectorAll('[pys-onClick]');
|
||||||
let output;
|
let output;
|
||||||
let source;
|
let source;
|
||||||
for (var el of matches) {
|
for (const el of matches) {
|
||||||
let handlerCode = el.getAttribute('pys-onClick');
|
const handlerCode = el.getAttribute('pys-onClick');
|
||||||
source = `Element("${ el.id }").element.onclick = ${ handlerCode }`;
|
source = `Element("${el.id}").element.onclick = ${handlerCode}`;
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
|
|
||||||
// TODO: Should we actually map handlers in JS instaed of Python?
|
// TODO: Should we actually map handlers in JS instaed of Python?
|
||||||
// el.onclick = (evt: any) => {
|
// el.onclick = (evt: any) => {
|
||||||
// console.log("click");
|
// console.log("click");
|
||||||
// new Promise((resolve, reject) => {
|
// new Promise((resolve, reject) => {
|
||||||
// setTimeout(() => {
|
// setTimeout(() => {
|
||||||
// console.log('Inside')
|
// console.log('Inside')
|
||||||
// }, 300);
|
// }, 300);
|
||||||
// }).then(() => {
|
// }).then(() => {
|
||||||
// console.log("resolved")
|
// console.log("resolved")
|
||||||
// });
|
// });
|
||||||
// // let handlerCode = el.getAttribute('pys-onClick');
|
// // let handlerCode = el.getAttribute('pys-onClick');
|
||||||
// // pyodide.runPython(handlerCode);
|
// // pyodide.runPython(handlerCode);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
handlersCollected = true;
|
handlersCollected = true;
|
||||||
|
|
||||||
matches = document.querySelectorAll('[pys-onKeyDown]');
|
matches = document.querySelectorAll('[pys-onKeyDown]');
|
||||||
for (var el of matches) {
|
for (const el of matches) {
|
||||||
let handlerCode = el.getAttribute('pys-onKeyDown');
|
const handlerCode = el.getAttribute('pys-onKeyDown');
|
||||||
source = `Element("${ el.id }").element.addEventListener("keydown", ${ handlerCode })`;
|
source = `Element("${el.id}").element.addEventListener("keydown", ${handlerCode})`;
|
||||||
output = await pyodide.runPythonAsync(source);
|
output = await pyodide.runPythonAsync(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Mount all elements with attribute py-mount into the Python namespace */
|
/** Mount all elements with attribute py-mount into the Python namespace */
|
||||||
async function mountElements() {
|
async function mountElements() {
|
||||||
console.log('Collecting nodes to be mounted into python namespace...');
|
console.log('Collecting nodes to be mounted into python namespace...');
|
||||||
let pyodide = await pyodideReadyPromise;
|
const pyodide = await pyodideReadyPromise;
|
||||||
let matches : NodeListOf<HTMLElement> = document.querySelectorAll('[py-mount]');
|
const matches: NodeListOf<HTMLElement> = document.querySelectorAll('[py-mount]');
|
||||||
let output;
|
let output;
|
||||||
let source = "";
|
let source = '';
|
||||||
for (var el of matches) {
|
for (const el of matches) {
|
||||||
let mountName = el.getAttribute('py-mount');
|
let mountName = el.getAttribute('py-mount');
|
||||||
if (!mountName){
|
if (!mountName) {
|
||||||
mountName = el.id.split("-").join("_");
|
mountName = el.id.split('-').join('_');
|
||||||
|
}
|
||||||
|
source += `\n${mountName} = Element("${el.id}")`;
|
||||||
}
|
}
|
||||||
source += `\n${ mountName } = Element("${ el.id }")`;
|
await pyodide.runPythonAsync(source);
|
||||||
}
|
|
||||||
await pyodide.runPythonAsync(source);
|
|
||||||
}
|
}
|
||||||
addInitializer(mountElements);
|
addInitializer(mountElements);
|
||||||
addPostInitializer(initHandlers);
|
addPostInitializer(initHandlers);
|
||||||
|
|||||||
@@ -2,33 +2,31 @@ import { BaseEvalElement } from './base';
|
|||||||
import { addClasses, ltrim, htmlDecode } from '../utils';
|
import { addClasses, ltrim, htmlDecode } from '../utils';
|
||||||
|
|
||||||
export class PyTitle extends BaseEvalElement {
|
export class PyTitle extends BaseEvalElement {
|
||||||
shadow: ShadowRoot;
|
shadow: ShadowRoot;
|
||||||
wrapper: HTMLElement;
|
wrapper: HTMLElement;
|
||||||
theme: string;
|
theme: string;
|
||||||
widths: Array<string>;
|
widths: Array<string>;
|
||||||
label: string;
|
label: string;
|
||||||
mount_name: string;
|
mount_name: string;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
connectedCallback() {
|
connectedCallback() {
|
||||||
this.label = htmlDecode(this.innerHTML);
|
this.label = htmlDecode(this.innerHTML);
|
||||||
this.mount_name = this.id.split("-").join("_");
|
this.mount_name = this.id.split('-').join('_');
|
||||||
this.innerHTML = '';
|
this.innerHTML = '';
|
||||||
|
|
||||||
let mainDiv = document.createElement('div');
|
const mainDiv = document.createElement('div');
|
||||||
let divContent = document.createElement('h1')
|
const divContent = document.createElement('h1');
|
||||||
|
|
||||||
addClasses(mainDiv, ["text-center", "w-full", "mb-8"]);
|
addClasses(mainDiv, ['text-center', 'w-full', 'mb-8']);
|
||||||
addClasses(divContent, ["text-3xl", "font-bold", "text-gray-800", "uppercase", "tracking-tight"]);
|
addClasses(divContent, ['text-3xl', 'font-bold', 'text-gray-800', 'uppercase', 'tracking-tight']);
|
||||||
divContent.innerHTML = this.label;
|
divContent.innerHTML = this.label;
|
||||||
|
|
||||||
mainDiv.id = this.id;
|
mainDiv.id = this.id;
|
||||||
this.id = `${this.id}-container`;
|
this.id = `${this.id}-container`;
|
||||||
mainDiv.appendChild(divContent);
|
mainDiv.appendChild(divContent);
|
||||||
this.appendChild(mainDiv);
|
this.appendChild(mainDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import { getLastPath } from "./utils";
|
import { getLastPath } from './utils';
|
||||||
|
|
||||||
// @ts-nocheck
|
|
||||||
// @ts-ignore
|
|
||||||
let pyodideReadyPromise;
|
let pyodideReadyPromise;
|
||||||
let pyodide;
|
let pyodide;
|
||||||
|
|
||||||
let additional_definitions = `
|
const additional_definitions = `
|
||||||
from js import document, setInterval, console, setTimeout
|
from js import document, setInterval, console, setTimeout
|
||||||
import micropip
|
import micropip
|
||||||
import time
|
import time
|
||||||
@@ -413,19 +411,18 @@ class OutputManager:
|
|||||||
pyscript = PyScript()
|
pyscript = PyScript()
|
||||||
output_manager = OutputManager()
|
output_manager = OutputManager()
|
||||||
|
|
||||||
`
|
`;
|
||||||
|
|
||||||
let loadInterpreter = async function(): Promise<any> {
|
const loadInterpreter = async function (): Promise<any> {
|
||||||
console.log("creating pyodide runtime");
|
console.log('creating pyodide runtime');
|
||||||
/* @ts-ignore */
|
|
||||||
pyodide = await loadPyodide({
|
pyodide = await loadPyodide({
|
||||||
stdout: console.log,
|
stdout: console.log,
|
||||||
stderr: console.log
|
stderr: console.log,
|
||||||
});
|
});
|
||||||
|
|
||||||
// now that we loaded, add additional convenience fuctions
|
// now that we loaded, add additional convenience fuctions
|
||||||
console.log("loading micropip");
|
console.log('loading micropip');
|
||||||
await pyodide.loadPackage("micropip");
|
await pyodide.loadPackage('micropip');
|
||||||
console.log('loading pyscript module');
|
console.log('loading pyscript module');
|
||||||
// await pyodide.runPythonAsync(`
|
// await pyodide.runPythonAsync(`
|
||||||
// from pyodide.http import pyfetch
|
// from pyodide.http import pyfetch
|
||||||
@@ -437,31 +434,36 @@ let loadInterpreter = async function(): Promise<any> {
|
|||||||
// `)
|
// `)
|
||||||
// let pkg = pyodide.pyimport("pyscript");
|
// let pkg = pyodide.pyimport("pyscript");
|
||||||
|
|
||||||
console.log("creating additional definitions");
|
console.log('creating additional definitions');
|
||||||
let output = pyodide.runPython(additional_definitions);
|
const output = pyodide.runPython(additional_definitions);
|
||||||
console.log("done setting up environment");
|
console.log('done setting up environment');
|
||||||
/* @ts-ignore */
|
|
||||||
return pyodide;
|
return pyodide;
|
||||||
}
|
};
|
||||||
|
|
||||||
let loadPackage = async function(package_name: string[] | string, runtime: any): Promise<any> {
|
const loadPackage = async function (package_name: string[] | string, runtime: any): Promise<any> {
|
||||||
let micropip = pyodide.globals.get('micropip');
|
const micropip = pyodide.globals.get('micropip');
|
||||||
await micropip.install(package_name)
|
await micropip.install(package_name);
|
||||||
micropip.destroy()
|
micropip.destroy();
|
||||||
}
|
};
|
||||||
|
|
||||||
let loadFromFile = async function(s: string, runtime: any): Promise<any> {
|
const loadFromFile = async function (s: string, runtime: any): Promise<any> {
|
||||||
let filename = getLastPath(s);
|
const filename = getLastPath(s);
|
||||||
await runtime.runPythonAsync(`
|
await runtime.runPythonAsync(
|
||||||
|
`
|
||||||
from pyodide.http import pyfetch
|
from pyodide.http import pyfetch
|
||||||
|
|
||||||
response = await pyfetch("`+s+`")
|
response = await pyfetch("` +
|
||||||
|
s +
|
||||||
|
`")
|
||||||
content = await response.bytes()
|
content = await response.bytes()
|
||||||
with open("`+filename+`", "wb") as f:
|
with open("` +
|
||||||
|
filename +
|
||||||
|
`", "wb") as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
`)
|
`,
|
||||||
|
);
|
||||||
|
|
||||||
runtime.pyimport(filename.replace(".py", ""));
|
runtime.pyimport(filename.replace('.py', ''));
|
||||||
}
|
};
|
||||||
|
|
||||||
export {loadInterpreter, pyodideReadyPromise, loadPackage, loadFromFile}
|
export { loadInterpreter, pyodideReadyPromise, loadPackage, loadFromFile };
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
import App from "./App.svelte";
|
import App from './App.svelte';
|
||||||
|
|
||||||
import { PyScript } from "./components/pyscript";
|
import { PyScript } from './components/pyscript';
|
||||||
import { PyRepl } from "./components/pyrepl";
|
import { PyRepl } from './components/pyrepl';
|
||||||
import { PyEnv } from "./components/pyenv";
|
import { PyEnv } from './components/pyenv';
|
||||||
import { PyBox } from "./components/pybox";
|
import { PyBox } from './components/pybox';
|
||||||
import { PyButton } from "./components/pybutton";
|
import { PyButton } from './components/pybutton';
|
||||||
import { PyTitle } from "./components/pytitle";
|
import { PyTitle } from './components/pytitle';
|
||||||
import { PyInputBox } from "./components/pyinputbox";
|
import { PyInputBox } from './components/pyinputbox';
|
||||||
import { PyWidget } from "./components/base";
|
import { PyWidget } from './components/base';
|
||||||
|
|
||||||
let xPyScript = customElements.define('py-script', PyScript);
|
|
||||||
let xPyRepl = customElements.define('py-repl', PyRepl);
|
|
||||||
let xPyEnv = customElements.define('py-env', PyEnv);
|
|
||||||
let xPyBox = customElements.define('py-box', PyBox);
|
|
||||||
let xPyButton = customElements.define('py-button', PyButton);
|
|
||||||
let xPyTitle = customElements.define('py-title', PyTitle);
|
|
||||||
let xPyInputBox = customElements.define('py-inputbox', PyInputBox);
|
|
||||||
let xPyWidget = customElements.define('py-register-widget', PyWidget);
|
|
||||||
|
|
||||||
|
const xPyScript = customElements.define('py-script', PyScript);
|
||||||
|
const xPyRepl = customElements.define('py-repl', PyRepl);
|
||||||
|
const xPyEnv = customElements.define('py-env', PyEnv);
|
||||||
|
const xPyBox = customElements.define('py-box', PyBox);
|
||||||
|
const xPyButton = customElements.define('py-button', PyButton);
|
||||||
|
const xPyTitle = customElements.define('py-title', PyTitle);
|
||||||
|
const xPyInputBox = customElements.define('py-inputbox', PyInputBox);
|
||||||
|
const xPyWidget = customElements.define('py-register-widget', PyWidget);
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
|
||||||
export const pyodideLoaded = writable({
|
export const pyodideLoaded = writable({
|
||||||
loaded: false,
|
loaded: false,
|
||||||
premise: null
|
premise: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const loadedEnvironments = writable([{}])
|
export const loadedEnvironments = writable([{}]);
|
||||||
export const DEFAULT_MODE = 'play';
|
export const DEFAULT_MODE = 'play';
|
||||||
|
|
||||||
export const navBarOpen = writable(false);
|
export const navBarOpen = writable(false);
|
||||||
@@ -14,43 +13,43 @@ export const componentsNavOpen = writable(false);
|
|||||||
export const componentDetailsNavOpen = writable(false);
|
export const componentDetailsNavOpen = writable(false);
|
||||||
export const mainDiv = writable(null);
|
export const mainDiv = writable(null);
|
||||||
export const currentComponentDetails = writable([]);
|
export const currentComponentDetails = writable([]);
|
||||||
export const mode = writable(DEFAULT_MODE)
|
export const mode = writable(DEFAULT_MODE);
|
||||||
export const scriptsQueue = writable([])
|
export const scriptsQueue = writable([]);
|
||||||
export const initializers = writable([])
|
export const initializers = writable([]);
|
||||||
export const postInitializers = writable([])
|
export const postInitializers = writable([]);
|
||||||
|
|
||||||
let scriptsQueue_ = [];
|
let scriptsQueue_ = [];
|
||||||
let initializers_ = [];
|
let initializers_ = [];
|
||||||
let postInitializers_ = [];
|
let postInitializers_ = [];
|
||||||
|
|
||||||
scriptsQueue.subscribe(value => {
|
scriptsQueue.subscribe(value => {
|
||||||
scriptsQueue_ = value;
|
scriptsQueue_ = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const addToScriptsQueue = (script) => {
|
export const addToScriptsQueue = script => {
|
||||||
scriptsQueue.set([...scriptsQueue_, script]);
|
scriptsQueue.set([...scriptsQueue_, script]);
|
||||||
};
|
};
|
||||||
|
|
||||||
scriptsQueue.subscribe(value => {
|
scriptsQueue.subscribe(value => {
|
||||||
scriptsQueue_ = value;
|
scriptsQueue_ = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
initializers.subscribe(value => {
|
initializers.subscribe(value => {
|
||||||
initializers_ = value;
|
initializers_ = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const addInitializer = (initializer) => {
|
export const addInitializer = initializer => {
|
||||||
console.log("adding initializer", initializer);
|
console.log('adding initializer', initializer);
|
||||||
initializers.set([...initializers_, initializer]);
|
initializers.set([...initializers_, initializer]);
|
||||||
console.log("adding initializer", initializer);
|
console.log('adding initializer', initializer);
|
||||||
};
|
};
|
||||||
|
|
||||||
postInitializers.subscribe(value => {
|
postInitializers.subscribe(value => {
|
||||||
postInitializers_ = value;
|
postInitializers_ = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const addPostInitializer = (initializer) => {
|
export const addPostInitializer = initializer => {
|
||||||
console.log("adding post initializer", initializer);
|
console.log('adding post initializer', initializer);
|
||||||
postInitializers.set([...postInitializers_, initializer]);
|
postInitializers.set([...postInitializers_, initializer]);
|
||||||
console.log("adding post initializer", initializer);
|
console.log('adding post initializer', initializer);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,44 +1,40 @@
|
|||||||
|
function addClasses(element: HTMLElement, classes: Array<string>) {
|
||||||
function addClasses(element: HTMLElement, classes: Array<string>){
|
for (const entry of classes) {
|
||||||
for (let entry of classes) {
|
element.classList.add(entry);
|
||||||
element.classList.add(entry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLastPath = function (str) {
|
const getLastPath = function (str) {
|
||||||
return str.split('\\').pop().split('/').pop();
|
return str.split('\\').pop().split('/').pop();
|
||||||
}
|
};
|
||||||
|
|
||||||
function htmlDecode(input) {
|
function htmlDecode(input) {
|
||||||
var doc = new DOMParser().parseFromString(input, "text/html");
|
const doc = new DOMParser().parseFromString(input, 'text/html');
|
||||||
return ltrim(doc.documentElement.textContent);
|
return ltrim(doc.documentElement.textContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ltrim(code: string): string {
|
function ltrim(code: string): string {
|
||||||
const lines = code.split("\n")
|
const lines = code.split('\n');
|
||||||
if (lines.length == 0)
|
if (lines.length == 0) return code;
|
||||||
return code
|
|
||||||
|
|
||||||
const lengths = lines
|
const lengths = lines
|
||||||
.filter((line) => line.trim().length != 0)
|
.filter(line => line.trim().length != 0)
|
||||||
.map((line) => {
|
.map(line => {
|
||||||
const [prefix] = line.match(/^\s*/)
|
const [prefix] = line.match(/^\s*/);
|
||||||
return prefix.length
|
return prefix.length;
|
||||||
})
|
});
|
||||||
|
|
||||||
const k = Math.min(...lengths)
|
const k = Math.min(...lengths);
|
||||||
|
|
||||||
if (k != 0)
|
if (k != 0) return lines.map(line => line.substring(k)).join('\n');
|
||||||
return lines.map((line) => line.substring(k)).join("\n")
|
else return code;
|
||||||
else
|
|
||||||
return code
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function guidGenerator(): string {
|
function guidGenerator(): string {
|
||||||
var S4 = function(): string {
|
const S4 = function (): string {
|
||||||
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
||||||
};
|
};
|
||||||
return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
|
return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
|
||||||
}
|
}
|
||||||
|
|
||||||
export {addClasses, getLastPath, ltrim, htmlDecode, guidGenerator}
|
export { addClasses, getLastPath, ltrim, htmlDecode, guidGenerator };
|
||||||
|
|||||||
Reference in New Issue
Block a user