Research Seminar on Generative Models
Universidad Nacional de Colombia
🌐 Website • 📚 Research • 👥 Team • 📧 Contact
The SIMG (Semillero de Investigación en Modelos Generativos) is a research seminar affiliated with the Universidad Nacional de Colombia - UNAL, dedicated to advancing the frontiers of artificial intelligence through rigorous research and collaborative innovation.
Our mission is to create a dynamic learning environment that introduces students to cutting-edge AI research, focusing on:
- 🔤 Natural Language Processing (NLP)
- 🧠 Long Short-Term Memory (LSTM) Networks
- ⚡ Transformers & Attention Mechanisms
- 🤖 Autonomous AI Agents
- 📊 Benchmarks & Model Fine-tuning
- 🎨 Diffusion Models
This website features a modern design with:
- Color Palette: Orange primary (#f97316), Blue secondary (#0ea5e9)
- Visual Effects: Glassmorphism, radial gradients, smooth animations
- Theme Support: Dark/Light mode with persistent storage
- Typography: Responsive, clamp-based sizing with system fonts
- Components: Reusable design tokens and CSS custom properties
- Astro 5.14.8 - Static Site Generator
- TypeScript - Type-safe development
- EmailJS - Client-side email service for contact form
- CSS Custom Properties - Comprehensive design token system
- Content Collections - Markdown-based content management
- Node.js v24.11.0 - Runtime environment
SIMG_W_Website/
├── public/
│ ├── images/
│ │ ├── events/ # Auto-generated event thumbnails (SVG/PNG)
│ │ ├── members/ # Team member photos
│ │ ├── research/ # Research project images
│ │ └── website/ # Site logos and assets
│ └── favicon.svg
├── scripts/
│ ├── create-event.mjs # CLI tool to create new events
│ └── generate-thumbnail.mjs # Thumbnail generator (Nano Banana / SVG fallback)
├── .github/
│ └── workflows/
│ └── create-event.yml # CI/CD: create events via GitHub Actions
├── src/
│ ├── components/
│ │ ├── Header.astro # Navigation with theme toggle
│ │ ├── EventCard.astro # Reusable event card with share/calendar/resources
│ │ ├── Footer.astro
│ │ └── SponsorCarousel.astro
│ ├── content/
│ │ ├── config.ts # Content Collections config (members, blog, research, events)
│ │ ├── events/ # 📅 Event content (en/es)
│ │ │ ├── en/ # English event markdown files
│ │ │ └── es/ # Spanish event markdown files
│ │ ├── members/ # Team member profiles (en/es)
│ │ ├── research/ # Research projects (en/es)
│ │ └── blog/ # Blog posts (en/es)
│ ├── layouts/
│ │ └── Layout.astro # Base layout with FOUC prevention
│ ├── pages/
│ │ ├── en/
│ │ │ ├── index.astro
│ │ │ ├── about.astro
│ │ │ ├── events.astro # 📅 Unified Events page (EN)
│ │ │ ├── research.astro
│ │ │ ├── members.astro
│ │ │ └── contact.astro
│ │ ├── es/
│ │ │ ├── events.astro # 📅 Unified Events page (ES)
│ │ │ └── ...
│ │ └── index.astro # Language redirect
│ └── styles/
│ └── global.css # Design tokens & global styles
├── astro.config.mjs
├── package.json
├── tsconfig.json
└── CHANGELOG.md
Run from the project root:
| Command | Action |
|---|---|
bun install |
Install dependencies |
bun run dev |
Start dev server at localhost:4321 |
bun run build |
Build production site to ./dist/ |
bun run preview |
Preview build locally before deploying |
bun run astro ... |
Run Astro CLI commands |
bun run new-event |
📅 Interactive CLI to create a new event |
bun run generate-thumbnails |
🎨 Generate thumbnails for all events |
- Auto-redirects to preferred language (en/es)
- Hero section with gradient backgrounds
- Featured research preview
- Call-to-action sections
- Mission statement
- Research focus areas with interactive cards
- Team introduction
- Filterable research projects grid
- Search functionality
- Category-based organization (Research, Products, Partnerships)
- Team profiles with photos
- Research interests and links
- Alumni section
- Email Contact Form powered by EmailJS
- Sends emails directly from the browser without a backend server
- Messages are sent to:
alesanchezpov@gmail.comandrobertgomez.datascience@gmail.com - Includes sender's name, email, subject, and message
- Real-time validation and loading states
- Success/error feedback with styled notifications
- Free tier: 200 emails/month
- Contact information (location, emails, meeting times)
- Interactive Google Maps integration showing Building 404 - Yu Takeuchi location
- Social media links
The Events page replaces the old separate in-person.astro and recordings.astro pages, merging all session content into a single, feature-rich page.
- Hero Section with Google Maps embed (Building 404 Yu Takeuchi)
- Recurring Google Calendar link (every Friday 2–4 PM COT)
- Get Directions button
- Location details with room number
- Upcoming Events grid (auto-filtered by
status: upcoming, limited to 3 most recent) - Past Events grid (auto-filtered by
status: completed, limited to 3 latest) - YouTube Recordings section (fetches latest 3 videos via YouTube Data API v3)
- Session Guidelines cards (open to all, bring laptop, Python basics, ask questions)
📊 Event Display Limits: All sections consistently show maximum 3 items for clean grid layout.
Event Cards (EventCard.astro) feature:
- Thumbnail display (16:9 ratio) with lazy loading
- Event type badges: 🔵 In-Person, 🟢 Virtual, 🟡 Hybrid
- Status badges: 🟡 Upcoming, 🔴 Live (pulsing), ⚪ Completed
- Google Calendar URL auto-generation from event date/time/location
- Share buttons: WhatsApp, LinkedIn, Copy Link (clipboard API)
- Resource links, recording link, meeting link
- Light/Dark mode support
- Duration badge
Grid Layout: Up to 3 cards per row on desktop, 1 column on mobile
The contact form uses EmailJS to send emails without requiring a backend server. Here's how it works:
- Service Setup: Connected to Gmail account (
alesanchezpov@gmail.com) - Email Template: Custom HTML template with SIMG branding
- Recipients:
- Primary:
alesanchezpov@gmail.com - Bcc:
robertgomez.datascience@gmail.com
- Primary:
- Reply-To: Automatically set to the sender's email from the form
Required Configuration:
EMAILJS_PUBLIC_KEY: Your EmailJS public keyEMAILJS_SERVICE_ID: Gmail service IDEMAILJS_TEMPLATE_ID: Contact form template ID
These values are configured in both /en/contact.astro and /es/contact.astro.
The session recordings page uses YouTube Data API v3 to fetch and display videos from the SIMG channel. Here's how it works:
- API Setup: Fetches latest videos from
@simg-UNYouTube channel - Video Data Retrieved:
- Video thumbnails (high quality)
- Title and description
- Publication date
- Video duration
- Video ID for direct YouTube links
- Display Features:
- Automatic grid layout (3 videos)
- Click-to-watch on YouTube
- Styled with Van Gogh palette
- Fallback error messages if API fails
Required Configuration:
PUBLIC_YOUTUBE_API_KEY: Your YouTube Data API v3 keyPUBLIC_YOUTUBE_CHANNEL_ID: SIMG YouTube channel ID
These values are configured in both /en/sessions/recordings.astro and /es/sessions/recordings.astro.
The Events system is the core feature for managing SIMG session content. It uses Astro Content Collections with a comprehensive Zod schema to validate event data.
There are 3 ways to create a new event:
bun run new-eventThis launches an interactive prompt that asks for:
- Title (EN + ES), description, date, time, speaker
- Event type (in-person / virtual / hybrid)
- Location details, meeting links, tags
- Body content in markdown
- Optional thumbnail generation
It automatically creates both EN and ES markdown files.
Create a new .md file in both src/content/events/en/ and src/content/events/es/:
---
title: "Your Event Title"
description: "Short description of the event"
date: 2026-04-01
time: "2:00 PM - 4:00 PM"
speaker: "SIMG Research Group"
eventType: "in-person"
location: "Universidad Nacional de Colombia, Bogotá"
building: "404 - Yu Takeuchi"
room: "202-405"
meetingLink: ""
meetingPlatform: ""
googleCalendarLink: ""
recurrent: false
thumbnail: "/images/events/your-event-slug.svg"
banner: ""
resources: []
recordingUrl: ""
tags: ["AI", "Research"]
status: "upcoming"
lang: "en"
translationKey: "your-event-slug"
participants: []
duration: "1h 30m"
---
Your event description in markdown format here.Then generate the thumbnail:
node scripts/generate-thumbnail.mjs --title "Your Event Title" --date "2026-04-01" --type "in-person"Go to Actions → Create Event → Run workflow and fill in the form fields. This automatically:
- Creates EN + ES markdown files
- Generates a thumbnail
- Opens a Pull Request with the new event
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | ✅ | Event title |
description |
string | ✅ | Short description |
date |
date | ✅ | Event date |
time |
string | ✅ | Time range (e.g., "2:00 PM - 4:00 PM") |
speaker |
string | ✅ | Speaker or group name |
eventType |
enum | ✅ | in-person, virtual, or hybrid |
location |
string | ❌ | Venue name |
building |
string | ❌ | Building name/number |
room |
string | ❌ | Room number |
meetingLink |
string | ❌ | URL for virtual events |
meetingPlatform |
string | ❌ | Platform name (Google Meet, Zoom, etc.) |
thumbnail |
string | ❌ | Path to thumbnail image |
resources |
array | ❌ | Array of { title, url, type } objects |
recordingUrl |
string | ❌ | YouTube/recording URL |
tags |
string[] | ❌ | Topic tags |
status |
enum | ✅ | upcoming, live, completed, or cancelled |
lang |
string | ✅ | en or es |
translationKey |
string | ✅ | Matches EN/ES counterparts |
duration |
string | ❌ | Duration text (e.g., "1h 30m") |
The thumbnail generator (scripts/generate-thumbnail.mjs) supports two modes:
- AI Generation (Nano Banana): If
BANANA_API_KEYandBANANA_MODEL_KEYenvironment variables are set, it generates AI-powered thumbnails via Banana.dev - SVG Fallback: Creates branded SVG thumbnails with SIMG colors (Yellow #F4C542 + Blue #2E6DB4), event title, date, type badge, and turtle icon
# Generate for a single event
node scripts/generate-thumbnail.mjs --title "GPU Programming" --date "2026-02-27" --type "in-person"
# Generate for all events
bun run generate-thumbnailsTo mark an event as completed (e.g., after the session):
- Open the event's
.mdfile (both EN and ES) - Change
status: "upcoming"tostatus: "completed" - Optionally add
recordingUrl: "https://youtube.com/watch?v=..."with the recording link
The project includes several testing scripts to verify that all components are working correctly.
Test YouTube API and Channel Connection:
# Test if YouTube API can fetch videos from your channel
node scripts/test-youtube.mjsExpected Output:
- ✅ Environment variables loaded correctly
- ✅ API key validation successful
- ✅ Channel found with video count
- ✅ Video list with titles, dates, and durations
Common Issues:
❌ API Key Error: API key not valid→ Check yourPUBLIC_YOUTUBE_API_KEY❌ No videos found→ Channel may be private or have no public videos❌ Channel ID Error→ VerifyPUBLIC_YOUTUBE_CHANNEL_IDis correct
Helper Script - Find Channel ID:
# Automatically find your channel ID using your API key
node scripts/get-youtube-channel-id.mjs YOUR_API_KEYTest Contact Form Integration:
-
Local Testing:
bun run dev # Navigate to: http://localhost:4321/en/contact # Fill and submit the contact form
-
Environment Variables Check:
# Verify all EmailJS variables are set cat .env | grep EMAILJS
Expected Behavior:
- ✅ Form submits without errors
- ✅ Success message displays
- ✅ Email arrives at
alesanchezpov@gmail.com - ✅ BCC copy sent to
robertgomez.datascience@gmail.com - ✅ Reply-To field set to sender's email
Test Complete Build Process:
# Verify all pages build successfully
bun run build
# Expected: "36 page(s) built" with no errorsTest Event System:
# Test event creation CLI
bun run new-event
# Test thumbnail generation
bun run generate-thumbnailsContent Collections Validation:
# Check for schema errors in events, members, research, blog
bun run build 2>&1 | grep -i errorTest Thumbnail Generation:
# Generate thumbnails for all events
node scripts/generate-thumbnail.mjs --all
# Generate thumbnail for specific event
node scripts/generate-thumbnail.mjs --title "Test Event" --date "2026-04-01" --type "in-person"Verify Thumbnails Load:
# Check thumbnail files exist
ls -la public/images/events/
# Test thumbnail accessibility in dev server
curl -I http://localhost:4321/images/events/gpu-programming-model.svg
# Expected: HTTP/1.1 200 OKTest Events Page:
# Visit events page and verify:
# - Map loads correctly
# - Events display in grid (max 3 each section)
# - YouTube videos appear
# - Event cards have thumbnails
open http://localhost:4321/en/eventsTest Internationalization:
# Verify both languages work
open http://localhost:4321/en/events
open http://localhost:4321/es/eventsRequired Variables Check:
# Run this to verify all required env vars are set:
echo "=== YouTube API ==="
echo "API Key: ${PUBLIC_YOUTUBE_API_KEY:0:20}..."
echo "Channel ID: ${PUBLIC_YOUTUBE_CHANNEL_ID}"
echo ""
echo "=== EmailJS ==="
echo "Public Key: ${PUBLIC_EMAILJS_PUBLIC_KEY:0:15}..."
echo "Service ID: ${PUBLIC_EMAILJS_SERVICE_ID}"
echo "Template ID: ${PUBLIC_EMAILJS_TEMPLATE_ID}"Production Deployment Test:
-
Vercel Environment Variables:
- Go to Vercel Project → Settings → Environment Variables
- Verify all
PUBLIC_*variables match your local.env - Redeploy:
vercel --prod
-
Live Site Verification:
# Test production YouTube integration curl -s "https://simg-website.vercel.app/en/events" | grep -o 'video-card' # Test contact form on live site open https://simg-website.vercel.app/en/contact
New Event Creation Flow:
- Run
bun run new-event - Fill out event details
- Verify files created in
src/content/events/en/andsrc/content/events/es/ - Check thumbnail generated in
public/images/events/ - Run
bun run devand visit/en/events - Confirm new event appears in grid
CI/CD Testing (GitHub Actions):
- Go to GitHub → Actions → "Create Event"
- Run workflow with test data
- Verify PR is created with new event files
- Check that build passes in PR
Lighthouse Testing:
# Install lighthouse CLI
npm install -g lighthouse
# Test key pages
lighthouse http://localhost:4321/en/events --only-categories=performance,accessibility --chrome-flags="--headless"
lighthouse http://localhost:4321/es/events --only-categories=performance,accessibility --chrome-flags="--headless"Expected Scores:
- Performance: >90
- Accessibility: >95
| Issue | Symptom | Solution |
|---|---|---|
| YouTube videos not loading | "Loading videos..." persists | Check API key and channel ID in .env |
| Contact form fails | Form submits but no email received | Verify EmailJS service ID and template ID |
| Thumbnails broken | Gray/missing thumbnails | Run node scripts/generate-thumbnail.mjs --all |
| Build fails | Astro build errors | Check content collection schema in config.ts |
| Events not appearing | Empty events grid | Verify event status is "upcoming" or "completed" |
| Dark/light mode broken | Theme doesn't persist | Check localStorage in browser devtools |
Create a comprehensive test with one command:
# Add this to package.json scripts:
"test": "node scripts/test-youtube.mjs && bun run build && echo '✅ All systems operational!'"
# Run full health check:
bun run test- 600+ lines of comprehensive CSS custom properties
- Light/Dark Mode with localStorage persistence
- FOUC Prevention via inline script in
<head> - Glassmorphism effects throughout
- Responsive design with mobile-first approach
- Primary: Orange (#f97316, #ea580c)
- Secondary: Blue (#0ea5e9, #8b5cf6)
- Gradients: 50/50 orange-blue in text, orange-dominant in backgrounds
- Theme toggle button (sun/moon icons)
- Language selector with hover dropdown
- Smooth scroll animations
- Hover effects with orange accents
- Contact Form Integration:
- Client-side email delivery via EmailJS
- No backend required
- Form validation and error handling
- Loading states and user feedback
The website supports both English and Spanish with:
- Parallel page structure (
/en/and/es/) - Automatic language detection from browser
- Manual language switching via header dropdown
- Bilingual content collections
The contact form uses EmailJS to send emails directly from the browser without a backend server.
-
Create EmailJS Account
- Visit emailjs.com
- Sign up for free (200 emails/month)
-
Configure Email Service
- Add Gmail service
- Connect with
alesanchezpov@gmail.com
-
Create Email Template
- Template ID: Save this for step 4
- Subject:
Nuevo mensaje de contacto SIMG: {{subject}} - To:
alesanchezpov@gmail.com - Bcc:
robertgomez.datascience@gmail.com - Reply-To:
{{reply_to}}
-
Configure Environment Variables
Copy the example file and fill in your credentials:
cp .env.example .env
Edit
.envwith your EmailJS credentials:PUBLIC_EMAILJS_PUBLIC_KEY=your_public_key_here PUBLIC_EMAILJS_SERVICE_ID=your_service_id_here PUBLIC_EMAILJS_TEMPLATE_ID=your_template_id_here
Where to find these values:
PUBLIC_EMAILJS_PUBLIC_KEY: EmailJS Dashboard → Account → API KeysPUBLIC_EMAILJS_SERVICE_ID: EmailJS Dashboard → Email Services (shows next to your Gmail service)PUBLIC_EMAILJS_TEMPLATE_ID: EmailJS Dashboard → Email Templates (shows next to your template)
Important: The
.envfile is already in.gitignoreand won't be committed to Git. -
For Production Deployment (Vercel)
Add the environment variables in your Vercel project settings:
- Go to Project Settings → Environment Variables
- Add all three
PUBLIC_EMAILJS_*variables - Deploy or redeploy your site
-
Test the Form
- Restart your dev server:
npm run dev - Fill out the contact form on the website
- Check both email inboxes for the message
- Verify reply-to functionality
- Restart your dev server:
The form sends these variables to EmailJS:
{{from_name}}- Sender's name{{reply_to}}- Sender's email address{{subject}}- Message subject{{message}}- Message content
- ✅ Environment variables are safe: The
.envfile is gitignored and never committed - ✅ Public keys are OK: EmailJS public keys are meant to be visible in client-side code
- ✅ Rate limiting: EmailJS has built-in rate limiting and spam protection
- ✅ Production ready: Works seamlessly with Vercel, Netlify, and other platforms
The session recordings page uses YouTube Data API v3 to automatically fetch and display the latest videos from the SIMG channel.
-
Create Google Cloud Project
- Visit Google Cloud Console
- Create a new project or select an existing one
- Name it (e.g., "SIMG Website")
-
Enable YouTube Data API v3
- In the Cloud Console, go to "APIs & Services" → "Library"
- Search for "YouTube Data API v3"
- Click "Enable"
-
Create API Credentials
- Go to "APIs & Services" → "Credentials"
- Click "Create Credentials" → "API Key"
- Copy the generated API key
- (Optional) Restrict the key to YouTube Data API v3 for security
-
Get Your YouTube Channel ID
- Visit your YouTube channel:
https://www.youtube.com/@simg-UN - Click "About" tab
- Click "Share channel" → "Copy channel ID"
- Alternative: Use the URL structure to find it
- Visit your YouTube channel:
-
Configure Environment Variables
Edit your
.envfile and add the YouTube credentials:# YouTube Data API v3 PUBLIC_YOUTUBE_API_KEY=your_youtube_api_key_here PUBLIC_YOUTUBE_CHANNEL_ID=your_channel_id_here
Where to find these values:
PUBLIC_YOUTUBE_API_KEY: Google Cloud Console → APIs & Services → CredentialsPUBLIC_YOUTUBE_CHANNEL_ID: YouTube Channel → About → Share → Copy channel ID
-
For Production Deployment (Vercel)
Add the environment variables in your Vercel project settings:
- Go to Project Settings → Environment Variables
- Add both
PUBLIC_YOUTUBE_API_KEYandPUBLIC_YOUTUBE_CHANNEL_ID - Redeploy your site
-
Test the Integration
- Restart your dev server:
bun run dev - Navigate to
/en/sessions/recordingsor/es/sessions/recordings - Verify that the latest 3 videos from your channel appear
- Click on a video to ensure it opens on YouTube
- Restart your dev server:
The integration automatically fetches:
- Video Thumbnails: High-quality images (480x360px)
- Video Titles: Displayed with 2-line truncation
- Publication Dates: Formatted in English/Spanish
- Video Duration: Converted from ISO 8601 format (PT1H2M3S → 1:02:03)
- Direct Links: Click any video card to watch on YouTube
YouTube Data API v3 has quota limits:
- Daily Quota: 10,000 units per day (default)
- Search Request Cost: ~100 units
- Videos Request Cost: ~1 unit
- Total per page load: ~101 units
- Daily page loads possible: ~99 loads (more than enough for normal traffic)
If the API key is not configured or requests fail:
- Displays friendly error message
- Provides direct link to YouTube channel
- Doesn't break the page functionality
- ✅ API restrictions recommended: Limit the API key to YouTube Data API v3 only
- ✅ HTTP referrer restrictions: Set allowed domains in Google Cloud Console
- ✅ Public exposure is safe: Client-side API keys are normal for YouTube API
- ✅ Rate limiting: Google handles quota limits automatically
The site is optimized for deployment on:
- Vercel (recommended)
- Netlify
- GitHub Pages
- Any static hosting service
Build command: npm run build
Output directory: ./dist/
Research Supervisor: [Professor Name]
Student Leaders: Multiple contributors
Alumni: Past members who've contributed to the project
This project is maintained by the SIMG research seminar at Universidad Nacional de Colombia.
We welcome contributions! Please feel free to submit issues or pull requests.
For major changes, please open an issue first to discuss what you would like to change.
Made with ❤️ by the SIMG Team
