@@ -3,7 +3,7 @@ console.log('Script loaded');
33// State
44const state = {
55 platforms : [ ] ,
6- folderCache : { } ,
6+ treeData : { } ,
77 currentView : 'loading' ,
88 currentPath : [ ] ,
99 currentItem : null
@@ -15,101 +15,106 @@ function getRepoPath() {
1515 return parts [ 1 ] && parts [ 2 ] ? `${ parts [ 1 ] } /${ parts [ 2 ] } ` : 'Vic-Nas/PythonSolutions' ;
1616}
1717
18- // Fetch folder contents with in-memory caching
19- async function fetchFolderContents ( path ) {
20- // Return cached data if available
21- if ( state . folderCache [ path ] ) {
22- return state . folderCache [ path ] ;
23- }
18+ // Load platforms from pre-generated data.json (no API calls!)
19+ async function loadPlatforms ( ) {
20+ console . log ( 'Loading platforms from data.json...' ) ;
2421
2522 try {
26- const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ path } ` ) ;
27-
23+ const res = await fetch ( 'data.json' ) ;
2824 if ( ! res . ok ) {
29- console . error ( ` Failed to fetch ${ path } : ${ res . status } ${ res . statusText } ` ) ;
30- return null ;
25+ console . error ( ' Failed to load data.json' ) ;
26+ return ;
3127 }
3228
33- const items = await res . json ( ) ;
34- state . folderCache [ path ] = items ;
35- return items ;
29+ const data = await res . json ( ) ;
30+ state . platforms = data . platforms || [ ] ;
31+
32+ // Build tree lookup for fast navigation
33+ state . platforms . forEach ( platform => {
34+ state . treeData [ platform . name ] = platform . tree || [ ] ;
35+ } ) ;
36+
37+ console . log ( 'Loaded platforms:' , state . platforms . map ( p => p . name ) ) ;
3638 } catch ( err ) {
37- console . error ( `Error fetching ${ path } :` , err ) ;
38- return null ;
39+ console . error ( 'Error loading data.json:' , err ) ;
3940 }
4041}
4142
42- // Check if folder contains files (is a "problem")
43- function hasFiles ( items ) {
44- return items . some ( item =>
45- item . type === 'file' &&
46- ( item . name . endsWith ( '.vn.py' ) ||
47- item . name . endsWith ( '.vn.png' ) ||
48- item . name . endsWith ( '.html' ) )
49- ) ;
43+ // Find node in tree by path
44+ function findInTree ( tree , pathParts , startIndex = 0 ) {
45+ if ( startIndex >= pathParts . length ) return null ;
46+
47+ const currentPart = pathParts [ startIndex ] ;
48+ const node = tree . find ( n => n . name === currentPart ) ;
49+
50+ if ( ! node ) return null ;
51+ if ( startIndex === pathParts . length - 1 ) return node ;
52+ if ( ! node . children ) return null ;
53+
54+ return findInTree ( node . children , pathParts , startIndex + 1 ) ;
5055}
5156
52- // Load all platforms
53- async function loadPlatforms ( ) {
54- console . log ( 'Loading platforms...' ) ;
55- const rootItems = await fetchFolderContents ( '' ) ;
56- if ( ! rootItems ) {
57- console . error ( 'Failed to load root directory' ) ;
58- return ;
59- }
57+ // Get folder contents from tree data
58+ function getFolderFromTree ( pathParts ) {
59+ if ( pathParts . length === 0 ) return null ;
6060
61- const platformFolders = rootItems . filter ( item =>
62- item . type === 'dir' &&
63- ! [ 'utils' , '.git' ] . includes ( item . name ) &&
64- ! item . name . startsWith ( '.' )
65- ) ;
61+ const platformName = pathParts [ 0 ] ;
62+ const tree = state . treeData [ platformName ] ;
6663
67- state . platforms = [ ] ;
64+ if ( ! tree ) return null ;
65+ if ( pathParts . length === 1 ) return tree ;
6866
69- for ( const folder of platformFolders ) {
70- const platformData = {
71- name : folder . name ,
72- path : folder . name ,
73- image : null ,
74- count : 0
75- } ;
76-
77- // Check for platform.png
78- const contents = await fetchFolderContents ( folder . name ) ;
79- if ( contents ) {
80- const platformImg = contents . find ( f => f . name === 'platform.png' ) ;
81- if ( platformImg ) {
82- platformData . image = platformImg . download_url || `${ folder . name } /platform.png` ;
83- }
84-
85- // Count items recursively
86- platformData . count = await countItems ( folder . name ) ;
87- }
88-
89- state . platforms . push ( platformData ) ;
90- }
67+ const node = findInTree ( tree , pathParts , 1 ) ;
68+ return node ? ( node . children || [ ] ) : null ;
69+ }
70+
71+ // Check if path has files (is a problem)
72+ function hasFiles ( pathParts ) {
73+ if ( pathParts . length === 0 ) return false ;
9174
92- state . platforms . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ;
93- console . log ( 'Loaded platforms:' , state . platforms ) ;
75+ const platformName = pathParts [ 0 ] ;
76+ const tree = state . treeData [ platformName ] ;
77+
78+ if ( ! tree ) return false ;
79+ if ( pathParts . length === 1 ) return false ;
80+
81+ const node = findInTree ( tree , pathParts , 1 ) ;
82+ return node ? node . has_files : false ;
9483}
9584
96- // Recursively count problem items
97- async function countItems ( path ) {
98- const items = await fetchFolderContents ( path ) ;
85+ // Count items in path
86+ function countItemsFromTree ( pathParts ) {
87+ const items = getFolderFromTree ( pathParts ) ;
9988 if ( ! items ) return 0 ;
10089
101- if ( hasFiles ( items ) ) return 1 ;
102-
103- const subdirs = items . filter ( i => i . type === 'dir' ) ;
10490 let count = 0 ;
10591
106- for ( const dir of subdirs ) {
107- count += await countItems ( `${ path } /${ dir . name } ` ) ;
92+ function countRecursive ( nodes ) {
93+ for ( const node of nodes ) {
94+ if ( node . has_files ) {
95+ count ++ ;
96+ } else if ( node . children ) {
97+ countRecursive ( node . children ) ;
98+ }
99+ }
108100 }
109101
102+ countRecursive ( items ) ;
110103 return count ;
111104}
112105
106+ // Fetch file contents from GitHub (only when viewing a problem)
107+ async function fetchFileContent ( path ) {
108+ try {
109+ const res = await fetch ( `https://api.github.com/repos/${ getRepoPath ( ) } /contents/${ path } ` ) ;
110+ if ( ! res . ok ) return null ;
111+ return await res . json ( ) ;
112+ } catch ( err ) {
113+ console . error ( `Error fetching ${ path } :` , err ) ;
114+ return null ;
115+ }
116+ }
117+
113118// Parse hash to navigate
114119function parseHash ( ) {
115120 let hash = window . location . hash . slice ( 1 ) ;
@@ -178,21 +183,17 @@ function renderPlatforms() {
178183 } ) ;
179184}
180185
181- async function renderFolder ( ) {
186+ function renderFolder ( ) {
182187 const view = document . getElementById ( 'folder-view' ) ;
183188 view . style . display = 'block' ;
184189
185- const pathStr = state . currentPath . join ( '/' ) ;
186- const items = await fetchFolderContents ( pathStr ) ;
190+ const items = getFolderFromTree ( state . currentPath ) ;
187191
188192 if ( ! items ) {
189193 view . innerHTML = '<p>Error loading folder</p>' ;
190194 return ;
191195 }
192196
193- // Get subdirectories only
194- const subdirs = items . filter ( i => i . type === 'dir' ) ;
195-
196197 // Set title
197198 const titleParts = state . currentPath . map ( capitalize ) ;
198199 document . getElementById ( 'folder-title' ) . textContent = titleParts . join ( ' / ' ) ;
@@ -205,22 +206,22 @@ async function renderFolder() {
205206 const container = document . getElementById ( 'folder-cards' ) ;
206207 container . innerHTML = '' ;
207208
208- for ( const dir of subdirs ) {
209+ items . forEach ( item => {
209210 const card = document . createElement ( 'div' ) ;
210211 card . className = 'folder-card' ;
211212
212- const fullPath = [ ...state . currentPath , dir . name ] ;
213- const count = await countItems ( fullPath . join ( '/' ) ) ;
213+ const fullPath = [ ...state . currentPath , item . name ] ;
214+ const count = countItemsFromTree ( fullPath ) ;
214215
215216 card . innerHTML = `
216- <div class="folder-card-title">${ dir . name } </div>
217+ <div class="folder-card-title">${ item . name } </div>
217218 <div class="folder-card-path">${ fullPath . join ( '/' ) } </div>
218219 <div class="folder-card-count">${ count } item${ count !== 1 ? 's' : '' } </div>
219220 ` ;
220221
221222 card . onclick = ( ) => navigateTo ( fullPath ) ;
222223 container . appendChild ( card ) ;
223- }
224+ } ) ;
224225}
225226
226227async function renderProblem ( ) {
@@ -229,7 +230,7 @@ async function renderProblem() {
229230 view . innerHTML = '<div style="text-align: center; padding: 3rem;">Loading...</div>' ;
230231
231232 const pathStr = state . currentPath . join ( '/' ) ;
232- const items = await fetchFolderContents ( pathStr ) ;
233+ const items = await fetchFileContent ( pathStr ) ;
233234
234235 if ( ! items ) {
235236 view . innerHTML = '<p>Error loading problem</p>' ;
@@ -367,19 +368,11 @@ async function renderProblem() {
367368}
368369
369370// Navigation
370- async function navigateTo ( path ) {
371+ function navigateTo ( path ) {
371372 console . log ( 'Navigating to:' , path ) ;
372373
373- const pathStr = path . join ( '/' ) ;
374- const items = await fetchFolderContents ( pathStr ) ;
375-
376- if ( ! items ) {
377- console . error ( 'Could not load path:' , pathStr ) ;
378- return ;
379- }
380-
381- // Check if this folder has files (is a "problem")
382- if ( hasFiles ( items ) ) {
374+ // Check if this folder has files (is a problem)
375+ if ( hasFiles ( path ) ) {
383376 window . location . hash = `view/${ path . map ( encodeURIComponent ) . join ( '/' ) } ` ;
384377 } else {
385378 window . location . hash = path . map ( encodeURIComponent ) . join ( '/' ) ;
@@ -435,7 +428,7 @@ function getDefaultEmoji(platformName) {
435428}
436429
437430// Event listeners
438- window . addEventListener ( 'hashchange' , async ( ) => {
431+ window . addEventListener ( 'hashchange' , ( ) => {
439432 console . log ( 'Hash changed' ) ;
440433 const parsed = parseHash ( ) ;
441434 state . currentView = parsed . view ;
@@ -459,7 +452,7 @@ window.addEventListener('hashchange', async () => {
459452 // Make goBack globally available
460453 window . goBack = goBack ;
461454
462- // Load platforms
455+ // Load platforms from data.json
463456 await loadPlatforms ( ) ;
464457
465458 // Parse hash and render
0 commit comments