@@ -528,6 +528,88 @@ function generateAutoThemeMonthlySVG(stats: MonthlyStats, params: BadgeParams):
528528` ;
529529}
530530
531+ /**
532+ * Generates a fallback SVG badge for users that do not exist
533+ * or when contribution data cannot be loaded.
534+ *
535+ * The SVG renders a ghost-style city layout with an animated
536+ * error-state design while preserving the standard badge layout.
537+ *
538+ * @param username - GitHub username displayed in the error badge.
539+ * @param bg - Background color used for the SVG container.
540+ * @param accent - Accent color used for highlights, outlines,
541+ * and animated elements.
542+ * @param text - Primary text color used throughout the badge.
543+ * @param radius - Border radius applied to the SVG background.
544+ * @param speed - Animation speed for the radar scan effect.
545+ * Defaults to '8s'.
546+ *
547+ * @returns A generated SVG string representing the not-found state.
548+ *
549+ * @example
550+ * const svg = generateNotFoundSVG(
551+ * 'octocat',
552+ * '#0d1117',
553+ * '#00ffaa',
554+ * '#ffffff',
555+ * 8,
556+ * '8s'
557+ * );
558+ */
559+ // Fixed isometric tower layout for the not-found ghost city.
560+ // Heights are deterministic so the silhouette always looks like a real city
561+ // regardless of the username or theme passed to generateNotFoundSVG.
562+ const GHOST_LAYOUT : { col : number ; row : number ; h : number } [ ] = [
563+ { col : 0 , row : 0 , h : 8 } ,
564+ { col : 1 , row : 0 , h : 20 } ,
565+ { col : 2 , row : 0 , h : 12 } ,
566+ { col : 3 , row : 0 , h : 30 } ,
567+ { col : 4 , row : 0 , h : 16 } ,
568+ { col : 5 , row : 0 , h : 10 } ,
569+ { col : 6 , row : 0 , h : 24 } ,
570+ { col : 7 , row : 0 , h : 8 } ,
571+ { col : 0 , row : 1 , h : 6 } ,
572+ { col : 1 , row : 1 , h : 14 } ,
573+ { col : 2 , row : 1 , h : 36 } ,
574+ { col : 3 , row : 1 , h : 22 } ,
575+ { col : 4 , row : 1 , h : 44 } ,
576+ { col : 5 , row : 1 , h : 18 } ,
577+ { col : 6 , row : 1 , h : 10 } ,
578+ { col : 7 , row : 1 , h : 28 } ,
579+ { col : 0 , row : 2 , h : 10 } ,
580+ { col : 1 , row : 2 , h : 26 } ,
581+ { col : 2 , row : 2 , h : 16 } ,
582+ { col : 3 , row : 2 , h : 38 } ,
583+ { col : 4 , row : 2 , h : 20 } ,
584+ { col : 5 , row : 2 , h : 32 } ,
585+ { col : 6 , row : 2 , h : 14 } ,
586+ { col : 7 , row : 2 , h : 6 } ,
587+ { col : 0 , row : 3 , h : 4 } ,
588+ { col : 1 , row : 3 , h : 18 } ,
589+ { col : 2 , row : 3 , h : 28 } ,
590+ { col : 3 , row : 3 , h : 12 } ,
591+ { col : 4 , row : 3 , h : 34 } ,
592+ { col : 5 , row : 3 , h : 8 } ,
593+ { col : 6 , row : 3 , h : 22 } ,
594+ { col : 7 , row : 3 , h : 16 } ,
595+ { col : 0 , row : 4 , h : 8 } ,
596+ { col : 1 , row : 4 , h : 30 } ,
597+ { col : 2 , row : 4 , h : 10 } ,
598+ { col : 3 , row : 4 , h : 20 } ,
599+ { col : 4 , row : 4 , h : 16 } ,
600+ { col : 5 , row : 4 , h : 40 } ,
601+ { col : 6 , row : 4 , h : 12 } ,
602+ { col : 7 , row : 4 , h : 24 } ,
603+ { col : 0 , row : 5 , h : 14 } ,
604+ { col : 1 , row : 5 , h : 8 } ,
605+ { col : 2 , row : 5 , h : 22 } ,
606+ { col : 3 , row : 5 , h : 32 } ,
607+ { col : 4 , row : 5 , h : 10 } ,
608+ { col : 5 , row : 5 , h : 18 } ,
609+ { col : 6 , row : 5 , h : 28 } ,
610+ { col : 7 , row : 5 , h : 6 } ,
611+ ] ;
612+
531613export function generateNotFoundSVG (
532614 username : string ,
533615 bg : string ,
@@ -537,65 +619,8 @@ export function generateNotFoundSVG(
537619 speed : string = '8s'
538620) : string {
539621 const safeName = escapeXML ( username . toUpperCase ( ) ) ;
540-
541- const ghostLayout : { col : number ; row : number ; h : number } [ ] = [
542- { col : 0 , row : 0 , h : 8 } ,
543- { col : 1 , row : 0 , h : 20 } ,
544- { col : 2 , row : 0 , h : 12 } ,
545- { col : 3 , row : 0 , h : 30 } ,
546- { col : 4 , row : 0 , h : 16 } ,
547- { col : 5 , row : 0 , h : 10 } ,
548- { col : 6 , row : 0 , h : 24 } ,
549- { col : 7 , row : 0 , h : 8 } ,
550-
551- { col : 0 , row : 1 , h : 6 } ,
552- { col : 1 , row : 1 , h : 14 } ,
553- { col : 2 , row : 1 , h : 36 } ,
554- { col : 3 , row : 1 , h : 22 } ,
555- { col : 4 , row : 1 , h : 44 } ,
556- { col : 5 , row : 1 , h : 18 } ,
557- { col : 6 , row : 1 , h : 10 } ,
558- { col : 7 , row : 1 , h : 28 } ,
559-
560- { col : 0 , row : 2 , h : 10 } ,
561- { col : 1 , row : 2 , h : 26 } ,
562- { col : 2 , row : 2 , h : 16 } ,
563- { col : 3 , row : 2 , h : 38 } ,
564- { col : 4 , row : 2 , h : 20 } ,
565- { col : 5 , row : 2 , h : 32 } ,
566- { col : 6 , row : 2 , h : 14 } ,
567- { col : 7 , row : 2 , h : 6 } ,
568-
569- { col : 0 , row : 3 , h : 4 } ,
570- { col : 1 , row : 3 , h : 18 } ,
571- { col : 2 , row : 3 , h : 28 } ,
572- { col : 3 , row : 3 , h : 12 } ,
573- { col : 4 , row : 3 , h : 34 } ,
574- { col : 5 , row : 3 , h : 8 } ,
575- { col : 6 , row : 3 , h : 22 } ,
576- { col : 7 , row : 3 , h : 16 } ,
577-
578- { col : 0 , row : 4 , h : 8 } ,
579- { col : 1 , row : 4 , h : 30 } ,
580- { col : 2 , row : 4 , h : 10 } ,
581- { col : 3 , row : 4 , h : 20 } ,
582- { col : 4 , row : 4 , h : 16 } ,
583- { col : 5 , row : 4 , h : 40 } ,
584- { col : 6 , row : 4 , h : 12 } ,
585- { col : 7 , row : 4 , h : 24 } ,
586-
587- { col : 0 , row : 5 , h : 14 } ,
588- { col : 1 , row : 5 , h : 8 } ,
589- { col : 2 , row : 5 , h : 22 } ,
590- { col : 3 , row : 5 , h : 32 } ,
591- { col : 4 , row : 5 , h : 10 } ,
592- { col : 5 , row : 5 , h : 18 } ,
593- { col : 6 , row : 5 , h : 28 } ,
594- { col : 7 , row : 5 , h : 6 } ,
595- ] ;
596-
597622 let ghostTowers = '' ;
598- for ( const { col, row, h } of ghostLayout ) {
623+ for ( const { col, row, h } of GHOST_LAYOUT ) {
599624 const tx = 300 + ( col - row ) * 16 ;
600625 const ty = 120 + ( col + row ) * 9 ;
601626
0 commit comments