| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="UTF-8" /> |
| <script src="https://cdn.jsdelivr.net/npm/jquery"></script> |
| <script src="https://cdn.jsdelivr.net/npm/jquery.terminal@2.32.0/js/jquery.terminal.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/jquery.terminal@2.23.0/js/unix_formatting.min.js"></script> |
| <link |
| href="https://cdn.jsdelivr.net/npm/jquery.terminal@2.32.0/css/jquery.terminal.min.css" |
| rel="stylesheet" |
| /> |
| <script src="./pyodide.js"></script> |
| <style> |
| .terminal { |
| --size: 1.5; |
| --color: rgba(255, 255, 255, 0.8); |
| } |
| </style> |
| </head> |
| <body> |
| <script> |
| "use strict"; |
| function sleep(s) { |
| return new Promise((resolve) => setTimeout(resolve, s)); |
| } |
| |
| async function main() { |
| globalThis.pyodide = await loadPyodide(); |
| let namespace = pyodide.globals.get("dict")(); |
| pyodide.runPython( |
| ` |
| import sys |
| from pyodide import to_js |
| from pyodide.console import PyodideConsole, repr_shorten, BANNER |
| import __main__ |
| BANNER = "Welcome to the Pyodide terminal emulator 🐍\\n" + BANNER |
| pyconsole = PyodideConsole(__main__.__dict__) |
| import builtins |
| async def await_fut(fut): |
| res = await fut |
| if res is not None: |
| builtins._ = res |
| return to_js([res], depth=1) |
| def clear_console(): |
| pyconsole.buffer = [] |
| `, |
| { globals: namespace } |
| ); |
| let repr_shorten = namespace.get("repr_shorten"); |
| let banner = namespace.get("BANNER"); |
| let await_fut = namespace.get("await_fut"); |
| let pyconsole = namespace.get("pyconsole"); |
| let clear_console = namespace.get("clear_console"); |
| namespace.destroy(); |
| |
| let ps1 = ">>> ", |
| ps2 = "... "; |
| |
| async function lock() { |
| let resolve; |
| let ready = term.ready; |
| term.ready = new Promise((res) => (resolve = res)); |
| await ready; |
| return resolve; |
| } |
| |
| async function interpreter(command) { |
| let unlock = await lock(); |
| term.pause(); |
| |
| for (const c of command.split("\n")) { |
| let fut = pyconsole.push(c); |
| term.set_prompt(fut.syntax_check === "incomplete" ? ps2 : ps1); |
| switch (fut.syntax_check) { |
| case "syntax-error": |
| term.error(fut.formatted_error.trimEnd()); |
| continue; |
| case "incomplete": |
| continue; |
| case "complete": |
| break; |
| default: |
| throw new Error(`Unexpected type ${ty}`); |
| } |
| |
| |
| |
| |
| let wrapped = await_fut(fut); |
| |
| try { |
| let [value] = await wrapped; |
| if (value !== undefined) { |
| term.echo( |
| repr_shorten.callKwargs(value, { |
| separator: "\n[[;orange;]<long output truncated>]\n", |
| }) |
| ); |
| } |
| if (pyodide.isPyProxy(value)) { |
| value.destroy(); |
| } |
| } catch (e) { |
| if (e.constructor.name === "PythonError") { |
| const message = fut.formatted_error || e.message; |
| term.error(message.trimEnd()); |
| } else { |
| throw e; |
| } |
| } finally { |
| fut.destroy(); |
| wrapped.destroy(); |
| } |
| } |
| term.resume(); |
| await sleep(10); |
| unlock(); |
| } |
| |
| let term = $("body").terminal(interpreter, { |
| greetings: banner, |
| prompt: ps1, |
| completionEscape: false, |
| completion: function (command, callback) { |
| callback(pyconsole.complete(command).toJs()[0]); |
| }, |
| keymap: { |
| "CTRL+C": async function (event, original) { |
| clear_console(); |
| term.enter(); |
| term.echo("KeyboardInterrupt"); |
| term.set_command(""); |
| term.set_prompt(ps1); |
| }, |
| TAB: (event, original) => { |
| const command = term.before_cursor(); |
| |
| if (command.trim() === "") { |
| term.insert("\t"); |
| return false; |
| } |
| return original(event); |
| }, |
| }, |
| }); |
| window.term = term; |
| pyconsole.stdout_callback = (s) => term.echo(s, { newline: false }); |
| pyconsole.stderr_callback = (s) => { |
| term.error(s.trimEnd()); |
| }; |
| term.ready = Promise.resolve(); |
| pyodide._api.on_fatal = async (e) => { |
| term.error( |
| "Pyodide has suffered a fatal error. Please report this to the Pyodide maintainers." |
| ); |
| term.error("The cause of the fatal error was:"); |
| term.error(e); |
| term.error("Look in the browser console for more details."); |
| await term.ready; |
| term.pause(); |
| await sleep(15); |
| term.pause(); |
| }; |
| } |
| window.console_ready = main(); |
| </script> |
| </body> |
| </html> |
|
|