123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- <script lang="ts">
- import Spinner from '$lib/components/common/Spinner.svelte';
- import { copyToClipboard } from '$lib/utils';
- import hljs from 'highlight.js';
- import 'highlight.js/styles/github-dark.min.css';
- import { loadPyodide } from 'pyodide';
- import { tick } from 'svelte';
- export let id = '';
- export let lang = '';
- export let code = '';
- let executing = false;
- let stdout = null;
- let stderr = null;
- let result = null;
- let copied = false;
- const copyCode = async () => {
- copied = true;
- await copyToClipboard(code);
- setTimeout(() => {
- copied = false;
- }, 1000);
- };
- const checkPythonCode = (str) => {
- // Check if the string contains typical Python keywords, syntax, or functions
- const pythonKeywords = [
- 'def',
- 'class',
- 'import',
- 'from',
- 'if',
- 'else',
- 'elif',
- 'for',
- 'while',
- 'try',
- 'except',
- 'finally',
- 'return',
- 'yield',
- 'lambda',
- 'assert',
- 'pass',
- 'break',
- 'continue',
- 'global',
- 'nonlocal',
- 'del',
- 'True',
- 'False',
- 'None',
- 'and',
- 'or',
- 'not',
- 'in',
- 'is',
- 'as',
- 'with'
- ];
- for (let keyword of pythonKeywords) {
- if (str.includes(keyword)) {
- return true;
- }
- }
- // Check if the string contains typical Python syntax characters
- const pythonSyntax = [
- 'def ',
- 'class ',
- 'import ',
- 'from ',
- 'if ',
- 'else:',
- 'elif ',
- 'for ',
- 'while ',
- 'try:',
- 'except:',
- 'finally:',
- 'return ',
- 'yield ',
- 'lambda ',
- 'assert ',
- 'pass',
- 'break',
- 'continue',
- 'global ',
- 'nonlocal ',
- 'del ',
- 'True',
- 'False',
- 'None',
- ' and ',
- ' or ',
- ' not ',
- ' in ',
- ' is ',
- ' as ',
- ' with ',
- ':',
- '=',
- '==',
- '!=',
- '>',
- '<',
- '>=',
- '<=',
- '+',
- '-',
- '*',
- '/',
- '%',
- '**',
- '//',
- '(',
- ')',
- '[',
- ']',
- '{',
- '}'
- ];
- for (let syntax of pythonSyntax) {
- if (str.includes(syntax)) {
- return true;
- }
- }
- // If none of the above conditions met, it's probably not Python code
- return false;
- };
- const executePython = async (code) => {
- if (!code.includes('input') && !code.includes('matplotlib')) {
- executePythonAsWorker(code);
- } else {
- result = null;
- stdout = null;
- stderr = null;
- executing = true;
- document.pyodideMplTarget = document.getElementById(`plt-canvas-${id}`);
- let pyodide = await loadPyodide({
- indexURL: '/pyodide/',
- stdout: (text) => {
- console.log('Python output:', text);
- if (stdout) {
- stdout += `${text}\n`;
- } else {
- stdout = `${text}\n`;
- }
- },
- stderr: (text) => {
- console.log('An error occured:', text);
- if (stderr) {
- stderr += `${text}\n`;
- } else {
- stderr = `${text}\n`;
- }
- }
- });
- try {
- const res = await pyodide.loadPackage('micropip');
- console.log(res);
- const micropip = pyodide.pyimport('micropip');
- await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
- let packages = [
- code.includes('requests') ? 'requests' : null,
- code.includes('bs4') ? 'beautifulsoup4' : null,
- code.includes('numpy') ? 'numpy' : null,
- code.includes('pandas') ? 'pandas' : null,
- code.includes('matplotlib') ? 'matplotlib' : null
- ].filter(Boolean);
- console.log(packages);
- await micropip.install(packages);
- result = await pyodide.runPythonAsync(`from js import prompt
- def input(p):
- return prompt(p)
- __builtins__.input = input`);
- result = await pyodide.runPython(code);
- if (!result) {
- result = '[NO OUTPUT]';
- }
- console.log(result);
- console.log(stdout);
- console.log(stderr);
- const pltCanvasElement = document.getElementById(`plt-canvas-${id}`);
- if (pltCanvasElement?.innerHTML !== '') {
- pltCanvasElement.classList.add('pt-4');
- }
- } catch (error) {
- console.error('Error:', error);
- stderr = error;
- }
- executing = false;
- }
- };
- const executePythonAsWorker = async (code) => {
- result = null;
- stdout = null;
- stderr = null;
- executing = true;
- let packages = [
- code.includes('requests') ? 'requests' : null,
- code.includes('bs4') ? 'beautifulsoup4' : null,
- code.includes('numpy') ? 'numpy' : null,
- code.includes('pandas') ? 'pandas' : null,
- code.includes('matplotlib') ? 'matplotlib' : null
- ].filter(Boolean);
- const pyodideWorker = new Worker('/pyodide-worker.js');
- pyodideWorker.postMessage({
- id: id,
- code: code,
- packages: packages
- });
- setTimeout(() => {
- if (executing) {
- executing = false;
- stderr = 'Execution Time Limit Exceeded';
- pyodideWorker.terminate();
- }
- }, 60000);
- pyodideWorker.onmessage = (event) => {
- console.log('pyodideWorker.onmessage', event);
- const { id, ...data } = event.data;
- console.log(id, data);
- data['stdout'] && (stdout = data['stdout']);
- data['stderr'] && (stderr = data['stderr']);
- data['result'] && (result = data['result']);
- executing = false;
- };
- pyodideWorker.onerror = (event) => {
- console.log('pyodideWorker.onerror', event);
- executing = false;
- };
- };
- $: highlightedCode = code ? hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value : '';
- </script>
- {#if code}
- <div class="mb-4">
- <div
- class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
- >
- <div class="p-1">{@html lang}</div>
- <div class="flex items-center">
- {#if lang === 'python' || checkPythonCode(code)}
- {#if executing}
- <div class="copy-code-button bg-none border-none p-1 cursor-not-allowed">Running</div>
- {:else}
- <button
- class="copy-code-button bg-none border-none p-1"
- on:click={() => {
- executePython(code);
- }}>Run</button
- >
- {/if}
- {/if}
- <button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
- >{copied ? 'Copied' : 'Copy Code'}</button
- >
- </div>
- </div>
- <pre
- class=" hljs p-4 px-5 overflow-x-auto"
- style="border-top-left-radius: 0px; border-top-right-radius: 0px; {(executing ||
- stdout ||
- stderr ||
- result) &&
- 'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
- class="language-{lang} rounded-t-none whitespace-pre">{@html highlightedCode || code}</code
- ></pre>
- <div id="plt-canvas-{id}" class="bg-[#202123] text-white" />
- {#if executing}
- <div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
- <div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
- <div class="text-sm">Running...</div>
- </div>
- {:else if stdout || stderr || result}
- <div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
- <div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
- <div class="text-sm">{stdout || stderr || result}</div>
- </div>
- {/if}
- </div>
- {/if}
|