198198 }
199199
200200 .loading-spinner {
201- display : inline-flex;
202- gap : 3px ;
203- align-items : flex-end;
201+ display : inline-block;
202+ width : 14px ;
204203 height : 14px ;
204+ position : relative;
205+ transform : rotate (-90deg );
205206 }
206207
207- .loading-bar {
208- width : 3px ;
209- background : currentColor;
210- border-radius : 2px ;
211- animation : loading-bounce 0.8s ease-in-out infinite;
212- }
213-
214- .loading-bar : nth-child (1 ) {
215- animation-delay : 0s ;
216- }
217-
218- .loading-bar : nth-child (2 ) {
219- animation-delay : 0.15s ;
220- }
221-
222- .loading-bar : nth-child (3 ) {
223- animation-delay : 0.3s ;
208+ .loading-spinner ::before {
209+ content : '' ;
210+ position : absolute;
211+ top : 0 ;
212+ left : 0 ;
213+ right : 0 ;
214+ bottom : 0 ;
215+ border : 2px solid rgba (128 , 128 , 128 , 0.25 );
216+ border-radius : 50% ;
224217 }
225218
226- @keyframes loading-bounce {
227- 0% , 60% , 100% {
228- height : 6px ;
219+ .loading-spinner ::after {
220+ content : '' ;
221+ position : absolute;
222+ top : 0 ;
223+ left : 0 ;
224+ width : 100% ;
225+ height : 100% ;
226+ border-radius : 50% ;
227+ background : conic-gradient (
228+ from 0deg ,
229+ currentColor 0deg ,
230+ currentColor var (--progress , 0deg ),
231+ transparent var (--progress , 0deg ),
232+ transparent 360deg
233+ );
234+ mask : radial-gradient (circle, transparent 4px , black 4px );
235+ -webkit-mask : radial-gradient (circle, transparent 4px , black 4px );
236+ transition : --progress 0.2s ease-out;
237+ }
238+
239+ @keyframes spinner-rotate {
240+ 0% {
241+ transform : rotate (0deg );
229242 }
230- 30 % {
231- height : 14 px ;
243+ 100 % {
244+ transform : rotate ( 360 deg ) ;
232245 }
233246 }
234247
@@ -514,11 +527,7 @@ <h1>Null-Safe Clang Playground</h1>
514527 </ button >
515528
516529 < span id ="status " class ="status ">
517- < span class ="loading-spinner ">
518- < span class ="loading-bar "> </ span >
519- < span class ="loading-bar "> </ span >
520- < span class ="loading-bar "> </ span >
521- </ span >
530+ < span class ="loading-spinner "> </ span >
522531 Initializing compiler...
523532 </ span >
524533 </ div >
@@ -645,6 +654,29 @@ <h1>Null-Safe Clang Playground</h1>
645654 }
646655 } ) ;
647656
657+ // Real-time diagnostics on content change
658+ let diagnosticsTimeout ;
659+ editor . onDidChangeModelContent ( ( ) => {
660+ clearTimeout ( diagnosticsTimeout ) ;
661+ diagnosticsTimeout = setTimeout ( async ( ) => {
662+ if ( ! scriptUrl || isCompiling ) {
663+ console . log ( '[Diagnostics] Skipped - scriptUrl:' , ! ! scriptUrl , 'isCompiling:' , isCompiling ) ;
664+ return ;
665+ }
666+ try {
667+ const code = getEditorValue ( ) ;
668+ console . log ( '[Diagnostics] Running clang on code...' ) ;
669+ const result = await compileCode ( code , [ ] ) ;
670+ console . log ( '[Diagnostics] stderr:' , result . stderr ) ;
671+ const diagnostics = parseDiagnostics ( result . stderr ) ;
672+ console . log ( '[Diagnostics] Parsed markers:' , diagnostics ) ;
673+ monaco . editor . setModelMarkers ( editor . getModel ( ) , 'clang' , diagnostics ) ;
674+ } catch ( error ) {
675+ console . error ( '[Diagnostics] Error:' , error ) ;
676+ }
677+ } , 500 ) ;
678+ } ) ;
679+
648680 resolve ( ) ;
649681 } ) ;
650682 } ) ;
@@ -661,6 +693,31 @@ <h1>Null-Safe Clang Playground</h1>
661693 }
662694 }
663695
696+ // Parse clang diagnostics into Monaco markers
697+ function parseDiagnostics ( stderr ) {
698+ const markers = [ ] ;
699+ const lines = stderr . split ( '\n' ) ;
700+ const diagnosticRegex = / ^ i n p u t \. c : ( \d + ) : ( \d + ) : \s + ( e r r o r | w a r n i n g | n o t e ) : \s + ( .+ ) $ / ;
701+
702+ for ( const line of lines ) {
703+ const match = line . match ( diagnosticRegex ) ;
704+ if ( match ) {
705+ const [ , lineNum , colNum , severity , message ] = match ;
706+ markers . push ( {
707+ startLineNumber : parseInt ( lineNum ) ,
708+ startColumn : parseInt ( colNum ) ,
709+ endLineNumber : parseInt ( lineNum ) ,
710+ endColumn : parseInt ( colNum ) + 1 ,
711+ message : message ,
712+ severity : severity === 'error' ? monaco . MarkerSeverity . Error
713+ : severity === 'warning' ? monaco . MarkerSeverity . Warning
714+ : monaco . MarkerSeverity . Info
715+ } ) ;
716+ }
717+ }
718+ return markers ;
719+ }
720+
664721 let clangVersion = '' ;
665722 let isCompiling = false ;
666723 let isDragging = false ;
@@ -677,11 +734,33 @@ <h1>Null-Safe Clang Playground</h1>
677734 }
678735
679736 // Status update helper
680- function setStatus ( message , showSpinner = false ) {
737+ function setStatus ( message , showSpinner = false , progressPercent = null ) {
681738 if ( showSpinner ) {
682- status . innerHTML = `<span class="loading-spinner"><span class="loading-bar"></span><span class="loading-bar"></span><span class="loading-bar"></span></span> ${ message } ` ;
739+ const spinner = `<span class="loading-spinner" style="${ progressPercent !== null ? `--progress: ${ progressPercent } ` : '' } "></span>` ;
740+ status . innerHTML = `${ spinner } ${ message } ` ;
741+ if ( progressPercent !== null ) {
742+ status . title = `${ progressPercent } % downloaded` ;
743+ const spinnerEl = status . querySelector ( '.loading-spinner::after' ) ;
744+ if ( spinnerEl ) {
745+ const degrees = ( progressPercent / 100 ) * 360 ;
746+ spinnerEl . style . transform = `rotate(${ degrees } deg)` ;
747+ }
748+ } else {
749+ status . title = '' ;
750+ }
683751 } else {
684752 status . textContent = message ;
753+ status . title = '' ;
754+ }
755+ }
756+
757+ // Update progress spinner
758+ function updateProgress ( percent ) {
759+ const spinner = status . querySelector ( '.loading-spinner' ) ;
760+ if ( spinner ) {
761+ const degrees = ( percent / 100 ) * 360 ;
762+ spinner . style . setProperty ( '--progress' , `${ degrees } deg` ) ;
763+ status . title = `${ Math . round ( percent ) } % downloaded` ;
685764 }
686765 }
687766
@@ -752,7 +831,7 @@ <h1>Null-Safe Clang Playground</h1>
752831 }
753832
754833 async function initCompiler ( ) {
755- setStatus ( 'Loading compiler (64MB)...' , true ) ;
834+ setStatus ( 'Loading compiler (64MB)...' , true , 0 ) ;
756835 status . className = 'status' ;
757836 loadingBar . classList . add ( 'active' ) ;
758837
@@ -767,10 +846,37 @@ <h1>Null-Safe Clang Playground</h1>
767846 ? './clang.wasm'
768847 : 'https://github.com/cs01/llvm-project/releases/latest/download/clang-nullsafe.wasm' ;
769848
770- // Pre-load the WASM binary ONCE
849+ // Pre-load the WASM binary ONCE with progress tracking
771850 console . log ( 'Downloading WASM binary...' ) ;
772851 const wasmResponse = await fetch ( wasmUrl ) ;
773- wasmBinary = await wasmResponse . arrayBuffer ( ) ;
852+ const contentLength = wasmResponse . headers . get ( 'content-length' ) ;
853+ const total = parseInt ( contentLength , 10 ) ;
854+
855+ let loaded = 0 ;
856+ const reader = wasmResponse . body . getReader ( ) ;
857+ const chunks = [ ] ;
858+
859+ while ( true ) {
860+ const { done, value } = await reader . read ( ) ;
861+ if ( done ) break ;
862+
863+ chunks . push ( value ) ;
864+ loaded += value . length ;
865+
866+ if ( total ) {
867+ const percent = ( loaded / total ) * 100 ;
868+ updateProgress ( percent ) ;
869+ }
870+ }
871+
872+ // Combine chunks into final array buffer
873+ const chunksAll = new Uint8Array ( loaded ) ;
874+ let position = 0 ;
875+ for ( const chunk of chunks ) {
876+ chunksAll . set ( chunk , position ) ;
877+ position += chunk . length ;
878+ }
879+ wasmBinary = chunksAll . buffer ;
774880 console . log ( 'WASM binary loaded:' , wasmBinary . byteLength , 'bytes' ) ;
775881
776882 // Now test with a worker to get the version
@@ -785,6 +891,7 @@ <h1>Null-Safe Clang Playground</h1>
785891
786892 status . textContent = 'Ready to compile' ;
787893 status . className = 'status ready' ;
894+ status . title = '' ;
788895 compileBtn . disabled = false ;
789896 loadingBar . classList . remove ( 'active' ) ;
790897 showToast ( 'Compiler loaded successfully!' ) ;
@@ -794,6 +901,7 @@ <h1>Null-Safe Clang Playground</h1>
794901 } catch ( error ) {
795902 status . textContent = 'Failed to load compiler' ;
796903 status . className = 'status error' ;
904+ status . title = '' ;
797905 loadingBar . classList . remove ( 'active' ) ;
798906 showToast ( 'Failed to load compiler: ' + error . message , 4000 ) ;
799907 console . error ( 'Failed to load Clang WASM:' , error ) ;
0 commit comments