fix: correction raccourci palette + filtrage ext/vault/tag dans palette fichiers
CI / lint (push) Successful in 2m40s
CI / security (push) Failing after 1m53s
CI / test (push) Successful in 4m36s
CI / build (push) Successful in 1m48s
CI / e2e (push) Successful in 17m29s

- Correction du raccourci clavier inversé dans l'aide :
  Ctrl+Shift+Space → Palette de fichiers (navigation rapide)
  Ctrl+Alt+Space → Palette de commandes
- Ajout du support des filtres ext:xxx, vault:nom, tag:monTag dans la palette de fichiers
- parseQuery étendu pour gérer les opérateurs de filtrage
- Filtrage client-side par extension et tag dans searchFiles et searchBrowseGlobal
- Backend : suggest_titles retourne les tags pour chaque suggestion
- Schema TitleSuggestion : ajout du champ tags
- Placeholders mis à jour pour indiquer les nouveaux filtres disponibles

Fixes : raccourci inversé, filtrage par extension/vault/tag dans palette fichiers
This commit is contained in:
2026-10-01 10:29:42 -04:00
parent c5c225a68e
commit 66a5505965
13 changed files with 66 additions and 27 deletions
+3 -4
View File
@@ -1196,16 +1196,15 @@
>Ctrl+Shift+Space</span
>
<span class="qh-desc"
>Palette de commandes</span
>Palette de fichiers (nav.
rapide)</span
>
</div>
<div class="qh-row">
<span class="qh-kbd"
>Ctrl+Alt+Space</span
>
<span class="qh-desc"
>Palette de fichiers (nav.
rapide)</span
<span class="qh-desc">Palette de commandes</span
>
</div>
<div class="qh-row">
+36 -7
View File
@@ -14,17 +14,26 @@ function getCurrentFile() {
return null;
}
// ── Query parser: +word = required, -word = exclude, word = required ──
// ── Query parser: +word = required, -word = exclude, word = required, ext:xxx, vault:xxx, tag:xxx ──
function parseQuery(raw) {
const positive = [];
const negative = [];
const filters = { ext: null, vault: null, tag: null };
const parts = (raw || '').split(/\s+/).filter(Boolean);
for (const p of parts) {
if (p.startsWith('-') && p.length > 1) negative.push(p.slice(1).toLowerCase());
const lower = p.toLowerCase();
if (lower.startsWith('ext:')) {
filters.ext = lower.slice(4).trim().replace(/^\./, '').toLowerCase();
} else if (lower.startsWith('vault:')) {
filters.vault = lower.slice(6).trim();
} else if (lower.startsWith('tag:') || (lower.startsWith('#') && p.length > 1)) {
const tag = lower.startsWith('tag:') ? lower.slice(4) : lower.slice(1);
if (tag) filters.tag = tag.trim();
} else if (p.startsWith('-') && p.length > 1) negative.push(p.slice(1).toLowerCase());
else if (p.startsWith('+')) positive.push(p.slice(1).toLowerCase());
else positive.push(p.toLowerCase());
}
return { positive, negative };
return { positive, negative, filters };
}
// ── DOM / state ──
@@ -407,8 +416,8 @@ function createDOM() {
_overlay.className = 'command-palette-overlay';
_overlay.tabIndex = -1;
_overlay.innerHTML = `<div class="command-palette">
<div class="cp-header"><input type="text" class="cp-input" placeholder="Rechercher un fichier... (+inclure -exclure)" spellcheck="false" autocomplete="off"><div class="cp-breadcrumb" id="cp-breadcrumb"></div><span class="cp-hint">Ctrl+Shift+Espace</span></div>
<input type="text" class="cp-browse-filter" id="cp-browse-filter" placeholder="Filtrer (+mot -mot) — recherche globale..." spellcheck="false" autocomplete="off" style="display:none">
<div class="cp-header"><input type="text" class="cp-input" placeholder="Rechercher un fichier... (+inclure -exclure ext:md vault:nom tag:monTag)" spellcheck="false" autocomplete="off"><div class="cp-breadcrumb" id="cp-breadcrumb"></div><span class="cp-hint">Ctrl+Shift+Espace</span></div>
<input type="text" class="cp-browse-filter" id="cp-browse-filter" placeholder="Filtrer (+mot -mot ext:xxx vault:nom tag:xxx) — recherche globale..." spellcheck="false" autocomplete="off" style="display:none">
<div class="cp-results"></div>
<div class="cp-footer"><span>↑↓ naviguer</span><span>↵ ouvrir/entrer</span><span>⌫ monter</span><span>Esc fermer</span></div></div>`;
_input = _overlay.querySelector('.cp-input');
@@ -488,11 +497,14 @@ async function searchFiles(rawQ) {
return;
}
const { positive, negative } = parseQuery(rawQ);
const { positive, negative, filters } = parseQuery(rawQ);
try {
const q = positive.join(' ');
const d = await api(`/api/suggest?q=${encodeURIComponent(q)}&vault=all`);
let vaultFilter = 'all';
if (filters.vault) vaultFilter = filters.vault;
const d = await api(`/api/suggest?q=${encodeURIComponent(q)}&vault=${encodeURIComponent(vaultFilter)}`);
let sug = d.suggestions || [];
// Client-side negative filter
@@ -506,6 +518,23 @@ async function searchFiles(rawQ) {
});
}
// Filter by extension if specified
if (filters.ext) {
sug = sug.filter(s => {
const name = s.path.split('/').pop() || '';
const ext = name.includes('.') ? name.split('.').pop().toLowerCase() : '';
return ext === filters.ext;
});
}
// Filter by tag if specified
if (filters.tag) {
sug = sug.filter(s => {
const tags = s.tags || [];
return tags.some(t => t.toLowerCase() === filters.tag.toLowerCase());
});
}
if (sug.length === 0) {
_items = [{ type:'_browse', label:'📂 Parcourir les répertoires...', sublabel:'Aucun résultat — naviguer manuellement', act:()=>startBrowse('open-file') }];
} else {