@@ -37,17 +37,48 @@ <h1>🎮 CodeClash Trajectory Viewer</h1>
3737 </ header >
3838
3939 < main class ="main-content ">
40+ <!-- Current Folder Path -->
41+ < section class ="folder-path-section ">
42+ < div class ="folder-path-container ">
43+ < h3 > 📁 Current Folder:</ h3 >
44+ < div class ="path-display ">
45+ < code class ="folder-path "> {{ selected_folder_path }}</ code >
46+ < button class ="copy-path-btn " data-path ="{{ selected_folder_path }} " title ="Copy folder path ">
47+ 📋 Copy path
48+ </ button >
49+ </ div >
50+ </ div >
51+ </ section >
52+
4053 <!-- Overall Results Section -->
4154 < section class ="results-section ">
4255 < h2 > 📊 Game Results</ h2 >
4356
44- < div class ="metadata-display ">
45- < div id ="metadata-jsoneditor " class ="json-viewer "> </ div >
46- </ div >
57+ <!-- Metadata Foldout -->
58+ < details class ="foldout ">
59+ < summary >
60+ 📋 Metadata
61+ {% if metadata.metadata_file_path %}
62+ < button class ="copy-path-btn-small " data-path ="{{ metadata.metadata_file_path }} " title ="Copy metadata file path ">
63+ 📋 Copy path
64+ </ button >
65+ {% endif %}
66+ </ summary >
67+ < div class ="metadata-display ">
68+ < div id ="metadata-jsoneditor " class ="json-viewer "> </ div >
69+ </ div >
70+ </ details >
4771
4872 <!-- Main Log Foldout -->
4973 < details class ="foldout ">
50- < summary > 📝 Tournament Log</ summary >
74+ < summary >
75+ 📝 Tournament Log
76+ {% if metadata.main_log_path %}
77+ < button class ="copy-path-btn-small " data-path ="{{ metadata.main_log_path }} " title ="Copy file path ">
78+ 📋 Copy path
79+ </ button >
80+ {% endif %}
81+ </ summary >
5182 < div class ="log-content ">
5283 < pre > < code > {{ metadata.main_log }}</ code > </ pre >
5384 </ div >
@@ -77,7 +108,12 @@ <h2>🎯 Game Rounds</h2>
77108 < div class ="log-content ">
78109 {% for sim_log in round_data.sim_logs %}
79110 < details class ="foldout sim-foldout ">
80- < summary > {{ sim_log.filename }}</ summary >
111+ < summary >
112+ {{ sim_log.filename }}
113+ < button class ="copy-path-btn-small " data-path ="{{ sim_log.full_path }} " title ="Copy file path ">
114+ 📋 Copy path
115+ </ button >
116+ </ summary >
81117 < div class ="log-content ">
82118 < pre > < code > {{ sim_log.content }}</ code > </ pre >
83119 </ div >
@@ -92,7 +128,9 @@ <h2>🎯 Game Rounds</h2>
92128
93129 <!-- Trajectory Container with Overview and Messages -->
94130 < div class ="trajectory-header ">
95- < h3 > 🤖 Player {{ trajectory.player_id }} Round {{ round_num }}</ h3 >
131+ < h3 >
132+ 🤖 Player {{ trajectory.player_id }} Round {{ round_num }}
133+ </ h3 >
96134 < div class ="trajectory-stats ">
97135 < div class ="stat-item ">
98136 < span class ="stat-label "> API Calls:</ span >
@@ -112,7 +150,14 @@ <h3>🤖 Player {{ trajectory.player_id }} Round {{ round_num }}</h3>
112150
113151 <!-- Messages Foldout inside the same container -->
114152 < details class ="trajectory-messages-foldout ">
115- < summary > 💬 Messages ({{ trajectory.messages|length }} total)</ summary >
153+ < summary >
154+ 💬 Trajectory ({{ trajectory.messages|length }} total)
155+ {% if trajectory.trajectory_file_path %}
156+ < button class ="copy-path-btn-small " data-path ="{{ trajectory.trajectory_file_path }} " title ="Copy trajectory file path ">
157+ 📋 Copy path
158+ </ button >
159+ {% endif %}
160+ </ summary >
116161 < div class ="trajectory-content ">
117162 < div class ="messages-container ">
118163 {% for message in trajectory.messages %}
@@ -318,8 +363,100 @@ <h3>🤖 Player {{ trajectory.player_id }} Round {{ round_num }}</h3>
318363 < script src ="https://unpkg.com/jsoneditor@latest/dist/jsoneditor.min.js "> </ script >
319364 < script src ="{{ url_for('static', filename='js/app.js') }} "> </ script >
320365 < script >
366+ // Copy to clipboard function
367+ function copyToClipboard ( text , button ) {
368+ function showSuccessMessage ( ) {
369+ if ( button ) {
370+ const originalText = button . textContent ;
371+ const originalColor = button . style . color ;
372+ button . textContent = '✓ Copied' ;
373+ button . style . color = 'green' ;
374+ setTimeout ( ( ) => {
375+ button . textContent = originalText ;
376+ button . style . color = originalColor ;
377+ } , 1500 ) ;
378+ }
379+ }
380+
381+ // Check if modern clipboard API is available
382+ if ( navigator . clipboard && navigator . clipboard . writeText ) {
383+ navigator . clipboard . writeText ( text ) . then ( function ( ) {
384+ showSuccessMessage ( ) ;
385+ console . log ( 'Copied to clipboard:' , text ) ;
386+ } ) . catch ( function ( err ) {
387+ console . error ( 'Failed to copy text with clipboard API: ' , err ) ;
388+ // Fall back to legacy method
389+ fallbackCopy ( ) ;
390+ } ) ;
391+ } else {
392+ // Use fallback method directly
393+ fallbackCopy ( ) ;
394+ }
395+
396+ function fallbackCopy ( ) {
397+ try {
398+ const textArea = document . createElement ( 'textarea' ) ;
399+ textArea . value = text ;
400+ textArea . style . position = 'fixed' ;
401+ textArea . style . left = '-9999px' ;
402+ textArea . style . top = '-9999px' ;
403+ document . body . appendChild ( textArea ) ;
404+ textArea . focus ( ) ;
405+ textArea . select ( ) ;
406+
407+ const successful = document . execCommand ( 'copy' ) ;
408+ document . body . removeChild ( textArea ) ;
409+
410+ if ( successful ) {
411+ showSuccessMessage ( ) ;
412+ console . log ( 'Copied to clipboard (fallback):' , text ) ;
413+ } else {
414+ console . error ( 'Failed to copy text with fallback method' ) ;
415+ if ( button ) {
416+ button . textContent = '✗ Failed' ;
417+ button . style . color = 'red' ;
418+ setTimeout ( ( ) => {
419+ button . textContent = button . getAttribute ( 'title' ) || 'Copy' ;
420+ button . style . color = '' ;
421+ } , 1500 ) ;
422+ }
423+ }
424+ } catch ( err ) {
425+ console . error ( 'Fallback copy failed:' , err ) ;
426+ if ( button ) {
427+ button . textContent = '✗ Failed' ;
428+ button . style . color = 'red' ;
429+ setTimeout ( ( ) => {
430+ button . textContent = button . getAttribute ( 'title' ) || 'Copy' ;
431+ button . style . color = '' ;
432+ } , 1500 ) ;
433+ }
434+ }
435+ }
436+ }
437+
438+ // Setup copy button event listeners
439+ function setupCopyButtons ( ) {
440+ // Add event listeners to all copy path buttons
441+ document . querySelectorAll ( '.copy-path-btn, .copy-path-btn-small' ) . forEach ( button => {
442+ button . addEventListener ( 'click' , function ( e ) {
443+ e . preventDefault ( ) ;
444+ e . stopPropagation ( ) ;
445+
446+ const path = this . getAttribute ( 'data-path' ) ;
447+ if ( path ) {
448+ copyToClipboard ( path , this ) ;
449+ } else {
450+ console . error ( 'No path found on button:' , this ) ;
451+ }
452+ } ) ;
453+ } ) ;
454+ }
455+
321456 // Initialize JSON editors when page loads
322457 document . addEventListener ( 'DOMContentLoaded' , function ( ) {
458+ // Setup copy buttons
459+ setupCopyButtons ( ) ;
323460 // Initialize metadata JSON editor
324461 const metadataEditor = new JSONEditor ( document . getElementById ( 'metadata-jsoneditor' ) , {
325462 mode : 'view' ,
0 commit comments