@@ -220,6 +220,25 @@ class SandboxUI {
220220
221221 window . addEventListener ( 'scroll' , toggleStickyNav ) ;
222222 toggleStickyNav ( ) ; // Check initial position
223+
224+ // Track when user views the features list
225+ const featuresList = document . querySelector ( '.features-list' ) ;
226+ if ( featuresList && typeof IntersectionObserver !== 'undefined' ) {
227+ let hasTrackedFeaturesView = false ;
228+ const featuresObserver = new IntersectionObserver ( ( entries ) => {
229+ entries . forEach ( entry => {
230+ if ( entry . isIntersecting && ! hasTrackedFeaturesView && typeof gtag === 'function' ) {
231+ gtag ( 'event' , 'view_item' , {
232+ content_type : 'section' ,
233+ item_id : 'features_list'
234+ } ) ;
235+ hasTrackedFeaturesView = true ;
236+ featuresObserver . disconnect ( ) ;
237+ }
238+ } ) ;
239+ } , { threshold : 0.3 } ) ;
240+ featuresObserver . observe ( featuresList ) ;
241+ }
223242 }
224243
225244 renderExamples ( ) {
@@ -275,6 +294,14 @@ class SandboxUI {
275294 docsBtn . innerHTML = '📖 Full Documentation' ;
276295 docsBtn . title = 'View full documentation on GitHub' ;
277296 docsBtn . onclick = ( ) => {
297+ // Track Documentation button click
298+ if ( typeof gtag === 'function' ) {
299+ gtag ( 'event' , 'button_click' , {
300+ button_type : 'feature_documentation' ,
301+ button_id : featureKey ,
302+ feature_title : feature . title
303+ } ) ;
304+ }
278305 const docUrl = `https://github.com/miller-28/luminara/tree/master/docs/features/${ docFilename } .md` ;
279306 window . open ( docUrl , '_blank' , 'noopener,noreferrer' ) ;
280307 } ;
@@ -283,7 +310,17 @@ class SandboxUI {
283310 const runFeatureBtn = document . createElement ( 'button' ) ;
284311 runFeatureBtn . className = 'btn btn-small' ;
285312 runFeatureBtn . textContent = `▶ Run All ${ feature . examples . length } ` ;
286- runFeatureBtn . onclick = ( ) => this . handleRunFeature ( featureKey ) ;
313+ runFeatureBtn . onclick = ( ) => {
314+ // Track feature group run button click
315+ if ( typeof gtag === 'function' ) {
316+ gtag ( 'event' , 'button_click' , {
317+ button_type : 'feature_group_run' ,
318+ button_id : featureKey ,
319+ feature_title : feature . title
320+ } ) ;
321+ }
322+ this . handleRunFeature ( featureKey ) ;
323+ } ;
287324 headerButtonContainer . appendChild ( runFeatureBtn ) ;
288325
289326 header . appendChild ( title ) ;
@@ -330,7 +367,17 @@ class SandboxUI {
330367 stopBtn . className = 'btn btn-small btn-stop' ;
331368 stopBtn . textContent = '⏹ Stop' ;
332369 stopBtn . style . display = 'none' ;
333- stopBtn . onclick = ( ) => this . handleStopTest ( example . id ) ;
370+ stopBtn . onclick = ( ) => {
371+ // Track Stop button click
372+ if ( typeof gtag === 'function' ) {
373+ gtag ( 'event' , 'button_click' , {
374+ button_type : 'example_stop' ,
375+ button_id : example . id ,
376+ example_title : example . title
377+ } ) ;
378+ }
379+ this . handleStopTest ( example . id ) ;
380+ } ;
334381 this . stopButtonElements . set ( example . id , stopBtn ) ;
335382
336383 buttonContainer . appendChild ( runBtn ) ;
@@ -341,12 +388,19 @@ class SandboxUI {
341388 const codeBtn = document . createElement ( 'button' ) ;
342389 codeBtn . className = 'example-code-btn' ;
343390 codeBtn . innerHTML = '📄 Code' ;
344- codeBtn . onclick = ( ) => this . handleShowCode ( example . title , example . code ) ;
391+ codeBtn . onclick = ( ) => {
392+ // Track Code button click
393+ if ( typeof gtag === 'function' ) {
394+ gtag ( 'event' , 'button_click' , {
395+ button_type : 'example_code' ,
396+ button_id : example . id ,
397+ example_title : example . title
398+ } ) ;
399+ }
400+ this . handleShowCode ( example . title , example . code ) ;
401+ } ;
345402 buttonContainer . appendChild ( codeBtn ) ;
346403 }
347- stopBtn . style . display = 'none' ;
348- stopBtn . onclick = ( ) => this . handleStopTest ( example . id ) ;
349- this . stopButtonElements . set ( example . id , stopBtn ) ;
350404
351405 cardHeader . appendChild ( titleDiv ) ;
352406 cardHeader . appendChild ( buttonContainer ) ;
@@ -410,6 +464,15 @@ class SandboxUI {
410464 }
411465 } ;
412466
467+ // Track example run in Google Analytics
468+ if ( typeof gtag === 'function' ) {
469+ gtag ( 'event' , 'button_click' , {
470+ button_type : 'example_run' ,
471+ button_id : example . id ,
472+ example_title : example . title
473+ } ) ;
474+ }
475+
413476 // Run example
414477 const result = await this . examplesController . runExample ( testId , updateOutput , onStatusChange ) ;
415478
@@ -441,10 +504,24 @@ class SandboxUI {
441504 }
442505
443506 async handleRunAll ( ) {
507+ // Track Run All button click
508+ if ( typeof gtag === 'function' ) {
509+ gtag ( 'event' , 'button_click' , {
510+ button_type : 'global_control' ,
511+ button_id : 'run_all_examples'
512+ } ) ;
513+ }
444514 await this . examplesController . runAll ( ( exampleId ) => this . handleRunTest ( exampleId ) ) ;
445515 }
446516
447517 handleClearAll ( ) {
518+ // Track Clear All button click
519+ if ( typeof gtag === 'function' ) {
520+ gtag ( 'event' , 'button_click' , {
521+ button_type : 'global_control' ,
522+ button_id : 'clear_all'
523+ } ) ;
524+ }
448525
449526 // Stop all running examples
450527 this . examplesController . stopAll ( ) ;
@@ -468,6 +545,14 @@ class SandboxUI {
468545 }
469546
470547 handleVerboseToggle ( isVerbose ) {
548+ // Track Verbose toggle
549+ if ( typeof gtag === 'function' ) {
550+ gtag ( 'event' , 'button_click' , {
551+ button_type : 'global_control' ,
552+ button_id : 'verbose_toggle' ,
553+ button_state : isVerbose ? 'enabled' : 'disabled'
554+ } ) ;
555+ }
471556
472557 // Save to localStorage
473558 this . saveVerboseState ( isVerbose ) ;
@@ -524,6 +609,33 @@ class ScrollToTop {
524609
525610}
526611
612+ // Footer Scroll Tracker
613+ class FooterScrollTracker {
614+ constructor ( ) {
615+ this . init ( ) ;
616+ }
617+
618+ init ( ) {
619+ const footer = document . querySelector ( '.footer' ) ;
620+ if ( footer && typeof IntersectionObserver !== 'undefined' ) {
621+ let hasTrackedFooter = false ;
622+ const footerObserver = new IntersectionObserver ( ( entries ) => {
623+ entries . forEach ( entry => {
624+ if ( entry . isIntersecting && ! hasTrackedFooter && typeof gtag === 'function' ) {
625+ gtag ( 'event' , 'view_item' , {
626+ content_type : 'section' ,
627+ item_id : 'footer'
628+ } ) ;
629+ hasTrackedFooter = true ;
630+ footerObserver . disconnect ( ) ;
631+ }
632+ } ) ;
633+ } , { threshold : 0.5 } ) ;
634+ footerObserver . observe ( footer ) ;
635+ }
636+ }
637+ }
638+
527639// Scroll to Bottom Button Manager
528640class ScrollToBottom {
529641
@@ -568,3 +680,6 @@ new ScrollToTop();
568680
569681// Initialize scroll to bottom button
570682new ScrollToBottom ( ) ;
683+
684+ // Initialize footer scroll tracker
685+ new FooterScrollTracker ( ) ;
0 commit comments