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:
+226
@@ -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;
|
||||
|
||||
@@ -548,6 +548,21 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Hidden Files/Folders Configuration -->
|
||||
<section class="config-section">
|
||||
<h2>📁 Fichiers et dossiers cachés</h2>
|
||||
<p class="config-description">Configurez la prise en charge des fichiers et dossiers cachés (commençant par un point) pour chaque vault. Vous pouvez activer tous les fichiers cachés ou ajouter des dossiers spécifiques à une liste blanche.</p>
|
||||
|
||||
<div id="hidden-files-vault-list">
|
||||
<!-- Vault-specific settings will be injected here -->
|
||||
</div>
|
||||
|
||||
<div class="config-actions-row" style="margin-top: 16px;">
|
||||
<button class="config-btn-save" id="cfg-save-hidden-files">Sauvegarder les paramètres</button>
|
||||
<button class="config-btn-secondary" id="cfg-reindex-hidden">Réindexer avec nouveaux paramètres</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Diagnostics -->
|
||||
<section class="config-section">
|
||||
<h2>Diagnostics</h2>
|
||||
|
||||
@@ -3408,6 +3408,99 @@ body.resizing-v {
|
||||
background: #22c55e;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Hidden Files Configuration
|
||||
--------------------------------------------------------------------------- */
|
||||
.hidden-files-vault-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.hidden-files-vault-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.hidden-files-vault-header h3 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.hidden-files-vault-type {
|
||||
font-size: 0.75rem;
|
||||
padding: 2px 8px;
|
||||
background: var(--tag-bg);
|
||||
color: var(--tag-text);
|
||||
border-radius: 4px;
|
||||
font-family: var(--mono);
|
||||
}
|
||||
|
||||
.hidden-files-whitelist {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.hidden-files-whitelist-items {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.hidden-files-whitelist-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 8px 4px 12px;
|
||||
background: var(--tag-bg);
|
||||
color: var(--tag-text);
|
||||
border-radius: 6px;
|
||||
font-size: 0.875rem;
|
||||
font-family: var(--mono);
|
||||
border: 1px solid color-mix(in srgb, var(--tag-text) 30%, transparent);
|
||||
}
|
||||
|
||||
.whitelist-item-text {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.whitelist-item-remove {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1;
|
||||
padding: 0 4px;
|
||||
transition: color 150ms ease;
|
||||
}
|
||||
|
||||
.whitelist-item-remove:hover {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.hidden-files-add-row {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.hidden-files-add-row .config-input {
|
||||
flex: 1;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
Utility — hidden class
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
Reference in New Issue
Block a user