# CV Project - Developer Memory ## ⚠️ CRITICAL: Read This Before Any Changes This document contains **non-negotiable rules** and **hard-learned lessons** from this project's development. Violating these causes bugs, breaks features, and wastes time. --- ## 🔴 ABSOLUTE RULES - NEVER VIOLATE ### 1. Icon Naming Convention (CRITICAL) **ALWAYS use "icons" - NEVER "logos"** ```javascript // ✅ CORRECT .show-icons localStorage.getItem('cv-icons') const showIcons = ... // ❌ WRONG - WILL BREAK ICON TOGGLE .show-logos localStorage.getItem('cv-logos') const showLogos = ... ``` **Why:** Icon toggle had critical bug (commit 3f77fed) because JavaScript used `.show-logos` but CSS checked for `.show-icons`. This caused toggles to only work after page refresh. **Test that enforces this:** `tests/mjs/1-toggles.test.mjs` **Memory file:** `~/.claude/cv-icons-migration.md` --- ### 2. Hyperscript Parser Limit (NO LONGER EXISTS ✅) **✅ CONFIRMED: NO def statement limit with hyperscript 0.9.14+** **Test Results (2025-11-17):** Test 9 (`tests/mjs/9-hyperscript-def-limit.test.mjs`) confirmed: - ✅ 1 def statement works - ✅ 2 def statements work - ✅ 3 def statements work - ✅ 4 def statements work (beyond historical limit) - ✅ 5+ def statements work (no limit) **Historical Context (for reference only):** - Hyperscript 0.9.12 had a hard 3 def limit (fixed in 0.9.14) - Current version has NO def statement limit - Functions can be freely organized across multiple files **Current Architecture (2025-11-17):** - Core logic in hyperscript (`static/hyperscript/*.hs`) - JavaScript wrappers for `call` command compatibility (`static/js/cv-functions.js`) - Pattern: `window.fn()` → `_hyperscript.evaluate('hyperscriptFn()')` **Current Best Practice:** Organize hyperscript functions by category in separate files ``` static/hyperscript/ ├── toggles._hs # Toggle functions (CV length, icons, theme) ├── hover-sync._hs # Hover synchronization functions ├── navigation._hs # Navigation functions (scroll-to-section) [2025-11-20] ├── keyboard._hs # Keyboard handler reference (inline in body tag) └── utils._hs # Utility functions (print, scroll, etc.) ``` **Migration in progress:** Moving functions from `cv-functions.js` back to hyperscript **Test that verifies no limit:** `tests/mjs/9-hyperscript-def-limit.test.mjs` **Reference:** `doc/4-HYPERSCRIPT-RULES.md` --- ### 2.1. Hyperscript Event Handler Externalization (2025-11-20) **Rule: Complex event handlers that inspect event properties MUST stay inline** **✅ CAN Be Externalized:** - Simple navigation handlers (scrollToSection) - Toggle handlers (toggleCVLength, toggleIcons) - Hover sync handlers (syncPdfHover, syncPrintHover) **❌ MUST Stay Inline:** - Keyboard handlers that inspect `event.key`, `event.target`, modifier keys - Event handlers with complex conditional logic based on event properties **Why:** The `event` variable in `_=""` attributes is a hyperscript runtime variable. External `def` functions don't have direct access to this event context from HTML attributes. **Optimization for Inline:** Use `then` chains to make compact: ```hyperscript -- Compact inline handler with then chains if event.key is '?' and not event.ctrlKey and not isInputField halt the event then set modal to #shortcuts-modal then if modal then call modal.showModal() end end ``` **Example - Navigation (Externalized):** ```html ``` ```hyperscript -- External function in navigation._hs def scrollToSection(event, sectionId) call event.preventDefault() set element to document.getElementById(sectionId) if element then call element.scrollIntoView({behavior: 'smooth'}) end end ``` **Example - Keyboard Handler (Inline):** ```html ``` **Files:** - `static/hyperscript/navigation._hs` - External navigation function - `templates/partials/navigation/hamburger-menu.html` - 9 clean navigation links - `templates/index.html` - Optimized inline keyboard handler (body tag) **Test:** `tests/mjs/2-keyboard-shortcuts.test.mjs` (keyboard shortcuts) **Reference:** `doc/4-HYPERSCRIPT-RULES.md` Section "Rule 4: Event Handler Externalization Guidelines" --- ### 3. Toggle Synchronization (WORKING) + Hover Sync (FIXED) **Toggle checkboxes WORK perfectly ✅** Every toggle checkbox exists in TWO places and stays synchronized: 1. Action bar (desktop) - `#lengthToggle`, `#iconToggle`, `#themeToggle` 2. Hamburger menu (mobile) - `#lengthToggleMenu`, `#iconToggleMenu`, `#themeToggleMenu` **Toggle behavior (WORKING):** - ✅ Clicking either toggle updates BOTH - ✅ Changes are real-time (no page refresh) - ✅ localStorage persists the state - ✅ Page load reads from localStorage and applies to both ```javascript // ✅ CORRECT - Update both toggles function toggleIcons(showIcons) { const paper = document.querySelector('.cv-paper'); const otherToggle = document.querySelector('#iconToggle') || document.querySelector('#iconToggleMenu'); if (showIcons) { paper?.classList.add('show-icons'); localStorage.setItem('cv-icons', 'true'); if (otherToggle) otherToggle.checked = true; } else { paper?.classList.remove('show-icons'); localStorage.setItem('cv-icons', 'false'); if (otherToggle) otherToggle.checked = false; } } ``` **✅ HOVER SYNC - PDF/Print buttons NOW WORKING** **Action buttons that sync hover states:** 1. Action bar (desktop): - `#action-bar-pdf-btn` - Download PDF button - `.action-bar-print-btn` - Print Friendly button 2. Hamburger menu (mobile): - `.menu-pdf-btn` - PDF button (templates/partials/navigation/hamburger-menu.html:178) - `.menu-print-btn` - Print button (templates/partials/navigation/hamburger-menu.html:186) **Working hover sync functions:** - `syncPdfHover(isHovering)` - Syncs PDF button hover between locations ✅ - `syncPrintHover(isHovering)` - Syncs Print button hover between locations ✅ **Implementation:** - Both buttons in each location have mouseenter/mouseleave handlers - Sync functions select all matching buttons (`.pdf-btn, .menu-pdf-btn`) - CSS classes `.pdf-hover-sync` and `.print-hover-sync` applied to all instances - Visual feedback matches across action bar and menu **Files involved:** - `templates/partials/navigation/action-buttons.html` (lines 9-10, 18-19) - `templates/partials/navigation/hamburger-menu.html` (lines 178-184, 186-189) - `static/js/cv-functions.js` (lines 71-99) - `static/css/main.css` (lines 2690-2712) **Test coverage:** `tests/mjs/8-hover-sync.test.mjs` ✅ **Test that enforces toggle sync:** `tests/mjs/1-toggles.test.mjs` ✅ --- ### 4. Real-Time Rendering + Persistence (CRITICAL) **ALL UI changes MUST happen in BOTH DOM and localStorage** **BOTH are required - not mutually exclusive:** ```javascript // ✅ CORRECT - DOM update AND localStorage paper.classList.add('show-icons'); // Visual change (instant feedback) localStorage.setItem('cv-icons', 'true'); // Persistence (survives reload) // ❌ WRONG - Only DOM (lost on page reload) paper.classList.add('show-icons'); // ❌ WRONG - Only storage (no visual feedback) localStorage.setItem('cv-icons', 'true'); ``` **Why both are needed:** - **DOM changes** = instant visual feedback (users see it immediately) - **localStorage** = state persists across page reloads (users don't lose their preferences) - Users need BOTH: instant feedback + remembered preferences **Test that enforces this:** `tests/mjs/1-toggles.test.mjs` (verifies both DOM and localStorage) --- ### 5. Test Suite as Single Source of Truth **If a test passes, the feature MUST work. If a feature works, the test MUST pass.** ``` tests/mjs/ ├── 0-zoom.test.mjs # Zoom functionality ├── 1-toggles.test.mjs # ALL toggles + sync + persistence ├── 2-keyboard-shortcuts.test.mjs # L, I, V, ? keys ├── 3-hyperscript.test.mjs # Parse errors + integrity ├── 4-htmx.test.mjs # HTMX integration ├── 5-language.test.mjs # EN/ES switching ├── 6-modals.test.mjs # Modal functionality ├── 7-mobile-responsive.test.mjs # Mobile viewports ├── 8-hover-sync.test.mjs # PDF/Print button hover sync └── 9-hyperscript-def-limit.test.mjs # Def statement limit verification ``` **Non-negotiable:** - ❌ NO code changes without test validation - ❌ NO bug fixes without regression test - ❌ NO features without test coverage - ❌ NO deployment if tests fail **Run before every commit:** ```bash bun tests/run-all.mjs ``` **Reference:** `tests/README.md`, `tests/TEST-SUMMARY.md` --- ## 📋 Architecture Decisions ### Tech Stack **Backend:** - ✅ Go 1.21+ (Backend server) - ✅ Standard library `net/http` (HTTP server) - ✅ Go templates (Server-side rendering) **Frontend:** - ✅ HTMX (Hypermedia-driven interactions) - ✅ Hyperscript (Latest version - event handling) - ✅ Vanilla JavaScript (cv-functions.js - toggles, keyboard shortcuts) - ✅ Iconify (Icon system) **Testing:** - ✅ Playwright (E2E browser automation) - ✅ Bun (Test runner ONLY - NOT the runtime!) **Why Go:** - Fast compilation - Single binary deployment - Built-in templating - Excellent standard library - Strong typing - Cross-platform **Why HTMX + Hyperscript:** - Server-driven UI (hypermedia pattern) - Minimal JavaScript - Progressive enhancement - Real-time updates capabilities ### File Organization ``` cv/ ├── main.go # Server entry point (v1.1.0) ├── go.mod, go.sum # Go dependencies ├── internal/ │ ├── cache/ # Application-level data caching (95.7% coverage) │ ├── config/ # Configuration (100% coverage) │ ├── constants/ # Project-wide constants (100% coverage) │ ├── email/ # Email service - SMTP (58% coverage) │ ├── fileutil/ # File path utilities (88.9% coverage) │ ├── handlers/ # HTTP handlers (62.9% coverage) │ ├── httputil/ # HTTP response helpers (100% coverage) │ ├── middleware/ # HTTP middleware (87.5% coverage) │ ├── models/ # Data models (cv: 83.3%, ui: 85.7%) │ ├── pdf/ # PDF generation (requires Chrome) │ ├── routes/ # Route definitions │ ├── templates/ # Template utilities │ └── validation/ # Input validation (91.9% coverage) ├── static/ │ ├── js/ │ │ └── cv-functions.js # Global functions (toggles, keyboard, hover sync) │ ├── css/ # Stylesheets │ ├── hyperscript/ │ │ └── functions._hs # Hyperscript functions (if any) │ ├── images/ # Static images │ └── pdf/ # PDF files ├── templates/ │ ├── index.html # Main page template │ ├── cv-content.html # CV content │ ├── language-switch.html # Language switcher │ └── partials/ │ ├── navigation/ │ │ ├── action-bar.html # Desktop action bar │ │ ├── action-buttons.html # PDF + Print buttons │ │ └── hamburger-menu.html # Mobile menu (ALL controls + buttons) │ ├── widgets/ # Reusable UI components │ └── modals/ # Modal dialogs └── tests/ └── mjs/ # Systematic test suite (Playwright + Bun runner) ``` **Critical files:** - `main.go` - Server entry point - `static/js/cv-functions.js` - Toggle, keyboard, and hover sync functions - `templates/partials/navigation/action-buttons.html` - PDF + Print buttons (action bar) - `templates/partials/navigation/hamburger-menu.html` - Complete mobile menu with toggles + buttons - `templates/index.html` - Main template structure --- ## 🐛 Historical Bugs (Learn From These) ### Bug 1: Icon Toggle Required Refresh **Commit:** 3f77fed **Date:** 2025-11-17 **Symptom:** Icon toggle only worked after page refresh **Root cause:** Class name mismatch - JavaScript: `classList.add('show-logos')` - CSS: `.cv-paper:not(.show-icons)` **Fix:** Changed all references from "logos" to "icons" **Test added:** Screenshot verification in `1-toggles.test.mjs` **Memory:** `~/.claude/cv-icons-migration.md` ### Bug 2: Hyperscript Parser Crash **Date:** 2025-11-16 **Symptom:** Silent failures, toggles stopped working **Root cause:** Exceeded 3 `def` statement limit **Fix:** Moved toggle logic to JavaScript (`cv-functions.js`) **Test added:** Def statement counter in `3-hyperscript.test.mjs` **Reference:** `HYPERSCRIPT-RULES.md` ### Bug 3: Toggle Desynchronization **Date:** 2025-11-15 **Symptom:** Action bar and menu toggles out of sync **Root cause:** Only updating one toggle, not both **Fix:** Every toggle function now updates both locations **Test added:** Sync verification in `1-toggles.test.mjs` --- ## 🎯 Development Workflow ### Before Making Changes 1. **Read relevant test:** Understand what's being tested 2. **Run the test:** See current behavior 3. **Make changes:** Code + test updates together 4. **Run test again:** Verify it passes 5. **Run ALL tests:** `bun tests/run-all.mjs` 6. **Manual verification:** Browser stays open - check it yourself 7. **Commit:** With clear description ### When Adding a Feature 1. **Write test FIRST** (test what you want to build) 2. **Implement feature** 3. **Test passes** (feature works) 4. **Update documentation** (TEST-SUMMARY.md) 5. **Commit both** (code + test together) ### When Fixing a Bug 1. **Write regression test** (reproduces the bug) 2. **Test FAILS** (proves bug exists) 3. **Fix the bug** 4. **Test PASSES** (proves fix works) 5. **Commit both** (fix + test together) --- ## 📝 Key Patterns ### Toggle Pattern (Standard) ```javascript function toggleX(enabled) { const target = document.querySelector('.target'); const otherToggle = document.querySelector('#xToggle') || document.querySelector('#xToggleMenu'); if (enabled) { target?.classList.add('state-class'); localStorage.setItem('cv-x', 'true'); if (otherToggle) otherToggle.checked = true; } else { target?.classList.remove('state-class'); localStorage.setItem('cv-x', 'false'); if (otherToggle) otherToggle.checked = false; } } ``` **Must have:** - ✅ Real-time DOM update - ✅ localStorage persistence - ✅ Sync both toggle locations - ✅ Safe navigation (`?.`) ### Hyperscript Pattern (LIMITED USE) ```html window.functionName = () => _hyperscript.evaluate('hyperscriptFunction()'); ``` **Why this matters:** - Hyperscript docs say "global hyperscript functions can be called from JavaScript" ✅ TRUE - BUT the reverse (`call` in `_=""` attributes) requires functions in `window` object - Hyperscript `def` functions are NOT automatically exposed to window - Templates use `_="on mouseenter call syncPdfHover(true)"` syntax **Solution - Wrapper Pattern:** ```javascript // static/js/cv-functions.js function syncPdfHover(show) { if (typeof _hyperscript !== 'undefined') { _hyperscript.evaluate('syncPdfHover(' + show + ')'); } } window.syncPdfHover = syncPdfHover; ``` **Files:** - Implementation: `static/hyperscript/*.hs` (toggles._hs, hover-sync._hs) - Wrappers: `static/js/cv-functions.js` - Test: `tests/mjs/8-hover-sync.test.mjs` **Bug History:** Hover sync broke when JavaScript functions were deleted during hyperscript migration. Restored as thin wrappers (commit 491aa66). --- ### 4. Zoom Architecture (CRITICAL - 2025-11-17) **Rule: Only CV content inside #zoom-wrapper, NOT UI chrome** ```html
CV Content
{{template "page-footer" .}} ``` **What gets zoomed (INSIDE #zoom-wrapper):** - ✅ CV paper (.cv-container) - ✅ CV content (.cv-paper) **What does NOT get zoomed (OUTSIDE #zoom-wrapper):** - ✅ Footer - ✅ Action bar - ✅ Hamburger menu - ✅ Fixed buttons (PDF, print, zoom toggle, etc.) **Zoom Range:** 25% - 300% (updated from 175% on 2025-11-17) **localStorage Keys:** - `cv-zoom` - Current zoom level (25-300) - `cv-zoom-visible` - Whether zoom control is shown (true/false) - `cv-zoom-position` - Draggable position of zoom control **Critical Bug Fixed (commit 52e97f1):** - Footer was INSIDE zoom-wrapper → got zoomed with content - Moved footer OUTSIDE zoom-wrapper → stays normal size - Test: `tests/mjs/11-zoom-ui-exclusion.test.mjs` **Critical Bug Fixed (commit 35a836a):** - Zoom persistence broken - set wrong element's value - zoom-control.html:10 was `set my value` (div) instead of `set #zoom-slider's value` - Test: `tests/mjs/10-zoom-persistence.test.mjs` --- ### 5. Test Maintenance (REQUIRED - 2025-11-17) **Rule: Update tests/TEST-SUMMARY.md every time you add a test** When adding new test files: 1. Create test file: `tests/mjs/{N}-{feature}.test.mjs` 2. Update `tests/TEST-SUMMARY.md` with test description 3. Update test count at bottom of TEST-SUMMARY.md 4. Add to New Tests section with date **Current Test Count:** 39 test files (comprehensive coverage) **Master test runner:** `tests/run-all.mjs` (auto-discovers numbered tests) --- ### 6. Contact Form (2025-12-01) **Secure contact form with comprehensive security middleware chain:** **Security Features:** - **BrowserOnly middleware** - Blocks curl/Postman/bots (requires HX-Request header) - **Rate limiting** - 5 submissions per hour per IP - **CSRF protection** - Token validation against session **Files:** - `internal/handlers/cv_contact.go` - Contact form handler - `internal/middleware/browser_only.go` - Browser validation middleware - `internal/middleware/contact_rate_limit.go` - Rate limiting - `templates/partials/modals/contact-modal.html` - Contact form UI **Documentation:** - `doc/17-CONTACT-FORM.md` - Quick start guide - `doc/18-SECURITY-AUDIT.md` - Security audit including contact form - `doc/19-SECURITY-IMPLEMENTATION.md` - Security controls documentation **Tests:** `tests/mjs/73-contact-form.test.mjs` --- ### 7. Plain Text CV (2025-12-01) **CLI-friendly plain text output for curl, wget, lynx, w3m:** **Features:** - Auto-detected via User-Agent header - 80-character line width - Unicode/emoji support with proper centering - Useful for AI assistants reading CV content **Files:** - `internal/handlers/cv_text.go` - Plain text handler - `templates/cv-text.txt` - Plain text template **Usage:** ```bash curl http://localhost:1999/text curl http://localhost:1999/text?lang=es ``` --- ### 8. CMD+K Command Palette (2025-12-01, updated 2025-12-04) **ninja-keys integration for quick navigation:** **Features:** - Dynamic entries from CV data (experiences, projects, courses) - Scroll-to-section functionality - Language-aware responses - 1-hour cache headers - **Search bar button** (2025-12-04): macOS Spotlight-style search bar in action bar - Integrated in dark action bar with semi-transparent styling - Shows keyboard shortcut indicators (⌘ K) as individual kbd elements - Replaces old simple search button with more discoverable design - CSS: `.search-bar-btn`, `.search-bar-icon`, `.search-bar-text`, `.search-bar-keys` - Responsive: kbd keys hidden on mobile (<900px) **Files:** - `internal/handlers/cv_cmdk.go` - CMD+K API handler - `static/js/ninja-keys-init.js` - Frontend initialization - `doc/16-CMD-K-API.md` - API documentation - `templates/partials/navigation/action-buttons.html` - Search bar button HTML - `static/css/04-interactive/_buttons.css` - Search bar button styles **Tests:** - `tests/mjs/71-cmd-k-api-scroll.test.mjs` - `tests/mjs/72-cmd-k-button.test.mjs` - Tests search bar styling, kbd elements, icon, click behavior --- ### 9. Go Backend Testing (2025-12-06) **Comprehensive Go test suite with ~75% average coverage:** **Commands:** ```bash # Run all Go tests go test ./internal/... # Run with coverage go test -cover ./internal/... # Generate HTML coverage report go test -coverprofile=coverage.out ./internal/... go tool cover -html=coverage.out -o coverage.html ``` **Coverage by Package:** | Package | Coverage | Notes | |---------|----------|-------| | config | 100% | Configuration loading | | constants | 100% | All constants validated | | httputil | 100% | Response helpers | | cache | 95.7% | Application-level data caching | | validation | 91.9% | Input validation rules | | middleware | 87.5% | Security, rate limiting, preferences | | fileutil | 88.9% | File path utilities | | models/ui | 85.7% | UI configuration models | | models/cv | 83.3% | CV data models | | handlers | 62.9% | HTTP handlers (PDF needs Chrome) | | email | 58.0% | Requires SMTP connection | **Test Files:** - `internal/cache/data_cache_test.go` - `internal/config/config_test.go` - `internal/constants/constants_test.go` - `internal/email/email_test.go` - `internal/handlers/errors_test.go` - `internal/httputil/response_test.go` - `internal/middleware/csrf_test.go` - `internal/middleware/logger_test.go` - `internal/middleware/contact_rate_limit_test.go` - `internal/middleware/security_logger_test.go` - `internal/validation/rules_test.go` **Documentation:** `doc/27-GO-TESTING.md`