diff --git a/src/components/ProjectImage.astro b/src/components/ProjectImage.astro index 63455d06..37ca99c3 100644 --- a/src/components/ProjectImage.astro +++ b/src/components/ProjectImage.astro @@ -54,44 +54,40 @@ type ImageConfig = { class: string; }; +// Image configuration lookup table +type ConfigKey = `${'valid' | 'placeholder'}-${'featured' | 'thumbnail'}`; + +const IMAGE_CONFIGS: Record> = { + 'valid-featured': { + widths: [400, 800, 1200], + formats: ['avif', 'webp'], + }, + 'valid-thumbnail': { + width: 96, + height: 96, + }, + 'placeholder-featured': { + width: 800, + height: 450, + }, + 'placeholder-thumbnail': { + width: 96, + height: 96, + }, +}; + function getImageConfig( hasImage: boolean, imageVariant: 'featured' | 'thumbnail', imageSource: ImageMetadata ): ImageConfig { - if (hasImage && imageVariant === 'featured') { - return { - src: imageSource, - widths: [400, 800, 1200], - formats: ['avif', 'webp'], - class: cn(baseImageClasses, hoverClasses), - }; - } - - if (hasImage && imageVariant === 'thumbnail') { - return { - src: imageSource, - width: 96, - height: 96, - class: cn(baseImageClasses, hoverClasses), - }; - } - - if (imageVariant === 'featured') { - return { - src: placeholderImage, - width: 800, - height: 450, - class: baseImageClasses, - }; - } + const configKey: ConfigKey = `${hasImage ? 'valid' : 'placeholder'}-${imageVariant}`; + const baseConfig = IMAGE_CONFIGS[configKey]; - // thumbnail placeholder return { - src: placeholderImage, - width: 96, - height: 96, - class: baseImageClasses, + ...baseConfig, + src: hasImage ? imageSource : placeholderImage, + class: hasImage ? cn(baseImageClasses, hoverClasses) : baseImageClasses, }; } diff --git a/src/data/profile.ts b/src/data/profile.ts index 5afa2461..2f0e4f00 100644 --- a/src/data/profile.ts +++ b/src/data/profile.ts @@ -78,10 +78,8 @@ export const PROFILE_DATA = { "I'm passionate about mentoring the next generation of technologists and contributing to open-source projects and research that advance the field of artificial intelligence.", }, - socialProfiles: [ - 'https://github.com/cbenge509', - 'https://www.linkedin.com/in/crisbenge/', - ], + // Derived from SOCIAL_LINKS for single source of truth + socialProfiles: SOCIAL_LINKS.map(link => link.href), education: [{name: 'Columbia University'}, {name: 'UC Berkeley'}], -} as const; +}; diff --git a/src/scripts/featured-project-card.ts b/src/scripts/featured-project-card.ts index b8a9d3b4..a42388ad 100644 --- a/src/scripts/featured-project-card.ts +++ b/src/scripts/featured-project-card.ts @@ -3,14 +3,15 @@ * Stops propagation on GitHub links to prevent card navigation. */ -let initialized = false; +// Track if listener already attached +let listenerAttached = false; function initFeaturedProjectCards(): void { - // Prevent multiple initializations - if (initialized) return; - initialized = true; + // Use event delegation for efficiency - safe to call multiple times + // as we only attach one delegated listener to document + if (listenerAttached) return; + listenerAttached = true; - // Use event delegation for efficiency document.addEventListener('click', event => { const target = event.target as HTMLElement; const githubLink = target.closest('[data-github-link]'); @@ -20,9 +21,13 @@ function initFeaturedProjectCards(): void { }); } -// Initialize on DOMContentLoaded +// Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initFeaturedProjectCards); } else { initFeaturedProjectCards(); } + +// Re-initialize on page navigation (for Astro view transitions) +// Event delegation is already set up, no need to re-attach +document.addEventListener('astro:page-load', initFeaturedProjectCards); diff --git a/src/scripts/navigation.ts b/src/scripts/navigation.ts index 813b7d2c..00e8174e 100644 --- a/src/scripts/navigation.ts +++ b/src/scripts/navigation.ts @@ -209,3 +209,6 @@ if (document.readyState === 'loading') { } else { initNavigation(); } + +// Re-initialize on page navigation (for Astro view transitions) +document.addEventListener('astro:page-load', () => initNavigation()); diff --git a/src/scripts/publication-card.ts b/src/scripts/publication-card.ts index 7775bbfa..e703c538 100644 --- a/src/scripts/publication-card.ts +++ b/src/scripts/publication-card.ts @@ -34,9 +34,12 @@ function initPublicationCards(): void { }); } -// Initialize on DOMContentLoaded +// Initialize when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initPublicationCards); } else { initPublicationCards(); } + +// Re-initialize on page navigation (for Astro view transitions) +document.addEventListener('astro:page-load', initPublicationCards); diff --git a/src/utils/badge.ts b/src/utils/badge.ts index 13ecaaa6..7697a321 100644 --- a/src/utils/badge.ts +++ b/src/utils/badge.ts @@ -66,6 +66,11 @@ export const AWARD_CATEGORY_COLORS = { /** * Education honor badge colors. * Blue color scheme for academic distinctions. + * + * NOTE: Kept as named constant (not inlined) for: + * - Consistency with other badge color exports + * - Semantic clarity in component usage + * - Single source of truth for design changes */ export const EDUCATION_HONOR_COLORS = 'bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-200' as const;