An easy-to-use platform for EEG experimentation in the classroom
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

maybe a solution

+17 -79
+16 -78
src/renderer/utils/webworker/webworker.js
··· 1 1 /** 2 - * Pyodide Web Worker — ES module worker following the pattern from 3 - * https://gitlab.com/castedo/pyodide-worker-example 2 + * Pyodide Web Worker — local node_modules implementation. 4 3 * 5 4 * Loading strategy 6 5 * ---------------- 7 - * 1. `import { loadPyodide } from "pyodide"` — Vite resolves this to 8 - * node_modules/pyodide/pyodide.mjs and serves it from /@fs/… in dev mode, 9 - * completely bypassing any publicDir transform issues. 10 - * 11 - * 2. `indexURL: '/pyodide/'` — tells pyodide where to find pyodide-lock.json 12 - * and binary package wheels (.whl). These are served from publicDir: 13 - * src/renderer/utils/webworker/src/pyodide/ 14 - * which is populated by: 15 - * • InstallPyodide.mjs (copies pyodide-lock.json + runtime from npm) 16 - * • InstallMNE.mjs (downloads binary wheels from Pyodide CDN) 17 - * 18 - * 3. Binary packages (numpy / scipy / matplotlib / pandas) — loaded via 19 - * pyodide.loadPackage(), resolved from local /pyodide/ files. 6 + * Use Vite's `?url` suffix on 'pyodide/pyodide.mjs' to get the resolved file URL 7 + * at build/dev time (/@fs/... in dev, an asset URL in prod), then dynamically 8 + * import from that URL. This bypasses Vite's SPA fallback and lets pyodide.mjs 9 + * resolve all sibling assets (pyodide.asm.wasm, pyodide-lock.json, etc.) via 10 + * import.meta.url — no CDN required. 20 11 * 21 - * 4. MNE + pure-Python deps — installed via micropip from pre-downloaded 22 - * wheels in /packages/, listed in /packages/manifest.json. 23 - * Populated by InstallMNE.mjs Part 2 (PyPI). 12 + * Production builds use the files copied to publicDir by InstallPyodide.mjs. 24 13 */ 25 14 26 - import { loadPyodide } from 'pyodide'; 27 - 28 - /** 29 - * Derive the renderer root URL from the worker's own location. 30 - * 31 - * Dev (Vite HTTP server): self.location.href is an http:// URL — the root is 32 - * just the origin, so root-relative paths work as normal. 33 - * 34 - * Production (Electron file://): Vite bundles workers into assets/, so the 35 - * renderer root is one directory above the worker file. 36 - */ 37 - function getRendererBaseUrl() { 38 - const loc = self.location.href; 39 - if (loc.startsWith('http')) { 40 - return new URL('/', loc).href; 41 - } 42 - // file:// — go up from assets/webworker-[hash].js to the renderer root 43 - return new URL('../', loc).href; 44 - } 45 - 46 - async function initPyodide() { 47 - const base = getRendererBaseUrl(); 48 - 49 - // indexURL tells pyodide where to load pyodide-lock.json and binary wheels. 50 - // Resolved from the renderer root so it works under both HTTP (dev) and 51 - // file:// (Electron production). 52 - const pyodide = await loadPyodide({ indexURL: new URL('pyodide/', base).href }); 53 - 54 - // Load binary packages from locally served .whl files. 55 - await pyodide.loadPackage(['numpy', 'scipy', 'matplotlib', 'pandas']); 15 + // ?url tells Vite to resolve the path and return a URL string rather than bundling 16 + // the module. In dev mode this is a /@fs/ URL (bypasses SPA fallback); in prod it 17 + // is an asset URL. We then dynamically import from that URL so pyodide.mjs can 18 + // resolve all its sibling assets (pyodide.asm.wasm, etc.) via import.meta.url. 19 + import pyodideMjsUrl from 'pyodide/pyodide.mjs?url'; 56 20 57 - // Install MNE and its pure-Python deps from pre-downloaded wheels. 58 - let manifest = {}; 59 - try { 60 - const res = await fetch(new URL('packages/manifest.json', base).href); 61 - if (res.ok) { 62 - manifest = await res.json(); 63 - } else { 64 - console.warn('[pyodide worker] manifest.json not found — MNE unavailable'); 65 - } 66 - } catch (err) { 67 - console.warn('[pyodide worker] Could not fetch manifest.json:', err); 68 - } 69 - 70 - const wheelUrls = Object.values(manifest).map( 71 - (entry) => new URL(`packages/${entry.filename}`, base).href 72 - ); 73 - 74 - if (wheelUrls.length > 0) { 75 - await pyodide.loadPackage('micropip'); 76 - const micropip = pyodide.pyimport('micropip'); 77 - await micropip.install(wheelUrls); 78 - } else { 79 - console.warn('[pyodide worker] No MNE wheels in manifest — skipping micropip install'); 80 - } 81 - 82 - return pyodide; 83 - } 84 - 85 - // Start loading immediately so it is ready when the first message arrives. 86 - const pyodideReadyPromise = initPyodide(); 21 + const pyodideReadyPromise = (async () => { 22 + const { loadPyodide } = await import(/* @vite-ignore */ pyodideMjsUrl); 23 + return loadPyodide(); 24 + })(); 87 25 88 26 self.onmessage = async (event) => { 89 27 // Propagate init failures back to the main thread rather than hanging silently.
+1 -1
vite.config.ts
··· 80 80 exclude: ['pyodide'], 81 81 }, 82 82 worker: { 83 - // ES module workers are required for `import { loadPyodide } from "pyodide"`. 83 + // ES module workers are required for the CDN import in webworker.js. 84 84 format: 'es', 85 85 }, 86 86 build: {