Skip to content

Commit b21d80e

Browse files
committed
Fixing editor
1 parent 696a940 commit b21d80e

File tree

2 files changed

+211
-15
lines changed

2 files changed

+211
-15
lines changed

script.js

Lines changed: 113 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ const state = {
99
currentItem: null,
1010
pyodide: null,
1111
pyodideLoading: false,
12-
originalCode: ''
12+
originalCode: '',
13+
uploadedFiles: [] // Track uploaded files
1314
};
1415

15-
// CORRIGÉ: Pyodide Management
16+
// Pyodide Management
1617
async function initPyodide() {
1718
if (state.pyodide) return state.pyodide;
1819
if (state.pyodideLoading) {
@@ -33,7 +34,6 @@ async function initPyodide() {
3334
});
3435
console.log('Pyodide loaded successfully');
3536

36-
// Load micropip
3737
await state.pyodide.loadPackage('micropip');
3838
console.log('Micropip loaded');
3939

@@ -48,6 +48,82 @@ async function initPyodide() {
4848
}
4949
}
5050

51+
// Handle file upload
52+
function handleFileUpload(event) {
53+
const files = event.target.files;
54+
const fileList = document.getElementById('uploaded-files-list');
55+
56+
for (let file of files) {
57+
// Check file size (1MB = 1048576 bytes)
58+
if (file.size > 1048576) {
59+
alert(`File ${file.name} is too large (max 1MB)`);
60+
continue;
61+
}
62+
63+
const reader = new FileReader();
64+
reader.onload = async (e) => {
65+
const content = e.target.result;
66+
67+
// Store file info
68+
state.uploadedFiles.push({
69+
name: file.name,
70+
content: content,
71+
size: file.size
72+
});
73+
74+
// Update UI
75+
const fileItem = document.createElement('div');
76+
fileItem.className = 'uploaded-file-item';
77+
fileItem.innerHTML = `
78+
<span>📄 ${file.name}</span>
79+
<span class="file-size">${(file.size / 1024).toFixed(1)} KB</span>
80+
<button class="remove-file-btn" onclick="window.removeUploadedFile('${file.name}')">✕</button>
81+
`;
82+
fileList.appendChild(fileItem);
83+
84+
// Write to Pyodide filesystem if loaded
85+
if (state.pyodide) {
86+
try {
87+
state.pyodide.FS.writeFile(file.name, new Uint8Array(content));
88+
console.log(`File ${file.name} written to virtual filesystem`);
89+
} catch (err) {
90+
console.error('Error writing file to filesystem:', err);
91+
}
92+
}
93+
};
94+
95+
// Read as ArrayBuffer for binary support
96+
reader.readAsArrayBuffer(file);
97+
}
98+
99+
// Reset input
100+
event.target.value = '';
101+
}
102+
103+
// Remove uploaded file
104+
function removeUploadedFile(filename) {
105+
state.uploadedFiles = state.uploadedFiles.filter(f => f.name !== filename);
106+
107+
// Update UI
108+
const fileList = document.getElementById('uploaded-files-list');
109+
const items = fileList.querySelectorAll('.uploaded-file-item');
110+
items.forEach(item => {
111+
if (item.textContent.includes(filename)) {
112+
item.remove();
113+
}
114+
});
115+
116+
// Remove from Pyodide filesystem
117+
if (state.pyodide) {
118+
try {
119+
state.pyodide.FS.unlink(filename);
120+
console.log(`File ${filename} removed from virtual filesystem`);
121+
} catch (err) {
122+
console.error('Error removing file:', err);
123+
}
124+
}
125+
}
126+
51127
// Open Interactive Editor
52128
function openEditor(pythonCode, problemTitle) {
53129
state.originalCode = pythonCode;
@@ -59,6 +135,10 @@ function openEditor(pythonCode, problemTitle) {
59135
editor.value = pythonCode;
60136
modal.style.display = 'flex';
61137

138+
// Clear previous uploads
139+
state.uploadedFiles = [];
140+
document.getElementById('uploaded-files-list').innerHTML = '';
141+
62142
// Load Pyodide in background if not loaded
63143
if (!state.pyodide && !state.pyodideLoading) {
64144
initPyodide();
@@ -69,9 +149,12 @@ function openEditor(pythonCode, problemTitle) {
69149
function closeEditor() {
70150
document.getElementById('code-editor-modal').style.display = 'none';
71151
document.getElementById('code-output').innerHTML = '';
152+
document.getElementById('test-input').value = '';
153+
state.uploadedFiles = [];
154+
document.getElementById('uploaded-files-list').innerHTML = '';
72155
}
73156

74-
// CORRIGÉ: Run Code
157+
// Run Code
75158
async function runCode() {
76159
const code = document.getElementById('code-editor').value;
77160
const testInput = document.getElementById('test-input').value;
@@ -84,7 +167,6 @@ async function runCode() {
84167
runBtn.disabled = true;
85168
runBtn.textContent = '⏳ Running...';
86169

87-
// Make sure Pyodide is loaded
88170
const pyodide = await initPyodide();
89171
if (!pyodide) {
90172
output.innerHTML = '<div style="color: red;">❌ Python environment not loaded. Please refresh the page.</div>';
@@ -96,6 +178,16 @@ async function runCode() {
96178
console.log('Pyodide ready, executing code...');
97179

98180
try {
181+
// Write uploaded files to virtual filesystem
182+
for (let file of state.uploadedFiles) {
183+
try {
184+
pyodide.FS.writeFile(file.name, new Uint8Array(file.content));
185+
console.log(`File ${file.name} available for open()`);
186+
} catch (err) {
187+
console.error(`Error writing ${file.name}:`, err);
188+
}
189+
}
190+
99191
// Capture stdout
100192
let outputText = '';
101193
pyodide.setStdout({
@@ -112,14 +204,13 @@ async function runCode() {
112204
}
113205
});
114206

115-
// If there's test input, make it available
207+
// Setup input() mock if test input provided
116208
if (testInput) {
117209
const inputLines = testInput.split('\n');
118210
pyodide.globals.set('__test_input_lines__', inputLines);
119211

120212
const inputMockCode = `
121213
import sys
122-
from io import StringIO
123214
124215
__test_input_lines__ = ${JSON.stringify(inputLines)}
125216
__test_input_index__ = [0]
@@ -134,7 +225,6 @@ def mock_input(prompt=''):
134225
return line
135226
return ''
136227
137-
# Replace built-in input
138228
__builtins__.input = mock_input
139229
`;
140230
await pyodide.runPythonAsync(inputMockCode);
@@ -491,14 +581,15 @@ async function renderProblem() {
491581
? `https://github.com/${getRepoPath()}/blob/main/${pathStr}/${pyFiles[0].name}`
492582
: `https://github.com/${getRepoPath()}/tree/main/${pathStr}`;
493583

584+
const runButtonId = `run-btn-${Date.now()}`;
494585
let html = `
495586
<div class="problem-header">
496587
<div class="problem-nav">
497588
<button onclick="window.goBack()" class="nav-link nav-button">← Back</button>
498589
<a href="#" class="nav-link">🏠 Home</a>
499590
${problemUrl ? `<a href="${problemUrl}" target="_blank" class="nav-link">🔗 Problem</a>` : ''}
500591
<a href="${githubFileUrl}" target="_blank" class="nav-link">📂 GitHub File</a>
501-
${pythonCode ? `<button onclick="window.openEditor(\`${escapeBackticks(pythonCode)}\`, '${escapeHtml(title)}')" class="nav-link nav-button run-code-nav">▶️ Run Code</button>` : ''}
592+
${pythonCode ? `<button id="${runButtonId}" class="nav-link nav-button run-code-nav">▶️ Run Code</button>` : ''}
502593
</div>
503594
<h1 class="problem-title">${title}</h1>
504595
<p class="problem-subtitle">${subtitle}</p>
@@ -568,6 +659,16 @@ async function renderProblem() {
568659
}
569660

570661
view.innerHTML = html;
662+
663+
if (pythonCode) {
664+
const runBtn = document.getElementById(runButtonId);
665+
if (runBtn) {
666+
runBtn.addEventListener('click', () => {
667+
openEditor(pythonCode, title);
668+
});
669+
}
670+
}
671+
571672
if (typeof hljs !== 'undefined') {
572673
hljs.highlightAll();
573674
}
@@ -619,10 +720,6 @@ function escapeHtml(text) {
619720
return div.innerHTML;
620721
}
621722

622-
function escapeBackticks(text) {
623-
return text.replace(/`/g, '\\`').replace(/\$/g, '\\$').replace(/\\/g, '\\\\');
624-
}
625-
626723
function getDefaultEmoji(platformName) {
627724
const emojis = {
628725
leetcode: '💡',
@@ -663,6 +760,8 @@ window.addEventListener('hashchange', () => {
663760
window.resetCode = resetCode;
664761
window.downloadCode = downloadCode;
665762
window.clearOutput = clearOutput;
763+
window.handleFileUpload = handleFileUpload;
764+
window.removeUploadedFile = removeUploadedFile;
666765

667766
// Setup modal event listeners
668767
const modal = document.getElementById('code-editor-modal');
@@ -678,6 +777,7 @@ window.addEventListener('hashchange', () => {
678777
document.getElementById('reset-code-btn')?.addEventListener('click', resetCode);
679778
document.getElementById('download-code-btn')?.addEventListener('click', downloadCode);
680779
document.getElementById('clear-output-btn')?.addEventListener('click', clearOutput);
780+
document.getElementById('file-upload-input')?.addEventListener('change', handleFileUpload);
681781
}
682782

683783
await loadPlatforms();

styles.css

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,8 +576,14 @@ body {
576576
}
577577

578578
/* Test Input Section */
579-
.test-input-section {
579+
.bottom-sections {
580+
display: grid;
581+
grid-template-columns: 1fr 1fr;
582+
gap: 1rem;
580583
height: 150px;
584+
}
585+
586+
.test-input-section {
581587
display: flex;
582588
flex-direction: column;
583589
background: #252526;
@@ -599,6 +605,80 @@ body {
599605
outline: none;
600606
}
601607

608+
/* File Upload Section */
609+
.file-upload-section {
610+
display: flex;
611+
flex-direction: column;
612+
background: #252526;
613+
border-radius: 8px;
614+
overflow: hidden;
615+
border: 1px solid #3e3e42;
616+
}
617+
618+
.file-upload-section .section-header {
619+
display: flex;
620+
align-items: center;
621+
gap: 1rem;
622+
}
623+
624+
.upload-btn {
625+
padding: 0.25rem 0.75rem;
626+
background: #4CAF50;
627+
border-radius: 4px;
628+
color: #fff;
629+
cursor: pointer;
630+
font-size: 0.8rem;
631+
transition: background 0.2s ease;
632+
margin-left: auto;
633+
}
634+
635+
.upload-btn:hover {
636+
background: #45a049;
637+
}
638+
639+
.uploaded-files-list {
640+
flex: 1;
641+
overflow-y: auto;
642+
padding: 0.5rem;
643+
background: #1e1e1e;
644+
}
645+
646+
.uploaded-file-item {
647+
display: flex;
648+
align-items: center;
649+
justify-content: space-between;
650+
padding: 0.5rem;
651+
background: #2d2d30;
652+
border-radius: 4px;
653+
margin-bottom: 0.5rem;
654+
gap: 0.5rem;
655+
}
656+
657+
.uploaded-file-item span {
658+
color: #d4d4d4;
659+
font-size: 0.85rem;
660+
}
661+
662+
.file-size {
663+
color: #888;
664+
font-size: 0.75rem !important;
665+
}
666+
667+
.remove-file-btn {
668+
background: #d32f2f;
669+
border: none;
670+
color: white;
671+
padding: 0.25rem 0.5rem;
672+
border-radius: 3px;
673+
cursor: pointer;
674+
font-size: 0.8rem;
675+
transition: background 0.2s ease;
676+
}
677+
678+
.remove-file-btn:hover {
679+
background: #c62828;
680+
}
681+
602682
/* Pyodide Loading Indicator */
603683
.pyodide-status {
604684
display: none;
@@ -643,6 +723,16 @@ body {
643723
grid-template-columns: 1fr;
644724
}
645725

726+
.bottom-sections {
727+
grid-template-columns: 1fr;
728+
height: auto;
729+
}
730+
731+
.test-input-section,
732+
.file-upload-section {
733+
height: 120px;
734+
}
735+
646736
.modal-content {
647737
height: 95vh;
648738
}
@@ -665,7 +755,13 @@ body {
665755
font-size: 0.8rem;
666756
}
667757

668-
.test-input-section {
758+
.bottom-sections {
759+
grid-template-columns: 1fr;
760+
height: auto;
761+
}
762+
763+
.test-input-section,
764+
.file-upload-section {
669765
height: 120px;
670766
}
671767
}

0 commit comments

Comments
 (0)