const PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.26.2/full/pyodide.js" export function buildCodeSandboxHtml(language: string, code: string): string { const lang = language.toLowerCase() if (lang === "html") return code if (lang === "css") return buildCssHtml(code) if (lang === "py" || lang === "python") return buildPythonHtml(code) const runnableCode = isTypeScriptLanguage(lang) ? stripTypeScript(code) : code return buildJavaScriptHtml(runnableCode) } function buildJavaScriptHtml(code: string): string { return `
` } function buildPythonHtml(code: string): string { const pyCode = JSON.stringify(code) return buildJavaScriptHtml(` (async()=>{ console.log('Đang tải Python (Pyodide)...'); const script=document.createElement('script'); script.src=${JSON.stringify(PYODIDE_CDN)}; script.onload=async()=>{ try{ const pyodide=await loadPyodide(); pyodide.setStdout({batched:(text)=>console.log(text)}); pyodide.setStderr({batched:(text)=>console.error(text)}); await pyodide.runPythonAsync(${pyCode}); }catch(e){console.error(e.message||String(e))} }; script.onerror=()=>console.error('Không tải được Pyodide'); document.body.appendChild(script); })() `) } function buildCssHtml(code: string): string { return `
Demo
` } function stripTypeScript(code: string): string { return code .replace(/\binterface\s+[A-Za-z_$][\w$]*(?:\s+extends\s+[^{}]+)?\s*\{[^{}]*\}\s*/gs, "") .replace(/^\s*type\s+[A-Za-z_$][\w$]*(?:<[^>]+>)?\s*=\s*[^;]+;?\s*$/gm, "") .replace(/\s+as\s+[A-Za-z_$][\w$<>,\s\[\]{}|&?.]*/g, "") .replace(/:\s*[A-Za-z_$][\w$<>,\s\[\]{}|&?.]*(?=\s*[,)=;])/g, "") } function isTypeScriptLanguage(language: string): boolean { return language === "ts" || language === "tsx" || language === "typescript" } function escapeClosingScript(code: string): string { return code.replace(/<\/script/gi, "<\\/script") } function escapeStyleContent(code: string): string { return code.replace(/<\/style/gi, "<\\/style") }