| import { loadPyodide, type PyodideInterface } from 'pyodide'; |
|
|
| declare global { |
| interface Window { |
| stdout: string | null; |
| stderr: string | null; |
| pyodide: PyodideInterface; |
| cells: Record<string, CellState>; |
| indexURL: string; |
| } |
| } |
|
|
| type CellState = { |
| id: string; |
| status: 'idle' | 'running' | 'completed' | 'error'; |
| result: any; |
| stdout: string; |
| stderr: string; |
| }; |
|
|
| const initializePyodide = async () => { |
| |
| if (!self.pyodide) { |
| self.indexURL = '/pyodide/'; |
| self.stdout = ''; |
| self.stderr = ''; |
| self.cells = {}; |
|
|
| self.pyodide = await loadPyodide({ |
| indexURL: self.indexURL |
| }); |
| } |
| }; |
|
|
| const executeCode = async (id: string, code: string) => { |
| if (!self.pyodide) { |
| await initializePyodide(); |
| } |
|
|
| |
| self.cells[id] = { |
| id, |
| status: 'running', |
| result: null, |
| stdout: '', |
| stderr: '' |
| }; |
|
|
| |
| self.pyodide.setStdout({ |
| batched: (msg: string) => { |
| self.cells[id].stdout += msg; |
| self.postMessage({ type: 'stdout', id, message: msg }); |
| } |
| }); |
| self.pyodide.setStderr({ |
| batched: (msg: string) => { |
| self.cells[id].stderr += msg; |
| self.postMessage({ type: 'stderr', id, message: msg }); |
| } |
| }); |
|
|
| try { |
| |
| await self.pyodide.loadPackagesFromImports(code, { |
| messageCallback: (msg: string) => { |
| self.postMessage({ type: 'stdout', id, package: true, message: `[package] ${msg}` }); |
| }, |
| errorCallback: (msg: string) => { |
| self.postMessage({ type: 'stderr', id, package: true, message: `[package] ${msg}` }); |
| } |
| }); |
|
|
| |
| const result = await self.pyodide.runPythonAsync(code); |
| self.cells[id].result = result; |
| self.cells[id].status = 'completed'; |
| } catch (error) { |
| self.cells[id].status = 'error'; |
| self.cells[id].stderr += `\n${error.toString()}`; |
| } finally { |
| |
| self.postMessage({ |
| type: 'result', |
| id, |
| state: self.cells[id] |
| }); |
| } |
| }; |
|
|
| |
| self.onmessage = async (event) => { |
| const { type, id, code, ...args } = event.data; |
|
|
| switch (type) { |
| case 'initialize': |
| await initializePyodide(); |
| self.postMessage({ type: 'initialized' }); |
| break; |
|
|
| case 'execute': |
| if (id && code) { |
| await executeCode(id, code); |
| } |
| break; |
|
|
| case 'getState': |
| self.postMessage({ |
| type: 'kernelState', |
| state: self.cells |
| }); |
| break; |
|
|
| case 'terminate': |
| |
| for (const key in self.cells) delete self.cells[key]; |
| self.close(); |
| break; |
|
|
| default: |
| console.error(`Unknown message type: ${type}`); |
| } |
| }; |
|
|