diff --git a/components/InteractiveViewer.tsx b/components/InteractiveViewer.tsx index ba6c65a49..0f29e9459 100644 --- a/components/InteractiveViewer.tsx +++ b/components/InteractiveViewer.tsx @@ -25,20 +25,23 @@ interface ParallaxParticle { * Deterministic math prevents random values from causing SSR/CSR mismatches. */ function buildParticles(): ParallaxParticle[] { const colors = ['#10b981', '#8b5cf6', '#06b6d4', '#3b82f6', '#f59e0b']; - return Array.from({ length: PARALLAX_PARTICLE_COUNT }, (_, i): ParallaxParticle => ({ - id: i, - // Spread particles across the container using prime-number strides - x: (i * 17 + 11) % 100, - y: (i * 23 + 7) % 100, - size: 4 + (i % 5) * 2, // range: 4–12 px - // Keep opacity low so particles never obscure the badge - opacity: 0.05 + (i % 4) * 0.025, // range: 0.05–0.125 - // Vary depth so each "layer" of particles shifts by a different amount, - // creating the illusion of 3-D depth. depth 0.1 = farthest; 0.7 = nearest. - depth: 0.1 + (i % 6) * 0.1, // range: 0.1–0.6 - color: colors[i % colors.length], - isCircle: i % 4 === 0, - })); + return Array.from( + { length: PARALLAX_PARTICLE_COUNT }, + (_, i): ParallaxParticle => ({ + id: i, + // Spread particles across the container using prime-number strides + x: (i * 17 + 11) % 100, + y: (i * 23 + 7) % 100, + size: 4 + (i % 5) * 2, // range: 4–12 px + // Keep opacity low so particles never obscure the badge + opacity: 0.05 + (i % 4) * 0.025, // range: 0.05–0.125 + // Vary depth so each "layer" of particles shifts by a different amount, + // creating the illusion of 3-D depth. depth 0.1 = farthest; 0.7 = nearest. + depth: 0.1 + (i % 6) * 0.1, // range: 0.1–0.6 + color: colors[i % colors.length], + isCircle: i % 4 === 0, + }) + ); } // How many pixels a depth-1.0 particle shifts when the cursor is at the @@ -399,29 +402,31 @@ export default function InteractiveViewer({ Each particle shifts by (parallaxX * depth, parallaxY * depth) px relative to its base position, so "closer" particles (higher depth) shift more — creating the impression of a multi-layered isometric space. */} - {particles.map((particle): ReactElement => ( -
- ))} + {particles.map( + (particle): ReactElement => ( + + ) + )} {/* ── Card content ────────────────────────────────────────────────────── diff --git a/components/burnout/BurnoutRiskTable.tsx b/components/burnout/BurnoutRiskTable.tsx index 7cf69d2c5..c276cd649 100644 --- a/components/burnout/BurnoutRiskTable.tsx +++ b/components/burnout/BurnoutRiskTable.tsx @@ -1,6 +1,7 @@ 'use client'; import { useState, useMemo } from 'react'; +import Image from 'next/image'; import { motion } from 'framer-motion'; import { Flame, diff --git a/lib/github.ts b/lib/github.ts index ebba54c16..d8d796ccd 100644 --- a/lib/github.ts +++ b/lib/github.ts @@ -287,13 +287,13 @@ export async function fetchWithRetry( } if (didThrow) { - if (options.signal?.aborted) throw fetchError; + if (options.signal?.aborted) throw new Error('AbortError'); const isTimeoutAbort = isAbortError(fetchError); if (attempt >= MAX_RETRIES) { if (isTimeoutAbort) { throw new Error(`GitHub API request timed out after ${resolvedTimeout / 1000}s`); } - throw fetchError; + throw new Error(fetchError instanceof Error ? fetchError.message : String(fetchError)); } const delay = getJitteredBackoff(attempt); await new Promise((resolve) => setTimeout(resolve, delay)); @@ -908,13 +908,6 @@ export async function fetchGitHubContributions( const loadWithTimeout = async (): Promise