MCPcopy Create free account
hub / github.com/dylan-sutton-chavez/edge-python / Playground

Function Playground

docs/components/Playground.jsx:44–141  ·  view source on GitHub ↗
({ code, output })

Source from the content-addressed store, hash-verified

42)
43
44export function Playground({ code, output }) {
45 const edRef = useRef(null)
46 const editorRef = useRef(null)
47 const defaultText = fromB64(output).replace(/\n$/, '')
48 const defaultCode = fromB64(code).replace(/\n$/, '')
49 // Stable ref: React 19 compares dangerouslySetInnerHTML by object identity, so a fresh `{__html}` each render re-applies innerHTML and wipes CodeJar/Shiki's spans (e.g. while running).
50 const seedHtml = useMemo(() => ({ __html: escapeHtml(defaultCode) }), [defaultCode])
51 const [result, setResult] = useState(null) // null = showing default; else { text, error, ms }
52 const [running, setRunning] = useState(false)
53 const [phase, setPhase] = useState(null) // cold-start/exec phase: 'runtime' | 'worker' | 'running'
54
55 // Live guard: the mount effect captures runCode once, so a `running` state read here would be the first render's `false` forever — Ctrl+Enter could then fire concurrent runs. A ref reads the current value through that stale closure.
56 const runningRef = useRef(false)
57 const runCode = useCallback(async (src) => {
58 if (runningRef.current) return
59 runningRef.current = true
60 setRunning(true)
61 // Byte-stream contract: each chunk is raw stdout (print already includes its own `end`); concatenate, don't join.
62 let buf = ''
63 // Don't clear the terminal up front (collapse + flicker); keep old text until the first chunk replaces it.
64 try {
65 const res = await run(src, (chunk) => {
66 buf += chunk
67 setResult({ text: buf, error: '', ms: 0 })
68 }, setPhase)
69 setResult({ text: buf, error: res.error, ms: res.ms })
70 } catch (e) {
71 setResult({ text: buf, error: String(e?.message ?? e), ms: 0 })
72 } finally {
73 runningRef.current = false
74 setRunning(false)
75 setPhase(null)
76 }
77 }, [])
78
79 // Mount the CodeJar editor + Shiki highlighter client-side (lazy: keeps codejar/shiki out of SSR).
80 useEffect(() => {
81 let editor
82 let highlight
83 let disposed = false
84 Promise.all([import('./editor.js'), import('./shiki.js')]).then(([{ createEditor }, { createHighlight }]) => {
85 if (disposed || !edRef.current) return
86 highlight = createHighlight(() => editor && editor.setCode(editor.getCode()))
87 editor = createEditor({
88 ed: edRef.current,
89 defaultCode,
90 onRun: (src) => runCode(src),
91 highlight,
92 })
93 editorRef.current = editor
94 })
95 // Tear down CodeJar and (critically) the Shiki MutationObserver on <html>; that observer holds a closure over the editor, so without dispose() each unmounted Playground stays pinned in memory.
96 return () => {
97 disposed = true
98 editor?.destroy()
99 highlight?.dispose()
100 editorRef.current = null
101 }

Callers

nothing calls this directly

Calls 7

escapeHtmlFunction · 0.90
runFunction · 0.90
fromB64Function · 0.85
createHighlightFunction · 0.85
createEditorFunction · 0.85
applyTerminalControlsFunction · 0.85
fmtFunction · 0.85

Tested by

no test coverage detected