@@ -19,8 +19,14 @@ interface SpeakerNotes {
1919 transition ?: string ;
2020}
2121
22+ interface CodeExecutionStep {
23+ line : string ;
24+ highlightType ?: 'human' | 'prediction' | 'execution' | 'feedback' | 'summary' ;
25+ annotation ?: string ;
26+ }
27+
2228interface Slide {
23- type : 'title' | 'concept' | 'code' | 'comparison' | 'visual' | 'takeaway' | 'marketingReality' ;
29+ type : 'title' | 'concept' | 'code' | 'comparison' | 'visual' | 'takeaway' | 'marketingReality' | 'codeExecution' ;
2430 title : string ;
2531 subtitle ?: string ;
2632 content ?: string [ ] ;
@@ -32,6 +38,7 @@ interface Slide {
3238 right ?: { label : string ; content : string [ ] } ;
3339 metaphor ?: { label : string ; content : string [ ] } ;
3440 reality ?: { label : string ; content : string [ ] } ;
41+ steps ?: CodeExecutionStep [ ] ;
3542 speakerNotes ?: SpeakerNotes ;
3643}
3744
@@ -96,11 +103,13 @@ export default function RevealSlideshow({ presentation, onClose }: RevealSlidesh
96103
97104 deck . initialize ( ) . then ( ( ) => {
98105 revealRef . current = deck ;
106+ deck . on ( 'fragmentshown' , handleFragmentShown ) ;
99107 } ) ;
100108
101109 // Cleanup
102110 return ( ) => {
103111 if ( revealRef . current ) {
112+ revealRef . current . off ( 'fragmentshown' , handleFragmentShown ) ;
104113 revealRef . current . destroy ( ) ;
105114 revealRef . current = null ;
106115 }
@@ -119,6 +128,27 @@ export default function RevealSlideshow({ presentation, onClose }: RevealSlidesh
119128 return ( ) => window . removeEventListener ( 'keydown' , handleKeyDown ) ;
120129 } , [ onClose ] ) ;
121130
131+ const handleFragmentShown = ( event : { fragment : HTMLElement } ) => {
132+ const fragment = event . fragment ;
133+
134+ // Find any scrollable container parent
135+ const scrollContainer = fragment . closest ( `.${ styles . executionFlow } ` ) ||
136+ fragment . closest ( `.${ styles . comparisonLeft } ` ) ||
137+ fragment . closest ( `.${ styles . comparisonRight } ` ) ||
138+ fragment . closest ( `.${ styles . metaphorColumn } ` ) ||
139+ fragment . closest ( `.${ styles . realityColumn } ` ) ;
140+
141+ if ( scrollContainer ) {
142+ // Scroll minimum amount needed to bring fragment into view
143+ // Does nothing if fragment already visible
144+ fragment . scrollIntoView ( {
145+ behavior : 'smooth' ,
146+ block : 'nearest' , // Only scroll if not visible
147+ inline : 'nearest'
148+ } ) ;
149+ }
150+ } ;
151+
122152 const renderSlide = ( slide : Slide , index : number ) => {
123153 const key = `slide-${ index } ` ;
124154
@@ -259,6 +289,38 @@ export default function RevealSlideshow({ presentation, onClose }: RevealSlidesh
259289 </ section >
260290 ) ;
261291
292+ case 'codeExecution' :
293+ return (
294+ < section key = { key } data-notes = { formatSpeakerNotes ( slide . speakerNotes ) } >
295+ < h2 > { slide . title } </ h2 >
296+ { slide . steps && (
297+ < div className = { styles . executionFlow } >
298+ { slide . steps . map ( ( step , i ) => {
299+ const highlightClass = step . highlightType
300+ ? styles [ `execution${ step . highlightType . charAt ( 0 ) . toUpperCase ( ) } ${ step . highlightType . slice ( 1 ) } ` ]
301+ : '' ;
302+
303+ return (
304+ < div
305+ key = { i }
306+ className = { `${ styles . executionStep } ${ highlightClass } fragment` }
307+ data-fragment-index = { i }
308+ >
309+ < div className = { styles . stepLine } >
310+ { i > 0 && < span className = { styles . flowArrow } > ↓</ span > }
311+ < span className = { styles . stepText } > { step . line } </ span >
312+ </ div >
313+ { step . annotation && (
314+ < div className = { styles . stepAnnotation } > { step . annotation } </ div >
315+ ) }
316+ </ div >
317+ ) ;
318+ } ) }
319+ </ div >
320+ ) }
321+ </ section >
322+ ) ;
323+
262324 default :
263325 return (
264326 < section key = { key } >
0 commit comments