fix: correction raccourci palette + filtrage ext/vault/tag dans palette fichiers
- 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:
+5
-1
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
|||||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||||
> [Unreleased](#unreleased). La dernière version livrée est **2.45.0**.
|
> [Unreleased](#unreleased). La dernière version livrée est **2.45.1**.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -14,6 +14,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2.45.1] — 2026-10-01
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## [2.45.0] — 2026-09-30
|
## [2.45.0] — 2026-09-30
|
||||||
|
|
||||||
### Ajouté
|
### Ajouté
|
||||||
|
|||||||
+3
-3
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.45.0).
|
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.45.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Projet : ObsiGate | Version : 2.45.0 | Dernière mise à jour : Septembre 2026*
|
*Projet : ObsiGate | Version : 2.45.1 | Dernière mise à jour : Septembre 2026*
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||||
|
|
||||||
[]()
|
[]()
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
[](https://www.python.org/)
|
[](https://www.python.org/)
|
||||||
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
|||||||
|
|
||||||
## 📝 Changelog
|
## 📝 Changelog
|
||||||
|
|
||||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.45.0).
|
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.45.1).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Project: ObsiGate | Version: 2.45.0 | Last updated: September 2026*
|
*Project: ObsiGate | Version: 2.45.1 | Last updated: September 2026*
|
||||||
|
|||||||
@@ -666,6 +666,7 @@ class TitleSuggestion(BaseModel):
|
|||||||
vault: str = Field(description="Vault name")
|
vault: str = Field(description="Vault name")
|
||||||
path: str = Field(description="Relative file path")
|
path: str = Field(description="Relative file path")
|
||||||
title: str = Field(description="File title")
|
title: str = Field(description="File title")
|
||||||
|
tags: list[str] = Field(default_factory=list, description="File tags")
|
||||||
|
|
||||||
|
|
||||||
class SuggestResponse(BaseModel):
|
class SuggestResponse(BaseModel):
|
||||||
|
|||||||
+9
-3
@@ -373,6 +373,7 @@ class InvertedIndex:
|
|||||||
self.doc_vault: dict[str, str] = {}
|
self.doc_vault: dict[str, str] = {}
|
||||||
self.vault_docs: dict[str, set] = defaultdict(set)
|
self.vault_docs: dict[str, set] = defaultdict(set)
|
||||||
self.tag_docs: dict[str, set] = defaultdict(set)
|
self.tag_docs: dict[str, set] = defaultdict(set)
|
||||||
|
self.doc_tags: dict[str, set] = defaultdict(set)
|
||||||
self._sorted_tokens: SortedList = SortedList()
|
self._sorted_tokens: SortedList = SortedList()
|
||||||
self._ready: bool = False # True after initial build
|
self._ready: bool = False # True after initial build
|
||||||
|
|
||||||
@@ -403,6 +404,7 @@ class InvertedIndex:
|
|||||||
self.doc_vault = {}
|
self.doc_vault = {}
|
||||||
self.vault_docs = defaultdict(set)
|
self.vault_docs = defaultdict(set)
|
||||||
self.tag_docs = defaultdict(set)
|
self.tag_docs = defaultdict(set)
|
||||||
|
self.doc_tags = defaultdict(set)
|
||||||
|
|
||||||
for vault_name, vault_data in _indexer.index.items():
|
for vault_name, vault_data in _indexer.index.items():
|
||||||
for file_info in vault_data.get("files", []):
|
for file_info in vault_data.get("files", []):
|
||||||
@@ -417,6 +419,7 @@ class InvertedIndex:
|
|||||||
# --- Per-document tag index ---
|
# --- Per-document tag index ---
|
||||||
for tag in file_info.get("tags", []):
|
for tag in file_info.get("tags", []):
|
||||||
self.tag_docs[tag.lower()].add(doc_key)
|
self.tag_docs[tag.lower()].add(doc_key)
|
||||||
|
self.doc_tags[file_info['path']].add(tag.lower())
|
||||||
|
|
||||||
# --- Title tokens ---
|
# --- Title tokens ---
|
||||||
title_tokens = tokenize(file_info.get("title", ""))
|
title_tokens = tokenize(file_info.get("title", ""))
|
||||||
@@ -1538,7 +1541,7 @@ def suggest_titles(
|
|||||||
prefix: str,
|
prefix: str,
|
||||||
vault_filter: str = "all",
|
vault_filter: str = "all",
|
||||||
limit: int = SUGGEST_LIMIT,
|
limit: int = SUGGEST_LIMIT,
|
||||||
) -> list[dict[str, str]]:
|
) -> list[dict[str, Any]]:
|
||||||
"""Suggest file titles matching a prefix (accent-insensitive).
|
"""Suggest file titles matching a prefix (accent-insensitive).
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -1547,7 +1550,7 @@ def suggest_titles(
|
|||||||
limit: Maximum suggestions.
|
limit: Maximum suggestions.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List of ``{"vault", "path", "title"}`` dicts.
|
List of ``{"vault", "path", "title", "tags"}`` dicts.
|
||||||
"""
|
"""
|
||||||
if not prefix or len(prefix) < MIN_PREFIX_LENGTH:
|
if not prefix or len(prefix) < MIN_PREFIX_LENGTH:
|
||||||
return []
|
return []
|
||||||
@@ -1565,7 +1568,10 @@ def suggest_titles(
|
|||||||
key = f"{entry['vault']}::{entry['path']}"
|
key = f"{entry['vault']}::{entry['path']}"
|
||||||
if key not in seen:
|
if key not in seen:
|
||||||
seen.add(key)
|
seen.add(key)
|
||||||
results.append(entry)
|
# Add tags from the index
|
||||||
|
entry_with_tags: dict[str, Any] = dict(entry)
|
||||||
|
entry_with_tags["tags"] = list(inv.doc_tags.get(entry["path"], set()))
|
||||||
|
results.append(entry_with_tags)
|
||||||
if len(results) >= limit:
|
if len(results) >= limit:
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.45.0"
|
version = "2.45.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "obsigate-desktop"
|
name = "obsigate-desktop"
|
||||||
version = "2.45.0"
|
version = "2.45.1"
|
||||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||||
authors = ["Bruno Charest"]
|
authors = ["Bruno Charest"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||||
"productName": "ObsiGate",
|
"productName": "ObsiGate",
|
||||||
"version": "2.45.0",
|
"version": "2.45.1",
|
||||||
"identifier": "com.obsigate.desktop",
|
"identifier": "com.obsigate.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../frontend",
|
"frontendDist": "../frontend",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# ObsiGate — Roadmap
|
# ObsiGate — Roadmap
|
||||||
|
|
||||||
> **Version :** 2.45.0 | **Dernière mise à jour :** 2026-09-30
|
> **Version :** 2.45.1 | **Dernière mise à jour :** 2026-10-01
|
||||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||||
> vers les fonctionnalités livrées.
|
> vers les fonctionnalités livrées.
|
||||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||||
|
|||||||
+3
-4
@@ -1196,16 +1196,15 @@
|
|||||||
>Ctrl+Shift+Space</span
|
>Ctrl+Shift+Space</span
|
||||||
>
|
>
|
||||||
<span class="qh-desc"
|
<span class="qh-desc"
|
||||||
>Palette de commandes</span
|
>Palette de fichiers (nav.
|
||||||
|
rapide)</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="qh-row">
|
<div class="qh-row">
|
||||||
<span class="qh-kbd"
|
<span class="qh-kbd"
|
||||||
>Ctrl+Alt+Space</span
|
>Ctrl+Alt+Space</span
|
||||||
>
|
>
|
||||||
<span class="qh-desc"
|
<span class="qh-desc">Palette de commandes</span
|
||||||
>Palette de fichiers (nav.
|
|
||||||
rapide)</span
|
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="qh-row">
|
<div class="qh-row">
|
||||||
|
|||||||
+36
-7
@@ -14,17 +14,26 @@ function getCurrentFile() {
|
|||||||
return null;
|
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) {
|
function parseQuery(raw) {
|
||||||
const positive = [];
|
const positive = [];
|
||||||
const negative = [];
|
const negative = [];
|
||||||
|
const filters = { ext: null, vault: null, tag: null };
|
||||||
const parts = (raw || '').split(/\s+/).filter(Boolean);
|
const parts = (raw || '').split(/\s+/).filter(Boolean);
|
||||||
for (const p of parts) {
|
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 if (p.startsWith('+')) positive.push(p.slice(1).toLowerCase());
|
||||||
else positive.push(p.toLowerCase());
|
else positive.push(p.toLowerCase());
|
||||||
}
|
}
|
||||||
return { positive, negative };
|
return { positive, negative, filters };
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DOM / state ──
|
// ── DOM / state ──
|
||||||
@@ -407,8 +416,8 @@ function createDOM() {
|
|||||||
_overlay.className = 'command-palette-overlay';
|
_overlay.className = 'command-palette-overlay';
|
||||||
_overlay.tabIndex = -1;
|
_overlay.tabIndex = -1;
|
||||||
_overlay.innerHTML = `<div class="command-palette">
|
_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>
|
<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) — recherche globale..." spellcheck="false" autocomplete="off" style="display:none">
|
<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-results"></div>
|
||||||
<div class="cp-footer"><span>↑↓ naviguer</span><span>↵ ouvrir/entrer</span><span>⌫ monter</span><span>Esc fermer</span></div></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');
|
_input = _overlay.querySelector('.cp-input');
|
||||||
@@ -488,11 +497,14 @@ async function searchFiles(rawQ) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { positive, negative } = parseQuery(rawQ);
|
const { positive, negative, filters } = parseQuery(rawQ);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const q = positive.join(' ');
|
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 || [];
|
let sug = d.suggestions || [];
|
||||||
|
|
||||||
// Client-side negative filter
|
// 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) {
|
if (sug.length === 0) {
|
||||||
_items = [{ type:'_browse', label:'📂 Parcourir les répertoires...', sublabel:'Aucun résultat — naviguer manuellement', act:()=>startBrowse('open-file') }];
|
_items = [{ type:'_browse', label:'📂 Parcourir les répertoires...', sublabel:'Aucun résultat — naviguer manuellement', act:()=>startBrowse('open-file') }];
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsigate",
|
"name": "obsigate",
|
||||||
"version": "2.45.0",
|
"version": "2.45.1",
|
||||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||||
"main": "patch.js",
|
"main": "patch.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|||||||
Reference in New Issue
Block a user