-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathWorkerContext.tsx
More file actions
109 lines (100 loc) · 3.02 KB
/
Copy pathWorkerContext.tsx
File metadata and controls
109 lines (100 loc) · 3.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import React, {
createContext,
useContext,
useRef,
useEffect,
ReactNode,
useState,
useCallback,
} from 'react'
import { proxy, Remote, wrap } from 'comlink'
import { Runner, PythonRunner } from './types'
interface WorkerContextProps {
runner: React.MutableRefObject<Remote<PythonRunner> | undefined>
runCode: (code: string, id: string) => Promise<void>
isLoading: boolean
pyodideVersion: string | undefined
output: string[]
}
// Create the context
const WorkerContext = createContext<WorkerContextProps | undefined>(undefined)
interface WorkerProviderProps {
children: ReactNode
}
// Create a provider component
export const WorkerProvider: React.FC<WorkerProviderProps> = ({ children }) => {
const workerRef = useRef<Worker | null>(null)
const runnerRef = useRef<Remote<PythonRunner>>()
const [isLoading, setIsLoading] = useState(false)
const [pyodideVersion, setPyodideVersion] = useState<string | undefined>()
const [output, setOutput] = useState<string[]>([])
const [stderr, setStderr] = useState('')
const runCode = useCallback(async (code: string, id: string) => {
if (!runnerRef.current) {
throw new Error('Pyodide is not loaded')
}
try {
setOutput([id])
await runnerRef.current.run(code)
} catch (error) {
console.error('Error:', error)
setStderr('Traceback (most recent call): \n' + error.message)
}
}, [])
// useEffect to check on stdErr
useEffect(() => {
if (stderr) {
console.log(stderr)
}
}, [stderr])
useEffect(() => {
const worker = new Worker(new URL('./python-worker', import.meta.url))
workerRef.current = worker
const init = async () => {
try {
setIsLoading(true)
const runner: Remote<PythonRunner> = wrap(workerRef.current as Worker)
runnerRef.current = runner
await runner.init(
proxy((msg: string) => {
setOutput((prev) => [...prev, msg])
}),
proxy(({ version }) => {
// The runner is ready once the Pyodide version has been set
setPyodideVersion(version)
console.debug('Loaded pyodide version:', version)
}),
[[], []]
)
} catch (error) {
console.error('Error loading Pyodide:', error)
} finally {
setIsLoading(false)
}
}
init()
// Cleanup worker on unmount
return () => {
if (workerRef.current) {
workerRef.current.terminate()
workerRef.current = null
}
}
}, [])
// The value provided by the context includes both the worker and functions to interact with it
return (
<WorkerContext.Provider
value={{ runner: runnerRef, isLoading, pyodideVersion, output, runCode }}
>
{children}
</WorkerContext.Provider>
)
}
// Create a custom hook to access the worker
export const useWorker = (): WorkerContextProps => {
const context = useContext(WorkerContext)
if (context === undefined) {
throw new Error('useWorker must be used within a WorkerProvider')
}
return context
}