Portage navigateur complet de l'app Android HormoneTrack v1.4.10 : - moteur PK fidèle (Estrannaise tables ODS, Transfem Science V3C, WHSAH fit Mona, Bateman) — paramètres identiques, invariants Android préservés (fixes #19-#23, #35, #52-#62), 121 tests Node épinglés - calibration par période d'ester et par modèle affiché (#60/#61) - UI 6 écrans + éditeur, CurveChart Canvas (zoom/pan fractionnaire, prévision, pics/creux, fuseau configurable), rappels web (Notification API), seuils d'alerte, i18n FR/EN - sauvegarde JSON v2 compatible Android bidirectionnelle (rétrocompat v1) - 100 % local : localStorage, aucun serveur applicatif, aucune télémétrie - processus : scripts/check.sh (syntaxe, i18n, tests, E2E navigateur, smoke HTTP), docs séparées (README + DEVELOPPEMENT + CHANGELOG) - dépôt GIT SÉPARÉ de l'Android : historique 100 % propre, versions alignées sur l'Android porté, changelogs indépendants Non porté (impossible dans un navigateur, documenté §12) : agenda récurrent, notifications onglet fermé, montre.
106 lines
3.6 KiB
JavaScript
106 lines
3.6 KiB
JavaScript
/**
|
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
* AppLog — journal de diagnostic (portage de `util/AppLog.kt`, v1.3.1).
|
|
*
|
|
* Ligne horodatée par événement notable (rappels, import/export, erreurs),
|
|
* buffer circulaire de MAX_LINES, persisté dans le localStorage (Android :
|
|
* `filesDir/debug-log.txt`). Exportable depuis Paramètres (« Logs de
|
|
* diagnostic ») pour le debug à distance — l'utilisateur joint le fichier à
|
|
* son rapport de bug.
|
|
*
|
|
* ⚠️ NE JAMAIS PROPAGER D'EXCEPTION depuis log() (fix Android v1.3.3, §14
|
|
* #45) : log() est appelé de partout, y compris dans des callbacks système —
|
|
* une erreur d'écriture (quota localStorage dépassé…) doit coûter AU PIRE
|
|
* une ligne de journal, jamais un crash.
|
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
*/
|
|
|
|
import { store } from '../data/store.js';
|
|
|
|
/** Taille maximale du buffer circulaire (identique au Kotlin). */
|
|
export const MAX_LINES = 500;
|
|
|
|
/** Buffer mémoire (miroir de l'ArrayDeque Kotlin). */
|
|
let buffer = [];
|
|
|
|
/** Formatteur partagé — 2 chiffres. */
|
|
function pad(n) {
|
|
return String(n).padStart(2, '0');
|
|
}
|
|
|
|
/**
|
|
* Ligne horodatée : « 2026-09-08 12:34:56 [tag] message » (heure locale —
|
|
* même format qu'Android SimpleDateFormat "yyyy-MM-dd HH:mm:ss").
|
|
*
|
|
* @param {number} tsMs
|
|
* @param {string} tag
|
|
* @param {string} message
|
|
* @returns {string}
|
|
*/
|
|
export function formatLine(tsMs, tag, message) {
|
|
const d = new Date(tsMs);
|
|
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} `
|
|
+ `${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())} `
|
|
+ `[${tag}] ${message}`;
|
|
}
|
|
|
|
/**
|
|
* Buffer circulaire : garde les MAX dernières lignes (pur — testable).
|
|
*
|
|
* @param {string[]} existing
|
|
* @param {number} max
|
|
* @returns {string[]}
|
|
*/
|
|
export function trimLines(existing, max) {
|
|
return existing.length <= max ? existing : existing.slice(existing.length - max);
|
|
}
|
|
|
|
/**
|
|
* Charge le journal persisté dans le buffer mémoire. À appeler une fois au
|
|
* démarrage — un journal corrompu ne doit jamais empêcher l'app de démarrer
|
|
* (on perd l'historique, pas le process).
|
|
*/
|
|
export function init() {
|
|
const raw = store.getDebugLog();
|
|
buffer = raw ? trimLines(raw.split('\n'), MAX_LINES) : [];
|
|
log('app', `démarrage — ${buffer.length} lignes de logs existantes`);
|
|
}
|
|
|
|
/**
|
|
* Journalise une ligne (buffer mémoire + persistance). Fail-safe : toute
|
|
* erreur d'IO est avalée (quota localStorage…).
|
|
*
|
|
* @param {string} tag
|
|
* @param {string} message
|
|
*/
|
|
export function log(tag, message) {
|
|
const line = formatLine(Date.now(), tag, message);
|
|
buffer = trimLines([...buffer, line], MAX_LINES);
|
|
try {
|
|
store.setDebugLog(buffer.join('\n'));
|
|
} catch (e) {
|
|
// Journalisation best-effort : ne jamais faire planter l'app pour un log
|
|
}
|
|
}
|
|
|
|
/** @returns {string} tout le journal (une ligne par \n). */
|
|
export function all() {
|
|
return buffer.join('\n');
|
|
}
|
|
|
|
/** @returns {number} nombre de lignes en buffer. */
|
|
export function count() {
|
|
return buffer.length;
|
|
}
|
|
|
|
/** Efface le journal (bouton Paramètres) puis journalise l'effacement. */
|
|
export function clear() {
|
|
buffer = [];
|
|
try {
|
|
store.setDebugLog('');
|
|
} catch (e) {
|
|
// idem : best-effort
|
|
}
|
|
log('app', 'logs effacés');
|
|
}
|