# 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 (REMOVED IN LATEST VERSION ✅) **✅ CONFIRMED: NO 3 def statement limit with latest hyperscript version** **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 (well beyond limit) **Historical Context:** - Hyperscript 0.9.12 had a hard 3 def limit - Hyperscript 0.9.14+ removed this limitation - Functions were moved to JavaScript as workaround - **NOW MIGRATED BACK** to hyperscript with JavaScript wrappers (2025-11-17) **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/ │ ├── config/ # Configuration │ ├── handlers/ # HTTP handlers │ ├── middleware/ # HTTP middleware │ ├── models/ # Data models │ ├── routes/ # Route definitions │ └── templates/ # Template utilities ├── 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:** 12 active (0-11), 60+ archived **Master test runner:** `tests/run-all.mjs` (auto-discovers numbered tests)