11// src/pages/EditorComponent.js
2- // (All your existing imports remain the same)
32import React , { useCallback , useEffect , useRef , useState } from "react" ;
43import { useSnackbar } from "notistack" ;
54import {
@@ -88,7 +87,6 @@ function EditorComponent() {
8887 const [ showLineNumbers , setShowLineNumbers ] = useState ( true ) ;
8988 const [ wordWrap , setWordWrap ] = useState ( false ) ;
9089 const [ fontSize , setFontSize ] = useState ( 14 ) ;
91-
9290 const [ isImporting , setIsImporting ] = useState ( false ) ;
9391 const isImportingRef = useRef ( false ) ;
9492 const fileInputRef = useRef ( null ) ;
@@ -151,11 +149,6 @@ function EditorComponent() {
151149 }
152150 } ;
153151
154- const getLanguageLogoById = ( id ) => {
155- const language = LANGUAGES . find ( ( lang ) => parseInt ( lang . ID , 10 ) === parseInt ( id , 10 ) ) ;
156- return language ? language . LOGO : null ;
157- } ;
158-
159152 const submitCode = useCallback ( async ( ) => {
160153 if ( ! editorRef . current ) {
161154 enqueueSnackbar ( "Editor not ready" , { variant : "error" } ) ;
@@ -176,6 +169,7 @@ function EditorComponent() {
176169
177170 try {
178171 const encodedCode = btoa ( codeToSubmit ) ;
172+
179173 const response = await fetch (
180174 `${ judge0SubmitUrl } ?base64_encoded=true&wait=false&fields=*` ,
181175 {
@@ -261,65 +255,199 @@ function EditorComponent() {
261255 }
262256 } , [ enqueueSnackbar , languageDetails ] ) ;
263257
264- // --- START: KEYBOARD SHORTCUT HANDLER ---
265- useEffect ( ( ) => {
266- const handleKeyDown = ( e ) => {
267- if ( ! editorRef . current ) return ;
258+ const handleFileImport = ( e ) => {
259+ const file = e . target . files ?. [ 0 ] ;
260+ if ( ! file ) return ;
268261
269- const isMac = navigator . platform . toUpperCase ( ) . indexOf ( "MAC" ) >= 0 ;
270- const ctrlKey = isMac ? e . metaKey : e . ctrlKey ;
262+ setCode ( "" ) ;
263+ if ( fileInputRef . current ) fileInputRef . current . value = "" ;
271264
272- // Ctrl+S → Export file
273- if ( ctrlKey && e . key === "s" ) {
274- e . preventDefault ( ) ;
275- exportFile ( ) ;
276- }
277- // Ctrl+O → Import file
278- else if ( ctrlKey && e . key === "o" ) {
279- e . preventDefault ( ) ;
280- fileInputRef . current ?. click ( ) ;
281- }
282- // Ctrl+L → Clear output
283- else if ( ctrlKey && e . key . toLowerCase ( ) === "l" ) {
284- e . preventDefault ( ) ;
285- clearOutput ( ) ;
286- }
287- // Ctrl+C → Copy output
288- else if ( ctrlKey && e . key . toLowerCase ( ) === "c" ) {
289- e . preventDefault ( ) ;
290- copyOutput ( ) ;
291- }
292- // Ctrl+/ → Toggle line numbers
293- else if ( ctrlKey && e . key === "/" ) {
294- e . preventDefault ( ) ;
295- setShowLineNumbers ( ( prev ) => ! prev ) ;
296- }
297- // Ctrl+Shift+W → Toggle word wrap
298- else if ( ctrlKey && e . shiftKey && e . key . toLowerCase ( ) === "w" ) {
299- e . preventDefault ( ) ;
300- setWordWrap ( ( prev ) => ! prev ) ;
265+ isImportingRef . current = true ;
266+ setIsImporting ( true ) ;
267+
268+ const extension = file . name . split ( "." ) . pop ( ) . toLowerCase ( ) ;
269+ const languageMap = { js : "Javascript" , jsx : "Javascript" , ts : "Typescript" , py : "Python3" , cpp : "C++" , c : "C" , java : "Java" } ;
270+ const languageName = languageMap [ extension ] ;
271+ const selectedLanguage = LANGUAGES . find ( ( lang ) => lang . NAME === languageName ) ;
272+
273+ if ( ! selectedLanguage ) {
274+ enqueueSnackbar ( "Unsupported file type" , { variant : "error" } ) ;
275+ isImportingRef . current = false ;
276+ setIsImporting ( false ) ;
277+ return ;
278+ }
279+
280+ const reader = new FileReader ( ) ;
281+ reader . onload = ( event ) => {
282+ setCurrentLanguage ( selectedLanguage . DEFAULT_LANGUAGE ) ;
283+ setLanguageDetails ( {
284+ ID : selectedLanguage . ID ,
285+ NAME : selectedLanguage . NAME ,
286+ DEFAULT_LANGUAGE : selectedLanguage . DEFAULT_LANGUAGE ,
287+ LANGUAGE_NAME : selectedLanguage . NAME ,
288+ HELLO_WORLD : selectedLanguage . HELLO_WORLD ,
289+ LOGO : selectedLanguage . LOGO ,
290+ } ) ;
291+ setCode ( String ( event . target . result ?? "" ) ) ;
292+ setOutput ( [ ] ) ;
293+ isImportingRef . current = false ;
294+ setIsImporting ( false ) ;
295+ } ;
296+ reader . onerror = ( ) => {
297+ enqueueSnackbar ( "Error reading file" , { variant : "error" } ) ;
298+ isImportingRef . current = false ;
299+ setIsImporting ( false ) ;
300+ } ;
301+ reader . readAsText ( file ) ;
302+ } ;
303+
304+ // ------------------- MISSING FUNCTIONS FIX -------------------
305+ const copyOutput = async ( ) => {
306+ if ( ! output || output . length === 0 ) {
307+ enqueueSnackbar ( "No output to copy" , { variant : "warning" } ) ;
308+ return ;
309+ }
310+ const outputText = Array . isArray ( output ) ? output . join ( "\n" ) : String ( output ) ;
311+ try {
312+ await navigator . clipboard . writeText ( outputText ) ;
313+ enqueueSnackbar ( "Output copied to clipboard!" , { variant : "success" } ) ;
314+ } catch {
315+ enqueueSnackbar ( "Failed to copy output" , { variant : "error" } ) ;
316+ }
317+ } ;
318+
319+ const clearOutput = ( ) => {
320+ setOutput ( [ ] ) ;
321+ enqueueSnackbar ( "Output cleared" , { variant : "info" } ) ;
322+ } ;
323+
324+ const exportFile = ( ) => {
325+ if ( ! code || code . length === 0 ) {
326+ enqueueSnackbar ( "No code to export" , { variant : "warning" } ) ;
327+ return ;
328+ }
329+
330+ const extensionMap = { javascript : "js" , javascriptreact : "js" , typescript : "ts" , python : "py" , cpp : "cpp" , c : "c" , java : "java" } ;
331+ const langKey = ( languageDetails ?. DEFAULT_LANGUAGE || "" ) . toLowerCase ( ) ;
332+ const extension = extensionMap [ langKey ] || "txt" ;
333+
334+ const blob = new Blob ( [ code ] , { type : "text/plain" } ) ;
335+ const url = URL . createObjectURL ( blob ) ;
336+ const link = document . createElement ( "a" ) ;
337+ link . href = url ;
338+ link . download = `code.${ extension } ` ;
339+ document . body . appendChild ( link ) ;
340+ link . click ( ) ;
341+ document . body . removeChild ( link ) ;
342+ URL . revokeObjectURL ( url ) ;
343+ } ;
344+ // --------------------------------------------------------------
345+
346+ const handleEditorDidMount = useCallback (
347+ ( editor , monaco ) => {
348+ editorRef . current = editor ;
349+ monacoRef . current = monaco ;
350+
351+ try {
352+ editor . addCommand ( monaco . KeyMod . CtrlCmd | monaco . KeyCode . Enter , ( ) => {
353+ submitCode ( ) ;
354+ } ) ;
355+ } catch ( err ) {
356+ if ( process . env . NODE_ENV !== "production" ) console . error ( "Failed to register command:" , err ) ;
301357 }
302- // Ctrl+Enter → Run code
303- else if ( ctrlKey && e . key === "Enter" ) {
304- e . preventDefault ( ) ;
358+ } ,
359+ [ submitCode ]
360+ ) ;
361+
362+ useEffect ( ( ) => {
363+ const handleKeyDown = ( event ) => {
364+ if ( ( event . ctrlKey || event . metaKey ) && event . key === "Enter" ) {
365+ event . preventDefault ( ) ;
305366 submitCode ( ) ;
306367 }
307368 } ;
308-
309369 document . addEventListener ( "keydown" , handleKeyDown ) ;
310370 return ( ) => document . removeEventListener ( "keydown" , handleKeyDown ) ;
311- } , [ exportFile , fileInputRef , clearOutput , copyOutput , submitCode ] ) ;
312- // --- END: KEYBOARD SHORTCUT HANDLER ---
371+ } , [ submitCode ] ) ;
313372
314- // (All your existing EditorComponent code remains unchanged after this point)
373+ useEffect ( ( ) => {
374+ return ( ) => {
375+ if ( timeoutRef . current ) {
376+ clearTimeout ( timeoutRef . current ) ;
377+ timeoutRef . current = null ;
378+ }
379+ } ;
380+ } , [ ] ) ;
381+
382+ function handleLanguageChange ( _ , value ) {
383+ if ( isImporting ) return ;
384+ setCurrentLanguage ( value . DEFAULT_LANGUAGE ) ;
385+ setOutput ( [ ] ) ;
386+ setCode ( ( prev ) => ( prev ? prev : value . HELLO_WORLD || "" ) ) ;
387+ }
315388
316- // For brevity: existing Editor rendering, import/export buttons, output area, settings, and footer remain unchanged
389+ const handleSignOut = async ( ) => {
390+ try {
391+ await logOut ( ) ;
392+ } catch ( error ) {
393+ if ( process . env . NODE_ENV !== "production" ) console . error ( "Sign out error:" , error ) ;
394+ }
395+ } ;
396+
397+ const handleLineNumbersToggle = ( event ) => setShowLineNumbers ( event . target . checked ) ;
398+ const handleWordWrapToggle = ( event ) => setWordWrap ( event . target . checked ) ;
399+ const handleFontSizeChange = ( event , newValue ) => setFontSize ( newValue ) ;
400+
401+ // ------------------- JSX RENDER -------------------
402+ const renderAuthenticatedContent = ( ) => (
403+ < >
404+ { /* Editor + Sidebar */ }
405+ < StyledLayout >
406+ < Editor
407+ className = "editor"
408+ theme = { currentEditorTheme . NAME }
409+ onMount = { handleEditorDidMount }
410+ value = { code }
411+ onChange = { ( value ) => setCode ( value ?? "" ) }
412+ language = { languageDetails . DEFAULT_LANGUAGE }
413+ options = { { minimap : { enabled : false } , lineNumbers : showLineNumbers ? "on" : "off" , wordWrap : wordWrap ? "on" : "off" , fontSize } }
414+ />
415+ < div className = "sidebar" >
416+ { /* Import / Export */ }
417+ < div style = { { display : "flex" , gap : "0.5rem" } } >
418+ < StyledButton onClick = { ( ) => fileInputRef . current . click ( ) } disabled = { isImporting } >
419+ { isImporting ? < CircularProgress size = { 16 } /> : < > < FaFileUpload /> Import</ > }
420+ </ StyledButton >
421+ < input type = "file" ref = { fileInputRef } style = { { display : "none" } } accept = ".java,.js,.jsx,.ts,.py,.cpp,.c" onChange = { handleFileImport } />
422+ < StyledButton onClick = { exportFile } >
423+ { isDownloading ? < CircularProgress size = { 16 } /> : < > < FaFileDownload /> Export</ > }
424+ </ StyledButton >
425+ </ div >
426+ { /* Run, Copy, Clear */ }
427+ < div style = { { display : "flex" , gap : "0.5rem" , marginTop : "1rem" } } >
428+ < StyledButton onClick = { submitCode } > < FaPlay /> Run</ StyledButton >
429+ < StyledButton onClick = { copyOutput } > < FaCopy /> Copy Output</ StyledButton >
430+ < StyledButton onClick = { clearOutput } > < FaTrash /> Clear Output</ StyledButton >
431+ </ div >
432+ { /* Output */ }
433+ < OutputLayout >
434+ < pre > { output && output . length ? output . join ( "\n" ) : "Output will appear here..." } </ pre >
435+ </ OutputLayout >
436+ </ div >
437+ </ StyledLayout >
438+ </ >
439+ ) ;
317440
318441 return (
319- < div className = "editor-container" >
320- { /* existing JSX remains */ }
321- { /* ... */ }
322- </ div >
442+ < Box sx = { { flexGrow : 1 } } >
443+ { currentUser ? renderAuthenticatedContent ( ) : (
444+ < div >
445+ < GithubSignIn />
446+ < GoogleSignIn />
447+ </ div >
448+ ) }
449+ < Footer />
450+ </ Box >
323451 ) ;
324452}
325453
0 commit comments