33< head >
44 < meta charset ="UTF-8 ">
55 < meta name ="viewport " content ="width=device-width, initial-scale=1.0 ">
6- < title > Python Solutions - LeetCode & Kattis </ title >
6+ < title > Python Solutions - LeetCode, Kattis & VicUtils </ title >
77 < link rel ="icon " href ="VN.ico ">
88 < style >
99 * {
8080
8181 .platforms {
8282 display : grid;
83- grid-template-columns : repeat (auto-fit, minmax (400 px , 1fr ));
83+ grid-template-columns : repeat (auto-fit, minmax (350 px , 1fr ));
8484 gap : 2rem ;
8585 margin-bottom : 3rem ;
8686 }
134134
135135 .leetcode-icon { background : # ffa116 ; }
136136 .kattis-icon { background : # 1e40af ; }
137+ .vicutils-icon { background : # 10b981 ; }
137138
138139 .platform-card p {
139140 color : # 666 ;
229230 }
230231
231232 .problem-item .html-page ::before {
232- background : # 10b981 ; /* Green for full HTML pages */
233+ background : # 10b981 ;
233234 }
234235
235236 .problem-item .png-page {
238239 }
239240
240241 .problem-item .png-page ::before {
241- background : # f59e0b ; /* Orange for PNG pages */
242+ background : # f59e0b ;
242243 }
243244
244245 .problem-item .code-only {
247248 }
248249
249250 .problem-item .code-only ::before {
250- background : # 6b7280 ; /* Gray for code-only pages */
251+ background : # 6b7280 ;
252+ }
253+
254+ .problem-item .util-script ::before {
255+ background : # 10b981 ;
251256 }
252257
253258 .problem-name {
310315 font-style : italic;
311316 }
312317
313- /* Generated page styles */
314318 .generated-page {
315319 background : white;
316320 min-height : 100vh ;
483487
484488 < header >
485489 < h1 > PythonSolutions</ h1 >
486- < p class ="subtitle "> LeetCode & Kattis Problem Solutions with Interactive Pages </ p >
490+ < p class ="subtitle "> LeetCode & Kattis Solutions + Utility Scripts </ p >
487491 </ header >
488492
489493 < div id ="loading " class ="loading ">
490494 < div class ="spinner "> </ div >
491- < span > Scanning repository for solutions ...</ span >
495+ < span > Scanning repository...</ span >
492496 </ div >
493497
494498 < div id ="main-view " class ="platforms " style ="display: none; ">
@@ -509,6 +513,15 @@ <h2>
509513 < p > Competitive programming solutions with detailed walkthroughs</ p >
510514 < div class ="problem-count " id ="kattis-count "> Loading...</ div >
511515 </ div >
516+
517+ < div class ="platform-card " onclick ="showProblems('vicutils') ">
518+ < h2 >
519+ < div class ="platform-icon vicutils-icon "> 🛠️</ div >
520+ VicUtils
521+ </ h2 >
522+ < p > Useful utility scripts and helper functions</ p >
523+ < div class ="problem-count " id ="vicutils-count "> Loading...</ div >
524+ </ div >
512525 </ div >
513526
514527 < div id ="problems-view " class ="problems-view ">
@@ -525,11 +538,12 @@ <h2 id="problems-title"></h2>
525538 < script >
526539 let problemsData = {
527540 leetcode : [ ] ,
528- kattis : [ ]
541+ kattis : [ ] ,
542+ vicutils : [ ]
529543 } ;
530544
531545 async function scanForProblems ( ) {
532- const platforms = [ 'leetcode' , 'kattis' ] ;
546+ const platforms = [ 'leetcode' , 'kattis' , 'vicutils' ] ;
533547
534548 for ( const platform of platforms ) {
535549 try {
@@ -540,30 +554,49 @@ <h2 id="problems-title"></h2>
540554 continue ;
541555 }
542556
543- const directories = await response . json ( ) ;
557+ const items = await response . json ( ) ;
544558 const problems = [ ] ;
545559
546- for ( const dir of directories ) {
547- if ( dir . type === 'dir' ) {
560+ if ( platform === 'vicutils' ) {
561+ // For vicutils, treat .py files directly (not directories)
562+ for ( const item of items ) {
563+ if ( item . type === 'file' && item . name . endsWith ( '.py' ) && item . name !== '__init__.py' ) {
564+ problems . push ( {
565+ name : item . name ,
566+ displayName : item . name . replace ( '.py' , '' ) . replace ( / _ / g, ' ' ) ,
567+ type : 'util-script' ,
568+ hasPage : true ,
569+ hasHtml : false ,
570+ hasPng : false ,
571+ hasPy : true ,
572+ mainPyFile : item ,
573+ htmlFiles : [ ] ,
574+ pngFiles : [ ] ,
575+ pyFiles : [ item ]
576+ } ) ;
577+ }
578+ }
579+ } else {
580+ // For leetcode and kattis, process directories
581+ const directories = items . filter ( item => item . type === 'dir' ) ;
582+
583+ for ( const dir of directories ) {
548584 try {
549585 const dirResponse = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ platform } /${ dir . name } ` ) ;
550586 if ( dirResponse . ok ) {
551587 const files = await dirResponse . json ( ) ;
552588
553- // Look for any .html file that matches the directory name
554589 const hasHtml = files . some ( file =>
555590 file . name . endsWith ( '.html' ) &&
556591 file . name . toLowerCase ( ) . includes ( dir . name . toLowerCase ( ) . replace ( / \s + / g, '' ) )
557592 ) ;
558593
559- // Look for any .vn.png files (not tied to directory name)
560594 const pngFiles = files . filter ( file =>
561595 file . name . endsWith ( '.vn.png' ) ||
562596 file . name . match ( / \. v n \. \d + \. p n g $ / )
563597 ) ;
564598 const hasPng = pngFiles . length > 0 ;
565599
566- // Look for any .vn.py files
567600 const pyFiles = files . filter ( file => file . name . endsWith ( '.vn.py' ) ) ;
568601 const hasPy = pyFiles . length > 0 ;
569602
@@ -579,12 +612,11 @@ <h2 id="problems-title"></h2>
579612 hasPage = true ;
580613 } else if ( hasPy ) {
581614 type = 'code-only' ;
582- hasPage = true ; // We'll generate a page for code-only too
615+ hasPage = true ;
583616 }
584617
585- // Find the main .vn.py file (there should typically be one)
586618 if ( pyFiles . length > 0 ) {
587- mainPyFile = pyFiles [ 0 ] ; // Take the first .vn.py file
619+ mainPyFile = pyFiles [ 0 ] ;
588620 }
589621
590622 if ( hasHtml || hasPng || hasPy ) {
@@ -599,7 +631,6 @@ <h2 id="problems-title"></h2>
599631 mainPyFile : mainPyFile ,
600632 htmlFiles : files . filter ( file => file . name . endsWith ( '.html' ) ) ,
601633 pngFiles : pngFiles . sort ( ( a , b ) => {
602- // Sort PNG files by number if they have one
603634 const aNum = a . name . match ( / \. ( \d + ) \. p n g $ / ) ? parseInt ( a . name . match ( / \. ( \d + ) \. p n g $ / ) [ 1 ] ) : 0 ;
604635 const bNum = b . name . match ( / \. ( \d + ) \. p n g $ / ) ? parseInt ( b . name . match ( / \. ( \d + ) \. p n g $ / ) [ 1 ] ) : 0 ;
605636 return aNum - bNum ;
@@ -635,26 +666,27 @@ <h2 id="problems-title"></h2>
635666 document . getElementById ( 'loading' ) . style . display = 'none' ;
636667 document . getElementById ( 'main-view' ) . style . display = 'grid' ;
637668
638- for ( const platform of [ 'leetcode' , 'kattis' ] ) {
669+ for ( const platform of [ 'leetcode' , 'kattis' , 'vicutils' ] ) {
639670 const problems = problemsData [ platform ] ;
640671 const totalProblems = problems . length ;
672+ const label = platform === 'vicutils' ? 'scripts' : 'problems' ;
641673
642- document . getElementById ( `${ platform } -count` ) . textContent = `${ totalProblems } problems ` ;
674+ document . getElementById ( `${ platform } -count` ) . textContent = `${ totalProblems } ${ label } ` ;
643675 }
644676 }
645677
646678 function showProblems ( platform ) {
647679 const problems = problemsData [ platform ] ;
648- const title = platform . charAt ( 0 ) . toUpperCase ( ) + platform . slice ( 1 ) ;
680+ const title = platform === 'vicutils' ? 'VicUtils' : platform . charAt ( 0 ) . toUpperCase ( ) + platform . slice ( 1 ) ;
649681
650682 document . getElementById ( 'main-view' ) . style . display = 'none' ;
651683 document . getElementById ( 'problems-view' ) . style . display = 'block' ;
652- document . getElementById ( 'problems-title' ) . textContent = `${ title } Problems` ;
684+ document . getElementById ( 'problems-title' ) . textContent = platform === 'vicutils' ? 'Utility Scripts' : `${ title } Problems` ;
653685
654686 const container = document . getElementById ( 'problems-container' ) ;
655687
656688 if ( problems . length === 0 ) {
657- container . innerHTML = '<div class="no-problems">No problems found for this platform yet.</div>' ;
689+ container . innerHTML = '<div class="no-problems">No items found for this section yet.</div>' ;
658690 return ;
659691 }
660692
@@ -668,14 +700,15 @@ <h2 id="problems-title"></h2>
668700 let icon = '' ;
669701 if ( problem . type === 'html' ) icon = '🌐' ;
670702 else if ( problem . type === 'png' ) icon = '🖼️' ;
703+ else if ( problem . type === 'util-script' ) icon = '🛠️' ;
671704 else icon = '💻' ;
672705
673706 item . innerHTML = `
674707 <div class="problem-name">
675708 <span class="problem-type-icon">${ icon } </span>
676709 ${ problem . displayName }
677710 </div>
678- <div class="problem-path">${ platform } /${ problem . name } / </div>
711+ <div class="problem-path">${ platform } /${ problem . name } </div>
679712 ` ;
680713
681714 if ( problem . hasHtml && problem . htmlFiles . length > 0 ) {
@@ -697,35 +730,29 @@ <h2 id="problems-title"></h2>
697730 }
698731
699732 async function generatePage ( platform , problem ) {
700- // Instead of opening a new window, we'll create a URL hash-based system
701- // This allows for shareable links
702733 const problemId = encodeURIComponent ( `${ platform } /${ problem . name } ` ) ;
703734 const newUrl = `${ window . location . origin } ${ window . location . pathname } #view/${ problemId } ` ;
704735 window . location . href = newUrl ;
705736 }
706737
707738 async function renderProblemPage ( platform , problemName ) {
708- // Find the problem data
709739 const problem = problemsData [ platform ] ?. find ( p => p . name === problemName ) ;
710740 if ( ! problem ) {
711- document . body . innerHTML = '<div class="error">Problem not found</div>' ;
741+ document . body . innerHTML = '<div class="error">Item not found</div>' ;
712742 return ;
713743 }
714744
715- // Create a new page content
716745 let problemUrl = '' ;
717746 let pythonCode = '' ;
718747
719- // Try to get the problem URL from the .vn.py file
720748 if ( problem . hasPy && problem . mainPyFile ) {
721749 try {
722- const pyResponse = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ platform } /${ problem . name } /${ problem . mainPyFile . name } ` ) ;
750+ const pyResponse = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ platform === 'vicutils' ? platform : ` ${ platform } /${ problem . name } ` } /${ problem . mainPyFile . name } ` ) ;
723751 if ( pyResponse . ok ) {
724752 const pyData = await pyResponse . json ( ) ;
725753 const content = atob ( pyData . content ) ;
726754 pythonCode = content ;
727755
728- // Extract URL from first comment
729756 const urlMatch = content . match ( / ^ # .* ?( h t t p s : \/ \/ [ ^ \s ] + ) / m) ;
730757 if ( urlMatch ) {
731758 problemUrl = urlMatch [ 1 ] ;
@@ -736,12 +763,12 @@ <h2 id="problems-title"></h2>
736763 }
737764 }
738765
739- // Generate the page HTML
740766 const pageTitle = problem . displayName . replace ( / - / g, ' ' ) . replace ( / \b \w / g, l => l . toUpperCase ( ) ) ;
741- const platformTitle = platform . charAt ( 0 ) . toUpperCase ( ) + platform . slice ( 1 ) ;
767+ const platformTitle = platform === 'vicutils' ? 'VicUtils' : platform . charAt ( 0 ) . toUpperCase ( ) + platform . slice ( 1 ) ;
742768
743- // Get GitHub repo URL for the folder
744- const repoUrl = `https://github.com/${ getRepoPath ( ) } /tree/main/${ platform } /${ problem . name } ` ;
769+ const repoUrl = platform === 'vicutils'
770+ ? `https://github.com/${ getRepoPath ( ) } /blob/main/${ platform } /${ problem . name } `
771+ : `https://github.com/${ getRepoPath ( ) } /tree/main/${ platform } /${ problem . name } ` ;
745772
746773 let pageContent = `
747774 <div class="generated-page">
@@ -756,14 +783,11 @@ <h2 id="problems-title"></h2>
756783 </div>
757784
758785 <h1 class="page-title">${ pageTitle } </h1>
759- <p class="page-subtitle">${ platformTitle } Problem Solution</p>
786+ <p class="page-subtitle">${ platformTitle } ${ platform === 'vicutils' ? ' Script' : ' Problem Solution' } </p>
760787 ` ;
761788
762- // Add images if available
763789 if ( problem . hasPng && problem . pngFiles . length > 0 ) {
764- pageContent += `
765- <div class="images-grid">
766- ` ;
790+ pageContent += `<div class="images-grid">` ;
767791
768792 problem . pngFiles . forEach ( ( pngFile , index ) => {
769793 const imageUrl = `${ platform } /${ problem . name } /${ pngFile . name } ` ;
@@ -780,25 +804,20 @@ <h1 class="page-title">${pageTitle}</h1>
780804 pageContent += `</div>` ;
781805 }
782806
783- // Add code section (always show if available, even with images)
784807 if ( pythonCode ) {
785808 pageContent += `
786809 <div class="code-section">
787- <h3>Python Solution</h3>
810+ <h3>Python ${ platform === 'vicutils' ? 'Script' : ' Solution' } </h3>
788811 <div class="code-content">${ escapeHtml ( pythonCode ) } </div>
789812 </div>
790813 ` ;
791814 }
792815
793816 pageContent += `</div>` ;
794817
795- // Replace the body content with the generated page
796818 document . body . innerHTML = pageContent ;
797-
798- // Update page title and favicon
799819 document . title = `${ pageTitle } - ${ platformTitle } ` ;
800820
801- // Add favicon if not already present
802821 if ( ! document . querySelector ( 'link[rel="icon"]' ) ) {
803822 const link = document . createElement ( 'link' ) ;
804823 link . rel = 'icon' ;
@@ -813,7 +832,6 @@ <h3>Python Solution</h3>
813832 return div . innerHTML ;
814833 }
815834
816- // Add navigation enhancement to problem pages
817835 function addNavigationToCurrentPage ( ) {
818836 const path = window . location . pathname ;
819837 const isLeetCodeProblem = path . includes ( '/leetcode/' ) && path . endsWith ( '.html' ) ;
@@ -892,7 +910,7 @@ <h3>Python Solution</h3>
892910 // Handle platform navigation
893911 window . addEventListener ( 'load' , ( ) => {
894912 const simpleHash = window . location . hash . substring ( 1 ) ;
895- if ( simpleHash === 'leetcode' || simpleHash === 'kattis' ) {
913+ if ( simpleHash === 'leetcode' || simpleHash === 'kattis' || simpleHash === 'vicutils' ) {
896914 setTimeout ( ( ) => showProblems ( simpleHash ) , 1000 ) ;
897915 }
898916 } ) ;
0 commit comments