fix(ai): accents dans les chemins et resolution des chemins prefixes par le vault (BUG-042)
CI / lint (push) Successful in 1m16s
CI / security (push) Successful in 50s
CI / test (push) Successful in 2m24s
CI / build (push) Successful in 47s
CI / e2e (push) Successful in 10m35s

- accents : classes de caracteres Unicode (\p{L}\p{N}\p{M}) pour les motifs
  de liens et _looksLikePath (PATH_NAME_RE) -> les chemins/fichiers accentues
  produisent enfin un lien cliquable ; comparaison de chemins normalisee NFC
  (_normKey), donc une mention decomposee (e + accent combinant, style macOS)
  correspond a une entree d'index precomposee, et inversement.
- prefixe de vault : _splitVaultPrefix() retire un premier segment egal au nom
  d'un vault connu (TestVault/Recettes/Pizza Maison.md) ; _fetchPathsForVault()
  interroge l'index de CE vault sans ecraser le cache du vault actif ;
  _openFileLink/_revealPath recoivent le vault cible ; repli 'retirer le
  premier segment' si le prefixe ne correspond a aucun vault connu.

Tests : frontend IA 62/62 (+5), 9 suites JSDOM, validate-imports 36 modules,
pytest 963 passed / 6 skipped. Verifie aussi contre l'instance live sur donnees
reelles accentuees/espaces (Recettes/Preparation.md, Recettes/Pat... ) : 8/8.
This commit is contained in:
2026-09-14 13:37:23 -04:00
parent 12a53d647e
commit c3e6841293
5 changed files with 224 additions and 43 deletions
+58 -1
View File
@@ -463,7 +463,7 @@ async function main() {
const revealed = [];
b._openFileLink = (p) => { opened.push(p); return Promise.resolve(); };
b._revealPath = (p) => { revealed.push(p); return Promise.resolve(); };
b._ensurePaths = async () => ([
b._fetchPathsForVault = async () => ([
{ path: "Mon dossier/Ma note.md", name: "Ma note.md", type: "file" },
{ path: "Mon dossier/Sous dossier", name: "Sous dossier", type: "directory" },
]);
@@ -474,6 +474,63 @@ async function main() {
assert.deepEqual(revealed, ["Mon dossier/Sous dossier"], "spaced directory resolves too");
});
// ── 12c. Accents and vault-prefixed paths ──
await test("_looksLikePath accepts accented names", () => {
const b = new BooksLM();
assert.equal(b._looksLikePath("Café.md"), true, "accented filename is a path");
assert.equal(b._looksLikePath("Recettes/Préparation.md"), true, "accented path is a path");
assert.ok(b._renderMarkdown("Voir `Recettes/Préparation.md`.").includes('data-path="Recettes/Préparation.md"'),
"accented inline code becomes a link");
});
await test("bare accented paths become links without the index", () => {
const b = new BooksLM();
const html = b._renderMarkdown("Ouvre Recettes/Préparation.md puis relis.");
assert.ok(html.includes('data-path="Recettes/Préparation.md"'), "accented bare path linkified");
});
await test("decomposed accents match precomposed index entries", async () => {
const b = new BooksLM();
b._activeVault = () => "TestVault";
b._fetchPathsForVault = async () => ([{ path: "Recettes/Préparation.md", name: "Préparation.md", type: "file" }]);
const opened = [];
b._openFileLink = (p) => { opened.push(p); return Promise.resolve(); };
// NFD input (e + combining acute) against an NFC index entry.
const nfd = "Recettes/Pre\u0301paration.md";
await b._activatePath(nfd, "file");
assert.deepEqual(opened, ["Recettes/Préparation.md"], "NFD mention resolves to the NFC path");
});
await test("_splitVaultPrefix strips a leading vault name", () => {
const b = new BooksLM();
b._vault = "TestVault";
assert.deepEqual(
b._splitVaultPrefix("TestVault/Recettes/Pizza Maison.md"),
{ vault: "TestVault", path: "Recettes/Pizza Maison.md" },
"vault prefix split off",
);
assert.deepEqual(
b._splitVaultPrefix("Recettes/Pizza Maison.md"),
{ vault: null, path: "Recettes/Pizza Maison.md" },
"no prefix when the first segment is not a vault",
);
});
await test("a vault-prefixed path opens the file in that vault", async () => {
const b = new BooksLM();
b._vault = "TestVault";
b._activeVault = () => "TestVault";
b._fetchPathsForVault = async (v) => (v === "TestVault"
? [{ path: "Recettes/Pizza Maison.md", name: "Pizza Maison.md", type: "file" }]
: []);
const opened = [];
b._openFileLink = (p, v) => { opened.push([v, p]); return Promise.resolve(); };
await b._activatePath("TestVault/Recettes/Pizza Maison.md", "file");
assert.deepEqual(opened, [["TestVault", "Recettes/Pizza Maison.md"]], "opened in the named vault");
});
await test("_renderMarkdown keeps external links external", () => {
const b = new BooksLM();
const html = b._renderMarkdown("[site](https://example.com/page)");