krinlove commited on
Commit
241be0a
·
verified ·
1 Parent(s): c677885

Upload CodeBlock.svelte

Browse files
src/lib/components/chat/Messages/CodeBlock.svelte ADDED
@@ -0,0 +1,319 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { copyToClipboard } from '$lib/utils';
3
+ import hljs from 'highlight.js';
4
+ import 'highlight.js/styles/github-dark.min.css';
5
+ import { loadPyodide } from 'pyodide';
6
+ import PyodideWorker from '$lib/workers/pyodide.worker?worker';
7
+ import { loadSandpackClient } from '@codesandbox/sandpack-client';
8
+
9
+ export let id = '';
10
+
11
+ export let lang = '';
12
+ export let code = '';
13
+
14
+ let highlightedCode = null;
15
+ let executing = false;
16
+
17
+ let stdout = null;
18
+ let stderr = null;
19
+ let result = null;
20
+
21
+ let copied = false;
22
+
23
+ const copyCode = async () => {
24
+ copied = true;
25
+ await copyToClipboard(code);
26
+
27
+ setTimeout(() => {
28
+ copied = false;
29
+ }, 1000);
30
+ };
31
+
32
+ const checkPythonCode = (str) => {
33
+ // Check if the string contains typical Python syntax characters
34
+ const pythonSyntax = [
35
+ 'def ',
36
+ 'else:',
37
+ 'elif ',
38
+ 'try:',
39
+ 'except:',
40
+ 'finally:',
41
+ 'yield ',
42
+ 'lambda ',
43
+ 'assert ',
44
+ 'nonlocal ',
45
+ 'del ',
46
+ 'True',
47
+ 'False',
48
+ 'None',
49
+ ' and ',
50
+ ' or ',
51
+ ' not ',
52
+ ' in ',
53
+ ' is ',
54
+ ' with '
55
+ ];
56
+
57
+ for (let syntax of pythonSyntax) {
58
+ if (str.includes(syntax)) {
59
+ return true;
60
+ }
61
+ }
62
+
63
+ // If none of the above conditions met, it's probably not Python code
64
+ return false;
65
+ };
66
+
67
+ const executePython = async (code) => {
68
+ if (!code.includes('input') && !code.includes('matplotlib')) {
69
+ executePythonAsWorker(code);
70
+ } else {
71
+ result = null;
72
+ stdout = null;
73
+ stderr = null;
74
+
75
+ executing = true;
76
+
77
+ document.pyodideMplTarget = document.getElementById(`plt-canvas-${id}`);
78
+
79
+ let pyodide = await loadPyodide({
80
+ indexURL: '/pyodide/',
81
+ stdout: (text) => {
82
+ console.log('Python output:', text);
83
+
84
+ if (stdout) {
85
+ stdout += `${text}\n`;
86
+ } else {
87
+ stdout = `${text}\n`;
88
+ }
89
+ },
90
+ stderr: (text) => {
91
+ console.log('An error occured:', text);
92
+ if (stderr) {
93
+ stderr += `${text}\n`;
94
+ } else {
95
+ stderr = `${text}\n`;
96
+ }
97
+ },
98
+ packages: ['micropip']
99
+ });
100
+
101
+ try {
102
+ const micropip = pyodide.pyimport('micropip');
103
+
104
+ // await micropip.set_index_urls('https://pypi.org/pypi/{package_name}/json');
105
+
106
+ let packages = [
107
+ code.includes('requests') ? 'requests' : null,
108
+ code.includes('bs4') ? 'beautifulsoup4' : null,
109
+ code.includes('numpy') ? 'numpy' : null,
110
+ code.includes('pandas') ? 'pandas' : null,
111
+ code.includes('matplotlib') ? 'matplotlib' : null,
112
+ code.includes('sklearn') ? 'scikit-learn' : null,
113
+ code.includes('scipy') ? 'scipy' : null,
114
+ code.includes('re') ? 'regex' : null,
115
+ code.includes('seaborn') ? 'seaborn' : null
116
+ ].filter(Boolean);
117
+
118
+ console.log(packages);
119
+ await micropip.install(packages);
120
+
121
+ result = await pyodide.runPythonAsync(`from js import prompt
122
+ def input(p):
123
+ return prompt(p)
124
+ __builtins__.input = input`);
125
+
126
+ result = await pyodide.runPython(code);
127
+
128
+ if (!result) {
129
+ result = '[NO OUTPUT]';
130
+ }
131
+
132
+ console.log(result);
133
+ console.log(stdout);
134
+ console.log(stderr);
135
+
136
+ const pltCanvasElement = document.getElementById(`plt-canvas-${id}`);
137
+
138
+ if (pltCanvasElement?.innerHTML !== '') {
139
+ pltCanvasElement.classList.add('pt-4');
140
+ }
141
+ } catch (error) {
142
+ console.error('Error:', error);
143
+ stderr = error;
144
+ }
145
+
146
+ executing = false;
147
+ }
148
+ };
149
+
150
+ const executePythonAsWorker = async (code) => {
151
+ result = null;
152
+ stdout = null;
153
+ stderr = null;
154
+
155
+ executing = true;
156
+
157
+ let packages = [
158
+ code.includes('requests') ? 'requests' : null,
159
+ code.includes('bs4') ? 'beautifulsoup4' : null,
160
+ code.includes('numpy') ? 'numpy' : null,
161
+ code.includes('pandas') ? 'pandas' : null,
162
+ code.includes('sklearn') ? 'scikit-learn' : null,
163
+ code.includes('scipy') ? 'scipy' : null,
164
+ code.includes('re') ? 'regex' : null,
165
+ code.includes('seaborn') ? 'seaborn' : null
166
+ ].filter(Boolean);
167
+
168
+ console.log(packages);
169
+
170
+ const pyodideWorker = new PyodideWorker();
171
+
172
+ pyodideWorker.postMessage({
173
+ id: id,
174
+ code: code,
175
+ packages: packages
176
+ });
177
+
178
+ setTimeout(() => {
179
+ if (executing) {
180
+ executing = false;
181
+ stderr = 'Execution Time Limit Exceeded';
182
+ pyodideWorker.terminate();
183
+ }
184
+ }, 60000);
185
+
186
+ pyodideWorker.onmessage = (event) => {
187
+ console.log('pyodideWorker.onmessage', event);
188
+ const { id, ...data } = event.data;
189
+
190
+ console.log(id, data);
191
+
192
+ data['stdout'] && (stdout = data['stdout']);
193
+ data['stderr'] && (stderr = data['stderr']);
194
+ data['result'] && (result = data['result']);
195
+
196
+ executing = false;
197
+ };
198
+
199
+ pyodideWorker.onerror = (event) => {
200
+ console.log('pyodideWorker.onerror', event);
201
+ executing = false;
202
+ };
203
+ };
204
+
205
+ let sandpackIframe;
206
+ let sandpackClient;
207
+ const executeHTML = async (code) => {
208
+ // Initialize Sandpack client
209
+ const content = {
210
+ files: {
211
+ '/package.json': {
212
+ code: JSON.stringify({
213
+ main: 'index.html',
214
+ devDependencies: {}
215
+ })
216
+ },
217
+ '/index.html': { code }
218
+ },
219
+ environment: 'vanilla',
220
+ template: 'static'
221
+ };
222
+ if (sandpackClient) {
223
+ sandpackClient.updateSandbox(content);
224
+ } else {
225
+ sandpackClient = await loadSandpackClient(sandpackIframe, content, {
226
+ showOpenInCodeSandbox: true
227
+ });
228
+ }
229
+ };
230
+
231
+ $: if (lang.toLowerCase() == 'php' || lang.toLowerCase() == 'html') {
232
+ if (!!sandpackIframe) {
233
+ executeHTML(code);
234
+ }
235
+ }
236
+
237
+ let debounceTimeout;
238
+ $: if (code) {
239
+ // Function to perform the code highlighting
240
+ const highlightCode = () => {
241
+ highlightedCode = hljs.highlightAuto(code, hljs.getLanguage(lang)?.aliases).value || code;
242
+ };
243
+
244
+ // Clear the previous timeout if it exists
245
+ clearTimeout(debounceTimeout);
246
+
247
+ // Set a new timeout to debounce the code highlighting
248
+ debounceTimeout = setTimeout(highlightCode, 10);
249
+ }
250
+ </script>
251
+
252
+ <div class="mb-4" dir="ltr">
253
+ <div
254
+ class="flex justify-between bg-[#202123] text-white text-xs px-4 pt-1 pb-0.5 rounded-t-lg overflow-x-auto"
255
+ >
256
+ <div class="p-1">{@html lang}</div>
257
+
258
+ <div class="flex items-center">
259
+ {#if lang.toLowerCase() === 'python' || lang.toLowerCase() === 'py' || (lang === '' && checkPythonCode(code))}
260
+ {#if executing}
261
+ <div class="copy-code-button bg-none border-none p-1 cursor-not-allowed">Running</div>
262
+ {:else}
263
+ <button
264
+ class="copy-code-button bg-none border-none p-1"
265
+ on:click={() => {
266
+ executePython(code);
267
+ }}>Run</button
268
+ >
269
+ {/if}
270
+ {/if}
271
+ <button class="copy-code-button bg-none border-none p-1" on:click={copyCode}
272
+ >{copied ? 'Copied' : 'Copy Code'}</button
273
+ >
274
+ </div>
275
+ </div>
276
+
277
+ <pre
278
+ class=" hljs p-4 px-5 overflow-x-auto"
279
+ style="border-top-left-radius: 0px; border-top-right-radius: 0px; {(executing ||
280
+ stdout ||
281
+ stderr ||
282
+ result) &&
283
+ 'border-bottom-left-radius: 0px; border-bottom-right-radius: 0px;'}"><code
284
+ class="language-{lang} rounded-t-none whitespace-pre"
285
+ >{#if highlightedCode}{@html highlightedCode}{:else}{code}{/if}</code
286
+ >
287
+ </pre>
288
+
289
+ <div
290
+ id="plt-canvas-{id}"
291
+ class="bg-[#202123] text-white max-w-full overflow-x-auto scrollbar-hidden"
292
+ />
293
+
294
+ {#if executing}
295
+ <div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
296
+ <div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
297
+ <div class="text-sm">Running...</div>
298
+ </div>
299
+ {:else if stdout || stderr || result}
300
+ <div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
301
+ <div class=" text-gray-500 text-xs mb-1">STDOUT/STDERR</div>
302
+ <div class="text-sm">{stdout || stderr || result}</div>
303
+ </div>
304
+ {/if}
305
+ {#if lang.toLowerCase() == 'php' || lang.toLowerCase() == 'html'}
306
+ <div class="bg-[#202123] text-white px-4 py-4 rounded-b-lg">
307
+ <div class="text-gray-500 text-xs mb-1 flex justify-between items-center">
308
+ <span>HTML</span>
309
+ <button
310
+ class="copy-code-button bg-none border-none p-1"
311
+ on:click={() => {
312
+ executeHTML(code);
313
+ }}>Refresh</button
314
+ >
315
+ </div>
316
+ <iframe bind:this={sandpackIframe} title="HTML Preview" class="w-full h-96 mt-4 bg-white" />
317
+ </div>
318
+ {/if}
319
+ </div>