refactor: Rename 'extended' → 'long' + add compact sidebar fonts
BREAKING CHANGE: API parameter renamed from 'extended' to 'long' ## Breaking Change: Terminology Standardization Renamed 'extended' to 'long' across entire codebase for consistency: **Backend (Go):** - internal/handlers/cv.go (7 locations) - Migration logic to auto-convert 'extended' → 'long' cookies - API validation now rejects 'extended', requires 'long' - Toggle state logic updated - internal/handlers/pdf_test.go (17 occurrences) - Test function renamed: TestExportPDF_ExtendedWithSkills → TestExportPDF_LongWithSkills - All test cases, parameters, and expected filenames updated - internal/pdf/generator.go (2 comment updates) **Frontend:** - PDF-EXPORT-FEATURE.md (3 occurrences) - doc/3-API.md (parameter documentation) - doc/7-CUSTOMIZATION.md (examples updated) - templates/partials/modals/pdf-modal.html (button text, URLs) - static/js/main.js (migration logic) - static/hyperscript/toggles._hs (toggle logic) - tests/mjs/24-pdf-download-params.test.mjs (test expectations) - tests/mjs/test-preference-migration.test.mjs (NEW) - tests/mjs/verify-migration.test.mjs (NEW) **PDFs Renamed:** - cv-extended-with_skills-jamr-2025-en.pdf → cv-long-with_skills-jamr-2025-en.pdf - cv-extended-with_skills-jamr-2025-es.pdf → cv-long-with_skills-jamr-2025-es.pdf **Migration:** Automatic cookie migration from 'extended' → 'long' for seamless UX ## New Feature: Compact Sidebar Fonts Reduces page count for short CV with skills from 6 → 5 pages: **Implementation:** - Location: internal/pdf/generator.go (lines 154-215) - Cookie detection: `cookies["cv-length"] == "short"` - Font reduction: 2-6% (0.94-0.98em) - very subtle - Only activates for: `length=short` + `version=with_skills` - Long version: Always uses full-size fonts **Impact:** - Page count: 6 pages → 5 pages (16.7% reduction) - Readability: Maintained - fonts remain professional - Design philosophy: Subtle, natural content flow **Testing:** - New test: TestPDFGenerator_CompactSidebarFonts - Comprehensive coverage of cookie detection and PDF generation - Manual verification: 5-page PDF with compact but readable fonts **Documentation:** - doc/LONG-PDF-GENERATION.md (NEW, 13 KB) - Complete feature documentation - Implementation details with code examples - Font size breakdown table - Testing and troubleshooting guides - Compact sidebar fonts section (comprehensive) **Files Changed:** - 11 modified (backend + frontend + docs) - 5 new files (2 PDFs, 1 doc, 2 tests) - 2 files renamed (PDFs) **Tests:** All Go tests passing, API validation verified, PDF generation tested
This commit is contained in:
+69
-30
@@ -4,6 +4,45 @@
|
||||
|
||||
The CV application provides a comprehensive PDF export system with three predefined options and dynamic filename generation. Users can download their CV in different formats through an interactive modal interface with an intuitive, clear naming convention.
|
||||
|
||||
## 🎯 Architecture: Dual Rendering Modes
|
||||
|
||||
**Critical Design Decision**: The PDF generator uses **different media emulation** depending on the version:
|
||||
|
||||
### Clean Version (Short CV) - Print Mode
|
||||
- **Uses**: `@media print` CSS rules (default browser print behavior)
|
||||
- **Purpose**: Print-friendly, minimal layout
|
||||
- **Characteristics**:
|
||||
- No skills sidebars (hidden by print.css)
|
||||
- No UI elements (navigation, buttons, footer hidden by print.css)
|
||||
- Print-optimized typography, margins, and page breaks
|
||||
- ~4 pages, ~2.2 MB
|
||||
- Clean, professional print appearance
|
||||
|
||||
### Extended Version (Long CV) - Screen Mode + UI Hiding
|
||||
- **Uses**: `@media screen` CSS (emulated via CDP) + injected CSS to hide UI elements
|
||||
- **Purpose**: Pixel-perfect screen capture with content sidebars, minus UI chrome
|
||||
- **Characteristics**:
|
||||
- Shows skills sidebars (screen layout preserved)
|
||||
- No UI elements (hidden via injected CSS: `.action-bar`, `.navigation-menu`, `footer`, etc.)
|
||||
- Uses natural screen layout (pixel-perfect rendering)
|
||||
- ~16 pages, ~3.6 MB
|
||||
- Full digital experience without UI chrome
|
||||
|
||||
**Why This Approach?**
|
||||
1. **Clean version** needs print-optimized layout (compact, professional)
|
||||
2. **Extended version** needs pixel-perfect screen layout with sidebars visible
|
||||
3. CDP's `emulation.SetEmulatedMedia()` allows switching between `@media print` and `@media screen`
|
||||
4. Injected CSS surgically hides only UI elements without affecting content layout
|
||||
|
||||
**Implementation Details**:
|
||||
- `RenderModePrint`: Uses default `@media print` (hides sidebars + UI via print.css)
|
||||
- `RenderModeScreen`:
|
||||
1. Emulates `@media screen` to preserve natural layout
|
||||
2. Injects CSS to hide UI: `.no-print, .action-bar, .navigation-menu, .hamburger-btn, footer, .back-to-top, .info-button, .info-modal, .error-toast, .cv-title-badges-header, .cv-footer`
|
||||
3. Keeps all content and sidebars with their natural screen styling
|
||||
|
||||
**See**: `internal/pdf/generator.go` lines 146-165 for implementation
|
||||
|
||||
## Feature Specifications
|
||||
|
||||
### Export Options
|
||||
@@ -19,10 +58,10 @@ The CV application provides a comprehensive PDF export system with three predefi
|
||||
#### 2. Long CV (Extended Version - **With Skills**)
|
||||
- **Length**: `extended` (comprehensive information)
|
||||
- **Version**: `with_skills` (includes skills sidebar)
|
||||
- **Page Count**: 8 pages
|
||||
- **Page Count**: ~16 pages (natural screen layout with sidebars preserved)
|
||||
- **Use Case**: Detailed applications requiring full work history with skills showcase
|
||||
- **Parameters**: `?lang={lang}&length=extended&icons=show&version=with_skills`
|
||||
- **Filename**: `cv-extended-with_skills-jamr-{year}-{lang}.pdf`
|
||||
- **Parameters**: `?lang={lang}&length=long&icons=show&version=with_skills`
|
||||
- **Filename**: `cv-long-with_skills-jamr-{year}-{lang}.pdf`
|
||||
|
||||
#### 3. Current View
|
||||
- **Length**: From localStorage (`cv-length`) - mapped to new naming
|
||||
@@ -40,7 +79,7 @@ All exported PDFs follow a consistent, intuitive naming convention:
|
||||
cv-{length}[-{version}]-{initials}-{year}-{lang}.pdf
|
||||
|
||||
WHERE:
|
||||
{length} = short | extended
|
||||
{length} = short | long
|
||||
{version} = OMITTED for clean | with_skills for extended
|
||||
{initials} = User initials (e.g., "jamr")
|
||||
{year} = Current year (2025)
|
||||
@@ -54,9 +93,9 @@ WHERE:
|
||||
| Modal Option | Settings | Generated Filename |
|
||||
|-------------|----------|-------------------|
|
||||
| **Short CV** | short + clean | `cv-short-jamr-2025-es.pdf` |
|
||||
| **Long CV** | extended + with_skills | `cv-extended-with_skills-jamr-2025-en.pdf` |
|
||||
| **Long CV** | long + with_skills | `cv-long-with_skills-jamr-2025-en.pdf` |
|
||||
| **Current View** | short + with_skills | `cv-short-with_skills-jamr-2025-es.pdf` |
|
||||
| **Current View** | extended + clean | `cv-extended-jamr-2025-en.pdf` |
|
||||
| **Current View** | long + clean | `cv-long-jamr-2025-en.pdf` |
|
||||
|
||||
### Comprehensive Combinations Matrix
|
||||
|
||||
@@ -64,8 +103,8 @@ WHERE:
|
||||
|--------|---------|------------------|
|
||||
| **short** | clean | `cv-short-jamr-{year}-{lang}.pdf` |
|
||||
| **short** | with_skills | `cv-short-with_skills-jamr-{year}-{lang}.pdf` |
|
||||
| **extended** | clean | `cv-extended-jamr-{year}-{lang}.pdf` |
|
||||
| **extended** | with_skills | `cv-extended-with_skills-jamr-{year}-{lang}.pdf` |
|
||||
| **long** | clean | `cv-long-jamr-{year}-{lang}.pdf` |
|
||||
| **long** | with_skills | `cv-long-with_skills-jamr-{year}-{lang}.pdf` |
|
||||
|
||||
### Dynamic Features
|
||||
|
||||
@@ -106,8 +145,8 @@ For backwards compatibility, the system automatically maps old localStorage valu
|
||||
|
||||
```javascript
|
||||
// Old → New mapping (for backwards compatibility)
|
||||
'long' → 'extended'
|
||||
'extended' (theme) → 'with_skills'
|
||||
'long' → 'long'
|
||||
'long' (theme) → 'with_skills'
|
||||
// Note: 'short' now stays as 'short' (no longer maps to 'detailed')
|
||||
'clean' → 'clean' (unchanged)
|
||||
```
|
||||
@@ -220,8 +259,8 @@ This defense-in-depth approach guarantees light mode PDFs even if:
|
||||
│ └── pdf/
|
||||
│ ├── cv-short-jamr-2025-es.pdf
|
||||
│ ├── cv-short-jamr-2025-en.pdf
|
||||
│ ├── cv-extended-with_skills-jamr-2025-es.pdf
|
||||
│ └── cv-extended-with_skills-jamr-2025-en.pdf
|
||||
│ ├── cv-long-with_skills-jamr-2025-es.pdf
|
||||
│ └── cv-long-with_skills-jamr-2025-en.pdf
|
||||
├── data/
|
||||
│ ├── cv-es.json # Spanish CV data with {{YEAR}} placeholders
|
||||
│ └── cv-en.json # English CV data with {{YEAR}} placeholders
|
||||
@@ -237,13 +276,13 @@ This defense-in-depth approach guarantees light mode PDFs even if:
|
||||
|
||||
**Parameter Validation:**
|
||||
```go
|
||||
// Length parameter: "short" or "extended"
|
||||
// Length parameter: "short" or "long"
|
||||
length := r.URL.Query().Get("length")
|
||||
if length == "" {
|
||||
length = "short"
|
||||
}
|
||||
if length != "short" && length != "extended" {
|
||||
HandleError(w, r, BadRequestError("Unsupported length. Use 'short' or 'extended'"))
|
||||
if length != "short" && length != "long" {
|
||||
HandleError(w, r, BadRequestError("Unsupported length. Use 'short' or 'long'"))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -285,7 +324,7 @@ if version == "clean" {
|
||||
|
||||
**Examples:**
|
||||
- short + clean → `cv-short-jamr-2025-es.pdf`
|
||||
- extended + with_skills → `cv-extended-with_skills-jamr-2025-en.pdf`
|
||||
- long + with_skills → `cv-long-with_skills-jamr-2025-en.pdf`
|
||||
|
||||
### Frontend: Modal Interaction with Legacy Mapping
|
||||
|
||||
@@ -303,13 +342,13 @@ function downloadPDF() {
|
||||
url = `/export/pdf?lang=${lang}&length=short&icons=show&version=clean`;
|
||||
} else if (selectedFormat === 'long') {
|
||||
// Long CV: with skills sidebar, extended length
|
||||
url = `/export/pdf?lang=${lang}&length=extended&icons=show&version=with_skills`;
|
||||
url = `/export/pdf?lang=${lang}&length=long&icons=show&version=with_skills`;
|
||||
} else if (selectedFormat === 'current') {
|
||||
// Current view: use localStorage settings with mapping
|
||||
let currentLength = localStorage.getItem('cv-length') || 'short';
|
||||
|
||||
// Map old values to new naming convention
|
||||
if (currentLength === 'long') currentLength = 'extended';
|
||||
if (currentLength === 'long') currentLength = 'long';
|
||||
// 'short' stays as 'short' - no mapping needed
|
||||
|
||||
const currentIcons = localStorage.getItem('cv-icons') || 'show';
|
||||
@@ -340,7 +379,7 @@ Tests the modal interface and user interactions:
|
||||
#### 2. Parameter Validation Test: `24-pdf-download-params.test.mjs`
|
||||
Tests PDF export parameters and filename generation:
|
||||
- ✅ Short CV parameters: `length=short&version=clean`
|
||||
- ✅ Long CV parameters: `length=extended&version=with_skills`
|
||||
- ✅ Long CV parameters: `length=long&version=with_skills`
|
||||
- ✅ Current View parameters: reads from localStorage with mapping
|
||||
- ✅ Filename format: `cv-{length}[-{version}]-{initials}-{year}-{lang}.pdf`
|
||||
- ✅ Version omitted for clean
|
||||
@@ -397,15 +436,15 @@ curl -O http://localhost:1999/export/pdf?lang=es&length=short&icons=show&version
|
||||
# Filename: cv-short-jamr-2025-es.pdf
|
||||
|
||||
# Extended with skills CV in English
|
||||
curl -O http://localhost:1999/export/pdf?lang=en&length=extended&icons=show&version=with_skills
|
||||
# Filename: cv-extended-with_skills-jamr-2025-en.pdf
|
||||
curl -O http://localhost:1999/export/pdf?lang=en&length=long&icons=show&version=with_skills
|
||||
# Filename: cv-long-with_skills-jamr-2025-en.pdf
|
||||
```
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
### Why This Naming Convention?
|
||||
|
||||
1. **Clarity**: "short" and "extended" clearly communicate content depth
|
||||
1. **Clarity**: "short" and "long" clearly communicate content depth
|
||||
2. **Simplicity**: Version omitted for clean keeps filenames concise
|
||||
3. **Consistency**: All components follow the same pattern
|
||||
4. **Intuitive**: Non-technical users can understand what each filename means
|
||||
@@ -416,7 +455,7 @@ curl -O http://localhost:1999/export/pdf?lang=en&length=extended&icons=show&vers
|
||||
| Old Naming | New Naming | Improvement |
|
||||
|-----------|------------|-------------|
|
||||
| `cv-detailed-jamr-2025-es.pdf` (v1) | `cv-short-jamr-2025-es.pdf` | Simpler, more intuitive |
|
||||
| `cv-long-extended-en-jamr-2025.pdf` | `cv-extended-with_skills-jamr-2025-en.pdf` | More descriptive, better clarity |
|
||||
| `cv-long-extended-en-jamr-2025.pdf` | `cv-long-with_skills-jamr-2025-en.pdf` | More descriptive, better clarity |
|
||||
| Language before year | Language after year | Better organization |
|
||||
|
||||
## Maintenance
|
||||
@@ -434,11 +473,11 @@ curl -o static/pdf/cv-short-jamr-2025-en.pdf \
|
||||
"http://localhost:1999/export/pdf?lang=en&length=short&icons=show&version=clean"
|
||||
|
||||
# Extended + with_skills
|
||||
curl -o static/pdf/cv-extended-with_skills-jamr-2025-es.pdf \
|
||||
"http://localhost:1999/export/pdf?lang=es&length=extended&icons=show&version=with_skills"
|
||||
curl -o static/pdf/cv-long-with_skills-jamr-2025-es.pdf \
|
||||
"http://localhost:1999/export/pdf?lang=es&length=long&icons=show&version=with_skills"
|
||||
|
||||
curl -o static/pdf/cv-extended-with_skills-jamr-2025-en.pdf \
|
||||
"http://localhost:1999/export/pdf?lang=en&length=extended&icons=show&version=with_skills"
|
||||
curl -o static/pdf/cv-long-with_skills-jamr-2025-en.pdf \
|
||||
"http://localhost:1999/export/pdf?lang=en&length=long&icons=show&version=with_skills"
|
||||
```
|
||||
|
||||
### Year Rollover
|
||||
@@ -552,7 +591,7 @@ The system automatically handles year rollovers:
|
||||
|
||||
**New Naming Examples:**
|
||||
- ✅ `cv-short-jamr-2025-es.pdf` (version omitted, final naming)
|
||||
- ✅ `cv-extended-with_skills-jamr-2025-en.pdf` (version included)
|
||||
- ✅ `cv-long-with_skills-jamr-2025-en.pdf` (version included)
|
||||
|
||||
#### 2. Light Mode Enforcement - Defense in Depth
|
||||
**CRITICAL**: PDFs are now GUARANTEED to always use light mode, regardless of user's color theme preference.
|
||||
@@ -592,8 +631,8 @@ The system automatically handles year rollovers:
|
||||
- **Generated**: 4 new PDFs with correct naming convention (2.2 MB each)
|
||||
- `cv-short-jamr-2025-es.pdf` (updated from `cv-detailed`)
|
||||
- `cv-short-jamr-2025-en.pdf` (updated from `cv-detailed`)
|
||||
- `cv-extended-with_skills-jamr-2025-es.pdf`
|
||||
- `cv-extended-with_skills-jamr-2025-en.pdf`
|
||||
- `cv-long-with_skills-jamr-2025-es.pdf`
|
||||
- `cv-long-with_skills-jamr-2025-en.pdf`
|
||||
- **Removed**: Old PDFs with deprecated naming (`cv-detailed-*`)
|
||||
|
||||
#### 6. Documentation
|
||||
|
||||
Reference in New Issue
Block a user