docs: add PROJECT-MEMORY.md - critical developer knowledge
Comprehensive memory document capturing: - ✅ Non-negotiable rules (icon naming, hyperscript limits, toggle sync) - ✅ Historical bugs and fixes (icon toggle, parser crash, desync) - ✅ Architecture decisions (HTMX+Hyperscript, Bun, file organization) - ✅ Standard patterns (toggles, keyboard shortcuts, hyperscript usage) - ✅ Development workflow (test-first, regression tests) - ✅ Common operations (adding toggles, debugging) - ✅ Lessons learned (naming, testing, framework limits) This document is the second most important after tests/README.md Read before making ANY changes to the project
This commit is contained in:
@@ -0,0 +1,451 @@
|
|||||||
|
# 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 (CRITICAL)
|
||||||
|
|
||||||
|
**Maximum 3 `def` statements total across ALL files**
|
||||||
|
|
||||||
|
Hyperscript 0.9.14 has a hard parser limit:
|
||||||
|
- ✅ Max 3 `def` statements across entire application
|
||||||
|
- ✅ Count includes ALL `<script type="_hyperscript">` blocks
|
||||||
|
- ✅ Count includes ALL inline `_="def ..."` attributes
|
||||||
|
|
||||||
|
**Solution:** Move complex logic to JavaScript functions
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT - JavaScript functions
|
||||||
|
function toggleIcons(showIcons) { ... }
|
||||||
|
function toggleCVLength(isLong) { ... }
|
||||||
|
function toggleTheme(isClean) { ... }
|
||||||
|
|
||||||
|
// Call from hyperscript
|
||||||
|
_="on change call toggleIcons(my.checked)"
|
||||||
|
|
||||||
|
// ❌ WRONG - Too many def statements
|
||||||
|
_="def toggleIcons(show)
|
||||||
|
if show then add .show-icons...
|
||||||
|
end"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why:** Parser crashes/fails silently when limit exceeded.
|
||||||
|
|
||||||
|
**Test that enforces this:** `tests/mjs/3-hyperscript.test.mjs`
|
||||||
|
|
||||||
|
**Reference:** `HYPERSCRIPT-RULES.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Toggle Synchronization (CRITICAL)
|
||||||
|
|
||||||
|
**Action bar and menu toggles MUST stay synchronized**
|
||||||
|
|
||||||
|
Every toggle exists in TWO places:
|
||||||
|
1. Action bar (desktop) - `#lengthToggle`, `#iconToggle`, `#themeToggle`
|
||||||
|
2. Hamburger menu (mobile) - `#lengthToggleMenu`, `#iconToggleMenu`, `#themeToggleMenu`
|
||||||
|
|
||||||
|
**Required behavior:**
|
||||||
|
- ✅ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test that enforces this:** `tests/mjs/1-toggles.test.mjs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Real-Time Rendering (CRITICAL)
|
||||||
|
|
||||||
|
**ALL UI changes MUST happen without page refresh**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// ✅ CORRECT - Immediate DOM update
|
||||||
|
paper.classList.add('show-icons'); // CSS change happens instantly
|
||||||
|
|
||||||
|
// ❌ WRONG - Requires refresh
|
||||||
|
localStorage.setItem('cv-icons', 'true'); // Only storage, no visual change
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why:** Users expect instant feedback. Any delay or refresh requirement feels broken.
|
||||||
|
|
||||||
|
**Test that enforces this:** `tests/mjs/1-toggles.test.mjs` (with screenshot verification)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 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 + def limit
|
||||||
|
├── 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
|
||||||
|
```
|
||||||
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
**Runtime & Framework:**
|
||||||
|
- ✅ Bun (JavaScript runtime)
|
||||||
|
- ✅ Hono (Web framework)
|
||||||
|
- ✅ HTMX (Hypermedia)
|
||||||
|
- ✅ Hyperscript 0.9.14 (Event handling - LIMITED USE)
|
||||||
|
|
||||||
|
**Why HTMX + Hyperscript:**
|
||||||
|
- Server-driven UI (hypermedia pattern)
|
||||||
|
- Minimal JavaScript
|
||||||
|
- Progressive enhancement
|
||||||
|
- Real-time updates via SSE
|
||||||
|
|
||||||
|
**Why Bun:**
|
||||||
|
- Fast runtime
|
||||||
|
- Built-in TypeScript
|
||||||
|
- Native test runner
|
||||||
|
- Single tool for everything
|
||||||
|
|
||||||
|
### File Organization
|
||||||
|
|
||||||
|
```
|
||||||
|
cv/
|
||||||
|
├── server.ts # Hono server
|
||||||
|
├── static/
|
||||||
|
│ ├── js/
|
||||||
|
│ │ └── cv-functions.js # Global functions (toggles, keyboard shortcuts)
|
||||||
|
│ └── css/
|
||||||
|
│ └── styles.css
|
||||||
|
├── templates/
|
||||||
|
│ ├── index.html # Main template
|
||||||
|
│ └── partials/
|
||||||
|
│ └── navigation/
|
||||||
|
│ ├── view-controls.html # Action bar toggles
|
||||||
|
│ └── hamburger-menu.html # Mobile menu
|
||||||
|
└── tests/
|
||||||
|
└── mjs/ # Systematic test suite
|
||||||
|
```
|
||||||
|
|
||||||
|
**Critical files:**
|
||||||
|
- `static/js/cv-functions.js` - All global toggle/keyboard functions
|
||||||
|
- `templates/partials/navigation/view-controls.html` - Action bar toggles
|
||||||
|
- `templates/partials/navigation/hamburger-menu.html` - Mobile menu toggles
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 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
|
||||||
|
<!-- ✅ GOOD - Simple event delegation -->
|
||||||
|
<button _="on click call myJavaScriptFunction(my.checked)">
|
||||||
|
|
||||||
|
<!-- ⚠️ AVOID - Complex logic (use JS instead) -->
|
||||||
|
<button _="on click
|
||||||
|
if condition then ...
|
||||||
|
else ... end">
|
||||||
|
|
||||||
|
<!-- ❌ FORBIDDEN - def statements (unless absolutely necessary) -->
|
||||||
|
<script type="_hyperscript">
|
||||||
|
def myFunction() <!-- COUNTS TOWARD 3 LIMIT -->
|
||||||
|
...
|
||||||
|
end
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keyboard Shortcuts Pattern
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
document.addEventListener('keydown', (e) => {
|
||||||
|
// ✅ ALWAYS check for input fields
|
||||||
|
if (e.target.matches('input, textarea, select')) return;
|
||||||
|
|
||||||
|
// ✅ Use lowercase key
|
||||||
|
const key = e.key.toLowerCase();
|
||||||
|
|
||||||
|
switch(key) {
|
||||||
|
case 'l': toggleCVLength(!body.classList.contains('cv-long')); break;
|
||||||
|
case 'i': toggleIcons(!paper.classList.contains('show-icons')); break;
|
||||||
|
case 'v': toggleTheme(!body.classList.contains('theme-clean')); break;
|
||||||
|
case '?': document.querySelector('#shortcuts-modal')?.showModal(); break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test:** `tests/mjs/2-keyboard-shortcuts.test.mjs`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Common Operations
|
||||||
|
|
||||||
|
### Adding a New Toggle
|
||||||
|
|
||||||
|
1. **Add to HTML:** Action bar + menu
|
||||||
|
2. **Add localStorage key:** Choose naming convention
|
||||||
|
3. **Add toggle function:** Follow standard pattern
|
||||||
|
4. **Add keyboard shortcut:** Optional but recommended
|
||||||
|
5. **Add to test:** Update `1-toggles.test.mjs`
|
||||||
|
6. **Add to keyboard test:** If you added shortcut
|
||||||
|
7. **Run ALL tests**
|
||||||
|
|
||||||
|
### Changing Class Names
|
||||||
|
|
||||||
|
1. **Search globally:** Find ALL references
|
||||||
|
2. **Update JavaScript:** cv-functions.js
|
||||||
|
3. **Update CSS:** All stylesheets
|
||||||
|
4. **Update HTML:** All templates
|
||||||
|
5. **Update tests:** Search for old name
|
||||||
|
6. **Create memory file:** `~/.claude/name-migration.md`
|
||||||
|
7. **Test thoroughly**
|
||||||
|
|
||||||
|
### Debugging Toggle Issues
|
||||||
|
|
||||||
|
**Checklist:**
|
||||||
|
- [ ] Check class name matches between JS and CSS
|
||||||
|
- [ ] Verify both toggles are updated (action bar + menu)
|
||||||
|
- [ ] Check localStorage key is correct
|
||||||
|
- [ ] Verify real-time DOM update happens
|
||||||
|
- [ ] Run `1-toggles.test.mjs` - does it catch the bug?
|
||||||
|
- [ ] If test doesn't catch it, FIX THE TEST FIRST
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 Key Documents
|
||||||
|
|
||||||
|
### Must Read Before Changes
|
||||||
|
- `tests/README.md` - Test suite accountability
|
||||||
|
- `HYPERSCRIPT-RULES.md` - Parser limits and workarounds
|
||||||
|
- `~/.claude/cv-icons-migration.md` - Icon naming convention
|
||||||
|
|
||||||
|
### Reference Documentation
|
||||||
|
- `tests/TEST-SUMMARY.md` - Complete test documentation
|
||||||
|
- `tests/mjs/README.md` - Test structure and patterns
|
||||||
|
- `MODERN-WEB-TECHNIQUES.md` - Architecture decisions
|
||||||
|
|
||||||
|
### Historical Record
|
||||||
|
- `tests/archive/README.md` - Legacy tests (reference only)
|
||||||
|
- Git history - Search for bug fix commits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎓 Lessons Learned
|
||||||
|
|
||||||
|
### 1. Name Things Correctly From the Start
|
||||||
|
**Lesson:** Renaming "logos" to "icons" required:
|
||||||
|
- Updating 4 files
|
||||||
|
- Creating memory document
|
||||||
|
- Adding test verification
|
||||||
|
- Could have been avoided with better initial naming
|
||||||
|
|
||||||
|
**Rule:** Think about naming conventions BEFORE first implementation
|
||||||
|
|
||||||
|
### 2. Tests Are Not Optional
|
||||||
|
**Lesson:** Icon toggle bug existed for days before we added screenshot verification to the test. The test was passing but the feature was broken.
|
||||||
|
|
||||||
|
**Rule:** Tests must verify ACTUAL behavior, not just code execution
|
||||||
|
|
||||||
|
### 3. Framework Limits Are Real
|
||||||
|
**Lesson:** Hyperscript's 3 `def` limit seems arbitrary but it's a hard constraint. We learned this after hitting silent failures.
|
||||||
|
|
||||||
|
**Rule:** Read framework documentation carefully, especially limits/constraints
|
||||||
|
|
||||||
|
### 4. Synchronization Is Hard
|
||||||
|
**Lesson:** Having toggles in two places (action bar + menu) means every change must update both. Forgetting this causes desync bugs.
|
||||||
|
|
||||||
|
**Rule:** If something appears in multiple places, use a single function to update ALL locations
|
||||||
|
|
||||||
|
### 5. Real-Time Updates Are Expected
|
||||||
|
**Lesson:** Users don't understand localStorage vs DOM updates. If they click a button, they expect INSTANT visual feedback.
|
||||||
|
|
||||||
|
**Rule:** Every toggle must update both localStorage AND DOM immediately
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Future Developers
|
||||||
|
|
||||||
|
**Before you change ANYTHING:**
|
||||||
|
|
||||||
|
1. Read this document
|
||||||
|
2. Read `tests/README.md`
|
||||||
|
3. Run `bun tests/run-all.mjs`
|
||||||
|
4. Understand which test covers what you're changing
|
||||||
|
5. Make your changes + update tests together
|
||||||
|
6. Run ALL tests again
|
||||||
|
7. Manual verification in browser
|
||||||
|
8. Update this document if you learn something new
|
||||||
|
|
||||||
|
**When you hit a bug:**
|
||||||
|
|
||||||
|
1. Which test should have caught this?
|
||||||
|
2. Why didn't it?
|
||||||
|
3. Fix the test FIRST
|
||||||
|
4. Then fix the bug
|
||||||
|
5. Document the lesson in this file
|
||||||
|
|
||||||
|
**Remember:**
|
||||||
|
- Tests are the specification
|
||||||
|
- If tests pass but feature fails → tests are wrong
|
||||||
|
- If feature works but tests fail → feature is wrong
|
||||||
|
- When in doubt, trust the tests MORE than the code
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Last Updated:** 2025-11-17
|
||||||
|
**Project Status:** Production
|
||||||
|
**Test Coverage:** 8 systematic tests, 100% core features
|
||||||
|
**Critical Memory Files:** This file + `~/.claude/cv-icons-migration.md`
|
||||||
Reference in New Issue
Block a user