Documentation · Debug & Maintenance

Debug & Maintenance V3

Journal complet des 6 bugs résolus avec cause exacte et fix appliqué. Guide de debug par symptôme. Tests manuels à passer avant chaque déploiement. Erreurs console courantes et leur signification.

6 bugs résolus Debug par symptôme Tests manuels
01

Historique complet — 6 bugs résolus

Bug #1 — Logo cassé sur les sous-pages
✓ RÉSOLU — V3.0
Fichier : assets/js/components.js
Sévérité : CRITIQUE
Pages affectées : legal/, blog/, products/ironlock/
Logo TRUvector absent dans la navbar sur toutes les pages hors racine. Aussi : CSS non chargé, JS non chargé, footer absent. La page est entièrement cassée visuellement.
Formule de calcul du root path incorrecte dans components.js. L'ancienne V2 comptait les slashes dans le pathname et utilisait depth - 1 pour calculer le nombre de ../ à générer. Pour /legal/page.html (2 slashes → depth=1), le résultat était '../'.repeat(0) = '' au lieu de '../'. Pour /products/ironlock/x.html (3 slashes → depth=2), le résultat était '../' au lieu de '../../'.
// AVANT (bugué) const depth = (window.location.pathname.match(/\//g) || []).length - 1; const root = depth <= 1 ? './' : '../'.repeat(depth - 1); // APRÈS (corrigé) const segments = window.location.pathname.split('/').filter(Boolean); const dirs = Math.max(0, segments.length - 1); const root = dirs === 0 ? './' : '../'.repeat(dirs);
Bug #2 — Lien actif navbar jamais surligné sur sous-pages
✓ RÉSOLU — V3.0
Fichier : assets/js/main.js
Sévérité : MINEUR
Le lien de navigation correspondant à la page actuelle n'est jamais surligné (classe .active) sur les pages en sous-dossier (legal/, blog/, products/ironlock/).
L'ancienne logique utilisait path.endsWith(href)href était un chemin relatif comme '../index.html'. window.location.pathname.endsWith('../index.html') est structurellement impossible — les navigateurs ne mettent jamais .. dans le pathname résolu.
// AVANT (bugué) const path = window.location.pathname.replace(/\/$/, ''); if (path.endsWith(href)) a.classList.add('active'); // APRÈS (corrigé) — résolution en URL absolue const currentPath = window.location.pathname .replace(/\/index\.html$/, '/').replace(/\/$/, '') || '/'; const absPath = new URL(a.href, window.location.href).pathname .replace(/\/index\.html$/, '/').replace(/\/$/, '') || '/'; if (currentPath === absPath) a.classList.add('active');
Bug #3 — SyntaxError : apostrophe dans searchIndex
✓ RÉSOLU — V3.1
Fichier : assets/js/components.js ligne 155
Sévérité : CRITIQUE
Symptôme : Toute la navbar disparaît
Console Firefox : Uncaught SyntaxError: missing } after property list [components.js:155:84]. La navbar ne s'injecte pas du tout, la page s'affiche sans navigation.
La chaîne 'Guide complet d'installation' dans le tableau searchIndex contient une apostrophe directe à l'intérieur d'une string délimitée par des guillemets simples. Le parser JS interprète le ' de d'installation comme la fermeture de la string.
// AVANT (bugué) — apostrophe directe { excerpt: 'Guide complet d'installation...' } // APRÈS (corrigé) — Unicode escape { excerpt: "Guide complet d\u2019installation..." } // Règle : utiliser guillemets DOUBLES dans searchIndex // + Unicode escapes pour tous caractères spéciaux : // \u2019 = ' | \u2014 = — | \u00e9 = é | \u00e8 = è
Bug #4 — RangeError: repeat count must be non-negative
✓ RÉSOLU — V3.1
Fichier : assets/js/components.js ligne 20
Sévérité : CRITIQUE
Pages affectées : / (pathname = '/')
Console : Uncaught RangeError: repeat count must be non-negative [components.js:20]. Navbar absente sur la page d'accueil quand le serveur sert directement / (sans /index.html dans le pathname).
Quand pathname = '/', split('/').filter(Boolean) retourne [] (tableau vide). Donc segments.length = 0, dirs = 0 - 1 = -1, et '../'.repeat(-1) lève un RangeError (JavaScript n'accepte pas un count négatif dans repeat()).
// AVANT (bugué) const dirs = segments.length - 1; // → -1 quand segments=[] // APRÈS (corrigé) — Math.max clamp à 0 const dirs = Math.max(0, segments.length - 1); // → 0 minimum // Vérification de tous les cas : // pathname='/' → segments=[] → dirs=0 → root='./' ✅ // pathname='/index.html' → segments=[index…] → dirs=0 → root='./' ✅ // pathname='/legal/x' → segments=[legal, x] → dirs=1 → root='../' ✅
Bug #5 — X-Frame-Options DENY bloque l'iframe promo
✓ RÉSOLU — V3.1
Fichier : _headers
Sévérité : MOYEN
Console Firefox : Le chargement de truvector-promo dans un cadre est refusé par la directive X-Frame-Options définie à DENY. La vidéo promo sur la page d'accueil affiche un cadre vide.
X-Frame-Options: DENY dans _headers interdit à n'importe quelle page d'être chargée dans une iframe, y compris depuis le même domaine. Hors truvector-promo.html est chargée en <iframe> sur index.html.
# _headers — AVANT /* X-Frame-Options: DENY # _headers — APRÈS /* X-Frame-Options: SAMEORIGIN ← iframes internes OK, externes bloquées
Bug #6 — Navbar invisible en thème Light
✓ RÉSOLU — V3.2
Fichier : assets/css/truvector.css
Sévérité : CRITIQUE
Conditions : Firefox avec prefers-color-scheme: light
En thème Light, la navbar semble absente — en réalité elle est là mais les textes sont blanc cassé sur fond blanc. Capture : logo visible mais liens navbar et boutons FR/EN/thème invisibles.
La variable --textd: #6a7f96 était utilisée pour colorer les liens de navigation. En thème dark c'est lisible (sur fond très sombre). En thème light, --nav-bg: rgba(244,246,249,.96) est quasi blanc — le contraste de #6a7f96 sur fond blanc est insuffisant (ratio ≈ 3:1, minimum WCAG = 4.5:1). De plus aucune surcharge [data-theme="light"] n'existait pour les éléments navbar.
/* truvector.css — Fixes thème light navbar */ /* 1. Fond navbar bien opaque et visible */ [data-theme="light"] { --nav-bg: rgba(255,255,255,.98); } /* 2. Liens navbar foncés sur fond clair */ .nav-links a { color: var(--textb); } [data-theme="light"] .nav-links a { color: var(--textb); } /* 3. Boutons FR/EN lisibles */ .lang-btn { color: var(--textb); } /* 4. Boutons thème lisibles */ .theme-btn { color: var(--textb); } /* 5. Hamburger mobile visible */ .nav-toggle span { background: var(--textb); } /* Ratio contraste final : #0f1e2e sur white = 15:1 (WCAG AAA) */
02

🔴 Symptôme — Navbar invisible ou absente

Vérification
Action
Ouvrir la console (F12) → des erreurs JS ?
Si SyntaxError → apostrophe dans searchIndex (Bug #3). Si RangeError → Math.max manquant (Bug #4). Voir historique bugs.
<div id="nav-placeholder"> présent dans le HTML ?
Inspecter le DOM. Si absent → ajouter en première ligne après <body>. Sans lui, components.js ne peut pas injecter la navbar.
components.js bien chargé ?
Onglet Network (F12) → filtrer "components" → vérifier statut 200. Si 404 → le chemin src est incorrect (mauvais root path).
Navbar présente mais invisible (thème light) ?
Inspecter le nav#navbar → background blanc, couleur texte blanc aussi. Bug #6. Vérifier que le CSS contient les surcharges [data-theme="light"] .nav-links a { color: var(--textb); }.
Ouverture directe avec file:// ?
Ne JAMAIS ouvrir avec file://. Le fetch des JSON i18n est bloqué. Utiliser python -m http.server 8080.
04

🔴 Symptôme — JavaScript ne charge pas

SyntaxError dans la console ?
Valider la syntaxe : node --check assets/js/components.js. Chercher les apostrophes directes dans les strings du searchIndex.
404 sur le script JS ?
Le <script src> utilise un root incorrect. Pour une page en products/ironlock/, le src doit être ../../assets/js/components.js.
main.js chargé avant components.js ?
main.js dépend d'éléments injectés par components.js (nav-toggle, etc.). Toujours charger components.js EN PREMIER. Vérifier l'ordre dans le HTML.
Cache navigateur sur un ancien fichier JS ?
Ctrl+Shift+R (hard reload) ou vider le cache. En prod, le header Cache-Control: max-age=31536000 sur /assets/* peut servir une ancienne version.
05

🟡 Symptôme — Thème ne se souvient pas / flash blanc

Flash blanc au chargement (FOUC) ?
Le script anti-FOUC est absent du <head>. Ajouter le script inline juste avant </head> sur la page concernée. Vérifier dans les 14 fichiers HTML.
Thème light mais site en dark après refresh ?
Vérifier localStorage : F12 → Application → Local Storage → trv-theme. Si absent, le script détecte prefers-color-scheme. Si Firefox est en mode dark système, le site sera dark même si on clique sur light et qu'on refresh (localStorage devrait persister).
Boutons thème ne répondent pas ?
components.js non chargé ou erreur JS avant l'event listener. Vérifier la console. Les boutons sont générés dynamiquement par components.js dans la navbar.
Couleurs incohérentes en thème light ?
Une couleur est hardcodée en hex au lieu d'utiliser var(--xxx). Inspecter l'élément → trouver les propriétés CSS non-variables → remplacer par la variable correspondante.
06

🟡 Symptôme — Traductions absentes ou clés affichées

La clé s'affiche ("nav.home") au lieu du texte ?
La clé n'existe pas dans le fichier JSON de la langue active. Vérifier assets/i18n/fr.json et en.json. La clé doit être identique dans les deux fichiers.
Onglet Network → erreur 404 sur fr.json ?
Le root path est incorrect → le fetch échoue → fallback sur les clés brutes. Vérifier le calcul root dans components.js.
Texte FR affiché même après switch EN ?
Vérifier localStorage → trv-lang. Si la clé existe dans fr.json mais pas dans en.json → le fallback s'applique et affiche le texte HTML initial (FR). Ajouter la traduction EN manquante.
data-i18n présent mais texte non traduit ?
La traduction est async — si la page est inspectée immédiatement après chargement, le DOM peut ne pas encore avoir été mis à jour. Attendre 200-500ms ou inspecter après que la page soit complètement chargée.
08

🟡 Symptôme — Formulaire contact/pricing échoue

Message "Configurez l'endpoint Formspree" apparaît ?
Normal — l'action du formulaire est encore "#". Configurer Formspree et remplacer action="#" par l'URL réelle. Voir doc-configuration.html section Formspree.
Erreur 422 Unprocessable Entity de Formspree ?
Un champ required est vide ou l'email est invalide. Vérifier la validation HTML5 (attribut required sur les champs).
Erreur CORS sur le fetch Formspree ?
Le header Accept: application/json est requis par Formspree pour les soumissions AJAX. Vérifier qu'il est bien présent dans le fetch de main.js.
Formulaire soumis mais pas d'email reçu ?
Vérifier le spam folder. Dans Formspree Dashboard → Form → Submissions → vérifier si la soumission est arrivée. Si oui, configurer l'email de notification dans Form Settings.
09

⚪ Erreurs console normales — À ignorer

Erreur consoleCauseAction requise
beacon.min.js CORS / sha512 integrity mismatchScript analytics Cloudflare Web Analytics bloqué par Firefox Enhanced Tracking Protection. Non bloquant.Aucune — comportement normal avec ETP activé
babel.min.js.map 404Source map manquante dans truvector-promo.html (page promo avec JSX Babel in-browser).Cosmétique — ne pas modifier sauf refonte de la promo
truvector-cube.png 404Image déclarative dans truvector-promo.html mais absente du repo.Ajouter l'image dans assets/ ou retirer la référence dans truvector-promo.html
You are using the in-browser Babel transformertruvector-promo.html utilise Babel en mode navigateur pour du JSX/React. Avertissement de performance.Non critique pour la prod — la promo est dans un iframe isolé
[TRV] i18n load failed for en — falling back to FRfetch de en.json a échoué (réseau, 404, CORS). Le fallback FR s'applique automatiquement.Vérifier que assets/i18n/en.json est committé et accessible
10

Tests manuels avant déploiement

Ces tests doivent être effectués localement (python -m http.server 8080) avant chaque git push sur main.

Tests prioritaires (toujours)

□ Logo visible sur index.html
□ Logo visible sur legal/mentions-legales.html
□ Logo visible sur products/ironlock/index.html
□ Thème dark → navbar textes visibles
□ Thème light → navbar textes visibles
□ Thème contrast → contraste maximal OK
□ Thème persisté après F5
□ Switch FR → textes changent
□ Switch EN → textes changent
□ Langue persistée après F5
□ Ctrl+K → modal s'ouvre
□ Taper "iron" → résultats apparaissent
□ Escape → modal se ferme

Tests mobile et formulaires

□ DevTools mobile (< 768px) → hamburger visible
□ Hamburger → menu s'ouvre
□ Clic lien dans menu → menu se ferme
□ Lien actif correct sur chaque page visitée
contact.html → formulaire avec action="#" → message d'avertissement Formspree apparaît
pricing.html → formulaire → idem
downloads/index.html → glisser un fichier → SHA-256 calculé
blog/index.html → bouton filtre "Crypto" → seuls articles crypto visibles
□ Scroll reveal → cards apparaissent progressivement
□ Boutons "Copier" sur les blocs code → feedback ✓ Copié !

11

Commandes de vérification

JavaScript

# Syntaxe components.js node --check assets/js/components.js # Syntaxe main.js node --check assets/js/main.js # Test calcul root path pour toutes les profondeurs node -e " ['/','index.html','/about.html', '/legal/x.html','/blog/x.html', '/products/ironlock/x.html'].forEach(p => { const s = p.split('/').filter(Boolean); const d = Math.max(0, s.length - 1); const r = d === 0 ? './' : '../'.repeat(d); console.log(p.padEnd(32), '->', JSON.stringify(r)); }); "

CSS & HTML

# Accolades CSS équilibrées python3 -c " c = open('assets/css/truvector.css').read() o, cl = c.count('{'), c.count('}') print(f'{{ = {o}, }} = {cl}') print('OK' if o == cl else 'ERREUR DESEQUILIBRE') " # Vérifier que toutes les pages HTML ont # les éléments obligatoires python3 -c " import os root = '.' checks = ['data-theme','trv-theme','nav-placeholder', 'footer-placeholder','skip-link','components.js'] for d,_,fs in os.walk(root): for f in fs: if not f.endswith('.html'): continue p = os.path.join(d, f) c = open(p).read() missing = [k for k in checks if k not in c] if missing: print(f'[{p}] manque: {missing}') print('Scan terminé.') "

Vérification des apostrophes dans searchIndex

# Détecter les apostrophes directes dans le searchIndex python3 -c " import re content = open('assets/js/components.js').read() # Chercher les lignes du searchIndex avec apostrophes suspectes idx_start = content.find('const searchIndex') idx_end = content.find('];', idx_start) idx_block = content[idx_start:idx_end] lines = idx_block.split('\n') for i, line in enumerate(lines): if \"'\" in line and 'd\\'in line or \"l\\'\" in line or \"n\\'\" in line: print(f'Ligne {i}: {line.strip()[:80]}') print('Scan terminé.') "
12

Maintenance courante

Mettre à jour le copyright

Le copyright est géré dynamiquement dans components.js via new Date().getFullYear(). Il se met à jour automatiquement chaque année. Aucune action nécessaire.

Mettre à jour le sitemap

Après ajout d'une page : ajouter l'URL dans sitemap.xml, mettre à jour le lastmod sur les pages modifiées. Resoumettre dans Google Search Console si modifications majeures.

Vérifier les liens morts

Mensuel : utiliser un outil comme deadlinkchecker.com sur https://truvector.dev. Corriger les liens 404 dans les pages HTML concernées.

Renouveler le domaine

Vérifier la date d'expiration de truvector.dev dans le registrar. Cloudflare Registrar propose le renouvellement automatique. Activer si ce n'est pas fait.

Audit Lighthouse

Trimestriel : lancer Lighthouse (Chrome DevTools → Lighthouse) sur index.html et products/ironlock/index.html. Objectif : Performance ≥ 90, SEO = 100, A11y ≥ 90, Best Practices = 100.

Vérifier Core Web Vitals

Dashboard Cloudflare → Pages → truvector → Analytics → Web Vitals. Vérifier LCP < 2.5s, CLS < 0.1, FID < 100ms. Si dégradation → investiguer les nouveaux assets lourds.