feat: add hidden files configuration with per-vault settings for includeHidden and hiddenWhitelist, supporting environment variables and UI controls for selective indexing of dot-prefixed files and folders

This commit is contained in:
2026-03-25 09:54:34 -04:00
parent 8c30b0d238
commit 9e42fb072b
8 changed files with 790 additions and 13 deletions
+226
View File
@@ -2968,6 +2968,7 @@
renderConfigFilters();
loadConfigFields();
loadDiagnostics();
await loadHiddenFilesSettings();
safeCreateIcons();
});
@@ -3009,6 +3010,13 @@
const diagBtn = document.getElementById("cfg-refresh-diag");
if (diagBtn) diagBtn.addEventListener("click", loadDiagnostics);
// Hidden files configuration
const saveHiddenBtn = document.getElementById("cfg-save-hidden-files");
if (saveHiddenBtn) saveHiddenBtn.addEventListener("click", saveHiddenFilesSettings);
const reindexHiddenBtn = document.getElementById("cfg-reindex-hidden");
if (reindexHiddenBtn) reindexHiddenBtn.addEventListener("click", reindexWithHiddenFiles);
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && modal.classList.contains("active")) {
closeConfigModal();
@@ -3221,6 +3229,224 @@
});
}
// --- Hidden Files Configuration ---
async function loadHiddenFilesSettings() {
const container = document.getElementById("hidden-files-vault-list");
if (!container) return;
container.innerHTML = '<div style="padding:12px;color:var(--text-muted)">Chargement...</div>';
try {
const settings = await api("/api/vaults/settings/all");
renderHiddenFilesSettings(container, settings);
} catch (err) {
console.error("Failed to load hidden files settings:", err);
container.innerHTML = '<div style="padding:12px;color:var(--error)">Erreur de chargement</div>';
}
}
function renderHiddenFilesSettings(container, allSettings) {
container.innerHTML = "";
if (!allVaults || allVaults.length === 0) {
container.innerHTML = '<div style="padding:12px;color:var(--text-muted)">Aucun vault configuré</div>';
return;
}
allVaults.forEach(vault => {
const settings = allSettings[vault.name] || { includeHidden: false, hiddenWhitelist: [] };
const vaultCard = el("div", { class: "hidden-files-vault-card", "data-vault": vault.name });
// Vault header
const header = el("div", { class: "hidden-files-vault-header" }, [
el("h3", {}, [document.createTextNode(vault.name)]),
el("span", { class: "hidden-files-vault-type" }, [document.createTextNode(vault.type || "VAULT")])
]);
// Include all hidden toggle
const toggleRow = el("div", { class: "config-row" }, [
el("label", { class: "config-label", for: `hidden-include-${vault.name}` }, [
document.createTextNode("Inclure tous les fichiers cachés")
]),
el("label", { class: "config-toggle" }, [
el("input", {
type: "checkbox",
id: `hidden-include-${vault.name}`,
"data-vault": vault.name,
checked: settings.includeHidden || false
}),
el("span", { class: "config-toggle-slider" })
]),
el("span", { class: "config-hint" }, [
document.createTextNode("Activer pour indexer tous les fichiers et dossiers cachés (commençant par un point)")
])
]);
// Whitelist section
const whitelistSection = el("div", { class: "hidden-files-whitelist" });
const whitelistLabel = el("label", { class: "config-label" }, [
document.createTextNode("Liste blanche (dossiers cachés spécifiques)")
]);
const whitelistHint = el("span", { class: "config-hint", style: "display:block;margin-bottom:8px" }, [
document.createTextNode("Ajoutez des dossiers cachés individuels à indexer (ex: .obsidian, .github)")
]);
const whitelistItems = el("div", { class: "hidden-files-whitelist-items", id: `whitelist-items-${vault.name}` });
// Render existing whitelist items
(settings.hiddenWhitelist || []).forEach(item => {
const itemEl = createWhitelistItem(vault.name, item);
whitelistItems.appendChild(itemEl);
});
// Add new item input
const addRow = el("div", { class: "hidden-files-add-row" }, [
el("input", {
type: "text",
class: "config-input",
id: `whitelist-input-${vault.name}`,
placeholder: "Ex: .obsidian"
}),
el("button", {
class: "config-btn-add",
type: "button",
"data-vault": vault.name
}, [document.createTextNode("Ajouter")])
]);
whitelistSection.appendChild(whitelistLabel);
whitelistSection.appendChild(whitelistHint);
whitelistSection.appendChild(whitelistItems);
whitelistSection.appendChild(addRow);
vaultCard.appendChild(header);
vaultCard.appendChild(toggleRow);
vaultCard.appendChild(whitelistSection);
container.appendChild(vaultCard);
// Event listeners
const addBtn = addRow.querySelector("button");
const input = addRow.querySelector("input");
addBtn.addEventListener("click", () => addWhitelistItem(vault.name));
input.addEventListener("keypress", (e) => {
if (e.key === "Enter") addWhitelistItem(vault.name);
});
});
}
function createWhitelistItem(vaultName, itemValue) {
const item = el("div", { class: "hidden-files-whitelist-item" }, [
el("span", { class: "whitelist-item-text" }, [document.createTextNode(itemValue)]),
el("button", {
class: "whitelist-item-remove",
type: "button",
title: "Supprimer",
"data-vault": vaultName,
"data-item": itemValue
}, [document.createTextNode("×")])
]);
const removeBtn = item.querySelector("button");
removeBtn.addEventListener("click", () => {
item.remove();
});
return item;
}
function addWhitelistItem(vaultName) {
const input = document.getElementById(`whitelist-input-${vaultName}`);
const container = document.getElementById(`whitelist-items-${vaultName}`);
if (!input || !container) return;
const value = input.value.trim();
if (!value) return;
// Validate format (should start with a dot)
if (!value.startsWith(".")) {
showToast("Le nom doit commencer par un point (ex: .obsidian)", "error");
return;
}
// Check for duplicates
const existing = Array.from(container.querySelectorAll(".whitelist-item-text"))
.map(el => el.textContent);
if (existing.includes(value)) {
showToast("Cet élément existe déjà", "error");
return;
}
const item = createWhitelistItem(vaultName, value);
container.appendChild(item);
input.value = "";
}
async function saveHiddenFilesSettings() {
const btn = document.getElementById("cfg-save-hidden-files");
if (btn) { btn.disabled = true; btn.textContent = "Sauvegarde..."; }
try {
const vaultCards = document.querySelectorAll(".hidden-files-vault-card");
const promises = [];
vaultCards.forEach(card => {
const vaultName = card.dataset.vault;
const includeHidden = document.getElementById(`hidden-include-${vaultName}`)?.checked || false;
const whitelistItems = Array.from(
document.querySelectorAll(`#whitelist-items-${vaultName} .whitelist-item-text`)
).map(el => el.textContent);
const settings = {
includeHidden,
hiddenWhitelist: whitelistItems
};
promises.push(
fetch(`/api/vaults/${encodeURIComponent(vaultName)}/settings`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(settings)
})
);
});
await Promise.all(promises);
showToast("Paramètres des fichiers cachés sauvegardés", "success");
} catch (err) {
console.error("Failed to save hidden files settings:", err);
showToast("Erreur de sauvegarde", "error");
} finally {
if (btn) { btn.disabled = false; btn.textContent = "Sauvegarder les paramètres"; }
}
}
async function reindexWithHiddenFiles() {
const btn = document.getElementById("cfg-reindex-hidden");
if (btn) { btn.disabled = true; btn.textContent = "Réindexation..."; }
try {
// First save the settings
await saveHiddenFilesSettings();
// Then trigger reindex
await api("/api/index/reload");
showToast("Réindexation avec nouveaux paramètres terminée", "success");
loadDiagnostics();
await Promise.all([loadVaults(), loadTags()]);
} catch (err) {
console.error("Reindex with hidden files error:", err);
showToast("Erreur de réindexation", "error");
} finally {
if (btn) { btn.disabled = false; btn.textContent = "Réindexer avec nouveaux paramètres"; }
}
}
function renderConfigFilters() {
const config = TagFilterService.getConfig();
const filters = config.tagFilters || TagFilterService.defaultFilters;