@@ -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
1617async 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
52128function 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) {
69149function 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
75158async 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 = `
121213import 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-
626723function 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 ( ) ;
0 commit comments