Changelog

Version actuelle : v2.10.1

v2.10.0

Dernière
Nouveau
  • Filarr Send — partage E2EE anonyme sans compte, accessible depuis `filarr.com/send`. Drag-drop, expiry / max downloads / mot de passe optionnel, barre de progression byte-par-byte. Table D1 dédiée `ephemeral_shares`, préfixe R2 séparé `send/{id}/`, endpoints Worker `/send/*` non authentifiés. Caps anonymes : 2 Go max fichier, 7 jours max, 10 téléchargements max, 3 sends actifs / IP. IP du créateur stockée hashée (SHA-256) jamais en clair.
  • Grille des limites par plan revue pour une progression strictement monotone : Free (1 Go / 14j / 10 actifs / 50 downloads), Solo (10 Go / 90j / 100 actifs / 1000 downloads), Pro (50 Go / 1 an / illimité / illimité). La version initiale laissait Pro à la même taille max fichier que l'anonyme gratuit (5 Go vs 5 Go) — corrigé. Anonyme reste utile pour les envois ponctuels (2 Go ≈ WeTransfer free) mais Free et au-dessus dominent clairement sur expiry, active count, features (revoke, history, 1-per-IP).
  • Audit log par share côté owner — nouvelle table D1 `share_views` (migration 0008) qui log timestamp + pays (`request.cf.country`) + sous-réseau `/16` à chaque chunk_0 servi. Jamais l'IP complète. Endpoint `GET /sync/share/:id/views` (auth owner-only) + bouton « Historique » dépliable dans la page /shares, lazy fetch + cache par shareId.
  • Compteurs séparés visites vs téléchargements — migration 0010 ajoute `info_view_count` à `shares` et `ephemeral_shares`. La page /shares affiche maintenant 2 chips : 👁 page ouverte, ⬇ download complété. Utile sur les shares password-protected où plein de visites n'aboutissent pas (mauvais password, abandon).
  • Option « 1 téléchargement par IP » dans `ShareFileModal` (migration 0011 : `one_download_per_ip` sur `shares` uniquement). Réutilise le `/16` de `share_views` pour la dédup — pas de stockage IP supplémentaire. Chunks 1+ ne sont pas IP-gated (un download interrompu peut reprendre). Hint UI sur le caveat NAT (carrier-grade NAT / corp office partagent la limite).
  • Page dédiée `/shares` dans la sidebar (entrée entre Automatisation et Corbeille), avec header large + sous-titre expliquant l'E2EE. Empty state propre pour les vaults locaux : « Sharing requires cloud sync » + CTA vers Settings. Retirée de Settings → Partages chiffrés.
  • URL recovery via localStorage `filarr-shares-keys-v1` (`{shareId → K_share_base64url}`) — le bouton « Copier le lien » dans /shares reconstitue l'URL même si la modal a été fermée trop vite. Cleanup auto via `pruneLocalShareKeys()` à chaque refresh. Message clair si K_share absent (share créé sur un autre appareil).
Amélioré
  • Page recipient `filarr.com/s/[id]` unifiée pour les deux modes : essaie `/share/{id}/info`, fallback sur `/send/{id}/info` en 404. Le destinataire voit la même URL pattern quel que soit le mode (authentifié ou anonyme).
  • Espacement de la meta row dans la liste /shares : `gap: spacing-2 / spacing-3` au lieu de 1-5, séparateurs rendus en cercles 4px à 40% opacité (les middots collaient au texte avant).
Corrigé
  • Page `filarr.com/send` ne 404 plus — le middleware `next-intl` redirigeait `/send` vers `/{locale}/send` qui n'existe pas. Mirror de l'exclusion `/s/` pour `/send`.

v2.9.0

Nouveau
  • Partage E2EE de fichiers — clic droit sur n'importe quel fichier puis « Partager par lien ». Filarr génère une URL `filarr.com/s/{id}#k={cle}` qui se déchiffre dans le navigateur du destinataire, sans compte requis. La clé `k` vit dans le fragment URL et n'est jamais envoyée à nos serveurs (RFC 3986) — zero-knowledge intact, même avec un compromis total de Filarr.
  • Pipeline « ré-encryption au moment du partage » : le client lit le fichier en clair localement, le chunk en 16 MB, re-chiffre chaque chunk avec une clé `K_share` fraîche, et uploade vers un préfixe R2 dédié au share. Conséquence pratique : ça marche pour tous les fichiers, y compris ceux uploadés avant la v2.8.0 (format de stockage interne legacy).
  • Mot de passe optionnel sur les partages (Solo et Pro) — `HKDF-SHA-256(K_share + password, salt)` dérive la clé de déchiffrement réelle, le serveur ne voit jamais le mot de passe ni la clé.
  • Limites configurables par share : expiration (1h / 24h / 7j / 30j / 90j selon plan), nombre maximum de téléchargements, révocation manuelle depuis Settings → Partages chiffrés.
  • Panel Settings → Partages chiffrés : liste les liens actifs et récemment inactifs (révoqués/expirés/limite atteinte), permet de recopier l'URL d'un share existant (la clé est mise en cache local après création) ou de révoquer.
  • Page publique de réception `filarr.com/s/{id}` en ligne : déchiffrement 100% navigateur (WebCrypto), saisie du mot de passe quand requis, barre de progression chunk-par-chunk, langue auto-détectée via `Accept-Language` avec toggle FR/EN. Bundle minimal (~7 kB), CSP stricte, `noindex` — la page n'apparaît pas dans les moteurs de recherche.
Amélioré
  • Modal de partage adapté à tous les thèmes — utilise désormais les composants Modal/Button/Checkbox du design system et les variables CSS d'accent, plus de couleurs hardcodées.
  • Les options « Limiter les téléchargements » et « Mot de passe » deviennent des cartes interactives qui montrent visuellement leur état (bordure accent quand sélectionné, opacity 60% quand le plan ne le permet pas) — plus de checkbox 16px invisibles.
  • Barre de progression réelle byte-par-byte pendant la création du share (utile pour les gros fichiers — la création prend ~1s par 16 MB).
  • Le compteur côté destinataire affiche maintenant « téléchargements » au lieu de « vues » — c'est le fetch du premier chunk qui incrémente côté serveur, pas l'ouverture de la page. Le wording reflète la réalité.
Corrigé
  • Worker `[placement] mode = "smart"` activé dans `wrangler.toml` (régression : la config avait été perdue après un rebase).
  • Migration D1 0007 : drop des colonnes `wrapped_fek` / `wrapped_fek_iv` (héritage de la v1 du design abandonnée) et ajout de `status TEXT NOT NULL DEFAULT 'pending'`. Les shares restent en `pending` tant que tous les chunks ne sont pas uploadés — les endpoints publics rejettent les `pending` avec un 404, donc impossible de récupérer un share half-baked.

v2.8.0

Nouveau
  • Compression côté client (deflate niveau 5) avant chiffrement — appliquée à tous les fichiers texte/JSON/Office/code. Le serveur ne voit toujours que du chiffré opaque, mais vous stockez ~2 à 3× plus de données originales dans le même quota R2. Marker de version sur chaque blob pour préserver la rétrocompatibilité avec les fichiers déjà uploadés.
  • Quotas augmentés sans hausse de prix — Solo passe de 10 → 25 Go, Pro passe de 50 → 150 Go. Le Free reste à 1 Go en essai 30 jours.
  • Taille max par fichier 500 Mo → 5 Go. Les uploads gros fichiers utilisent désormais le protocole multipart R2 (parts de 16 Mo, parallélisme 3) qui contourne la limite 100 Mo des Cloudflare Workers et permet la reprise sur coupure réseau.
  • Tous les types de fichiers acceptés — la blacklist d'extensions (.exe, .msi, .bat, .dmg, .deb…) a été supprimée. Filarr est end-to-end chiffré : bloquer une extension côté client était cosmétique (un user pouvait toujours renommer). Vous pouvez maintenant stocker installeurs, scripts, ISO, sans contournement. L'UI affichera un message informatif rappelant qu'on ne peut pas scanner les fichiers chiffrés.
  • Manifest cloud compressé avec deflate — sur un compte avec beaucoup de fichiers, le manifest peut faire plusieurs Mo de JSON. Compression ~5× sur ce type de payload, latence de sync réduite proportionnellement.
Amélioré
  • GET /sync/manifest gère désormais If-None-Match / 304 — le client envoie sa version locale, le Worker répond 304 sans corps si rien n'a changé. Économise une fetch + decrypt complet à chaque cycle de sync en steady state (la majorité des cycles).
  • Worker Smart Placement activé — le runtime Cloudflare colocalise désormais l'exécution avec D1+R2 au lieu de l'eyeball. 50 à 200 ms de latence en moins sur les calls /sync/* selon votre localisation.
  • Compression sautée pour les formats déjà compressés (mp4, jpg, png, pdf, docx, zip, 7z…) — détection par extension ET par magic bytes pour rester robuste. CPU gagné, ratio inchangé.
Corrigé
  • Le fallback de décryptage tente le format v0 (legacy, sans marker) en cas d'échec d'authentification GCM sur les formats v1/v2 — couvre le cas où un IV legacy commence par un octet 0x01/0x02 ressemblant à un marker.
  • Le détecteur de quota côté Worker vérifie maintenant le projection contre la consommation actuelle ET le delta du multipart en cours, évitant l'overshoot quand deux uploads multipart concurrents passent la pré-vérification.

v2.7.0

Nouveau
  • Kanban — swimlanes horizontales avec 4 modes (Aucune, Carnet, Premier tag, Épinglées). Chaque voie est repliable, état persisté en localStorage. Drag-drop volontairement intra-voie uniquement (cross-lane changerait le critère de groupement, trop implicite pour un geste). Édition inline du titre (double-clic) + bouton « + » par colonne qui crée une note pré-affectée à cette colonne et au carnet courant.
  • Vue graphe — panneau latéral détaillé au clic d’un nœud : entrants/sortants/wordcount, tags résolus, dernière modif, preview 240 chars, voisins ↙/↗ cliquables pour pivoter sans quitter le canvas, bouton « Ouvrir la note » pour l’ancien comportement. Flèches directionnelles sur les arêtes wiki-links (markers SVG, liens réciproques fusionnés en une arête bidirectionnelle).
  • Mind map — collapse + expand par branche (chevron −/+ sur chaque nœud avec enfants), état per-note dans localStorage (entrées vides élaguées). Boutons « Collapse all » / « Expand all ». Clic sur un nœud heading → la note s’ouvre et l’éditeur scrolle automatiquement sur cette section (bridge via flag transient pendingScrollToHeading, NoteEditor calcule la position ProseMirror dans un requestAnimationFrame).
  • Calendrier — monté comme vue à part entière dans la barre de modes. Modal jour au clic d’une case avec notes prévues + éditions + bouton « Créer une note quotidienne » qui pré-remplit titre Daily YYYY-MM-DD et scheduledDate. Champ scheduledDate dédié sur la note (drop sur une case ne réécrit plus createdAt). Plage de l’agenda configurable (passé + futur tunables).
  • Vue sticky — resize per-note (handle bas-droite, dimensions stockées dans viewSizes, MIN/MAX bornés) + contenu scrollable. Bouton « Fit all » qui recadre le canvas pour englober toutes les stickies. Persist du zoom + pan en localStorage, restauré au remount.
  • Mosaïque — toolbar dédiée de tri (updatedAt/createdAt/title/wordcount) + filtre (tag/notebook/pinned), persistée dans filarr.masonryView.config.v1.
  • Indicateur de save state dans le header de l’éditeur — 4 états (synced / saving / pending / error), couleurs distinctes, aria-live="polite" pour les screen readers. Distinct du badge sync cloud : les users local-only ont enfin un signal d’état pour leur travail.
Amélioré
  • Couleurs stables dans sticky et mosaïque — dérivées du note.id (hash) plutôt que de l’index dans la liste, donc plus de saut de couleur au filtrage/réordre. Palette dark mode revue pour rester distinguable et lisible.
  • Configuration de vue base-de-données persistée (colonnes affichées, ordre, largeurs, sort, filtres) en localStorage par dataset.
  • Lisibilité du calendrier sur thèmes warm — chips et onglets de sous-vues utilisent --color-text-primary avec opacité (au lieu de --color-text-secondary qui devenait invisible). Guard regex isValidCssColor ajouté pour rejeter les couleurs custom polluées style "x,y".
  • Clusters déconnectés du graphe plus rapprochés — tuning des forces d3 (gravité augmentée, repulsion réduite entre composants connexes) pour que les sous-graphes orphelins ne s’éloignent pas à l’infini.
  • Erreurs localStorage du graphe remontées via toast d’avertissement au lieu d’être avalées silencieusement (QuotaExceededError, accès refusés).
  • Migration coverColor → viewPositions['sticky'] — la vue Sticky écrivait "x,y" dans coverColor, ce qui polluait l’affichage de la couverture dans les autres vues. Reducer one-shot migrateStickyPositionPollution qui détecte le pattern, parse les coords, vide coverColor.
Corrigé
  • « Collapse all » du mind map qui cassait la vue — collectCollapsibleIds poussait root dans la liste, donc Collapse all repliait root → écran vide avec juste un « + ». Désormais root est exclu, et loadCollapsed strip tout 'root' legacy en localStorage (self-healing).
  • Timeline préserve createdAt/updatedAt au cloud sync — le sync écrasait avec Date.now() les timestamps lors de la rehydratation cloud, toute la timeline était écrasée à chaque cycle. Fix dans StorageService.addItemToFolder qui propage maintenant les timestamps reçus.
  • Timeline affiche tous les matchs sous filtre actif — un cap silencieux de 100 entrées coupait le résultat des recherches sans indicateur.
  • Note active visible dans la mode bar sur toutes les vues (avant : seulement liste).
  • Notes corbeille masquées immédiatement des colonnes Kanban (filtre n.deletedAt == null, plus besoin d’un refresh).
  • Drag intra-colonne dans le Kanban — préserve l’ordre via un nouveau champ kanbanOrder + reducer reorderKanbanCard. Les drops cross-colonnes mettent toujours à jour kanbanStatus.
  • Clic sur une note en vues non-liste ouvre maintenant l’éditeur (handleOpenNote partagé qui switch en mode liste + set l’editing note).
  • currentDate du calendrier sync entre sous-vues — clic sur une cellule en mode Mois passe en mode Jour positionné sur ce jour-là.
  • Split panel en mode Notes — chaque panneau a maintenant son propre viewMode et editingNote, plus de leak de state entre les deux moitiés.
  • Récursion infinie dans handleViewModeChange (un replace_all malheureux avait fait s’auto-appeler la fonction). Restauration du dispatch(setNotesViewMode(mode)) dans la branche else.

v2.6.0

Nouveau
  • Surveillance des téléchargements OS — Filarr peut maintenant surveiller un dossier OS (typiquement Téléchargements) et importer automatiquement chaque nouveau fichier dans une boîte de réception Filarr de votre choix. Watcher chokidar avec awaitWriteFinish 2 s pour ne pas attraper les fichiers en cours d’écriture, watch top-level seulement (depth: 0), ignoreInitial: true pour ne pas re-importer en boucle au démarrage. Multi-dossiers supportés, config profile-scopée, redémarré automatiquement sur switch de profil.
  • Sécurité défense en profondeur — blocklist en dur des extensions exécutables (.exe, .msi, .bat, .cmd, .ps1, .vbs, .js, .scr, .lnk, .jar, .app, .dmg…) appliquée avant tout filtre utilisateur. Refus de surveiller les chemins système (C:\Windows, C:\Program Files, /etc, /usr/bin…) — un appConfig falsifié ne peut pas faire lire un dossier protégé. deleteSource IPC restreint aux chemins effectivement surfacés par le watcher ET sous une racine surveillée.
  • Filtre extensions opt-in — chips ajoutables/supprimables dans Settings (vide = tout est importé sauf la blocklist exécutables, jamais désactivable). Sanitization stricte côté main : trim, lowercase, strip leading dot, regex [a-z0-9]+ only.
  • Bouton « Scanner » — catch-up scan one-shot qui énumère chaque dossier surveillé et défère chaque fichier à la même pipeline handleNewFile (donc applique blocklist + filtre extension + size limit + dedup). Retourne { queued, skipped } pour feedback. Désactivé tant que le watcher n’est pas active.
  • Nouveau trigger d’automatisation file_imported_from_os — fire alongside file_created quand le watcher importe. Permet d’écrire des règles ciblées OS-imports uniquement (ex : « PDF importé depuis l’OS → Documents », sans toucher aux PDFs ajoutés in-app). Nouvelle condition os_source_path pour matcher sur le chemin d’origine (avec normalisation des séparateurs Windows/Unix).
  • 3 templates d’automatisation utilisant le nouveau trigger : « Router les téléchargements OS », « Trier les PDFs téléchargés », « Trier les images téléchargées ».
  • UI Settings avec feedback runtime — badge de statut live (Active / Démarrage… / Erreur / Inactive) avec compteur de dossiers surveillés. Compteur « X fichiers importés » + nom du dernier import, mis à jour en temps réel via le canal IPC status-changed (pas de polling). Bandeau d’erreur rouge si state === error, bandeau ambré si certains dossiers configurés sont introuvables sur le disque. Toggle master désactivé tant que la config n’est pas complète, avec hint inline indiquant ce qui manque.
Amélioré
  • Auto-routage des fichiers ajoutés in-app — le moteur d’automatisation avait file_created comme trigger depuis longtemps mais rien ne l’émettait jamais, les règles utilisateur ne tournaient pas sur drag-drop / import button. Fix : addFileToFolder dispatch maintenant processFileEvent('file_created', …) après chaque upload réussi. Nouveau template « Router les fichiers importés » (no-condition + move_to_folder). Garde anti-boucle : move_to_folder / copy_to_folder deviennent no-op silencieux si source == cible.
  • Reporting honnête en fin de sync — Sync complete: X/N uploaded avec compteur de failed séparé, au lieu de X uploaded qui mentait sur le résultat réel.
  • Cache mémoire des profils zombies (profilesFailedToRestore) — un profil cloud qui échoue à se restaurer (typiquement parce que sa encryptionKey a été wrappée avec une autre clé OS / DPAPI / Keychain et n’est pas déchiffrable ici) n’est plus retenté à chaque cycle de sync. Vidé sur switch de profil.
Corrigé
  • CRITIQUE — Uploads meta:* débloqués au worker Cloudflare. Le hardening pentest du 25 avr avait restreint le FILE_ID_REGEX à [a-zA-Z0-9_-]{1,128}, ce qui rejetait silencieusement tout fileId portant le préfixe meta: (meta:notes, meta:<id>). Conséquence en prod : les notes nouvellement éditées ne se synchronisaient plus, le manifest distant était incrémenté pendant que les blobs R2 sous-jacents n’étaient jamais écrits. Fix : autoriser le : dans le regex (le seul séparateur structurel utilisé pour <namespace>:<id>). Worker redéployé le 2026-04-29.
  • uploadFile / downloadFile rethrow maintenant après log + enqueue retry — elles avalaient leurs exceptions, donc le try/catch de processQueue atteignait markSuccess à chaque retry, finissant par un log syncQueue Completed même quand R2 avait répondu 400. Les Promise.allSettled des boucles batch absorbent la rejection sans casser le cycle.

v2.5.0

Nouveau
  • Badge de synchronisation par note — 4 états distincts (Synchronisée, Synchronisation…, Non synchronisée, Hors ligne) dérivés du dirty-tracking client-side en croisant note.updatedAt avec sync.lastSyncAt + sync.state. Deux variantes : full (icône + label) dans le header de l’éditeur, compact (point coloré 8 px) dans chaque carte de la vue liste. Masqué automatiquement en mode local-only.
  • Toggle « Afficher les carnets » dans la vue graphe — off par défaut pour préserver la philosophie actuelle, persisté dans localStorage. Quand activé : nœud losange violet par carnet (uniquement ceux avec une note dans le graphe), arêtes pointillées discrètes carnet → notes (opacité ~22 %) qui ne concurrencent pas les wiki-links. Clic sur un carnet = filtre par carnet + switch en vue liste.
Amélioré
  • Frappe dans une note longue ~300× à ~9,5 M× plus rapide — le hot-path onUpdate → Redux faisait un JSON.stringify de tout le document à chaque keystroke debouncé. Ref lastSyncedContentRef cache la dernière string émise pour transformer la comparaison en identity-compare O(1) sur les round-trips internes. Sur une note de 5,9 Mo, l’ancien path brûlait 30 ms de main-thread par seconde de frappe, le nouveau consomme 4 ns.
  • Ouverture d’une note longue : INP 743 ms → 230 ms (~70 % de réduction) — retrait du key={editingNote.id} sur NoteEditor qui démontait/remontait TipTap (30+ extensions, schéma ProseMirror, panneaux enfants) à chaque clic. Trois garde-fous ajoutés pour éviter les fuites d’état entre notes : reset effect sur note.id, snapshot prevOnUpdateRef, flush synchrone dans onBlur.
  • Double JSON.parse éliminé sur l’ouverture — le ref de sync est seedé avec note.content dès l’initialisation, l’effet voit l’égalité au premier passage et skip.
  • Panneaux latéraux différés avec useDeferredValue — BacklinksPanel faisait trois scans synchrones O(N) à O(N·M) sur note-switch. React fractionne maintenant : commit urgent pour rendre l’éditeur interactif, puis recompute en priorité basse.
  • 17 tests unitaires ajoutés (8 syncEditorContent + 9 getNoteSyncState) + microbenchmark reproductible scripts/bench-note-editor-sync.mjs pour valider tout futur refactor du hot-path.
Corrigé
  • Pollution des icônes par le Kanban — la vue Kanban écrivait l’id de la colonne (inbox, in-progress, review, done…) directement dans le champ icon, écrasant l’emoji choisi à chaque drag-drop. Nouveau champ dédié kanbanStatus + reducer migrateKanbanIconPollution one-shot idempotent qui heal le parc existant (heuristique kebab-case).

v2.4.2

Amélioré
  • Auto-healing du compteur storage — syncService appelle POST /sync/recalculate/:profileId une fois par session par profil au retour d’un sync réussi, pour réconcilier le compteur depuis la vérité R2 sans attendre une action utilisateur.
  • Endpoints admin one-shot — POST /sync/admin/reconcile-all (scan complet bucket, agrège par profil, overwrite storage_used_bytes — exécuté en prod : 78 objets, 5 profils corrigés, total ramené à 102,7 Mo) et POST /sync/admin/list-keys/:userId/:profileId (inspection R2 avec catégorisation manifest / file-chunk + top fichiers, utile pour identifier les orphelins).
Corrigé
  • Compteur de stockage cloud sur-comptait massivement — chaque ré-upload d’un chunk R2 incrémentait storage_used_bytes de la taille totale du nouveau body sans soustraire la taille de l’objet existant. Après ~26 ré-uploads cumulés, le compteur divergeait d’un facteur ×26 (D1 : 2,5 Go annoncés vs R2 : 104 Mo réels, observé en prod). Fix : HEAD R2 avant le put + update par delta avec MAX(0, …) pour blinder contre un compteur négatif.
  • Quota presign symétrique — le check de quota dans POST /sync/presign/upload comparait used + newSize > limit sans tenir compte de la taille du chunk déjà en place. Un user proche de sa limite ne pouvait plus ré-uploader un chunk existant. Désormais : used − previous + new > limit.

v2.4.0

Nouveau
  • Historique des versions de notes — snapshots chiffrés automatiques à chaque save (dedup par hash, throttle 2 min, rétention 50 versions ou 30 jours), 3 modes UI interchangeables : liste latérale, scrapbook plein écran 2 colonnes avec diff condensé, ou timeline style Figma avec scrubber horizontal et bulle de date flottante. Restauration sûre qui crée un nouveau snapshot automatique.
  • Couvertures de notes — 40 presets en 6 catégories (Couleurs, Pastels, Sombres, Tropicaux, Minimal, Mesh) + upload d’image personnelle avec compression JPEG automatique (max 1.2 MB après compression). 3 sliders pour zoomer (30-300%, dézoom inclus) et repositionner l’image verticalement et horizontalement en live.
  • Picker d’icônes hybride à 3 onglets — 280 emojis curatés en 8 catégories avec recherche par mots-clés et 18 récents persistés, 145 icônes Lucide SVG en 17 catégories, ou upload d’une petite image personnelle (logo de projet).
Amélioré
  • Extensions TipTap complètes dans le viewer de versions (callouts, math, toggle, wiki-links, file embeds, code blocks lowlight, tables) — aucune note ne rend blanc dans l’historique, quelle que soit sa complexité.
  • Fallback défensif si un snapshot a un contenu JSON désynchronisé : banner jaune "Rendu indisponible" + affichage du texte brut pour que l’utilisateur ne soit jamais devant un panneau vide.
Corrigé
  • Restauration d’une ancienne version qui ne s’appliquait pas dans l’éditeur (NoteEditor ne resynchronisait TipTap avec Redux que sur changement de note.id, pas sur note.content).
  • Modal d’historique qui se fermait à chaque sync cloud (loadNotesFromDisk.fulfilled nullait systématiquement les pointeurs d’édition, démontant NoteEditor).
  • Doublons d’historique après restart (cache mémoire vide traitait le premier save comme "first seen") — lazy warm-up depuis le disque avant la décision de dedup.
  • Clics qui passaient derrière la barre de titre en mode Timeline (contrôles natifs de la fenêtre à z-index 99999) — padding top ajouté sur le wrapper.
  • Previews de versions qui rendaient vide : double cause, recréation de l’instance TipTap sans repaint + extensions manquantes faisant dropper les nodes custom. Fix combiné setContent impératif + remount forcé + liste d’extensions complète.
  • Piste des range sliders invisible sur thèmes clairs (accent-color ne colorait que le thumb) — styling explicite des pseudo-éléments webkit/moz.

v2.3.3

Nouveau
  • Synchronisation du PIN entre appareils — le hash PIN transite désormais dans le manifest chiffré par la FEK. Configurer un PIN sur un appareil le propage automatiquement aux autres (last-write-wins sur horodatage). Sync déclenché immédiatement au changement de PIN au lieu d’attendre le prochain cycle.
Amélioré
  • Écran de verrouillage route correctement vers le PIN lock dès qu’un PIN est reçu d’un autre appareil (au lieu de retomber sur le mot de passe vault).

v2.3.2

Amélioré
  • Indicateur FEK réactif — nouveau check disque (4 chemins candidats cloud/local × dev/prod) au lieu d’une variable mémoire renderer qui était toujours inactive en mode cloud.
  • Log throttling côté sync — les skips « not-authenticated / offline / no-fek » ne se loguent plus qu’aux transitions, supprimant le spam toutes les 15-20 s.
  • Écran Vault Password : logo Filarr + nom réel du profil (via chaîne de fallback profil → localProfile → email local-part), clés i18n ajoutées (FR/EN).
Corrigé
  • CRITIQUE — Perte de notes par auto-save prématuré : à l’ouverture d’un profil, saveNotesToDisk pouvait s’exécuter avec l’état vide avant que loadNotesFromDisk ait fini, écrasant jusqu’à 30 Ko de notes. Double parade : guard côté renderer (auto-save bloqué tant qu’un load n’a pas complété) + guard côté main (refus d’écrire < 200 octets au-dessus d’un fichier existant de > 200 octets).
  • Enhanced Lock (handler before-quit) : le handler async laissait Electron quitter avant la suppression de .fek_safe. Remplacé par event.preventDefault() + quit explicite après complétion — .fek_safe est désormais garanti d’être supprimé avant le quit.
  • Enhanced Lock (enforcement au lancement) : handleProfileSelected n’activait pas isLocked=true quand Enhanced Lock était activé mais .fek_safe absent. L’app passait directement à l’écran principal. Pre-check ajouté.
  • Rechargement post-unlock : après déverrouillage, les dossiers et notes sont rechargés depuis le disque (Enhanced Lock saute ces loads au startup, il fallait les rejouer).
  • Graph view : liens visibles sur thèmes clairs — le stroke des arêtes était quasi invisible avec des faibles densités.

v2.3.1

Amélioré
  • Version de maintenance — correctifs mineurs d’UI pour le widget Pomodoro introduit en v2.2.0 (anneau de progression, toggles des paramètres).
Corrigé
  • Anneau du Pomodoro : temps et label empilés verticalement au lieu de chevaucher.
  • Cases à cocher des paramètres Pomodoro remplacées par de vrais switches visibles.
  • Contraste des onglets de mode amélioré (texte blanc sur accent au lieu de blanc sur blanc).

v2.3.0

Amélioré
  • Electron 39.2.1 → 41.2.1 — corrige 18 CVEs Electron accumulées dans la branche 39 : context isolation bypass, AppleScript injection, header injection, use-after-free multiples, path injection sur setAsDefaultProtocolClient (Windows), USB device spoofing, etc.
  • electron-builder 24.13.3 → 26.8.1 — aligné avec Electron 41.
  • npm audit : passage de 55 à 47 vulnérabilités, 0 critique, 0 high runtime direct. Seul react-scripts reste (build-only, non exposé en prod).
  • Smoke test runtime OK : authentification, sync cloud, merge manifest, upload/download, aucune régression sur les APIs Electron utilisées (BrowserWindow, webContents, session, Tray, autoUpdater, safeStorage, dialog, shell.openExternal, contextBridge).

v2.2.2

Amélioré
  • Fuses Electron complets — 8 fuses explicites avec strictlyRequireAllFuses : RunAsNode, EnableCookieEncryption (cookies chiffrés via DPAPI/Keychain/libsecret), EnableNodeOptionsEnvironmentVariable, EnableNodeCliInspectArguments, EnableEmbeddedAsarIntegrityValidation, OnlyLoadAppFromAsar, LoadBrowserProcessSpecificV8Snapshot, GrantFileProtocolExtraPrivileges. Une future version d’Electron ne pourra pas introduire un fuse avec un défaut non-sûr à notre insu.
  • HKDF-SHA-256 sur le secret partagé ECDH du pairing multi-appareils — les 256 bits bruts sont désormais expansés via HKDF avec le code de pairing comme salt et un domain separator « filarr.pairing.wrap.v1 » comme info label. Plus conforme aux hypothèses de sécurité d’AES-GCM et isole la clé de wrap de tout autre usage futur du secret partagé.
Corrigé
  • Clés i18n manquantes (warnings i18next::translator missingKey en console au lancement) : commandPalette.commands.focusTimer/focusTimerDesc, settings.font/fontDesc, settings.importVaultModalDesc, settings.importSelectFile.

v2.2.1

Amélioré
  • Audit sécurité complet post-v2.2.0 — 24 findings vérifiés directement dans le code, 8 patchs livrés immédiatement, restants déférés et documentés.
Corrigé
  • CRITICAL — Path traversal via import:readFile / import:readDirectory : un XSS dans le renderer pouvait exfiltrer ~/.ssh/id_rsa, .aws/credentials, l’historique git, etc. Désormais les handlers exigent une whitelist de paths approuvés par dialog.showOpenDialog + rejet des symlinks + cap de 20 000 entrées au walk.
  • CRITICAL — ZIP bomb dans vault:importZip : aucune limite de décompression. Cap ajouté : 500 Mo total, 50 Mo par entrée, 10 000 entrées max. Rejet préalable des chemins absolus ou contenant « .. ».
  • HIGH — UUIDs prévisibles : generateUUID / generateUniqueId utilisaient Math.random() (non-cryptographique). Bascule vers crypto.randomUUID() / crypto.getRandomValues() (source OS).
  • HIGH — Service Worker désactivé : un SW dans Electron pouvait servir du JS obsolète après mise à jour (l’utilisateur continuait d’exécuter du code vulnérable après patch). Remplacé par unregister() + purge des Cache Storage au démarrage.
  • HIGH — Timing attack sur comparaison hash : filePasswordService comparait les hash legacy SHA-256 et PBKDF2 avec « === ». Remplacé par une comparaison en temps constant (XOR sur toute la longueur).
  • MEDIUM — URL protocol check : shell.openExternal était gardé par un startsWith(“https://”) naïf. Remplacé par un parseur new URL() strict rejetant tout sauf http/https, les contrôles ASCII, et les URLs > 8 Ko. Appliqué aussi aux URLs de billing (defence-in-depth contre backend compromis).
  • MEDIUM — DOMPurify FORBID_TAGS élargi : MarkdownPreview bloque désormais svg, math, foreignObject, annotation-xml, object, embed, base, meta, link, et les attributs srcdoc, formaction, ping (historiquement utilisés pour bypass le sanitizer).
  • MEDIUM — safeStorage fail-closed : hybrid:storeFEK retournait false silencieusement si safeStorage était indisponible. Un caller distrait pouvait continuer sans persistance chiffrée. Désormais lève une erreur explicite.

v2.2.0

Nouveau
  • Minuteur Focus / Pomodoro — widget flottant persistant avec 3 modes (focus 25 min, pause courte 5 min, pause longue 15 min — tous configurables). Avance automatique focus → pause selon le cycle. Deux états : pilule minimisée (horloge + play/pause) et carte étendue avec anneau de progression, onglets de mode, compteur quotidien et panneau de paramètres. Statistiques journalières 90 jours. Notifications système. Accessible via bouton dans la barre d’en-tête ou via la palette de commandes.
  • Thème Forêt — thème sombre pin-vert profond, canopée avec lucioles ambrées et rayons de lumière verts. Pensé pour les longues sessions d’écriture.
  • Police Atkinson Hyperlegible — conçue par le Braille Institute pour maximiser la lisibilité (formes de lettres distinctives qui réduisent les confusions b/d, 0/O). Utile pour les utilisateurs malvoyants comme pour tous.
Amélioré
  • Audit dépendances — passage de 59 vulnérabilités (1 critique) à 55 (0 critique). Upgrade jspdf 4.2.0 → 4.2.1 (fixe XSS critique GHSA-wfv2-pwc8-crg5, CVSS 9.6), axios 1.7.7 → 1.15.0 (DoS via __proto__, SSRF NO_PROXY, header injection), dompurify 3.3.1 → 3.4.0 (5 bypass du sanitizer).

v2.1.0

Nouveau
  • Import depuis Obsidian — wizard 4 étapes (source → fichier → options → progression). Parse le YAML frontmatter, les wiki-links [[]], les tags #tag, les callouts > [!note]. Mapping automatique des dossiers top-level vers les carnets Filarr.
  • Thème Sakura — thème clair inspiré des cerisiers en fleurs, rose doux sur crème chaud.
  • Thème Crépuscule — thème sombre crépusculaire, ambre et pourpre sur brun profond.
  • Police Space Grotesk — nouvelle option typographique dans Apparence.
  • Halos atmosphériques — overlay gradient subtil sur les thèmes à effets (Space, Aurora, Sakura, Crépuscule).
Amélioré
  • Graphe Canvas2D — remplacement du rendu SVG par Canvas2D (10-50x plus rapide). Simulation d3-force avec Barnes-Hut, frustum culling, HiDPI, zoom vers le curseur.
  • Graphe organique — formule de link strength 1/√max(degré), les sous-structures émergent naturellement au lieu de former une boule.
  • Taille des nœuds proportionnelle à √connexions (6-40px), les hubs sont visuellement proéminents.
  • Drag interactif — déplacer un nœud relance la simulation, les voisins réagissent. Le nœud reste épinglé.
  • Labels intelligents — visibles selon le zoom et l’importance du nœud (hubs affichés en premier).
  • Limite de nœuds passée de 500 à 2000.
  • NotesList virtualisée — react-virtuoso réduit ~99% des nœuds DOM pour le scroll fluide avec des milliers de notes.
  • Wiki-links cliquables — les [[note]] importées sont conservées en texte brut pour le plugin de décoration Filarr.
  • Liens externes — Ctrl+Clic ouvre les URLs http(s):// dans le navigateur.
  • Wiki-suggestions coupées à 20 résultats (perf avec 6000+ notes).
  • Inline parser — support de ==highlight==, [[wiki-link|alias]], et conversion HTML→Markdown.
Corrigé
  • Billing webhook — fallback +30 jours si Stripe ne renvoie pas current_period_end.
  • NoteEditor — adaptation des couleurs de fond et texte par thème.
  • Obsidian preprocessor — nettoyage des commentaires %%, boutons HTML, badges shields.io.

v2.0.0

Nouveau
  • Synchronisation cloud zero-knowledge — chiffrement AES-256-GCM côté client, stockage R2 Europe (Frankfurt)
  • Jumelage multi-appareils sécurisé — échange de clés ECDH P-256 via code à 6 chiffres
  • Plans d’abonnement — Free (1 Go, 1 appareil), Solo (10 Go, 3 appareils, 4€/mois), Pro (50 Go, illimité, 8€/mois)
  • Intégration Stripe — checkout, portail client, webhooks, détection automatique du changement de plan
  • Synchronisation des profils entre appareils — nom, avatar, structure de dossiers
  • Verrouillage automatique — configurable (5/15/30/60 min), efface la FEK de la mémoire
  • Mode verrouillage renforcé — supprime .fek_safe à la fermeture, mot de passe vault requis à chaque lancement
  • Clé de secours exportable — fichier JSON chiffré avec PBKDF2 + AES-GCM, restauration en 2 étapes
  • Indicateur cloud-only — badge gris sur les fichiers disponibles uniquement en ligne
  • Page filarr.com/account — gestion des appareils, abonnement et suppression de compte depuis le navigateur
  • Onboarding multi-appareils — détection automatique device principal vs secondaire
Amélioré
  • Manifest cloud chiffré avec la FEK (plus la clé locale) — compatible multi-appareils
  • Deduplication des appareils — device ID persisté localement, upsert au login
  • Auto-login après vérification email dans l’onboarding cloud
  • Recalcul du quota storage depuis R2 (POST /sync/recalculate)
  • Token refresh résilient — ne déconnecte plus sur erreur fichier Windows (EPERM)
  • Notes supprimées avec le dossier parent (soft delete, plus d’orphelins)
  • Changement de mot de passe synchronisé avec le compte cloud
Corrigé
  • Quota storage désynchronisé après suppressions locales
  • Pairing affiché sur le premier device (faux positif device count)
  • FEK introuvable après réinitialisation StorageService (recherche multi-chemins)
  • Metadata.json illisible sur Device B (clé de chiffrement non partagée)
  • .fek_safe introuvable après changement de profil actif
  • Sync ne démarrait pas automatiquement après l’onboarding
  • Bouton Suivant visible pendant l’étape de jumelage
  • Flag onboarding non persisté sur disque pour le flow secondaire

v1.7.1

Nouveau
  • Indentation Tab / Shift+Tab dans les listes (checkboxes, puces, numérotées)
  • Chevron collapse sur les checkboxes — replie/déplie le contenu imbriqué
  • Détection automatique de la langue OS dès l’onboarding
  • Sélecteur de langue sur la page de bienvenue (🇫🇷/🇬🇧)
  • Single instance lock — empêche les lancements multiples, réactive la fenêtre existante
Amélioré
  • Thème appliqué en temps réel dans l’onboarding
  • Mise à jour silencieuse — installateur NSIS en arrière-plan, comme VS Code et Obsidian
  • Installation auto à la fermeture quand une mise à jour est prête
  • Persistance du thème sur disque via IPC (survit aux mises à jour)
  • Thème pré-appliqué au chargement — plus de flash blanc en dark mode
  • Marges des titres réduites (aligné sur Notion/Obsidian)
  • Pipeline de release interactive — choix des plateformes à builder
  • Workflow CI conditionnel — jobs séparés par plateforme
  • Nom d’artefact Windows avec tirets (corrige le 404 auto-updater)
  • Télémétrie rebranchée — stats KV Cloudflare reprennent
Corrigé
  • setupAutoUpdater() appelé deux fois, doublant les event listeners
  • Les thèmes Space, Lofi, Sky n’étaient pas restaurés au redémarrage
  • Plusieurs instances pouvaient se lancer simultanément
  • Le script release.js perdait les guillemets JSON dans le message de tag

v1.6.3

Nouveau
  • Mot de passe de chiffrement à l’onboarding — dérivation KEK et initialisation FEK dès la création du profil
  • Phrase de récupération (12 mots, style MetaMask) pour restaurer le mot de passe de chiffrement
  • Réinitialisation du PIN via mot de passe de chiffrement ou phrase de récupération
  • 3 nouveaux thèmes : Space (étoilé), Lofi (tons terre), Sky (bleus aériens)
  • Code blocks améliorés — sélecteur de langage (24), copier, compteur de lignes, fold/collapse
  • Import de vault complet (ZIP) — notes, dossiers, tags, paramètres, supports plain et chiffré
  • Export enrichi — contenu TipTap JSON, métadonnées étendues, templates, flashcards, automations
  • Nouveau logo SVG Filarr (composant adaptatif au thème) + tous les assets refaits
  • Panneau latéral Notes repliable (Ctrl+B)
  • 30+ couleurs d’accent et 36 couleurs d’avatar pour les profils
Amélioré
  • Vue graphe — centrage instantané dès le premier frame, répulsion et ressorts adaptatifs
  • Flashcards — parsing TipTap corrigé, pattern Q:/A: plus permissif
  • Wiki-links — détection via texte brut TipTap au lieu de plainText (corrige les liens non détectés)
  • Aperçu au survol des liens — popover au-dessus, délai 400ms, clic navigant
  • Double onglet / Split view — chaque panneau gère son propre noteId indépendamment
  • Persistance fenêtre — position, taille et état maximisé sauvegardés
  • Sélecteur de thème — grille dynamique avec prévisualisation miniature
  • Onboarding — conteneur scrollable, largeur augmentée
Corrigé
  • Wiki-links insérés via [[ n’apparaissaient pas dans les linkedNoteIds
  • Le popover de lien et le bouton « Lier une note » se superposaient au survol
  • Flashcards ne trouvaient aucune paire Q/R (doubles sauts de ligne TipTap)
  • En mode split, cliquer sur une note changeait aussi la note dans l’autre panneau

v1.6.2

Nouveau
  • Import de notes (.md, .html, .txt, .filarr, .json) avec conversion automatique en TipTap JSON
  • Recherche globale enrichie — trouve les notes (titre + contenu avec extrait) et les sections de paramètres
  • Thèmes éditeur dark mode — variantes sombres automatiques pour Default, Writer et Developer
Amélioré
  • Export de notes complet — conserve titres, listes, code blocks, tableaux, task lists, callouts, formules math, Mermaid et formatage inline
  • Vue graphe style Obsidian — simulation de forces en espace infini, répulsion Coulomb, ressorts Hooke, auto-fit du viewport
  • Dark mode sur les vues Masonry, Sticky Notes, Dataview et sélecteur de thème
  • SearchResults redesigné avec icônes par type et navigation directe
  • DashboardStats : compatibilité dark mode du donut chart SVG
  • FolderView : miniatures masquées pour les fichiers verrouillés
  • ManageProfilesModal : reset complet avec flag d’onboarding persisté sur disque
Corrigé
  • Corrections CSS dark mode : BacklinksPanel, CalendarWidget, NoteEditor, NotesList, PeriodicNotes, TasksAggregator

v1.6.1

Amélioré
  • Toast de mise à jour en bas à droite avec barre de progression et bouton « Redémarrer »
  • Transmission de la version disponible et du pourcentage de téléchargement
Corrigé
  • Le bandeau de mise à jour ne transmettait pas la version disponible

v1.6.0

Amélioré
  • Lancement instantané — les services backend s’initialisent en parallèle du rendu UI
  • Migration vers HashRouter pour compatibilité Electron en production
  • Mise à jour automatique via releases.filarr.com (plus besoin de GH_TOKEN)
  • Vérification automatique des mises à jour toutes les 4 heures
  • Upload des manifestes .yml et .blockmap (mises à jour delta) vers R2
  • Correction des chemins d’icônes en mode packagé

v1.5.3

Nouveau
  • Wiki intégré traduit EN/FR (170+ clés) : guides, raccourcis, éditeur, vues, graph, templates
  • Formulaire de rapport de bug et suggestions dans les paramètres
Amélioré
  • Refonte du wiki intégré : navigation par catégories, rendu Markdown, recherche

v1.5.2

Nouveau
  • Association de notes à un dossier parent (NoteFolderPicker)
  • Système de flags persistants sur disque (survivent aux resets de localStorage)
  • Protection SSRF : blocage des requêtes vers réseaux privés et endpoints cloud
  • Validation de chemin et sanitization pour empêcher le path traversal
Amélioré
  • PBKDF2 renforcé : 600 000 itérations (×6) + SHA-512 pour les exports
  • Sel aléatoire par installation au lieu d’un sel fixe
  • Permissions fichiers restreintes (0o600) pour les clés et exports
  • PIN lockout dès 3 tentatives échouées au lieu de 5
  • Refonte complète de l’onboarding (820+ lignes)

v1.5.1

Nouveau
  • Fenêtre frameless style VS Code / Obsidian avec contrôles natifs en overlay
  • Zone de drag dans la barre de titre (WebkitAppRegion)
Amélioré
  • Refonte des templates de démarrage (1200+ lignes)
  • Panneau latéral dans NotesView avec backlinks et dossier parent
  • CSS global mis à jour pour le mode frameless (342 lignes)

v1.5.0

Corrigé
  • Correction du quoting basename dans le workflow CI
  • Amélioration du workflow release multi-OS

v1.4.6

Nouveau
  • Liens de donation Ko-fi et Stripe dans les paramètres
Amélioré
  • Détection dev/prod via app.isPackaged
  • Résolution d’icône dynamique (dev vs packagé)
Corrigé
  • Correction du type ProfileMetadata (champ avatarImage)
  • Correction de l’erreur window renderer

v1.4.0

Nouveau
  • Image personnalisée sur les avatars de profil
  • UI Tour complet avec ouverture automatique de la sidebar
  • Script de release automatisé
Amélioré
  • Palette étendue de couleurs pour les profils
  • Écran de verrouillage PIN avec support d’image

v1.3.0

Nouveau
  • Système multi-profil avec PIN lock et isolation des données
  • Chiffrement local KEK/FEK : FEK dérivé du mot de passe, chiffrement transparent
  • Éditeur de notes riche avec wiki-links [[]], vues multiples (kanban, masonry, canvas, mind map...)
  • Internationalisation EN/FR complète
  • Sécurité Electron : fuses, rate limiting, IPC allowlist, CSP
Amélioré
  • Refonte complète du codebase (394 fichiers)
  • Migration TypeScript (99.26% couverture de tests)
  • Optimisation scroll : lazy thumbnails, content-visibility, memo
Corrigé
  • Pipeline FEK pour chiffrement hybride
  • 17 issues critiques de production résolues
  • 7 erreurs TypeScript backend corrigées

v1.2.6

Amélioré
  • Mise à jour du système de cryptage
Corrigé
  • Corrections de bugs divers
  • Correction du bug de mise à jour automatique

↓ 18 mois de développement solo — refonte complète du moteur de chiffrement, éditeur de notes, graph view, multi-profil.

v1.2.0

Amélioré
  • Passage en production avec variable d’environnement

v1.1.0

Nouveau
  • Création du squelette du logiciel
  • Stockage local avec chiffrement AES
  • Personnalisation des fichiers et dossiers (couleurs, icônes)
  • Sélection multiple avec actions groupées
  • Système de rappels avec notifications desktop
  • Mise à jour automatique (electron-updater)
Corrigé
  • Synchronisation des fichiers
  • Erreur de renommage
  • Suppression dans les sous-dossiers
  • Copier/coller
  • Notifications de rappel après complétion