@@ -10,7 +10,12 @@ export function isMobileViewport(): boolean {
1010}
1111
1212// Scroll target types for animation phases
13- export type ScrollTarget = 'browser' | 'network' | 'server' | 'explanation' | 'controls' ;
13+ export type ScrollTarget =
14+ | 'browser' | 'network' | 'server' | 'explanation' | 'controls'
15+ // Fix step specific targets
16+ | 'vulnerable-code' | 'fixed-code' | 'remediation-nextjs' | 'remediation-react' | 'remediation-verify'
17+ // Lessons step specific targets
18+ | 'lesson-1' | 'lesson-2' | 'lesson-3' | 'lesson-4' | 'quote' | 'resources' ;
1419
1520// Smoothly scroll an element into the center of the viewport
1621export function scrollElementIntoView (
@@ -39,11 +44,24 @@ export function getScrollTargetElement(
3944 if ( ! container ) return null ;
4045
4146 const selectors : Record < ScrollTarget , string > = {
42- browser : '.client-side, .render-side:first-child, .attacker-entity, .code-diff-container, .lessons-grid' ,
47+ browser : '.client-side, .render-side:first-child, .attacker-entity, .code-diff-container, .code-diff, . lessons-grid' ,
4348 network : '.data-flow-column, .flight-channel, .exploit-flow' ,
4449 server : '.server-side, .render-side:last-child, .server-entity' ,
45- explanation : '.explanation-panel, .impact-section, .comparison-summary, .vulnerable-code, .remediation-section, .resources-section' ,
46- controls : '.video-controls, .timeline-container'
50+ explanation : '.explanation-panel, .impact-section, .comparison-summary, .vulnerable-code, .remediation-section, .resources-section, .final-section, .resources-grid' ,
51+ controls : '.video-controls, .timeline-container' ,
52+ // Fix step specific selectors
53+ 'vulnerable-code' : '.diff-panel.before-panel' ,
54+ 'fixed-code' : '.diff-panel.after-panel' ,
55+ 'remediation-nextjs' : '.remediation-card:nth-child(1)' ,
56+ 'remediation-react' : '.remediation-card:nth-child(2)' ,
57+ 'remediation-verify' : '.remediation-card:nth-child(3)' ,
58+ // Lessons step specific selectors
59+ 'lesson-1' : '.lesson-card:nth-child(1)' ,
60+ 'lesson-2' : '.lesson-card:nth-child(2)' ,
61+ 'lesson-3' : '.lesson-card:nth-child(3)' ,
62+ 'lesson-4' : '.lesson-card:nth-child(4)' ,
63+ 'quote' : '.final-section' ,
64+ 'resources' : '.resources-section'
4765 } ;
4866
4967 return container . querySelector ( selectors [ target ] ) as HTMLElement | null ;
@@ -250,23 +268,36 @@ const DEFAULT_FIX_TRIGGERS: FixTriggerTimes = {
250268} ;
251269
252270// Determine scroll target for Fix step based on current time and trigger points
253- // Flow: Code diff (vulnerable → fixed) → Remediation cards
271+ // Flow: Vulnerable code → Fixed code → Next.js card → React card → Verify card
254272export function getFixScrollTarget ( params : {
255273 currentTime : number ;
256274 triggers ?: FixTriggerTimes | null ;
257275} ) : ScrollTarget {
258276 const { currentTime, triggers } = params ;
259277 const t = triggers ?? DEFAULT_FIX_TRIGGERS ;
260278
261- // Phase 1: Code diff section - showing vulnerable and fixed code (0 to patchApplication)
262- // User should see the before/after code diff
279+ // Phase 1: Vulnerable code (0 to fixAdds)
280+ if ( currentTime < t . fixAdds ) {
281+ return 'vulnerable-code' ;
282+ }
283+
284+ // Phase 2: Fixed code (fixAdds to patchApplication)
263285 if ( currentTime < t . patchApplication ) {
264- return 'browser' ; // Code diff container
286+ return 'fixed-code' ;
265287 }
266288
267- // Phase 2: Remediation section (patchApplication onwards)
268- // Show the patching instructions and version cards
269- return 'explanation' ;
289+ // Phase 3: Next.js remediation card (patchApplication to reactDirect)
290+ if ( currentTime < t . reactDirect ) {
291+ return 'remediation-nextjs' ;
292+ }
293+
294+ // Phase 4: React Direct card (reactDirect to verify)
295+ if ( currentTime < t . verify ) {
296+ return 'remediation-react' ;
297+ }
298+
299+ // Phase 5: Verify card (verify onwards)
300+ return 'remediation-verify' ;
270301}
271302
272303// Lessons step trigger times interface (matches LessonsKeyframeTriggers)
@@ -290,23 +321,41 @@ const DEFAULT_LESSONS_TRIGGERS: LessonsTriggerTimes = {
290321} ;
291322
292323// Determine scroll target for Lessons step based on current time and trigger points
293- // Flow: Lesson cards (1-4) → Final quote/summary → Resources
324+ // Flow: Lesson 1 → Lesson 2 → Lesson 3 → Lesson 4 → Quote → Resources
294325export function getLessonsScrollTarget ( params : {
295326 currentTime : number ;
296327 triggers ?: LessonsTriggerTimes | null ;
297328} ) : ScrollTarget {
298329 const { currentTime, triggers } = params ;
299330 const t = triggers ?? DEFAULT_LESSONS_TRIGGERS ;
300331
301- // Phase 1: Lessons cards (0 to remember)
302- // User should see the lesson cards being highlighted
332+ // Phase 1: Lesson 1 (0 to secondLesson)
333+ if ( currentTime < t . secondLesson ) {
334+ return 'lesson-1' ;
335+ }
336+
337+ // Phase 2: Lesson 2 (secondLesson to thirdLesson)
338+ if ( currentTime < t . thirdLesson ) {
339+ return 'lesson-2' ;
340+ }
341+
342+ // Phase 3: Lesson 3 (thirdLesson to fourthLesson)
343+ if ( currentTime < t . fourthLesson ) {
344+ return 'lesson-3' ;
345+ }
346+
347+ // Phase 4: Lesson 4 (fourthLesson to remember)
303348 if ( currentTime < t . remember ) {
304- return 'browser' ; // Lessons grid
349+ return 'lesson-4' ;
305350 }
306351
307- // Phase 2: Quote and resources (remember onwards)
308- // Show the final summary and further reading
309- return 'explanation' ;
352+ // Phase 5: Quote (remember to bakedIn)
353+ if ( currentTime < t . bakedIn ) {
354+ return 'quote' ;
355+ }
356+
357+ // Phase 6: Resources (bakedIn onwards)
358+ return 'resources' ;
310359}
311360
312361// Module-level state for scroll tracking
@@ -330,7 +379,7 @@ export function scrollToTarget(
330379 return ;
331380 }
332381
333- // Throttle scrolls to prevent jank
382+ // Throttle scrolls to prevent jarring scroll behavior
334383 if ( ! force && now - lastScrollTime < minIntervalMs ) {
335384 return ;
336385 }
0 commit comments