Retour aux projets

Suivi d’habitudes

Suivi d’habitudes

Une application personnelle de suivi de vie, sur ordinateur et sur téléphone, née d’un petit suivi d’habitudes open source et devenue onze vues : des défis qui rapportent des points, une pyramide rêves → objectifs → projets, des arbres de tâches récursifs, un relevé heure par heure de la journée, un temps d’écran échantillonné nativement sous Windows et Android, un journal, et un coach IA sur vos propres données. React 19 et TypeScript au-dessus, Rust et Kotlin en dessous, et un serveur de synchronisation Node sans dépendance à la place d’un compte.

Résultats

Vues dans une seule coquille d’application
11Vues dans une seule coquille d’application
Composants React, contre 8 dans le modèle de départ
46Composants React, contre 8 dans le modèle de départ
Cibles depuis un seul code : web, Windows, Android
3Cibles depuis un seul code : web, Windows, Android
Compte ni base de données derrière la synchronisation
0Compte ni base de données derrière la synchronisation

Chiffres relevés dans le dépôt le 11 août 2026 — environ 12 000 lignes de TypeScript, 550 de Rust et 130 de Kotlin.

Dans l’application

  • Née d’un modèle

    Un suivi d’habitudes open source de huit composants en dessous, toujours à son poste, sous 46 composants et près de 12 000 lignes de travail nouveau.

  • Une pyramide, pas une liste

    Rêves → objectifs → sous-objectifs → projets, avec des arbres de tâches récursifs et un minuteur qui inscrit le travail réel dans le relevé du jour.

  • Un temps d’écran relevé nativement

    Un fil Rust interroge la fenêtre active de Windows toutes les cinq secondes et écarte l’inactivité ; Android lit UsageStatsManager via un plugin Kotlin écrit à la main.

  • Une IA qui apprend vos corrections

    Des titres de fenêtres envoyés par lots à l’API d’Anthropic, vos corrections rejouées comme exemples, et tous les effets dérivés d’un registre unique — l’annulation est gratuite.

  • Synchroniser sans compte

    Un fichier Node sans dépendance derrière un tunnel Cloudflare — dernière écriture gagnante par clé, sauf le temps d’écran, fusionné depuis les deux appareils.

  • La confidentialité par exclusion

    Applications et motifs de titre exclus sont filtrés dans l’échantillonneur, et la clé d’API est délibérément tenue hors du fichier de sauvegarde comme de la synchronisation.

Comment ça marche

Ce projet a d’abord été celui de quelqu’un d’autre : un suivi d’habitudes React sous licence MIT, l’équivalent de huit composants — une grille hebdomadaire de cases à cocher, des séries, des barres de progression, une recherche et un thème clair/sombre. Il fait très bien ce travail, et il est toujours là. Mais je voulais que l’application accueille ma vie réelle plutôt qu’une liste d’habitudes, et chaque réponse à « est-ce qu’elle pourrait aussi… » devenait une vue de plus. Elle compte aujourd’hui 46 composants, 17 modules utilitaires et environ 12 000 lignes de TypeScript, avec un service Rust en arrière-plan et un plugin Android que l’original n’a jamais eus.

L’idée organisatrice est une pyramide, et elle est venue avant le code : les rêves occupent le sommet, à l’état de pur désir, les objectifs donnent forme à un rêve, les sous-objectifs le décomposent, et les projets sont l’endroit où le travail se fait réellement. Les projets portent des échéances et un arbre de tâches récursif — parents et enfants, dépliés et repliés comme des dossiers dans un éditeur — ainsi qu’un minuteur avec lecture et pause : mesurer du travail réel écrit directement un bloc dans le relevé du jour, à l’heure d’Africa/Casablanca. À côté vivent les défis, qui sont des conteneurs d’habitudes sur une durée fixe : on choisit une durée ou on garde les sept jours par défaut, on coche chaque habitude chaque jour, et l’application compte dix points par validation, avec un bonus de complétion et une salve de confettis lorsque toute la série est réussie.

« Ma journée » est l’autre moitié du registre, et sa saisie est un curseur à deux extrémités plutôt qu’un formulaire : on tire une extrémité sur 1 h et l’autre sur 10 h, et le sommeil est consigné, parmi dix catégories de couleurs. Ce que je ne voulais surtout pas saisir à la main, c’est le temps d’écran : l’application le relève donc nativement. Sous Windows, un fil Rust échantillonne la fenêtre au premier plan toutes les cinq secondes, regroupe les échantillons consécutifs d’une même application en sessions, ferme une session au bout de deux minutes d’inactivité et purge les lignes brutes de plus de soixante jours ; sous Android, un plugin Kotlin écrit à la main lit UsageStatsManager derrière l’autorisation système « accès à l’utilisation ». La webview interroge le traceur dont elle dispose toutes les trente secondes, en relisant toujours les trois derniers jours pour qu’une session prolongée, ou synchronisée en retard, se réconcilie au lieu de se dupliquer. Les applications et les motifs de titre exclus sont filtrés à l’intérieur même de l’échantillonneur : un titre de fenêtre privé n’atteint donc jamais le stockage.

Des titres de fenêtres bruts ne sont pas une information, l’application les étiquette donc avec l’API d’Anthropic — par lots de quarante titres, la classification étant mise en cache par titre pour ne jamais payer deux fois la même vidéo. Ce dont je suis le plus content, c’est le traitement d’une mauvaise réponse : les corrections sont conservées et rejouées dans les invites suivantes comme exemples, si bien que le système apprend qu’une chaîne YouTube donnée relève de l’entraînement au français et non du divertissement. Et les effets ne sont jamais écrits directement. Un registre unique des sessions traitées fait foi ; le journal d’étude des projets et les cases que l’IA coche sur les défis en sont tous deux dérivés, puis re-dérivés après chaque changement — corriger, et annuler une correction, revient donc à modifier une ligne plutôt qu’à défaire des effets de bord. Une vue coach séparée dialogue sur les mêmes données, avec un modèle au choix entre Opus 5, Sonnet 5 et Haiku 4.5.

Deux appareils, donc de la synchronisation — et je ne voulais pas d’un compte pour mon propre journal. Le serveur est un unique fichier Node sans dépendance qui stocke un document JSON sur mon PC, publié vers le téléphone par un tunnel Cloudflare gratuit et protégé par un secret partagé. La résolution se fait clé par clé, dernière écriture gagnante, à une exception près et voulue : les sessions de temps d’écran sont produites par les deux appareils, elles sont donc fusionnées élément par élément plutôt que remplacées, et un repère de purge empêche les sessions supprimées de revenir. La règle qui m’a coûté le plus cher est là aussi : se connecter à un serveur, c’est adopter ses données — car une installation neuve écrit des valeurs vides au montage, et ces « modifications » ont un jour écrasé des semaines de suivi réel sur l’autre appareil. L’historique local des modifications est donc effacé au moment de la connexion. De ce même code, l’application se livre de trois façons : une version web, une application Windows avec icône de barre système et démarrage automatique pour que l’échantillonneur et le récapitulatif quotidien continuent de tourner, et un APK Android signé.

Comment un titre de fenêtre devient une case cochée

Schéma du pipeline : la fenêtre active est échantillonnée nativement, les sessions sont versées dans le stockage local, les titres sont envoyés par lots à Claude, et les décisions obtenues alimentent un registre unique des sessions traitées dont dérivent le journal d’étude des projets et les cases cochées des défis ; une correction revient à la fois dans le registre et dans l’invite suivante.

Le pipeline d’étiquetage, et la raison de sa forme : un registre qui fait foi, deux effets dérivés, et une correction dont l’annulation ne coûte rien. Cliquer sur le schéma pour l’ouvrir en taille réelle.

Architecture

Schéma d’architecture : un seul code React et TypeScript se compile en version web, en application Windows et en APK Android ; un échantillonneur Rust et un plugin Kotlin alimentent en sessions de temps d’écran le stockage local, que lisent à la fois le moteur de synchronisation et l’API d’Anthropic, avec un serveur Node sans dépendance atteint par un tunnel Cloudflare.

Une webview, deux moitiés natives et aucune base de données — où tourne chaque pièce, et ce qui circule entre elles. Cliquer sur le schéma pour l’ouvrir en taille réelle.

Stack technique

React 19TypeScriptViteTauri 2RustKotlinNode.jsAnthropic API
Dépôt privé