# Changelog
## [prod] v0.1.9 — 17 juillet 2026
### Corrections
- `minAppVersion` redescendu à `1.12.7` (depuis `1.13.0`) — Obsidian 1.13 reste réservé à l'accès anticipé Catalyst ; la majorité des utilisateurs sont encore sur la version publique 1.12.7
- Onglet Paramètres : ajout d'un fallback `display()` en complément de `getSettingDefinitions()`, pour que l'onglet s'affiche toujours sur Obsidian < 1.13, qui ignore l'API déclarative des réglages
- `setDestructive()` (exclusif à 1.13.0) remplacé à nouveau par `setWarning()` sur les boutons supprimer/confirmer de `EditRuleModal` et `ConfirmModal`, pour la même raison de compatibilité
- Sécurité : `protobufjs` forcé en `^7.6.5` via `overrides` npm — la dépendance transitive apportée par `@xenova/transformers` → `onnxruntime-web` → `onnx-proto` était figée sur une version vulnérable à l'exécution de code arbitraire ([GHSA-xq3m-2v4x-88gg](https://github.com/advisories/GHSA-xq3m-2v4x-88gg), CVSS 9.8)
## [prod] v0.1.8 — 9 juillet 2026
### Corrections
- `NoScribeHtmlParser` — les noms d'ancre dont le suffixe contient des caractères hors `\w` (ex. `ts_95480_96512_//S01`, produit quand une annotation de chevauchement `//S01:` est tapée juste avant une coupure de segment noScribe) étaient rejetés par `TS_NAME_RE` (`\w*`), faisant disparaître silencieusement tout le texte de l'ancre. Des chevauchements entiers pouvaient ainsi disparaître à l'import HTML. Le suffixe est désormais accepté tel quel.
## [prod] v0.1.7 — 10 juin 2026
### Nouvelles fonctionnalités
**i18n**
- Ajout des locales russe (ru) et espagnole (es) (~240 clés chacune), en plus du français et de l'anglais
**Exports**
- `markdownToNoScribeHtml()` — reconstruction du HTML noScribe pseudonymisé avec ancres temporelles `ts_` au mot près, via mapping proportionnel sur `words.json` ; nouvelle commande et bouton d'export HTML
- `markdownToEaf()` — export ELAN `.eaf` (FORMAT 3.0) : tiers par locuteur, `TIME_SLOT` dédupliqués/triés, `MEDIA_DESCRIPTOR` optionnel, échappement XML ; nouvelle commande et bouton d'export EAF (visible pour les fichiers issus de VTT/HTML)
**Paramètres**
- Onglet Paramètres réécrit avec l'API déclarative `getSettingDefinitions()` (Obsidian 1.13.0)
- `minAppVersion` relevé à `1.13.0`
### Corrections
- `autoConvert` ignore désormais les fichiers créés dans les dossiers exports/mappings (évite la réimportation des exports fraîchement écrits)
- `pseudonymizeActiveFile` respecte `exportMirrorClasses` — le `.pseudonymized.md` intermédiaire est écrit dans le bon sous-dossier de classe (helper partagé `getClassSub()`)
- Le bouton "Nouvelle classe" de l'onglet Corpus ouvre désormais `CorpusModal` au lieu de `window.prompt` (bloqué dans Electron) ; `window.confirm`/`window.prompt` remplacés partout par `ConfirmModal`/`TextInputModal`
- Suppression de l'option d'export "à côté du fichier source"
- Diverses corrections issues de la revue communautaire Obsidian (plus de `require()`, plus d'assertions de type inutiles, plus de promesses non gérées, `setDestructive()` à la place de `setWarning()` déprécié)
---
## [prod] v0.1.6 — 17 mai 2026
### Nouvelles fonctionnalités
**Onglet Corpus**
- Nouvel onglet **Corpus** (dernier dans le panneau) — transcriptions par classe, badges statut : règles, version pseudo, format d'export final
- **Gestion des classes** : créer, supprimer (déplace les fichiers à la racine)
- **Déplacer des fichiers entre classes** : dropdown par fichier — déplace `.md` + `.mapping.json` + `.words.json` + audio + fichiers source en miroir
- **Paramètres de destination des exports finaux** (inline dans l'onglet) : dossier vault (avec option miroir de classes), à côté du fichier source, ou dossier externe ; `FolderSuggest` autocomplete sur tous les champs de dossier
- Composant `FolderSuggest` — autocomplete dossiers vault dans les Paramètres et le wizard
**Modale de scan — candidats par occurrence**
- `MappingScanReviewModal` : colonne occurrences cliquable (`N / total` si exceptions)
- `OccurrencesContextModal` : cartes par occurrence (✓ / ✗ / ⚠) avec contexte, callback "Confirmer la sélection", sans application immédiate
- **Bouton "Enregistrer les exceptions"** : persiste les décisions ignorées dans `mapping.json` sans pseudonymiser
- Pré-remplit les décisions depuis `ignoredOccurrences` existantes à la réouverture
**Exceptions**
- `IgnoredOccurrence {text, contextBefore, contextAfter}` sur `MappingRule.ignoredOccurrences`
- Persistance dans `mapping.json`, chargée au démarrage — rouge sans re-scan
- Surlignage rouge : positionnel, par contexte (`findByContext`) — seulement l'occurrence précise est rouge
- `findSpansForRule()` exclut les occurrences ignorées du remplacement à l'export
- Clic droit "Déclarer une exception ici" sur les termes orange (contexte 30 caractères)
- Section Exceptions dans l'onglet Mappings avec cartes contextuelles et suppression
**Exports**
- `markdownToSrt()` et `markdownToCha()` — re-export du Markdown pseudonymisé en SRT/CHAT
- `resolveExportPath()` — miroir de classes depuis le dossier mapping quand le fichier est dans exports
- `writeExport()` — vault, à côté du source, ou externe (Node.js fs)
- Onglet Exports : bouton adapté selon source ou pseudonymisé ; masque pseudonymiser pour les fichiers déjà exportés
**Avertissement nom de fichier**
- Bannière au-dessus des onglets si le nom contient un terme pseudonymisable
- Deux boutons : ✏ renommage manuel · ✨ suggestion neutre (`transcript_N`)
- Statuts clarifiés : "Actif / Partiel / Ignoré / Suggéré"
- `getValidatedFor()` inclut `partial` comme état actif
**Cascade de renommage**
- Écouteur `vault.on('rename')` — cascade automatique au renommage natif Obsidian
- `cascadeRelatedRename()` — met à jour : frontmatter (`processFrontMatter`), `.mapping.json` (scope store + règles), `.words.json`, exports pseudonymisés, audio (même basename + référence `pseudobs-audio`), fichiers source (.srt/.vtt/.cha/.html/.yml)
- `moveFileToClass()` déplace aussi l'audio et met à jour les deux niveaux de scope
- Guard `_renamingRelated` prévient les boucles
### Corrections
- `renameFileAndRelated` : `oldFilePath` capturé avant `vault.rename()` (TFile muté in-place)
- Renommage audio : `oldAudioName` capturé avant rename (même problème de mutation)
- `processFrontMatter` pour toutes les modifications de frontmatter (regex peu fiable)
- Scope store ET scope des règles mis à jour lors du déplacement/renommage
- `esbuild` : `copyStylesPlugin` copie `styles.css` dans le vault de test à chaque rebuild dev
---
## [prod] v0.1.5 — 17 mai 2026
### Nouvelles fonctionnalités
**Intégration noScribe**
- **`VttParser`** — WebVTT standard avec word timestamps Whisper (`<00:00:01.240>mot`), tags ``, round-trip garanti
- **`NoScribeHtmlParser`** — HTML Qt Rich Text noScribe : ancres `ts_DEBUT_FIN_SXX` (ms), labels locuteurs (`S00:`, `S01:`), timestamps d'affichage filtrés, entités HTML décodées ; `extractAudioSource()` lit ``
- **`NoScribeVttParser`** — VTT noScribe v0.7 : cues splitées (label / timestamp affichage / texte), détection locuteur par préfixe `SXX:`, entités HTML, source audio depuis `NOTE media:`
- **Format Markdown noScribe** — `**S00** [HH:MM:SS] : texte` par tour ; `pseudobs-format: html|vtt` et `pseudobs-audio` optionnel en frontmatter ; timestamps word-level dans `.words.json`
- **Import audio automatique** — depuis la meta tag HTML ou le dossier source (VTT, un seul fichier audio)
- **`Redaction.ts`** — caviardage par syllabes avec `🀫` ; `generateRedaction()` et `isRedaction()`
- **Re-export VTT** (commande "Exporter en VTT") — WebVTT propre depuis `.md` pseudonymisé + `.words.json`
**Modale de scan — candidats par occurrence**
- **`MappingScanReviewModal`** — colonne occurrences cliquable (compteur mis à jour en direct : `N / total` si exceptions)
- **`OccurrencesContextModal`** — cartes par occurrence (✓ / ✗ / ⚠) avec contexte, callback "Confirmer la sélection", sans application immédiate
- **Bouton "Enregistrer les exceptions"** — sauvegarde les décisions dans le `mapping.json` sans pseudonymiser
- `MappingRuleResult` porte maintenant `occurrences: Occurrence[]`
**Exceptions**
- Type **`IgnoredOccurrence`** (`{text, contextBefore, contextAfter}`) sur `MappingRule.ignoredOccurrences`
- Persistance dans `mapping.json`, chargement au démarrage — surlignage rouge sans re-scan
- **Surlignage rouge** (`pseudobs-exception`, sensible à la casse, priorité 0)
- **Section "Exceptions" dans l'onglet Mappings** — cartes contextuelles avec bouton supprimer
### Modifications
- Labels de statut clarifiés : `validated` → "Actif", `ignored` → "Ignoré", `partial` → "Partiel", `suggested` → "Suggéré" ; colonne renommée "État"
- `getValidatedFor()` inclut maintenant `partial` comme état actif (avec `validated`)
- Bouton "Scanner" de `PseudonymizationView` corrigé (`occurrences` manquant dans `MappingRuleResult`)
### Tests
149 tests · 10 suites · `VttParser`, `NoScribeHtmlParser`, `NoScribeVttParser`, `markdownToVtt`, `extractWordData` couverts ; fixtures : `fight_club.vtt`, `juste-leblanc.html`
---
## [prod] v0.1.3 — 14 mai 2026
**Branche :** `main` | **Tag :** `0.1.3`
### Nouvelles fonctionnalités
- **Système de dictionnaires structurés** — format `DictionaryFile` v1.1 : champs `roles` (détection / remplacement / classes), `configSchema` (variables configurables avec valeurs possibles), `config` (valeurs actives), `author`, `doi`
- **`DictionaryLoader`** — service de chargement, index de détection normalisé, résolution de classes par conditions/regex/word-to-word, calcul d'index incrémental par portée
- **Repo dédié [`pseudobsidian-dictionaries`](https://github.com/core-hn/pseudobsidian-dictionaries)** — catalogue de dictionnaires téléchargeables, premier dictionnaire : communes françaises GeoAPI INSEE (34 957 entrées, classes Village → Métropole)
- **Wizard — catalogue de dictionnaires** : tableau scrollable avec bouton icône `cloud-download` → `refresh-cw` (spin) → `cloud-check` ; installation depuis le repo dédié sans quitter Obsidian
- **`scripts/build-cities.mjs`** — script one-shot de génération du dictionnaire communes depuis GeoAPI INSEE
- **Scan par dictionnaire** (`Onglet Dictionnaires → Scanner`) : fenêtre glissante de n-grammes, filtre par dictionnaires cochés, commande Ctrl+P disponible
- **`DictScanReviewModal`** — modale de révision en cards : terme source, catégorie (tiny), extrait de contexte avec terme surligné, préfixe éditable + index calculé en lecture seule (recalcul dynamique si décoché), checkbox par card
- **`MappingScanReviewModal`** — modale de révision pour le scan par règles : tableau source → remplacement · occurrences, application directe dans le fichier source
- **Onglet Mappings** — bouton "Scanner le fichier" ouvre `MappingScanReviewModal`
- **Onglet NER** — bouton "Identifier des candidats" en haut de l'onglet
- **`RuleModal`** — suggestions de remplacement par classe (dictionnaire) séparées des suggestions Coulmont ; modal Coulmont n'affiche plus les suggestions dictionnaire (prénoms ≠ lieux)
### Suppressions
- **Onglet "Candidats"** supprimé — le scan par règles est maintenant dans Mappings, le scan NER dans NER
### Corrections
- `DictScanReviewModal` : l'index n'est plus modifiable manuellement — seul le préfixe l'est, l'index se recalcule quand on coche/décoche des items
- CSS : animation spin sur l'icône de téléchargement en cours, mini cards dictionnaires dans le panneau
---
## [prod] v0.1.2 — 13 mai 2026
**Branche :** `main` | **Tag :** `0.1.2`
### Corrections
- `setInterval`/`clearInterval` remplacés par partage de Promise dans `OnnxNerScanner` — plus de polling, appels concurrents correctement gérés
---
## [prod] v0.1.1 — 13 mai 2026
**Branche :** `main` | **Tag :** `0.1.1`
### Nouvelles fonctionnalités
- **Onglet Dictionnaires** — liste les `.dict.json` du vault avec nombre d'entrées, import et suppression
### Corrections portail communautaire Obsidian
- `onunload` : suppression de `detachLeavesOfType`
- `eslint-disable` et `any` : type `TransformersModule` explicite dans `OnnxNerScanner`
- `window.setInterval`/`clearInterval`, `window.setTimeout`, `activeDocument`
- `FileManager.trashFile()` à la place de `Vault.delete()`
- `activeLeaf` déprécié → `getActiveViewOfType(ItemView)`
- Imports inutilisés supprimés (`Setting`, `RuleLocation`, `fs`)
- Promises dans les event listeners wrappées avec `void (async () => {})()`
- CSS `text-decoration-color` → `border-bottom` (support partiel)
- Workflow `.github/workflows/release.yml` — attestations GitHub Actions
- Régénération `package-lock.json` (erreur CI `concat-map` 404)
---
## [prod] v0.1.0 — 13 mai 2026
**Branche :** `main` | **Tag :** `0.1.0`
## [dev] v0.1.0 — en cours
**Branche :** `dev` | **Publié :** non
### Nouvelles fonctionnalités
- **Panneau latéral** (`PseudonymizationView`) — 5 onglets : Candidats, Mappings, Dictionnaires, Exports, NER
- **Détection NER** (`OnnxNerScanner`) — modèle BERT-NER multilingue via `transformers.js` (WASM, 100 % local)
- Surlignage bleu des candidats dans l'éditeur
- Filtrage des sous-termes de règles composées (Saint-Jean-de-Luz filtre Jean/Luz)
- Paramètres ajustables : seuil de confiance, mots fonctionnels exclus (onglet NER)
- **Wizard onboarding** — 3 étapes : bienvenue, setup NER (téléchargement WASM ~19 Mo), import dictionnaires
- **Marqueurs `{{...}}`** activés par défaut dans les remplacements en direct et les exports
- **Clic droit → Annuler la pseudonymisation** — restaure le terme original (sur termes verts)
- **Surlignage** : vert + souligné pour les remplacements en direct ; fichiers `*.pseudonymized.*` surlignés avec les règles du fichier source
- **Import de dictionnaires `.dict.json`** depuis le wizard
- **Paramètres NER** : `nerBackend`, `nerMinScore`, `nerFunctionWords` dans les settings
- **eslint_violations.md** — référence du lint Obsidian et commande de test local
### Corrections
- `style.marginLeft` → classe CSS (`no-static-styles-assignment`)
- Icônes UI (✓ ✗ ← → +) déplacées en CSS `::before`/`::after`
- `getActiveFile()` polyfill pour le panneau latéral (garde le dernier fichier connu)
- `import.meta.url` polyfill dans esbuild pour `@xenova/transformers/src/env.js`
- Alias `onnxruntime-node → ort-web.node.js` pour éviter les binaires natifs non bundlables
- `env.cacheDir` fixé avant `pipeline()` (évite `path.join(null, ...)`)
- Références institutionnelles incorrectes supprimées (ICAR, CNRS)
### Technique
- `@xenova/transformers` v2.17.2 ajouté comme dépendance
- esbuild : `target: es2020`, `platform: browser` retiré, banner polyfill `import.meta.url`
- `tsconfig.json` : target ES2020, lib ES2020
---
## [prod] v0.0.4 — 12 mai 2026
**Branche :** `main` | **Tag :** `0.0.4` | **Publié :** GitLab + GitHub (`core-hn/pseudobsidian-ization`)
### Corrections bot Obsidian
- Suppression du heading "Pseudonymizer tool" en tête des settings (`settings-tab/no-manual-html-headings`)
---
## [prod] v0.0.3 — mai 2026
**Branche :** `main` | **Tag :** `0.0.3`
### Corrections bot Obsidian (Required)
- `style.cssText` et `setAttribute('style')` → classes CSS (`no-static-styles-assignment`)
- Headings ``/`` dans les settings → `new Setting().setHeading()` (`settings-tab/no-manual-html-headings`)
- Promesses flottantes → opérateur `void` (`no-floating-promises`)
- Sentence case sur les labels UI
---
## [prod] v0.0.2 — mai 2026
**Branche :** `main` | **Tag :** `0.0.2`
### Changements
- Rename du plugin : `pseudobsidianization` → `pseudonymizer-tool` (exigence répertoire communautaire)
- Description `manifest.json` en anglais
---
## [prod] v0.0.1 — mai 2026
**Branche :** `main` | **Tag :** `0.0.1`
### Première release
- Parsers SRT et CHAT/CHA avec round-trip garanti
- Moteur de pseudonymisation (spans, priorité z-index, protection chevauchements)
- Portées fichier / dossier / vault
- Surlignage CM6 (orange = sources, vert = remplacements)
- Validation sélective par occurrence (OccurrencesModal)
- Import de transcriptions avec conversion automatique
- Intégration Coulmont (suggestions de prénoms sociologiquement équivalents)
- Soumission PR #12766 au répertoire communautaire Obsidian