Commit Graph

128 Commits

Author SHA1 Message Date
juanatsap 542419de45 feat: category icon badges on projects — CLI, App, Web, Plugin, SDK
Each project now shows a colored icon badge indicating its type:
- CLI (purple terminal), App (blue apple), Web (cyan globe)
- WebApp (teal app), Plugin (amber puzzle), SDK (violet package)
- Contrib (gray pull request)
2026-05-04 14:28:37 +01:00
juanatsap 29aa3d1fd7 fix: stars badge on all projects with a GitHub repo, not just openSource 2026-05-04 14:19:26 +01:00
juanatsap 7322c1a158 fix: stars count rendered server-side with 30min cache, no client-side API calls 2026-05-04 13:52:53 +01:00
juanatsap 9a2343a71e feat: GitHub stars badge on open-source projects via shields.io
- New openSource field in project model distinguishes OS from private
- shields.io badge shows live star count for open-source projects
- SoundInbox marked as NOT open source (no badge, no stars)
- Immich Photo Manager, Cmux Resurrect, Gotify Commander, CDC Starter Kit → open source
- Stars badge hidden in print view
2026-05-04 13:42:40 +01:00
juanatsap 7a242ca7b4 fix: enforce project link format (#proj-ID) so icons appear in chat responses 2026-05-04 13:32:12 +01:00
juanatsap af456cafc2 fix: chat responds in CV language (en/es), not random 2026-05-04 13:27:06 +01:00
juanatsap 65eadcada7 fix: chat tone — neutral intros, no exclamatory openers, open-source chip
- Agent avoids "¡Claro que sí!" / "Absolutely!" openers
- Decline message says "aquí sólo puedo responder"
- Replaced Certifications chip with Open source suggestion
2026-04-27 00:44:05 +01:00
juanatsap 9547bc7130 feat: remove Commando project, add Gotify Commander logo, drolosoft linked
- Remove unreleased Commando project from CV
- Add gotify-commander.png logo from GitHub repo
- "drolosoft" lowercase with link to drolosoft.com in summary
- Replace Certifications chip with Open source chip in chat
2026-04-27 00:32:54 +01:00
juanatsap f5c78e6845 feat: chat speaks as Juan — first person, CV photo avatar, disclaimer tooltip
- Agent prompt rewritten to first person ("I worked at...", "I built...")
- Bot avatar replaced with dni-thumb.jpeg (2.6KB, 56x56 retina)
- Greeting: "Pregúntame lo que quieras sobre mi currículum"
- Underlined "mi currículum" with floating tooltip disclaimer
- Every response ends with cordial email contact invitation
- Background photos now visible in production (random per load)
- Toggle button remains dev-only
2026-04-26 23:32:48 +01:00
juanatsap 3b6d5e781a feat: background photo system — random Lanzarote landscapes behind CV grid
Dev-only toggle button enables/disables photo backgrounds. Photos are
auto-discovered from static/images/backgrounds/ and randomly selected
on each page load. Production is unaffected — no button, no photo.
2026-04-25 14:46:33 +01:00
juanatsap 2fbd88f28e feat: CV overhaul — modernize skills, add projects, fix proficiency scale
- Title: "Senior Technical Consultant & Full-Stack Developer"
- Add Swift & macOS Development skill category (SoundInbox, Commando)
- Rename "AI-Assisted Development" → "AI Engineering & Integration" with MCP, ADK, Gemini, CLIP
- Remove "Design Tools" (Corel Draw, GIMP) and "Legacy Enterprise" (Struts, Yii, Zend)
- Remove jQuery, Assembler, Groovy; add Swift to programming languages
- Rewrite Team Management with professional language
- Proficiency scale: 1-5 → 1-10 (validation, tests, chat agent prompt)
- Add SoundInbox (Swift) and Commando (Go+SwiftUI) to projects
- Remove personal details: dateOfBirth, placeOfBirth, domestika, driverLicense
- Trim weak LinkedIn Learning courses (speed reading, persuasive UX)
- Fix Spanish soft_skills duplicates
- Chat agent: 11 new assertions (proficiency scale, new projects, removed skills)
- Fix hardcoded year 2025 in TestDefaultCVShortcut → time.Now().Year()
2026-04-13 00:07:51 +01:00
juanatsap aae818fbc0 feat: email as mailto link + chat response rules tests (21 assertions)
- Email txeo.msx@gmail.com rendered as clickable mailto: link in chat
- New test: 86-chat-response-rules.test.mjs covering:
  contact (email shown, no contact form), phone (private),
  location (Lanzarote), language matching (EN/ES),
  off-topic redirect, tech search, no hallucination, years
- Tests are non-deterministic (live LLM) — may need re-runs
2026-04-10 00:01:49 +01:00
juanatsap 562552add8 fix: agent prompt — Lanzarote location, email always, no contact form reference
- "Where does he live?" → Lanzarote, Canary Islands, Spain
- Never mention contact form/page (doesn't exist)
- Always use txeo.msx@gmail.com for contact
2026-04-09 23:55:11 +01:00
juanatsap 9018edd21a fix: chat agent — show email on contact questions, hide phone, update LLM context
- Email (txeo.msx@gmail.com) is now shared when users ask for contact info
- Phone remains private (never revealed)
- Updated bonus context: ADK Go 1.0 with Gemini (prod) / Gemma 4 (dev)
2026-04-09 21:01:16 +01:00
juanatsap 08b39653ba fix: SEO — prevent Google from indexing /text instead of main page
- Add X-Robots-Tag: noindex, nofollow header to /text endpoint
- Add Link: canonical header pointing to HTML version
- Add Disallow: /text to robots.txt
- Update sitemap.xml lastmod to 2026-04-09
2026-04-09 12:24:06 +01:00
juanatsap 21c33d2833 feat: chat icons — image fallbacks, external links, smaller inline size
- Support image file fallback when no sprite index exists
  (Immich Photo Manager, Cmux Resurrect now show their logos)
- Render external links [text](https://...) as clickable links
  (fixes Third Party Contributions raw markdown)
- Smaller inline icons (20px) to fit chat bubble aesthetic
- Separate icon-chat CSS class for chat-specific sizing
2026-04-09 11:34:14 +01:00
juanatsap 8e029d1363 feat: chat UX overhaul — GLM local model, icons, layout modes, instant bubbles
- Add GLM-4.7-Flash as default Ollama model (replaces Mistral)
- Fix WRITE_TIMEOUT (15s→120s) and HTMX timeout (5s→120s) for local LLM
- Auto-warmup model on startup in development mode
- Add /api/chat/status endpoint for model readiness polling
- Show "Initializing AI model..." indicator while model loads
- Add user avatar (mdi:account) on chat messages
- Inject company/project/course sprite icons inline in chat responses
- Replace cramped header icons with 4 icon buttons + tooltips
  (Compact, Side panel, Floating, Full screen)
- Add floating/draggable chat mode with smooth drag support
- Chip questions show user bubble instantly and clear input
- Help modal prefills input instead of auto-sending
- User bubble rendered client-side for immediate feedback
2026-04-09 10:54:23 +01:00
juanatsap d5c90248cc feat: Teams-style chat UX overhaul
Bubbles:
- Teams-style layout: bot avatar (green circle) on left, message beside it
- User messages right-aligned, no avatar (clean, like Teams)
- Rounded bubbles (border-radius: 16px) instead of square
- Distinct corner radii for conversation flow

Navigation:
- Links no longer close the chat — panel stays open for continued navigation
- Added #certifications anchor (alias to courses section)
- Fixed agent instruction to use #courses for certifications references

Theme:
- All colors use CSS variables from _themes.css
- Automatically adapts to light/dark without explicit .theme-clean overrides
- Panel uses --paper-bg (white in light, dark in dark theme)

Size modes:
- 3 discrete toggle buttons: compact, half-screen, fullscreen
- Active state highlighted, direct selection (no confusing cycling)
- Removed chat-half-left (simplified to compact/half/full)

Intelligence:
- React query now returns results (verified: 4 companies + 2 projects + skills)
2026-04-08 17:51:14 +01:00
juanatsap be5fdd03c4 feat: chat avatars + dark theme fix + text overflow fix
Avatars:
- Robot icon (green circle) before each agent message
- Person icon (dark circle) before each user message
- New .chat-bubble wrapper with flex layout for avatar + message

Dark theme fixes:
- Panel background: #1e1e1e (not pure black)
- Agent bubbles: #2d2d2d with light text (not dark/invisible)
- Input area: #2d2d2d (not black)
- Header stays green (--accent-green) in both themes
- Chips, suggestions consistent with panel background

Text overflow:
- overflow-wrap + word-break on messages
- min-width: 0 prevents flex overflow
- User bubble properly right-aligned with avatar
2026-04-08 17:31:07 +01:00
juanatsap c44e9e8c67 feat: CV navigation links in chat responses (GPS for the CV)
Agent instruction now requires markdown links to CV anchors:
- Companies: [Olympic Broadcasting](#exp-olympic-broadcasting)
- Projects: [Immich Photo Manager](#proj-immich-photo-manager)
- Sections: [Skills](#skills), [Experience](#experience)

formatResponse converts [text](#anchor) → clickable green links
that close the chat panel, smooth-scroll to the target, and
pulse a green highlight for 2 seconds.

All existing CV anchor IDs used: exp-{companyID}, proj-{projectID},
course-{courseID}, plus section IDs (experience, projects, skills, etc.)
2026-04-08 17:11:22 +01:00
juanatsap 160be31b31 feat: auto-fallback Gemini→Ollama + model warmup on chat open
Dual-provider architecture:
- Both Gemini and Ollama initialize at startup (if configured)
- Primary (Gemini) tried first for every request
- On any error (429, 503, timeout), automatically falls back to Ollama
- No manual switching needed — completely transparent to the user
- Log shows: "Primary failed (gemini: ...), falling back to ollama: ..."

Warmup:
- POST /api/chat/warmup called silently when chat panel opens
- Pre-loads Ollama model in background (10-15s) while user reads welcome
- By the time user types, model is ready for instant response
- Warms up fallback provider specifically (Gemini doesn't need it)

Timeout:
- Agent context increased to 60s (Ollama first response can be slow)
- Each request creates a fresh session (stateless for fallback compat)
2026-04-08 14:57:38 +01:00
juanatsap 8205a22972 feat: Ollama adapter + chat rate limiter (30 req/hour)
Ollama adapter (internal/chat/ollama.go):
- Implements model.LLM interface for ADK Go
- Talks to Ollama's OpenAI-compatible API (/v1/chat/completions)
- Full tool/function calling support (tested with Mistral Small 3.2)
- Converts ADK types to OpenAI format (messages, tools, tool_calls)
- Configurable via OLLAMA_HOST and OLLAMA_MODEL env vars

Multi-provider handler:
- MODEL_PROVIDER env: "gemini" (default) or "ollama"
- Gemini: requires GOOGLE_API_KEY (pay-as-you-go recommended)
- Ollama: connects to local or Tailscale-remote instance

Rate limiter:
- 30 requests/hour per IP on /api/chat endpoint
- Uses existing middleware.NewRateLimiter pattern

Tested: Ollama + Mistral Small 3.2 on M4 Pro 64GB — correct answers
2026-04-08 14:47:14 +01:00
juanatsap 4f558ac842 fix: replace Hyperscript with plain JS for chat interactions
The Hyperscript trigger/call commands couldn't reliably trigger HTMX
form submissions or call global JS functions. Moved all chat
interactions to plain JavaScript:

- toggleChatPanel(): open/close panel + icon swap
- sendChatQuestion(q): set input + htmx.trigger(form, 'submit')
- closeChatHelpAndAsk(q): close modal + open chat + send question
- htmx:afterRequest listener clears input after submit

Hyperscript kept only for site-wide patterns (closeOnBackdrop) that
work reliably.

Also: better error message for rate-limited API responses (429).
2026-04-08 14:11:11 +01:00
juanatsap 94976e1c19 feat: help modal, comprehensive intelligence, fix right-side position
Position fix:
- Remove _chat.css @import from main.css (was overriding with old
  left:2rem cached version). Chat CSS now loaded only via head-styles.
- Button confirmed at right:2rem, bottom:6rem (above back-to-top)

Help modal:
- New chat-help-modal.html using same <dialog> pattern as shortcuts
- 6 organized categories: Experience, Technologies, Projects,
  Education, Skills, How it works
- Bilingual EN/ES with example questions per category
- ? button in header opens modal via commandfor/show-modal
- Removed inline help card (modal replaces it)

Intelligence:
- Comprehensive query strategy for 8 question types
- Technology queries always use cross-section search
- Company queries use experience without filter for full listing
- Agent knows CV site is built with Go/HTMX (bonus context)
- Skills report proficiency levels when technology found
2026-04-08 13:15:07 +01:00
juanatsap 795ba88d6f fix: match CV design system, right-side positioning, smarter agent
CSS:
- Button moved to right: 2rem, above back-to-top (bottom: 6rem)
- Uses CV design tokens: --black-bar, --accent-blue, --paper-bg
- Fonts: Quicksand (header), Source Sans Pro (body)
- Tooltip on the left side (tooltip-left class)
- Dark theme uses CV-consistent grays

Intelligence:
- Agent instruction emphasizes exhaustive reporting of ALL matches
- Cross-section search results must not be truncated
- Mentions CV site itself is built with Go when relevant

Tests:
- Updated positioning assertions (right side, x > viewport/2)
- Added 5 intelligence tests: Go cross-section, company count,
  years of experience, React cross-section, Spanish response
- Resilient to API errors (waits for any message, not just user)
- 42 total test assertions
2026-04-08 13:04:47 +01:00
juanatsap 93e33f6496 fix: chat submission and session handling + 37 Playwright tests
- Fix chip auto-submit: use htmx.trigger() instead of native submit
  to bypass browser validation on required field
- Remove required from input (server validates already)
- Fix session_id duplication: use hx-swap-oob to replace single input
- Fix agent context: use background context with 30s timeout instead
  of HTTP request context (prevents premature cancellation)
- Remove redundant close button from header (toggle button handles it)
- Add 83-chat-mascot.test.mjs: 37 tests covering button, panel,
  help card, chips, typed questions, session, Spanish, positioning
2026-04-08 11:31:09 +01:00
juanatsap eddc424962 fix: cross-section search and CSS loading for chat widget
- agent.go: add section="search" that queries experience, projects,
  skills, and courses simultaneously — fixes missing results when
  a technology spans multiple CV sections (e.g. Java at Insa)
- head-styles.html: use modular CSS in development mode and load
  chat CSS separately — fixes unstyled page when bundle is stale
2026-04-08 00:44:16 +01:00
juanatsap f5276431ea feat: add AI chat widget powered by ADK Go 1.0
Visitors can ask questions about the CV via a floating chat panel.
The agent uses Gemini to answer questions about experience, projects,
skills, and education by querying the cached CV JSON data.

- internal/chat/agent.go: LLM agent with query_cv tool that searches
  CV data by section (experience, projects, skills, etc.) with keyword filtering
- internal/chat/handler.go: POST /api/chat endpoint with session management,
  graceful degradation when GOOGLE_API_KEY is not set
- chat-widget.html: HTMX-powered floating chat panel with Hyperscript toggle
- _chat.css: Responsive chat UI with dark theme support
- Wired into existing architecture via dependency injection (CVHandler,
  routes, main.go) — zero breaking changes, all existing tests pass
2026-04-08 00:20:48 +01:00
juanatsap 1d0cf46dd3 fix: resolve errcheck warnings in security_test.go
Use t.Setenv for env var management in tests.
2026-03-15 20:22:41 +00:00
juanatsap cafd117437 fix: resolve golangci-lint errcheck and staticcheck warnings
Use t.Setenv in tests, add error return handling, and replace
WriteString(fmt.Sprintf(...)) with fmt.Fprintf.
2026-03-15 20:22:41 +00:00
juanatsap 585949b709 chore: update Matomo analytics URL to matomo.txeo.club
Migrate from matomo.morenorub.io to matomo.txeo.club in both
CSP headers and tracking script. Also fix errcheck lint warnings
in cv_streaming.go.
2026-03-15 20:22:41 +00:00
juanatsap 69012bb1ae test: add comprehensive Go test suite with ~75% coverage
New test files:
- config/config_test.go (100% coverage)
- constants/constants_test.go (100% coverage)
- httputil/response_test.go (100% coverage)
- validation/rules_test.go (91.9% coverage)
- middleware/logger_test.go, security_test.go, security_logger_test.go
- handlers/errors_test.go

Updated documentation:
- doc/27-GO-TESTING.md: Complete testing guide
- doc/00-GO-DOCUMENTATION-INDEX.md: Added testing section
- doc/01-ARCHITECTURE.md: Updated package structure
- doc/DECISIONS.md: Added ADR-004 caching decision
- PROJECT-MEMORY.md: Added Go testing section
2025-12-06 17:51:20 +00:00
juanatsap 6ed6c7780b docs: add CLAUDE.md pointing to key project documentation
Links to PROJECT-MEMORY.md and DECISIONS.md for development rules
and architectural decisions, plus quick commands and doc index.
2025-12-06 17:49:13 +00:00
juanatsap c89b67a06d refactor: consolidate lang into constants, rename services to email
- Merge lang package into constants (add IsValidLang, ValidateLang, AllLangs)
- Rename internal/services to internal/email for consistency with pdf package
- Rename types to avoid redundancy: EmailService→Service, EmailConfig→Config
- Update all imports and references across codebase
- Delete internal/lang directory (functions moved to constants)
2025-12-06 17:05:17 +00:00
juanatsap 30ed21ff7a refactor: use 'c' alias for constants package
- Update all imports from 'constants' to 'c' for brevity
- Replace all 'constants.' references with 'c.'
- Fix remaining hardcoded content-type headers in httputil
- Fix remaining hardcoded User-Agent and Accept headers
- Rename CSRF receiver from 'c' to 'csrf' to avoid conflict
- Add ContentTypePlainSimple constant for Accept header matching
- Fix JSONCached to use proper integer formatting
2025-12-06 16:31:42 +00:00
juanatsap 2c7f8de242 refactor: centralize constants and reorganize documentation
- Create internal/constants package with all hardcoded values
  (environment, cookies, themes, headers, routes, cache)
- Create internal/httputil package for HTTP helper functions
- Update all handlers and middleware to use centralized constants
- Reorganize documentation with numbered prefixes (00-26)
- Remove duplicate docs from validation folder and docs/
- Delete handlers/constants.go (moved to internal/constants)
2025-12-06 16:27:12 +00:00
juanatsap 71d9258c58 feat: add application-level data caching for CV/UI
Eliminate per-request file I/O by loading CV and UI data once at startup.

## Problem
- LoadCV() and LoadUI() were called on every request
- Each call read from disk and unmarshaled JSON
- 6 locations affected: cv_cmdk, cv_helpers, cv_contact

## Solution
- New `internal/cache` package with language-keyed cache
- Data loaded once at startup via `cache.New(["en", "es"])`
- Handlers use `h.dataCache.GetCV(lang)` / `GetUI(lang)`
- Thread-safe concurrent reads via sync.RWMutex
- Deep copy for mutable slices (Experience, Projects)

## Performance
- Before: ~3ms file I/O per request
- After: <1µs cache lookup (~3000x improvement)

## Files
- internal/cache/data_cache.go (new)
- internal/cache/data_cache_test.go (new)
- internal/cache/README.md (new)
- internal/handlers/cv.go (added dataCache field)
- internal/handlers/cv_*.go (use cache)
- main.go (initialize cache at startup)
2025-12-06 15:57:23 +00:00
juanatsap d51e1f4520 chore: remove duplicate docs from validation folder
Documentation lives in docs/go-validation-system.md
2025-12-06 15:22:13 +00:00
juanatsap 6c7595b041 feat: add tag-based validation system with reflection caching
Implement a declarative struct tag validation system for Go:

- Add validator.go with sync.Map caching for reflection metadata
- Add rules.go with 11 built-in validation rules (required, email,
  pattern, honeypot, timing, etc.)
- Add errors.go with FieldError and ValidationErrors types
- Update ContactFormRequest with validate tags
- Add ValidateContactFormV2() using the new tag-based validator

Rules implemented:
- required/optional: field presence validation
- trim/sanitize: automatic value transformations
- min/max: UTF-8 aware length validation
- email: RFC 5322 email format validation
- pattern: predefined regex patterns (name, subject, company)
- no_injection: email header injection prevention
- honeypot: bot trap (must be empty)
- timing: timestamp validation for bot detection

Documentation:
- docs/go-validation-system.md: complete validation guide
- docs/go-template-system.md: template manager documentation
- docs/go-routes-api.md: routes and API reference
- docs/README.md: documentation index
2025-12-06 15:20:45 +00:00
juanatsap b5a50ca3ef feat: implement CSS sprite system for image optimization
Reduces HTTP requests from 44+ individual images to 3 sprite sheets
(~93% reduction). Includes Go sprite generator tool, CSS classes,
template integration, and E2E tests.

- Add cmd/sprites/main.go for sprite generation (60x60px + 120x120px @2x)
- Add _sprites.css with responsive sizing and retina support
- Update templates to use sprites with logoIndex fallback
- Add Makefile targets: sprites, sprites-clean
- Add 9-test E2E suite for sprite functionality
- Add doc/22-SPRITES.md with usage documentation
2025-12-04 11:38:36 +00:00
juanatsap bd859c318f docs: update architecture and add contact handler unit tests
Architecture updates:
- Add EmailService documentation with config and flow diagram
- Update CVHandler struct to show all dependencies
- Add new middleware components (BrowserOnly, RateLimiter, etc.)
- Update package structure to include services, pdf, validation

New unit tests for HandleContact (9 tests):
- Valid submission
- Missing email/message validation
- Honeypot bot protection
- Timing-based bot protection (too fast)
- Invalid HTTP method (405)
- Invalid email format
- Message too short
- Spanish language support

Includes MockEmailService for isolated testing.
2025-12-02 14:35:37 +00:00
juanatsap f3842a3486 fix: connect EmailService to contact form handler
The contact form was logging submissions but never actually sending emails.
This commit:
- Adds EmailService field to CVHandler
- Initializes EmailService in main.go with SMTP config
- Calls SendContactForm in HandleContact handler
- Updates all test files to pass nil for emailService parameter
2025-12-02 14:27:03 +00:00
juanatsap fc63151dcd fix: errcheck for client.Close in STARTTLS error path 2025-12-02 14:14:02 +00:00
juanatsap ff74946d2d feat: add Udemy courses and fix footer i18n + golangci-lint errors
- Add 5 Udemy courses with PDF certificate links (Go, Fyne, HTMX)
- Fix cv-footer.html to use CV data instead of hardcoded values
- Add i18n labels for footer (linkedin, github, domestika, email, phone)
- Fix 11 golangci-lint errors:
  - errcheck: proper Close() error handling in email/security/tests
  - staticcheck: simplify return and merge variable declaration
  - unused: remove legacy sendEmail and formatMessage functions
2025-12-02 14:11:36 +00:00
juanatsap 3edeb5274d fix: security tests with mock email sender and rate limit isolation
- Add EmailSender interface to allow mocking in tests
- Add IsInitialized() method to template.Manager for nil-safe checks
- Update contact handler to use interface and safe initialization checks
- Add mockEmailSender in security tests to avoid SMTP connection attempts
- Use unique IPs per test case to avoid rate limiting interference
2025-12-02 13:49:54 +00:00
juanatsap 41dbd77c2f feat: responsive HTML email templates with DreamHost SMTP
- Add professional HTML email template matching CV aesthetic
- Implement multipart emails (HTML + plain text fallback)
- Configure DreamHost SMTP with SSL (port 465)
- Add "light only" color scheme for Gmail iOS compatibility
- Include Reply-To header for easy sender response
- Add email validation and integration tests
- Update .env.example with DreamHost/Gmail SMTP examples
- Add .env to .gitignore to protect credentials
- Document email template customization and dark mode approach
2025-12-02 13:42:36 +00:00
juanatsap 40733034ca feat: comprehensive WCAG 2.1 AA accessibility audit
- Add aria-labels to menu action buttons (PDF, Print, Contact)
- Add aria-labelledby to toggle checkboxes (desktop + mobile)
- Add -webkit-user-select prefix for Safari compatibility
- Add DynamicCacheControl middleware for HTML pages
- Add accessibility test suite (60-accessibility.test.mjs)
- Add comprehensive accessibility documentation (21-ACCESSIBILITY.md)
- Update Modern Web Techniques doc to mark audit complete
2025-12-02 10:46:53 +00:00
juanatsap 2d3d3de8cd feat: lazy load ninja-keys + HTML Invoker Commands API
- Lazy load ninja-keys only on CMD+K press (0 requests on initial load)
- Use esm.sh bundled module (3 requests vs ~81 previously)
- Add esm.sh to CSP whitelist
- Implement HTML Invoker Commands API for modals:
  - commandfor="modal-id" + command="show-modal" for opening
  - commandfor="modal-id" + command="close" for closing
  - Removes need for onclick handlers on modal buttons
- Refactor index.html into layout partials (head, body-scripts)
- Add comprehensive tests for both features
2025-12-02 08:29:54 +00:00
juanatsap c6411db9c8 chore: consolidate contact templates to single location
Remove duplicate contact templates:
- templates/partials/contact_success.html (old, 1.2KB)
- templates/partials/contact_error.html (old, 1.1KB)

The active templates remain in templates/partials/contact/:
- contact-success.html
- contact-error.html

Updated contact.go to use the new template names to match cv_contact.go.
The old templates had inline styles and were larger; the new ones use
external CSS and are more maintainable.

All contact form tests pass (7/7).
2025-12-01 14:18:11 +00:00
juanatsap e0d445b92a refactor: simplify toggle handlers to return 204 No Content
Remove empty toggle templates (length-toggle.html, theme-toggle.html,
logo-toggle.html) that were just placeholders. The frontend uses
hx-swap="none" so the response body was always ignored anyway.

Now the handlers:
- Set the preference cookie
- Return 204 No Content immediately
- Hyperscript handles the UI state toggle on the frontend

This removes unnecessary template rendering overhead and cleans up
dead code. Tests updated to expect 204 instead of 200.
2025-12-01 14:16:24 +00:00