@@ -54,8 +54,36 @@ <h1 class="text-4xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-cl
5454 </ p >
5555 </ div >
5656
57- <!-- Community Submissions -->
58- < div id ="community-submissions " class ="grid gap-6 ">
57+ <!-- Community Leaderboard -->
58+ < div class ="bg-gray-800 rounded-lg border border-gray-700 mb-6 sm:mb-8 ">
59+ < div class ="w-full overflow-x-auto table-container ">
60+ < table class ="w-full table-auto rounded-lg overflow-hidden ">
61+ < thead class ="bg-gray-700 ">
62+ < tr >
63+ < th class ="px-2 py-3 text-center text-xs sm:text-sm font-medium text-gray-300 w-12 sm:w-16 "> Rank</ th >
64+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 min-w-32 max-w-48 "> Model</ th >
65+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 "> Provider</ th >
66+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 "> Avg Final Round</ th >
67+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden md:table-cell "> Ante</ th >
68+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden md:table-cell "> Win %</ th >
69+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden lg:table-cell "> Tool Call</ th >
70+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden lg:table-cell "> Tool Call Errors</ th >
71+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell "> Token (Total)</ th >
72+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell "> Input Token</ th >
73+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-20 sm:w-24 hidden xl:table-cell "> Output Token</ th >
74+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 hidden lg:table-cell "> Avg Time</ th >
75+ < th class ="px-2 py-3 text-left text-xs sm:text-sm font-medium text-gray-300 w-16 sm:w-20 "> Cost</ th >
76+ </ tr >
77+ </ thead >
78+ < tbody id ="community-leaderboard-body " class ="divide-y divide-gray-700 ">
79+ <!-- Data will be loaded here by JavaScript -->
80+ </ tbody >
81+ </ table >
82+ </ div >
83+ </ div >
84+
85+ <!-- Community Submissions (Legacy) -->
86+ < div id ="community-submissions " class ="grid gap-6 hidden ">
5987 <!-- Data will be loaded here by JavaScript -->
6088 </ div >
6189
@@ -114,89 +142,224 @@ <h4 class="font-semibold text-white mb-2">3. Submit PR</h4>
114142 </ div >
115143
116144 < script >
117- // Load community submissions
118- async function loadCommunitySubmissions ( ) {
145+ // Load community leaderboard data
146+ async function loadCommunityLeaderboard ( ) {
119147 try {
120- // Fetch official leaderboard data
121- const officialResponse = await fetch ( 'data/benchmarks/v0.2.0/default/leaderboard.json' ) ;
122- const officialData = await officialResponse . json ( ) ;
123- const officialSubmissions = officialData . entries . map ( entry => ( {
124- title : entry . model ,
125- author : 'Official Benchmark' ,
126- model : entry . model ,
127- score : entry . performance_score . toFixed ( 1 ) ,
128- votes : entry . total_runs ,
129- date : officialData . generated_at ,
130- description : `Official benchmark run with ${ entry . total_runs } runs. Reached ante ${ entry . avg_final_ante } on average.`
131- } ) ) ;
132-
133- // Fetch community submissions
134- // In a real application, you would dynamically list files in the directory.
135- // For this static site, we list them manually.
136- const communityFiles = [ 'data/strategies/default/community-submission-1.json' ] ;
137- const communitySubmissions = [ ] ;
138- for ( const file of communityFiles ) {
139- try {
140- const response = await fetch ( file ) ;
141- if ( response . ok ) {
142- const submission = await response . json ( ) ;
143- communitySubmissions . push ( submission ) ;
144- }
145- } catch ( error ) {
146- console . warn ( `Could not load community submission: ${ file } ` , error ) ;
148+ // Discover community runs
149+ const communityRuns = await discoverCommunityRuns ( ) ;
150+
151+ if ( communityRuns . length === 0 ) {
152+ showError ( 'No community runs found' ) ;
153+ return ;
154+ }
155+
156+ // Sort runs by ante reached (descending), then by score (descending)
157+ communityRuns . sort ( ( a , b ) => {
158+ if ( b . stats . ante_reached !== a . stats . ante_reached ) {
159+ return b . stats . ante_reached - a . stats . ante_reached ;
160+ }
161+ return b . stats . final_score - a . stats . final_score ;
162+ } ) ;
163+
164+ // Add rank
165+ communityRuns . forEach ( ( run , index ) => {
166+ run . rank = index + 1 ;
167+ } ) ;
168+
169+ renderCommunityLeaderboard ( communityRuns ) ;
170+
171+ } catch ( error ) {
172+ console . error ( 'Error loading community leaderboard:' , error ) ;
173+ showError ( 'Error loading community data' ) ;
174+ }
175+ }
176+
177+ // Discover community runs from the data/community directory
178+ async function discoverCommunityRuns ( ) {
179+ // Known run directories (in a real app, this would be dynamic)
180+ const knownRuns = [
181+ '20250826_180521_RedDeck_s1_OOOO155' ,
182+ '20250826_185432_RedDeck_s1_OOOO155'
183+ ] ;
184+
185+ const communityRuns = [ ] ;
186+
187+ for ( const runDir of knownRuns ) {
188+ try {
189+ const configResponse = await fetch ( `data/community/${ runDir } /config.json` ) ;
190+ const statsResponse = await fetch ( `data/community/${ runDir } /stats.json` ) ;
191+
192+ if ( configResponse . ok && statsResponse . ok ) {
193+ const config = await configResponse . json ( ) ;
194+ const stats = await statsResponse . json ( ) ;
195+
196+ communityRuns . push ( {
197+ runDir,
198+ config,
199+ stats,
200+ timestamp : runDir . split ( '_' ) [ 1 ] // Extract timestamp from directory name
201+ } ) ;
147202 }
203+ } catch ( error ) {
204+ console . warn ( `Failed to load run: ${ runDir } ` , error ) ;
148205 }
206+ }
207+
208+ return communityRuns ;
209+ }
149210
150- // Combine and sort
151- const allSubmissions = [ ...officialSubmissions , ...communitySubmissions ] ;
152- allSubmissions . sort ( ( a , b ) => parseFloat ( b . score ) - parseFloat ( a . score ) ) ;
153-
154- const container = document . getElementById ( 'community-submissions' ) ;
155- container . innerHTML = allSubmissions . map ( submission => `
156- <div class="bg-gray-800 rounded-lg border border-gray-700 shadow-lg hover:shadow-xl transition-all duration-300">
157- <div class="p-6">
158- <div class="flex items-start justify-between mb-4">
159- <div>
160- <h3 class="text-xl font-bold text-white mb-2">${ submission . title } </h3>
161- <div class="flex items-center gap-4 text-sm text-gray-400">
162- <span>by ${ submission . author } </span>
163- <span class="bg-gray-700 px-2 py-1 rounded text-xs">${ submission . model } </span>
164- <span>${ new Date ( submission . date ) . toLocaleDateString ( ) } </span>
165- </div>
166- </div>
167- <div class="text-right">
168- <div class="text-2xl font-bold bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">
169- ${ submission . score } Ante
170- </div>
171- <div class="text-sm text-gray-400">
172- ${ submission . votes } votes
211+ // Render community leaderboard
212+ function renderCommunityLeaderboard ( runs ) {
213+ const tbody = document . getElementById ( 'community-leaderboard-body' ) ;
214+ if ( ! tbody ) return ;
215+
216+ tbody . innerHTML = runs . map ( ( run , index ) => {
217+ const modelParts = run . config . model . split ( '/' ) ;
218+ const provider = modelParts [ 0 ] || '' ;
219+ const modelName = modelParts . slice ( 1 ) . join ( '/' ) || run . config . model ;
220+
221+ return `
222+ <tr class="hover:bg-gray-700 transition-colors cursor-pointer" onclick="toggleCommunityStats(${ index } )">
223+ <td class="px-2 py-3 text-center">
224+ <span class="text-xs sm:text-sm font-bold ${ getRankColor ( run . rank ) } ">#${ run . rank } </span>
225+ </td>
226+ <td class="px-2 py-3 min-w-32 max-w-48">
227+ <div class="overflow-hidden">
228+ <div class="font-medium text-white text-xs sm:text-sm break-words" title="${ modelName } ">${ modelName } </div>
229+ <div class="text-xs text-gray-400 mt-0.5">
230+ <span class="md:hidden">${ run . stats . ante_reached } ante • ${ run . stats . run_won ? 'Win' : 'Loss' } </span>
231+ <span class="hidden md:inline">${ run . config . deck } • ${ run . config . seed } </span>
232+ </div>
233+ </div>
234+ </td>
235+ <td class="px-2 py-3">
236+ <div class="text-xs sm:text-sm font-medium text-gray-300 capitalize">${ provider } </div>
237+ </td>
238+ <td class="px-2 py-3">
239+ <div class="text-xs sm:text-sm font-bold text-white">${ run . stats . final_round || 'N/A' } </div>
240+ <div class="text-xs text-gray-400">final round</div>
241+ </td>
242+ <td class="px-2 py-3 hidden md:table-cell">
243+ <div class="text-xs sm:text-sm font-bold text-white">${ run . stats . ante_reached } </div>
244+ <div class="text-xs text-gray-400">${ run . stats . run_won ? 'Win' : 'Loss' } </div>
245+ </td>
246+ <td class="px-2 py-3 hidden md:table-cell">
247+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . run_won ? '100' : '0' } %</div>
248+ <div class="text-xs text-gray-400">1 run</div>
249+ </td>
250+ <td class="px-2 py-3 hidden lg:table-cell">
251+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . tool_calls || 'N/A' } </div>
252+ <div class="text-xs text-gray-400">calls</div>
253+ </td>
254+ <td class="px-2 py-3 hidden lg:table-cell">
255+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . tool_call_errors || 0 } </div>
256+ <div class="text-xs text-gray-400">errors</div>
257+ </td>
258+ <td class="px-2 py-3 hidden xl:table-cell">
259+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . total_tokens ? ( run . stats . total_tokens / 1000 ) . toFixed ( 0 ) + 'k' : 'N/A' } </div>
260+ <div class="text-xs text-gray-400">total</div>
261+ </td>
262+ <td class="px-2 py-3 hidden xl:table-cell">
263+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . input_tokens ? ( run . stats . input_tokens / 1000 ) . toFixed ( 0 ) + 'k' : 'N/A' } </div>
264+ <div class="text-xs text-gray-400">input</div>
265+ </td>
266+ <td class="px-2 py-3 hidden xl:table-cell">
267+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . output_tokens ? ( run . stats . output_tokens / 1000 ) . toFixed ( 0 ) + 'k' : 'N/A' } </div>
268+ <div class="text-xs text-gray-400">output</div>
269+ </td>
270+ <td class="px-2 py-3 hidden lg:table-cell">
271+ <div class="font-medium text-white text-xs sm:text-sm">${ run . stats . avg_response_time ? run . stats . avg_response_time . toFixed ( 1 ) + 's' : 'N/A' } </div>
272+ <div class="text-xs text-gray-400">avg time</div>
273+ </td>
274+ <td class="px-2 py-3">
275+ <div class="text-white text-xs sm:text-sm font-semibold">${ run . stats . total_cost ? '$' + run . stats . total_cost . toFixed ( 3 ) : 'N/A' } </div>
276+ <div class="text-xs text-gray-400 lg:hidden">${ ( run . stats . run_duration_seconds / 60 ) . toFixed ( 1 ) } m</div>
277+ </td>
278+ </tr>
279+ <tr id="community-stats-row-${ index } " class="hidden">
280+ <td colspan="13" class="px-2 py-4 bg-gray-800">
281+ ${ renderCommunityDetailedStats ( run , index ) }
282+ </td>
283+ </tr>
284+ ` ;
285+ } ) . join ( '' ) ;
286+ }
287+
288+ // Render detailed stats for community run
289+ function renderCommunityDetailedStats ( run , index ) {
290+ return `
291+ <div class="px-2 sm:px-4 w-full overflow-hidden">
292+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
293+ <div class="bg-gray-700 rounded-lg p-3 sm:p-4 min-w-0">
294+ <h4 class="font-semibold text-white mb-1.5 sm:mb-2 text-sm sm:text-base lg:text-base">Configuration</h4>
295+ <div class="space-y-1.5 sm:space-y-2 text-gray-300 text-sm sm:text-sm lg:text-sm">
296+ ${ Object . entries ( run . config ) . map ( ( [ key , value ] ) => `
297+ <div class="flex justify-between items-center min-w-0">
298+ <span class="truncate mr-2 capitalize">${ key . replace ( / _ / g, ' ' ) } :</span>
299+ <span class="text-white font-medium flex-shrink-0">${ typeof value === 'object' ? JSON . stringify ( value ) : value } </span>
173300 </div>
174- </div>
301+ ` ) . join ( '' ) }
175302 </div>
176- <p class="text-gray-300 mb-4">${ submission . description } </p>
177- <div class="flex items-center gap-2">
178- <button class="border border-gray-600 text-gray-300 hover:border-gray-500 hover:text-white px-3 py-1 rounded text-sm transition-colors">
179- View Details
180- </button>
181- <button class="text-gray-400 hover:text-green-400 px-3 py-1 text-sm transition-colors">
182- <i class="fas fa-arrow-up"></i> Upvote
183- </button>
184- <button class="text-gray-400 hover:text-blue-400 px-3 py-1 text-sm transition-colors">
185- Try Strategy
186- </button>
303+ </div>
304+ <div class="bg-gray-700 rounded-lg p-3 sm:p-4 min-w-0">
305+ <h4 class="font-semibold text-white mb-1.5 sm:mb-2 text-sm sm:text-base lg:text-base">Statistics</h4>
306+ <div class="space-y-1.5 sm:space-y-2 text-gray-300 text-sm sm:text-sm lg:text-sm">
307+ ${ Object . entries ( run . stats ) . slice ( 0 , 15 ) . map ( ( [ key , value ] ) => `
308+ <div class="flex justify-between items-center min-w-0">
309+ <span class="truncate mr-2 capitalize">${ key . replace ( / _ / g, ' ' ) } :</span>
310+ <span class="text-white font-medium flex-shrink-0">${
311+ typeof value === 'number' && key . includes ( 'time' ) ? ( value / 60 ) . toFixed ( 1 ) + 'm' :
312+ typeof value === 'number' && key . includes ( 'rate' ) ? ( value * 100 ) . toFixed ( 1 ) + '%' :
313+ typeof value === 'object' ? JSON . stringify ( value ) : value
314+ } </span>
315+ </div>
316+ ` ) . join ( '' ) }
187317 </div>
188318 </div>
189319 </div>
190- ` ) . join ( '' ) ;
191- } catch ( error ) {
192- console . error ( 'Error loading community submissions:' , error ) ;
193- const container = document . getElementById ( 'community-submissions' ) ;
194- container . innerHTML = '<p class="text-red-500">Error loading submissions. Please check the data files.</p>' ;
320+ </div>
321+ ` ;
322+ }
323+
324+ // Calculate a simple performance score for community runs
325+ function calculatePerformanceScore ( stats ) {
326+ // Simple scoring: ante_reached * 10 + money efficiency
327+ return stats . ante_reached * 10 + ( stats . final_money * 0.1 ) ;
328+ }
329+
330+ // Show error message
331+ function showError ( message ) {
332+ const tbody = document . getElementById ( 'community-leaderboard-body' ) ;
333+ if ( tbody ) {
334+ tbody . innerHTML = `<tr><td colspan="13" class="px-2 py-4 text-center text-gray-400 text-sm">${ message } </td></tr>` ;
335+ }
336+ }
337+
338+ // Utility function for rank colors
339+ function getRankColor ( rank ) {
340+ switch ( rank ) {
341+ case 1 : return 'text-yellow-400' ; // Gold
342+ case 2 : return 'text-gray-300' ; // Silver
343+ case 3 : return 'text-orange-400' ; // Bronze
344+ default : return 'text-gray-400' ;
345+ }
346+ }
347+
348+ // Toggle statistics display for a community run
349+ function toggleCommunityStats ( index ) {
350+ const statsRow = document . getElementById ( `community-stats-row-${ index } ` ) ;
351+
352+ if ( statsRow ) {
353+ if ( statsRow . classList . contains ( 'hidden' ) ) {
354+ statsRow . classList . remove ( 'hidden' ) ;
355+ } else {
356+ statsRow . classList . add ( 'hidden' ) ;
357+ }
195358 }
196359 }
197360
198361 // Load data when page loads
199- document . addEventListener ( 'DOMContentLoaded' , loadCommunitySubmissions ) ;
362+ document . addEventListener ( 'DOMContentLoaded' , loadCommunityLeaderboard ) ;
200363 </ script >
201364</ body >
202365</ html >
0 commit comments