Simplify sidebar tabs, improve saved search display, and refactor

suggestions
This commit is contained in:
Bruno Charest 2026-05-27 09:27:26 -04:00
parent 1c59300f11
commit 0d3de28967
3 changed files with 91 additions and 75 deletions

View File

@ -453,52 +453,48 @@
const historyItems = SearchHistory.filter(inputValue);
this._renderHistory(historyItems, inputValue);
// Title and tag suggestions from API (debounced)
// Title and tag suggestions from API (debounced) — always fetch both
clearTimeout(this._suggestTimer);
if (ctx.prefix && ctx.prefix.length >= 2) {
this._suggestTimer = setTimeout(() => this._fetchSuggestions(ctx, vault, inputValue), SUGGEST_DEBOUNCE_MS);
const prefix = ctx.prefix;
if (prefix && prefix.length >= 2) {
this._suggestTimer = setTimeout(() => this._fetchSuggestions(prefix, vault), 150);
} else {
this._renderTitles([], "");
this._renderTags([], "");
this._titlesSection.hidden = true;
this._tagsSection.hidden = true;
}
// Show/hide sections
this._titlesSection.hidden = true;
this._tagsSection.hidden = true;
const hasContent = historyItems.length > 0;
this._historySection.hidden = historyItems.length === 0;
this._emptyEl.hidden = hasContent;
if (hasContent || (ctx.prefix && ctx.prefix.length >= 2)) {
if (hasContent || (prefix && prefix.length >= 2)) {
this.show();
} else if (!hasContent) {
} else {
this.hide();
}
this._collectItems();
},
async _fetchSuggestions(ctx, vault, inputValue) {
async _fetchSuggestions(prefix, vault) {
suggestAbortController = new AbortController();
try {
const [titlesRes, tagsRes] = await Promise.all([
ctx.type !== "tag" ? api(`/api/suggest?q=${encodeURIComponent(ctx.prefix)}&vault=${encodeURIComponent(vault)}&limit=8`, { signal: suggestAbortController.signal }) : Promise.resolve({ suggestions: [] }),
ctx.type === "tag" || ctx.type === "text" ? api(`/api/tags/suggest?q=${encodeURIComponent(ctx.prefix)}&vault=${encodeURIComponent(vault)}&limit=6`, { signal: suggestAbortController.signal }) : Promise.resolve({ suggestions: [] }),
api(`/api/suggest?q=${encodeURIComponent(prefix)}&vault=${encodeURIComponent(vault)}&limit=8`, { signal: suggestAbortController.signal }),
api(`/api/tags/suggest?q=${encodeURIComponent(prefix)}&vault=${encodeURIComponent(vault)}&limit=8`, { signal: suggestAbortController.signal }),
]);
this._renderTitles(titlesRes.suggestions || [], ctx.prefix);
this._renderTags(tagsRes.suggestions || [], ctx.prefix);
this._renderTitles(titlesRes.suggestions || [], prefix);
this._renderTags(tagsRes.suggestions || [], prefix);
// Update visibility
const hasTitles = (titlesRes.suggestions || []).length > 0;
const hasTags = (tagsRes.suggestions || []).length > 0;
this._titlesSection.hidden = !hasTitles;
this._tagsSection.hidden = !hasTags;
const historyVisible = !this._historySection.hidden;
const hasAny = historyVisible || hasTitles || hasTags;
this._emptyEl.hidden = hasAny;
if (hasAny) this.show();
else if (!historyVisible) this.hide();
if (hasTitles || hasTags) this.show();
this._collectItems();
} catch (err) {
if (err.name !== "AbortError") console.error("Suggestion fetch error:", err);
@ -6056,18 +6052,26 @@
return;
}
if (empty) empty.style.display = "none";
list.innerHTML = searches.map(s => `
list.innerHTML = searches.map(s => {
const badges = [];
if (s.case_sensitive) badges.push('<span class="search-filter-badge">Aa</span>');
if (s.whole_word) badges.push('<span class="search-filter-badge">wd</span>');
if (s.regex) badges.push('<span class="search-filter-badge">.*</span>');
const pathFilters = [];
if (s.include_paths) pathFilters.push(`<span class="saved-search-path" title="Inclure: ${escapeHtml(s.include_paths)}">📥 ${escapeHtml(s.include_paths)}</span>`);
if (s.exclude_paths) pathFilters.push(`<span class="saved-search-path" title="Exclure: ${escapeHtml(s.exclude_paths)}">📤 ${escapeHtml(s.exclude_paths)}</span>`);
const vaultStr = s.vault && s.vault !== "all" ? `<span class="saved-search-vault">📁 ${escapeHtml(s.vault)}</span>` : "";
return `
<div class="saved-search-item">
<div class="saved-search-query" title="${escapeHtml(s.query)}">${escapeHtml(s.query.length > 50 ? s.query.slice(0, 50) + "..." : s.query)}</div>
<div class="saved-search-query">${escapeHtml(s.query)}</div>
<div class="saved-search-meta">
${s.case_sensitive ? '<span class="search-filter-badge">Aa</span>' : ""}
${s.whole_word ? '<span class="search-filter-badge">wd</span>' : ""}
${s.regex ? '<span class="search-filter-badge">.*</span>' : ""}
<span class="saved-search-vault">${escapeHtml(s.vault)}</span>
${badges.join("")}
${vaultStr}
</div>
${pathFilters.length ? '<div class="saved-search-filters">' + pathFilters.join(" ") + '</div>' : ""}
<button class="saved-search-delete" data-id="${s.id}" title="Supprimer"></button>
</div>
`).join("");
`}).join("");
list.querySelectorAll(".saved-search-item").forEach(item => {
item.addEventListener("click", (e) => {
if (e.target.classList.contains("saved-search-delete")) return;
@ -6090,6 +6094,7 @@
if (excl) excl.value = s.exclude_paths;
}
// Execute the search
AutocompleteDropdown.hide();
const vault = s.vault || "all";
if (input) { input.dispatchEvent(new Event("input")); }
clearTimeout(searchTimeout);

View File

@ -306,24 +306,20 @@
<!-- Tab bar -->
<div class="sidebar-tabs" role="tablist" aria-label="Sections sidebar">
<button class="sidebar-tab active" id="sidebar-tab-vaults" role="tab"
aria-selected="true" aria-controls="sidebar-panel-vaults" data-tab="vaults">
<i data-lucide="folder-tree" style="width:13px;height:13px"></i>
<span>Vaults</span>
aria-selected="true" aria-controls="sidebar-panel-vaults" data-tab="vaults" title="Vaults">
<i data-lucide="folder-tree" style="width:18px;height:18px"></i>
</button>
<button class="sidebar-tab" id="sidebar-tab-tags" role="tab"
aria-selected="false" aria-controls="sidebar-panel-tags" data-tab="tags">
<i data-lucide="tag" style="width:13px;height:13px"></i>
<span>Tags</span>
aria-selected="false" aria-controls="sidebar-panel-tags" data-tab="tags" title="Tags">
<i data-lucide="tag" style="width:18px;height:18px"></i>
</button>
<button class="sidebar-tab" id="sidebar-tab-recent" role="tab"
aria-selected="false" aria-controls="sidebar-panel-recent" data-tab="recent">
<i data-lucide="clock" style="width:13px;height:13px"></i>
<span>Récent</span>
aria-selected="false" aria-controls="sidebar-panel-recent" data-tab="recent" title="Récent">
<i data-lucide="clock" style="width:18px;height:18px"></i>
</button>
<button class="sidebar-tab" id="sidebar-tab-saved" role="tab"
aria-selected="false" aria-controls="sidebar-panel-saved" data-tab="saved">
<i data-lucide="bookmark" style="width:13px;height:13px"></i>
<span>Sauvegardes</span>
aria-selected="false" aria-controls="sidebar-panel-saved" data-tab="saved" title="Sauvegardes">
<i data-lucide="bookmark" style="width:18px;height:18px"></i>
</button>
</div>

View File

@ -755,23 +755,14 @@ select {
display: flex;
align-items: center;
justify-content: center;
gap: 5px;
padding: 9px 8px 8px;
padding: 8px 4px 6px;
border: none;
border-bottom: 2px solid transparent;
background: transparent;
color: var(--text-muted);
font-family: "JetBrains Mono", monospace;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.07em;
cursor: pointer;
transition:
color 150ms ease,
border-color 150ms ease;
transition: color 150ms ease, border-color 150ms ease;
margin-bottom: -1px;
white-space: nowrap;
}
.sidebar-tab:hover {
@ -1854,24 +1845,25 @@ select {
transition:
background 120ms ease,
border-color 120ms ease;
position: relative;
}
.search-result-item:hover {
background: var(--bg-hover);
border-color: var(--accent);
position: relative;
}
.search-result-ext {
position: absolute;
top: 8px;
right: 10px;
font-size: 0.6rem;
font-weight: 600;
font-size: 0.65rem;
font-weight: 700;
text-transform: uppercase;
color: var(--text-muted);
background: var(--bg-hover);
padding: 1px 6px;
border-radius: 3px;
color: var(--accent);
background: var(--accent-bg, rgba(99, 102, 241, 0.12));
padding: 2px 8px;
border-radius: 4px;
letter-spacing: 0.5px;
border: 1px solid rgba(99, 102, 241, 0.2);
}
.search-filters-row {
display: flex;
@ -1910,42 +1902,65 @@ select {
/* Saved searches sidebar */
.saved-search-item {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 10px;
cursor: pointer;
border-radius: 4px;
transition: background 0.15s;
position: relative;
padding: 10px 12px;
cursor: pointer;
border-radius: 6px;
transition: background 0.15s;
border: 1px solid transparent;
margin-bottom: 4px;
}
.saved-search-item:hover { background: var(--bg-hover); }
.saved-search-item:hover { background: var(--bg-hover); border-color: var(--border); }
.saved-search-query {
flex: 1;
font-size: 0.8rem;
font-size: 0.82rem;
font-family: "JetBrains Mono", monospace;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
line-height: 1.4;
word-break: break-word;
overflow-wrap: anywhere;
margin-bottom: 6px;
}
.saved-search-meta {
display: flex;
gap: 3px;
gap: 4px;
align-items: center;
flex-shrink: 0;
flex-wrap: wrap;
margin-bottom: 2px;
}
.saved-search-vault {
font-size: 0.65rem;
color: var(--text-muted);
margin-left: 4px;
padding: 1px 5px;
background: var(--bg-secondary);
border-radius: 3px;
}
.saved-search-filters {
font-size: 0.65rem;
color: var(--accent);
display: flex;
gap: 4px;
flex-wrap: wrap;
}
.saved-search-path {
font-size: 0.6rem;
color: #059669;
background: rgba(5,150,105,0.08);
padding: 1px 5px;
border-radius: 3px;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.saved-search-delete {
position: absolute;
top: 6px;
right: 8px;
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 0.8rem;
padding: 2px 4px;
font-size: 0.9rem;
padding: 2px 6px;
opacity: 0;
transition: opacity 0.15s;
}