- Replace chromedp.Flag() with chromedp.NoSandbox
- Replace chromedp.Flag() with chromedp.DisableGPU
- Apply fix to both GenerateFromURL and GenerateFromURLWithOptions
- Fixes Ubuntu 23.10+ AppArmor sandbox restrictions
The chromedp.Flag() method wasn't properly disabling the sandbox, causing
Chrome to crash with 'No usable sandbox!' fatal error in CI.
- Configure chromedp with headless mode and CI-friendly flags
- Add --no-sandbox flag (required for Docker/CI environments)
- Add --disable-gpu and --disable-dev-shm-usage for stability
- Use NewExecAllocator with DefaultExecAllocatorOptions
This fixes 'chrome failed to start' errors in GitHub Actions CI where
Chrome needs to run without a display and with relaxed sandbox restrictions.
- Add findDataFile() helper to search up directory tree for data files
- Fixes tests running from subdirectories (internal/handlers)
- Install Chrome in GitHub Actions for PDF generation tests
This resolves test failures that have existed since PDF tests were introduced:
- Error: 'open data/cv-es.json: no such file or directory'
- Error: 'chrome failed to start'
Tests now properly locate data files from any working directory and
have Chrome available for PDF generation in CI environment.
**Issue 1: URL corruption in "See this CV in..." links**
- Bug: replaceYearPlaceholder used fmt.Sprintf on ALL URLs
- URLs like "/?lang=es" were corrupted to "/?lang=es%!(EXTRA string=2025)"
- Fix: Changed to strings.ReplaceAll("{{YEAR}}", year)
- Result: Only replaces actual {{YEAR}} placeholders, leaves other URLs intact
**Issue 2: Download filename not respected**
- Bug: Shortcut URLs (cv-jamr-2025-en.pdf) redirected with HTTP 301
- Browsers used original URL filename instead of Content-Disposition header
- Fix: Generate PDF directly in DefaultCVShortcut handler
- Result: Returns PDF with correct filename in Content-Disposition header
Files changed:
- internal/models/cv.go: Fixed replaceYearPlaceholder function
- internal/handlers/cv.go: Changed redirect to direct PDF generation
Both fixes verified:
- "See this CV in Spanish" link: href="/?lang=es" ✓
- Download link: filename=cv-jamr-2025-en.pdf ✓
## Shortcut URLs
- New routes: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf)
- Year validation: Only current year accepted, returns 404 for past/future
- Auto-redirects (301) to: /export/pdf?lang={lang}&length=short&icons=show&version=with_skills
- Both languages supported: en and es
## PDF Modal Updates
- Replaced "Current View" option with "Default CV (Recommended)"
- Visual highlighting: purple gradient badge, star emoji ⭐, bold text
- Uses shortcut URL with dynamic year detection
- Clear recommendation for users (5 pages, short with skills)
## Technical Details
- Handler: DefaultCVShortcut() in internal/handlers/cv.go
- Pattern check in Home() handler for proper routing
- Helper function: window.openPdfModal() for references section
- Documentation: PDF-SHORTCUT-IMPLEMENTATION.md
Benefits:
- Memorable, shareable URLs (juan.andres.morenorub.io/cv-jamr-2025-en.pdf)
- Auto-updates yearly without code changes
- Clear user guidance for recommended CV format
Changed PDF filename format to use hyphens instead of underscores for
consistency with other filename components, while keeping API parameter
as `version=with_skills`.
## Changes
**Backend:**
- internal/handlers/cv.go: Add underscore-to-hyphen conversion in filename generation
- New logic: `strings.ReplaceAll(version, "_", "-")` for filename only
- API parameter unchanged: still accepts `version=with_skills`
**Tests:**
- internal/handlers/pdf_test.go: Update expected filenames to use hyphens
- cv-*-with_skills-*.pdf → cv-*-with-skills-*.pdf
**Documentation:**
- Updated all PDF filename references to use hyphens
- PDF-EXPORT-FEATURE.md
- doc/LONG-PDF-GENERATION.md
- PDF-VALIDATION-REPORT.md (new validation report)
**PDFs:**
- Regenerated all 8 PDFs with new naming convention
- Old: cv-short-with_skills-jamr-2025-en.pdf
- New: cv-short-with-skills-jamr-2025-en.pdf
## Examples
API calls unchanged:
- GET /export/pdf?version=with_skills (still works)
Generated filenames:
- cv-short-with-skills-jamr-2025-en.pdf ✓
- cv-long-with-skills-jamr-2025-es.pdf ✓
**Tests:** All passing ✓
**API:** Backwards compatible ✓
Problem: Inline icons embedded in responsibilities, courses, and
projects had explicit width='60' height='60' attributes that made
them too large (60px instead of ~16px).
Solution:
- Added CSS with !important to override inline width/height attributes
- Targeted inline icons in:
* Course responsibilities and descriptions
* Project descriptions and technologies
* Experience responsibilities (within divs)
- Preserved large icons (80px) for main company/course/project logos
Changes:
- static/css/03-components/_courses.css: Override to 1.2em
- static/css/03-components/_projects.css: Override to 1.2em
- static/css/03-components/_cv-section.css: Override to 1.2em
Test Results:
✅ 7 course inline icons: 16px × 16px
✅ Main company icons: 80px × 80px (preserved)
Complete color theme system (light/dark/auto) with dynamic UI:
Features:
- Color theme switcher with auto/light/dark modes
- Dynamic button colors on hover (purple/yellow/blue per theme)
- localStorage persistence across sessions
- Proper button positioning (desktop and mobile)
- Mobile: 5-button layout with theme before info button
Fixes:
- CSP updated to allow jsDelivr CDN for iconify icons
- Button repositioning: Download PDF and Print Friendly at top
- Hover-only colors (not persistent)
- Mobile button order corrected
Files:
- static/css/color-theme.css - Theme system with CSS variables
- static/js/color-theme.js - Theme switching logic
- templates/partials/color-theme-switcher.html - Button component
- internal/middleware/security.go - CSP fix for jsDelivr
- tests/mjs/13-color-theme-switcher.test.mjs - Comprehensive test
- tests/TEST-SUMMARY.md - Updated test documentation
- Removed over-engineered cache system for static CV data that only changes on deployment
- Extracted all route configuration to internal/routes/routes.go for better organization
- Implemented rate limiting and cache control middleware for PDF endpoint protection
Previously, HTML in short descriptions was being escaped and displayed
as raw text instead of rendering properly. This happened because the
safeHTML template function had been removed for security reasons.
Changes:
- Added safeHTML function back to template.FuncMap (template.go:53-55)
- Updated three template locations to use safeHTML pipe:
* Experience descriptions (cv-content.html:122)
* Award descriptions (cv-content.html:180)
* Project descriptions (cv-content.html:232)
Security note:
The safeHTML function is safe to use here because CV data comes from
trusted YAML files controlled by the site owner, not user input.
Clear documentation added to prevent misuse with untrusted content.
Examples now rendering correctly:
- Award: "Premio por excelencia en marketing B2B...con <a href=...>Clicplan</a>"
- Projects: Links to Lidering, Jorpack, Delivery Bikes BCN, Mobbeel
- Added missing ViewSourceSubtext field to models.InfoModal
- Resolves template rendering error that was preventing JavaScript from loading
- Fixes toggleTheme is not defined error
- Add proper error handling for w.Write() call in ExportPDF function
- Fixes golangci-lint errcheck issue at cv.go:285
- Log error if response write fails
References section improvements:
- Remove TwenTIC recommendation letter
- Add Megabanner recommendations with author attribution (David Amorós)
- Update Presentation Letter URL to Domestika profile
- Update Chronological CV link to trigger PDF download modal
- Add 'action' field to Reference struct for custom behaviors
Print layout fixes:
- Show experience company logos in print view (40×40px)
- Remove print media query that was hiding .company-logo elements
- Maintain consistency with other logo types (courses, projects, awards)
Technical changes:
- Add Action field to Reference model with omitempty JSON tag
- Implement conditional rendering for downloadPDF action in template
- Links with action="downloadPDF" now call openPdfModal() function
Comprehensive mobile optimization (≤768px) for improved consistency and readability:
- Center profile photo between name and intro text on mobile
- Unify all logo/icon sizes to 60×60px for visual consistency
- Standardize spacing across experience, courses, projects, and awards
- Reduce font sizes proportionally for better mobile readability
- Remove justified text alignment (except intro/skills) to prevent awkward spacing
- Apply consistent 1rem gaps and 1.5rem padding throughout
- Optimize sidebar items with reduced margins and font sizes
Technical improvements:
- Add comprehensive mobile breakpoint rules at 768px
- Implement flexible photo positioning (absolute on desktop, static on mobile)
- Ensure uniform typography scale across all content types
- Created separate ui-en.json and ui-es.json files for UI strings
- Removed 'ui' section from cv-en.json and cv-es.json
- Added LoadUI() function to load UI translations separately
- Updated handlers to load UI data independently from CV data
- Updated template to use .UI instead of .CV.UI
This separation follows proper concerns:
- CV JSON files contain only professional CV content
- UI JSON files contain only application interface strings
- Each can be updated independently without affecting the other
- Added 'ui' section to cv-en.json and cv-es.json with modal translations
- Updated Go models to include UI, InfoModal, and TechStack structs
- Modified template to use JSON data instead of inline if/else statements
- Used template.HTML for Description field to allow HTML rendering
- Modal now fully supports English and Spanish translations
Translations include:
- Modal title
- Description text
- Tech stack labels (HTML5 Semántico vs Semantic HTML5, etc.)
- View source button text
- Implemented origin checker middleware to prevent external sites from hotlinking the PDF generation endpoint
- Added rate limiter (3 requests per minute per IP) to protect resource-intensive PDF operations
- Configured allowed origins via ALLOWED_ORIGINS environment variable with localhost defaults for development
## Project Title Links
- Add projectName and projectDesc fields to Project struct
- Split project titles to make only project name clickable
- Update template logic for conditional title rendering
- Apply changes to both English and Spanish versions
## Experience Duration Display
- Restore duration calculation display in experience section
- Move duration from date line to after company name
- Style duration in light gray (#999) for subtle appearance
- Calculate durations dynamically (e.g., "4 years 10 months")
## Certifications Section Enhancement
- Add Codecademy Certifications (2022-2024) with AI Transformers and React courses
- Add LinkedIn Learning Certifications (2019-2020) with 5 professional courses
- Implement colored icon system with brand colors (purple, cyan, green, etc.)
- Use responsibilities format matching Third Party Contributions layout
- Reorder courses chronologically (most recent first)
## localStorage Improvements
- Save CV length preference (short/long)
- Save logos visibility preference (show/hide)
- Save theme preference (default/clean)
- Restore all preferences on page load
## Navigation UX Enhancements
- Fix scroll positioning to show sections below action bar
- Add keepHeaderVisible flag to maintain header visibility after navigation
- Ensure smooth scrolling with proper offset calculations
- Reset flag on scroll up to restore normal hide/show behavior
## Files Modified
- internal/models/cv.go: Add ProjectName, ProjectDesc fields
- templates/cv-content.html: Update project and experience rendering
- static/css/main.css: Add duration-text styling
- templates/index.html: Enhance scroll behavior and localStorage
- data/cv-*.json: Add certifications, split project titles, reorder courses
- static/images/courses/: Add codecademy.png, linkedin.png
- Add courseLogo field to Course struct in Go
- Update template to display course logos with fallback to school icon
- Add CSS styling for course logo images (80x80px, contained, bordered)
- Add logos to three courses in both English and Spanish:
* Servoy World 2011 (servoy.png)
* Train the Trainers / Formador de Formadores (forem.png)
* Windows 2003 Server (camaracomercio.png)
- Course logos stored in /static/images/courses/ folder
- Similar implementation to project logos with error handling
- Remove hardcoded startDate from La Porra project
- Add gitRepoUrl field to Project struct for dynamic date fetching
- Implement backend logic to fetch first commit date from git repositories
- Add processProjectDates function to calculate dates dynamically
- Update template to display computed dates and dynamic "Present/Presente"
- Add support for both static and git-based project start dates
When a project has a gitRepoUrl, the system automatically fetches the first
commit date from the repository. For current projects, it displays
"Present" (English) or "Presente" (Spanish) dynamically from the backend.
The La Porra project now uses git repository path for date calculation
instead of hardcoded JSON values.
- Add sidebar field to SkillCategory model for explicit left/right control
- Update splitSkills to respect sidebar field instead of automatic splitting
- Add responsive CSS for 1024-1280px: collapse labels, icons-only buttons, EN/ES language selector
- Remove language switcher animations
- Ensure desktop view (>1280px) always shows full sidebar content
- Move Databases and Infrastructure to right sidebar
- Reduce font sizes in responsive range
- Update project logos (Lidering, Jorpack, Delivery Bikes)
- Update splitSkills logic to put 7 categories on left sidebar, 6 on right
- Reorder skills: Frontend Technologies now at position 5, Legacy Enterprise Technologies at position 6 (last on left)
- Apply changes to both English and Spanish CV data
- Clean up unused enhanced template and CSS files
Left sidebar (Page 1):
1. AI-Assisted Development
2. SAP Technologies
3. Programming Languages
4. Go Ecosystem
5. JavaScript Ecosystem
6. Frontend Technologies
7. Legacy Enterprise Technologies
Right sidebar (Page 2):
8. Backend Technologies
9. Databases
10. Infrastructure & Servers
11. DevOps & CI/CD
12. Team Management
13. Design Tools
Added visual badge to show CDC Starter Kit is now maintained by SAP:
1. Go struct changes:
- Added MaintainedBy field to Project struct (cv.go:108)
2. Data updates:
- Added "maintainedBy": "SAP" to CDC Starter Kit in cv-en.json
- Added "maintainedBy": "SAP" to CDC Starter Kit in cv-es.json
3. Template changes:
- Added maintained-badge display in project header (cv-content.html:226-228)
- Shows "MAINTAINED BY SAP" (EN) or "MANTENIDO POR SAP" (ES)
4. CSS styling:
- Added .maintained-badge style with blue background (#3498db)
- Matches current-badge and expired-badge styling
Badge appears next to project title to highlight SAP's ongoing maintenance.
Changes:
- Created /static/images/projects/ folder for project logos
- Added ProjectLogo field to Project struct
- Updated cv-en.json and cv-es.json with projectLogo fields:
- somosunaola.png for Somos Una Ola
- herrumbre-vivo.png for Herrumbre Vivo Arte
- Updated template to display project logos with fallback to web icon
- Added CSS styling for project logo images (80×80px)
- Renamed section from "Projects" to "Personal Projects"
Logo images will display when PNG files are added to /static/images/projects/
Added new Projects section with two initial projects:
- Somos Una Ola - Beach cleaning initiative website (Node.js/Express/HTMX)
- Herrumbre Vivo Arte - Artist portfolio for recycled art
Changes:
- Added projects data to cv-en.json and cv-es.json
- Updated Project struct in models/cv.go with all required fields
- Added Projects section CSS matching Awards/Courses styling (80×80px icons)
- Added Projects template with icons, current badges, and Domestika link
- Reordered sections: Courses → Projects → Awards (as requested)
Features:
- Clickable project titles linking to websites
- Current badge for ongoing projects
- Period and location display
- Short descriptions (always visible)
- Responsibilities list (long version only)
- Technologies list (long version only)
- Footer with link to Domestika portfolio
- Updated personal.behance to personal.domestika in both EN and ES
- Changed all Behance URLs to Domestika portfolio URL
- Updated footer label from "behance_" to "domestika_"
- Updated JSON-LD schema to reference Domestika
- Updated Go model struct field from Behance to Domestika
- New portfolio URL: https://www.domestika.org/es/txeo/portfolio
- Added safeHTML template function to prevent HTML escaping
- Applied safeHTML filter to ShortDescription fields in Experience, Awards, and Courses
- Applied safeHTML filter to Responsibilities in all sections
- Links and HTML formatting now render correctly instead of showing raw HTML tags
- Updated action bar with transparent buttons (colored on hover only)
- Repositioned language selector after CV title for better flow
- Simplified toggle labels (removed parentheses values)
- Changed button styling: transparent by default, green/gray on hover
- Updated name format to "Moreno Rubio, Juan Andrés" with right alignment
- Added LIVGolf experience (Apr 2024-present) with detailed responsibilities
- Updated profile photo to dni.jpeg
- Refined summary text focusing on consultant/analyst/developer roles
- Added award logos (clicplan.png, drolosoft.png, teseo.png)
- Implemented smooth logo animations (fade/scale transitions)
- Adjusted toggle dimensions (80px wide, 30px tall) with smaller icons (16x16)
- Added breathing room to title and icon with proper padding
- Removed italic styling from name per user preference
Experience Section Improvements:
- Increased company logo size from 48px to 64px
- Added default office building icon for companies without logos
- Increased spacing between entries (2.5rem margin, 2rem padding)
- Added visible separator lines (2px solid #ddd)
- Made dates bold (weight: 600) and larger (1.05rem)
- Changed date color to #555 for better contrast
Dynamic Duration Calculation:
- Added automatic years/months calculation for each position
- Format: "(4 years 10 months)", "(2 years)", "(6 months)"
- Smart pluralization in English and Spanish
- Handles current positions (calculates to today)
- Added Duration field to Experience model
Iconify Integration:
- Added Iconify library (v3.1.1) for icon management
- Replaced EN/ES text with round flag icons (circle-flags:us, circle-flags:es)
- Updated CV site icon to mdi:file-account
- Replaced toggle text with intuitive icons:
* Short/Long: mdi:file-document-outline / mdi:file-document-multiple-outline
* Logos: mdi:image-off-outline / mdi:image-multiple-outline
- Default company icon: mdi:office-building (64x64px, light gray background)
Logo Display:
- Logos now show by default (toggle checked on page load)
- Toggle controls icon visibility
- Consistent spacing with default icon placeholder
Files modified:
- internal/handlers/cv.go: Added calculateDuration() function
- internal/models/cv.go: Added Duration field to Experience struct
- templates/index.html: Iconify integration, flag icons, toggle icons
- templates/cv-content.html: Duration display, default icon logic
- static/css/main.css: Bold dates, larger font sizes
- static/css/logo-toggle.css: Icon styling, separator lines, spacing
- Add dynamic years of experience calculation from April 1, 2005
- Update professional title badges: ANALYST | TECHNICAL CONSULTANT
- Add all missing skill categories from React CV (Programming Languages, JavaScript Frameworks, PHP Frameworks, Java Frameworks, Application Servers, CMS, Design Tools, Team Management)
- Add complete References section with LinkedIn, Behance, portfolios, and recommendation letters
- Refine years-of-experience subtitle styling to match original design
- Achieve 100% content parity with old React CV (English: 7→14 skill categories)
**Social Links in Footer (Page 2):**
- Replace address/phone with LinkedIn, GitHub, and Behance links
- Maintain email@ link
- All links are clickable and open in new tabs
- Footer displays social media profiles prominently
**Company Logo Toggle Feature:**
- Add "Show logos" toggle switch in top action bar
- Toggle displays company logos (48x48px) to the left of each experience item
- LinkedIn-style layout when logos are shown
- Logos hidden by default, optional display via toggle
- Graceful fallback: missing logos don't break layout (onerror handler)
- Logos directory created at static/images/logos/ with README
**Technical Implementation:**
- New CSS file: logo-toggle.css for toggle switch and logo layout
- JavaScript: toggleLogos() function for show/hide functionality
- Template updates: experience items now support flex layout with logos
- Action bar grid updated to accommodate 4 columns
- Logo display uses CSS class `.show-logos` on `.cv-paper`
- Print CSS: logos hidden in PDF exports by default
**User Experience:**
- Clean toggle switch UI with smooth animations
- Mobile responsive design
- Accessibility: proper ARIA labels for toggle
- Optional feature that doesn't clutter default view
- Professional LinkedIn-style appearance when enabled
Logos can be added to static/images/logos/ directory using filenames
from the companyLogo field in CV JSON data.
- Add companyURL field to Experience model (optional)
- Update cv-content.html template to display company name with conditional link
- Add company-link CSS styling for clickable company names
- Add companyURL data for major companies (Olympic Broadcasting, AENA, SAP, Gigya, Everis, Indra)
- Companies without URLs display as plain text, maintaining flexibility
This addresses the issue where company names were missing from the experience section
and ensures links are included throughout the CV as per the original React version.
**Page 1 Changes:**
- Left sidebar with first half of skills categories
- Main content: Personal info, Education, Skills summary, Experience
- No footer on page 1
**Page 2 Changes:**
- Main content: Awards, Courses, Languages, References, Other
- Right sidebar with second half of skills categories
- Footer with address, phone, email (dark gray #303030 background)
- Same header as page 1
**Technical Implementation:**
- Split skills between left/right sidebars using midpoint calculation in Go handler
- CSS Grid layout: Page 1 (left sidebar + main), Page 2 (main + right sidebar)
- Footer styled to match React CV exactly (centered, horizontal layout)
- Print CSS with proper page breaks for A4 layout
- Mobile responsive: stacks to single column, hides page 2 header
- All links in References section are clickable
**Data Model:**
- Moved Languages, Courses, References, Other from sidebar to page 2 main content
- Skills split evenly between SkillsLeft and SkillsRight template variables
Matches pixel-perfect design from original React CV screenshot.