@@ -44,6 +44,7 @@ import type {
4444 RepoContribution ,
4545 ExtendedContributionData ,
4646 ContributionCalendar ,
47+ StreakStats ,
4748} from '@/types' ;
4849import { getNormalizedThemeKey , themes } from '@/lib/svg/themes' ;
4950import { streakParamsSchema , coerceQueryParams } from '@/lib/validations' ;
@@ -550,6 +551,31 @@ export async function GET(request: Request) {
550551 } ) ;
551552 clearTimeout ( timeoutId ) ;
552553 }
554+ // Pre-calculate full, unsliced statistics first
555+ let fullStats : StreakStats ;
556+ let fullVersusStats : StreakStats | undefined ;
557+ let fullWeekdayStats : StreakStats | undefined ;
558+
559+ if ( versus && versusCalendar ) {
560+ // Normalize both calendars to the target timezone for accurate comparison
561+ const normalizedCalendar = normalizeCalendarToTimezone ( calendar , timezone ) ;
562+ const normalizedVersusCalendar = normalizeCalendarToTimezone ( versusCalendar , timezone ) ;
563+
564+ fullStats = calculateStreak ( normalizedCalendar , timezone , undefined , grace ) ;
565+ fullVersusStats = calculateStreak ( normalizedVersusCalendar , timezone , undefined , grace ) ;
566+ } else {
567+ fullStats = calculateStreak ( calendar , timezone , undefined , grace ) ;
568+ if ( normalizedView === 'weekday' ) {
569+ const normalizedCalendar = normalizeCalendarToTimezone ( calendar , timezone ) ;
570+ fullWeekdayStats = calculateStreak ( normalizedCalendar , timezone , undefined , grace ) ;
571+ }
572+ }
573+
574+ const fullMonthlyStats = calculateMonthlyStats (
575+ calendar ,
576+ timezone ,
577+ getMonthlyReferenceDate ( year , timezone )
578+ ) ;
553579 if ( normalizedView !== 'monthly' ) {
554580 let effectiveDays = days ;
555581
@@ -567,18 +593,20 @@ export async function GET(request: Request) {
567593 totalContributions : filteredDays . reduce ( ( sum , d ) => sum + d . contributionCount , 0 ) ,
568594 weeks : chunkDaysIntoWeeks ( filteredDays ) ,
569595 } ;
596+
597+ if ( versusCalendar ) {
598+ const versusDays = versusCalendar . weeks . flatMap ( ( w ) => w . contributionDays ) ;
599+ const filteredVersusDays = versusDays . slice ( - effectiveDays ) ;
600+ versusCalendar = {
601+ totalContributions : filteredVersusDays . reduce ( ( sum , d ) => sum + d . contributionCount , 0 ) ,
602+ weeks : chunkDaysIntoWeeks ( filteredVersusDays ) ,
603+ } ;
604+ }
570605 }
571606 }
572607
573608 // ─── JSON output mode ──────────────────────────────────────────────────
574609 if ( format === 'json' ) {
575- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
576- const monthlyStats = calculateMonthlyStats (
577- calendar ,
578- timezone ,
579- getMonthlyReferenceDate ( year , timezone )
580- ) ;
581-
582610 const secondsToMidnight = tzParam
583611 ? getSecondsUntilMidnightInTimezone ( timezone )
584612 : getSecondsUntilUTCMidnight ( ) ;
@@ -592,8 +620,8 @@ export async function GET(request: Request) {
592620
593621 const jsonPayload = JSON . stringify ( {
594622 user : targetEntity ,
595- stats,
596- monthlyStats,
623+ stats : fullStats ,
624+ monthlyStats : fullMonthlyStats ,
597625 calendar : {
598626 totalContributions : calendar . totalContributions ,
599627 weeks : calendar . weeks ,
@@ -632,63 +660,50 @@ export async function GET(request: Request) {
632660 // ─── SVG output mode (default) ──────────────────────────────────────────
633661 let svg = '' ;
634662 if ( normalizedView === 'monthly' ) {
635- const stats = calculateMonthlyStats (
636- calendar ,
637- timezone ,
638- getMonthlyReferenceDate ( year , timezone )
639- ) ;
640- svg = generateMonthlySVG ( stats , params ) ;
663+ svg = generateMonthlySVG ( fullMonthlyStats , params ) ;
641664 } else if ( normalizedView === 'languages' ) {
642- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
643- svg = generateLanguagesSVG ( stats , params , repoContributions ) ;
665+ svg = generateLanguagesSVG ( fullStats , params , repoContributions ) ;
644666 } else if ( normalizedView === 'heatmap' ) {
645- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
646- svg = generateHeatmapSVG ( stats , params , calendar ) ;
667+ svg = generateHeatmapSVG ( fullStats , params , calendar ) ;
647668 } else if ( normalizedView === 'pulse' ) {
648669 // We still use calculateStreak here to efficiently parse totalContributions for the stat display,
649670 // even though the sparkline generator will extract its own daily 30-day timeline below.
650- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
651- svg = generatePulseSVG ( stats , params , calendar ) ;
671+ svg = generatePulseSVG ( fullStats , params , calendar ) ;
652672 } else if ( normalizedView === 'skyline' ) {
653- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
654- svg = generateSkylineSVG ( stats , params , calendar ) ;
673+ svg = generateSkylineSVG ( fullStats , params , calendar ) ;
655674 } else if ( normalizedView === 'constellation' ) {
656- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
657- svg = generateConstellationSVG ( stats , params , calendar ) ;
675+ svg = generateConstellationSVG ( fullStats , params , calendar ) ;
658676 } else if ( normalizedView === 'radar' ) {
659- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
660677 const hourCounts = await fetchCommitHourDistribution ( user , undefined , timezone ) . catch (
661678 ( ) => undefined
662679 ) ;
663- svg = generateRadarSVG ( stats , params , calendar , hourCounts ) ;
680+ svg = generateRadarSVG ( fullStats , params , calendar , hourCounts ) ;
664681 } else if ( normalizedView === 'doughnut' || normalizedView === 'pie' ) {
665- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
666- svg = generateDoughnutSVG ( stats , params , calendar ) ;
682+ svg = generateDoughnutSVG ( fullStats , params , calendar ) ;
667683 } else if ( normalizedView === 'activity_graph' ) {
668- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
669- svg = generateActivityGraphSVG ( stats , params , calendar ) ;
684+ svg = generateActivityGraphSVG ( fullStats , params , calendar ) ;
670685 } else if ( normalizedView === 'commit_clock' ) {
671- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
672686 const hourCounts = await fetchCommitHourDistribution ( user , undefined , timezone ) . catch ( ( ) =>
673687 new Array ( 24 ) . fill ( 0 )
674688 ) ;
675- svg = generateCommitClockSVG ( hourCounts , stats , params ) ;
689+ svg = generateCommitClockSVG ( hourCounts , fullStats , params ) ;
676690 } else if ( normalizedView === 'weekday' ) {
677- // ← INSERT YOUR NEW BLOCK HERE
678691 const normalizedCalendar = normalizeCalendarToTimezone ( calendar , timezone ) ;
679- const stats = calculateStreak ( normalizedCalendar , timezone , undefined , grace ) ;
680- svg = generateWeekdaySVG ( stats , params , normalizedCalendar ) ;
692+ svg = generateWeekdaySVG ( fullWeekdayStats || fullStats , params , normalizedCalendar ) ;
681693 } else if ( versus && versusCalendar ) {
682694 // Normalize both calendars to the target timezone for accurate comparison
683695 const normalizedCalendar = normalizeCalendarToTimezone ( calendar , timezone ) ;
684696 const normalizedVersusCalendar = normalizeCalendarToTimezone ( versusCalendar , timezone ) ;
685697
686- const stats1 = calculateStreak ( normalizedCalendar , timezone , undefined , grace ) ;
687- const stats2 = calculateStreak ( normalizedVersusCalendar , timezone , undefined , grace ) ;
688- svg = generateVersusSVG ( stats1 , stats2 , params , normalizedCalendar , normalizedVersusCalendar ) ;
698+ svg = generateVersusSVG (
699+ fullStats ,
700+ fullVersusStats ! ,
701+ params ,
702+ normalizedCalendar ,
703+ normalizedVersusCalendar
704+ ) ;
689705 } else {
690- const stats = calculateStreak ( calendar , timezone , undefined , grace ) ;
691- svg = generateSVG ( stats , params , calendar , individualCalendars ) ;
706+ svg = generateSVG ( fullStats , params , calendar , individualCalendars ) ;
692707 }
693708
694709 if ( servedFromStaleCache ) {
0 commit comments