diff --git a/pyscriptjs/src/components/pyrepl.ts b/pyscriptjs/src/components/pyrepl.ts
new file mode 100644
index 0000000..e6208f0
--- /dev/null
+++ b/pyscriptjs/src/components/pyrepl.ts
@@ -0,0 +1,253 @@
+import {EditorState, EditorView, basicSetup} from "@codemirror/basic-setup"
+import { python } from "@codemirror/lang-python"
+// @ts-ignore
+import { StateCommand, Compartment } from '@codemirror/state';
+import { keymap, ViewUpdate } from "@codemirror/view";
+import { defaultKeymap } from "@codemirror/commands";
+import { oneDarkTheme } from "@codemirror/theme-one-dark";
+
+import { pyodideLoaded, loadedEnvironments, componentDetailsNavOpen, currentComponentDetails, mode, addToScriptsQueue } from '../stores';
+import { addClasses } from '../utils';
+
+// Premise used to connect to the first available pyodide interpreter
+let pyodideReadyPromise;
+let environments;
+let currentMode;
+
+pyodideLoaded.subscribe(value => {
+ pyodideReadyPromise = value;
+});
+loadedEnvironments.subscribe(value => {
+ environments = value;
+});
+
+let propertiesNavOpen;
+componentDetailsNavOpen.subscribe(value => {
+ propertiesNavOpen = value;
+});
+
+mode.subscribe(value => {
+ currentMode = value;
+});
+
+
+const languageConf = new Compartment
+
+function createCmdHandler(el){
+ // Creates a codemirror cmd handler that calls the el.evaluate when an event
+ // triggers that specific cmd
+ const toggleCheckbox:StateCommand = ({ state, dispatch }) => {
+ return el.evaluate(state)
+ }
+ return toggleCheckbox
+}
+
+
+export class PyRepl extends HTMLElement {
+ shadow: ShadowRoot;
+ wrapper: HTMLElement;
+ editor: EditorView;
+ editorNode: HTMLElement;
+ code: string;
+ cm: any;
+ btnConfig: HTMLElement;
+ btnRun: HTMLElement;
+ editorOut: HTMLElement; //HTMLTextAreaElement;
+ theme: string;
+ // editorState: EditorState;
+
+ constructor() {
+ super();
+
+ // attach shadow so we can preserve the element original innerHtml content
+ this.shadow = this.attachShadow({ mode: 'open'});
+
+ this.wrapper = document.createElement('slot');
+
+ // add an extra div where we can attach the codemirror editor
+ this.editorNode = document.createElement('div');
+ addClasses(this.editorNode, ["editor-box"])
+ this.shadow.appendChild(this.wrapper);
+ }
+
+
+ connectedCallback() {
+ this.code = this.innerHTML;
+ this.innerHTML = '';
+
+ let extensions = [
+ basicSetup,
+ languageConf.of(python()),
+ keymap.of([
+ ...defaultKeymap,
+ { key: "Ctrl-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
+ // EditorView.updateListener.of((v:ViewUpdate) => {
+ // if (v.docChanged) {
+ // console.log(v.changes);
+
+ // }
+ // })
+ ];
+
+ if (!this.hasAttribute('theme')) {
+ this.theme = this.getAttribute('theme');
+ if (this.theme == 'dark'){
+ 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", "flex", "flex-col", "border-4", "border-dashed", "border-gray-200", "rounded-lg"])
+ // add Editor to main PyScript div
+
+ // Butons DIV
+ var eDiv = document.createElement('div');
+ addClasses(eDiv, "buttons-box 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 = '';
+ 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 = '';
+ this.btnConfig.onclick = function toggleNavBar(evt){
+ console.log('clicked');
+ componentDetailsNavOpen.set(!propertiesNavOpen);
+
+ currentComponentDetails.set([
+ {key: "auto-generate", value: true},
+ {key:"target", 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: define with an id. should always have an id. More than one 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('target')) {
+ this.editorOut = document.getElementById(this.getAttribute('target'));
+
+ // 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{
+ // Editor Output Div
+ this.editorOut = document.createElement('div');
+ this.editorOut.classList.add("output");
+ this.editorOut.hidden = true;
+ this.editorOut.id = this.id + "-" + this.getAttribute("exec-id");
+
+ // add the output div id there's not target
+ mainDiv.appendChild(this.editorOut);
+ }
+
+ this.appendChild(mainDiv);
+
+ console.log('connected');
+ }
+
+ addToOutput(s: string) {
+ this.editorOut.innerHTML += ""+s+"
";
+ this.editorOut.hidden = false;
+ }
+
+ async evaluate() {
+ console.log('evaluate');
+ let pyodide = await pyodideReadyPromise;
+ // debugger
+ try {
+ // @ts-ignore
+ let source = this.editor.state.doc.toString();
+ let output;
+ if (source.includes("asyncio")){
+ output = pyodide.runPythonAsync(source);
+ }else{
+ output = pyodide.runPython(source);
+ }
+
+ if (output !== undefined){
+ let Element = pyodide.globals.get('Element');
+ let out = Element(this.editorOut.id);
+ // @ts-ignore
+ out.write(output);
+ out.write.callKwargs(output, { append : false});
+
+ if (!this.hasAttribute('target')) {
+ this.editorOut.hidden = false;
+ }
+ // this.addToOutput(output);
+ }
+
+ if (this.hasAttribute('auto-generate')) {
+ let nextExecId = parseInt(this.getAttribute('exec-id')) + 1;
+ const newPyRepl = document.createElement("py-repl");
+ newPyRepl.setAttribute('root', this.getAttribute('root'));
+ newPyRepl.id = this.getAttribute('root') + "-" + nextExecId.toString();
+ newPyRepl.setAttribute('auto-generate', null);
+ if (this.hasAttribute('target')){
+ newPyRepl.setAttribute('target', this.getAttribute('target'));
+ }
+
+ newPyRepl.setAttribute('exec-id', nextExecId.toString());
+ this.parentElement.appendChild(newPyRepl);
+ }
+ } catch (err) {
+ this.addToOutput(err);
+ }
+ }
+
+ render(){
+ console.log('rendered');
+
+ }
+ }
+
+
\ No newline at end of file
diff --git a/pyscriptjs/src/main.ts b/pyscriptjs/src/main.ts
index 0bf2e9c..86aca79 100644
--- a/pyscriptjs/src/main.ts
+++ b/pyscriptjs/src/main.ts
@@ -6,11 +6,11 @@ import { keymap } from "@codemirror/view";
import { defaultKeymap } from "@codemirror/commands";
import { oneDarkTheme } from "@codemirror/theme-one-dark";
import { PyScript } from "./components/pyscript";
-import { pyodideLoaded } from './stores';
-
+import { PyRepl } from "./components/pyrepl";
let xPyScript = customElements.define('py-script', PyScript);
+let xPyRepl = customElements.define('py-repl', PyRepl);
const app = new App({
diff --git a/pyscriptjs/src/stores.ts b/pyscriptjs/src/stores.ts
index 43e14ed..36f5587 100644
--- a/pyscriptjs/src/stores.ts
+++ b/pyscriptjs/src/stores.ts
@@ -9,7 +9,7 @@ export const pyodideLoaded = writable({
});
export const loadedEnvironments = writable([{}])
-
+export const DEFAULT_MODE = 'play';
export const pyodideReadyPromise = promisable(
loadInterpreter,
@@ -20,4 +20,16 @@ export const navBarOpen = writable(false);
export const componentsNavOpen = writable(false);
export const componentDetailsNavOpen = writable(false);
export const mainDiv = writable(null);
-export const currentComponentDetails = writable([]);
\ No newline at end of file
+export const currentComponentDetails = writable([]);
+export const mode = writable(DEFAULT_MODE)
+export const scriptsQueue = writable([])
+
+let scriptsQueue_ = []
+
+scriptsQueue.subscribe(value => {
+ scriptsQueue_ = value;
+});
+
+export const addToScriptsQueue = (script) => {
+ scriptsQueue.set([...scriptsQueue_, script]);
+};