/* ObsiGate — Legacy module: remaining functions for the orchestrator Extracted from the monolithic frontend/app.js IIFE. Functions already in other modules are re-exported. */ // --- State imports --- import { state } from './state.js'; // --- Search imports --- import { AutocompleteDropdown, SearchChips, SearchHistory, performAdvancedSearch, } from './search.js'; // --- Dashboard imports --- import { DashboardRecentWidget, DashboardStatsWidget, DashboardBookmarkWidget, DashboardSharedWidget, } from './dashboard.js'; // --- Re-exports from modules that already have these functions --- export { initSidebarTabs, initConfigModal, initHelpModal, initRecentTab } from './config.js'; export { initSyncStatus } from './sync.js'; export { DashboardRecentWidget } from './dashboard.js'; // ========================================================================= // Progress bar helpers (used by showWelcome) // ========================================================================= function showProgressBar() { const bar = document.getElementById("search-progress-bar"); if (bar) bar.classList.add("active"); } function hideProgressBar() { const bar = document.getElementById("search-progress-bar"); if (bar) bar.classList.remove("active"); } // ========================================================================= // loadVaultSettings // ========================================================================= export async function loadVaultSettings() { try { const settings = await api("/api/vaults/settings/all"); state.vaultSettings = settings; } catch (err) { console.error("Failed to load vault settings:", err); state.vaultSettings = {}; } } // ========================================================================= // Config helpers (needed by initSearch) // ========================================================================= const _FRONTEND_CONFIG_KEY = "obsigate-perf-config"; function _getFrontendConfig() { try { return JSON.parse(localStorage.getItem(_FRONTEND_CONFIG_KEY) || "{}"); } catch { return {}; } } function _getEffective(key, fallback) { const cfg = _getFrontendConfig(); return cfg[key] !== undefined ? cfg[key] : fallback; } /** Check if user is focused on an input/textarea/contenteditable */ function _isInputFocused() { const tag = document.activeElement?.tagName; if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true; return document.activeElement?.isContentEditable === true; } // ========================================================================= // initSearch // ========================================================================= export function initSearch() { const input = document.getElementById("search-input"); if (!input) return; const caseBtn = document.getElementById("search-case-btn"); const wordBtn = document.getElementById("search-word-btn"); const regexBtn = document.getElementById("search-regex-btn"); const filterBtn = document.getElementById("search-filter-btn"); const clearBtn = document.getElementById("search-clear-btn"); const filterRow = document.getElementById("search-filter-row"); const prevBtn = document.getElementById("search-prev-btn"); const nextBtn = document.getElementById("search-next-btn"); const countEl = document.getElementById("search-match-count"); function _updateToggleUI() { caseBtn.classList.toggle("active", state.searchCaseSensitive); wordBtn.classList.toggle("active", state.searchWholeWord); regexBtn.classList.toggle("active", state.searchRegex); filterBtn.classList.toggle("active", state.searchFilterVisible); } // Toggle buttons caseBtn.addEventListener("click", () => { state.searchCaseSensitive = !state.searchCaseSensitive; _updateToggleUI(); _research(); }); if (wordBtn) wordBtn.addEventListener("click", () => { state.searchWholeWord = !state.searchWholeWord; _updateToggleUI(); _research(); }); if (regexBtn) regexBtn.addEventListener("click", () => { state.searchRegex = !state.searchRegex; _updateToggleUI(); _research(); }); if (filterBtn) filterBtn.addEventListener("click", () => { state.searchFilterVisible = !state.searchFilterVisible; if (filterRow) filterRow.style.display = state.searchFilterVisible ? "flex" : "none"; _updateToggleUI(); }); // Result navigation (up/down arrows + Enter) let _searchResultIdx = -1; let _searchResultItems = []; function _updateResultHighlight() { _searchResultItems.forEach((el, i) => { el.classList.toggle("search-result-active", i === _searchResultIdx); }); if (_searchResultIdx >= 0 && _searchResultIdx < _searchResultItems.length) { _searchResultItems[_searchResultIdx].scrollIntoView({ block: "nearest", behavior: "smooth" }); } const countEl = document.getElementById("search-match-count"); if (countEl) countEl.textContent = _searchResultIdx >= 0 ? `${_searchResultIdx + 1}/${_searchResultItems.length}` : `0/${_searchResultItems.length}`; } function _refreshResultItems() { _searchResultItems = Array.from(document.querySelectorAll(".search-result-item")); _searchResultIdx = _searchResultItems.length > 0 ? 0 : -1; _updateResultHighlight(); } window.navigateSearchResults = function(delta) { _searchResultItems = Array.from(document.querySelectorAll(".search-result-item")); if (_searchResultItems.length === 0) return; _searchResultIdx = Math.max(0, Math.min(_searchResultItems.length - 1, _searchResultIdx + delta)); _updateResultHighlight(); }; if (prevBtn) prevBtn.addEventListener("click", () => navigateSearchResults(-1)); if (nextBtn) nextBtn.addEventListener("click", () => navigateSearchResults(1)); function _research() { const q = input.value.trim(); if (q.length >= _getEffective("min_query_length", 2)) { clearTimeout(state.searchTimeout); state.searchTimeout = setTimeout(() => { const vault = document.getElementById("vault-filter").value; const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null; state.advancedSearchOffset = 0; performAdvancedSearch(q, vault, tagFilter); }, _getEffective("debounce_ms", 300)); } } // Keyboard shortcuts document.addEventListener("keydown", (e) => { if (e.altKey && !e.ctrlKey && !e.metaKey) { if (e.key === "c" || e.key === "C") { e.preventDefault(); caseBtn.click(); } else if (e.key === "w" || e.key === "W") { e.preventDefault(); if (wordBtn) wordBtn.click(); } else if (e.key === "r" || e.key === "R") { e.preventDefault(); if (regexBtn) regexBtn.click(); } else if (e.key === "f" || e.key === "F") { e.preventDefault(); if (filterBtn) filterBtn.click(); input.focus(); } } }); // Initialize sub-controllers AutocompleteDropdown.init(); SearchChips.init(); // Initially hide clear button if (clearBtn) clearBtn.style.display = "none"; // Input handler: debounced search + autocomplete dropdown input.addEventListener("input", () => { const hasText = input.value.length > 0; clearBtn.style.display = hasText ? "flex" : "none"; // Show autocomplete dropdown while typing AutocompleteDropdown.populate(input.value, input.selectionStart); // Debounced search execution clearTimeout(state.searchTimeout); state.searchTimeout = setTimeout( () => { const q = input.value.trim(); const vault = document.getElementById("vault-filter").value; const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null; state.advancedSearchOffset = 0; if (q.length >= _getEffective("min_query_length", state.MIN_SEARCH_LENGTH) || tagFilter) { performAdvancedSearch(q, vault, tagFilter); } else if (q.length === 0) { SearchChips.clear(); showWelcome(); } }, _getEffective("debounce_ms", 300), ); }); // Focus handler: show history dropdown input.addEventListener("focus", () => { if (input.value.length === 0) { const historyItems = SearchHistory.filter("").slice(0, 5); if (historyItems.length > 0) { AutocompleteDropdown.populate("", 0); } } }); // Keyboard navigation in dropdown input.addEventListener("keydown", (e) => { if (AutocompleteDropdown.isVisible()) { if (e.key === "ArrowDown") { e.preventDefault(); AutocompleteDropdown.navigateDown(); } else if (e.key === "ArrowUp") { e.preventDefault(); AutocompleteDropdown.navigateUp(); } else if (e.key === "Enter") { // First: check dropdown suggestions (higher priority than search results) if (AutocompleteDropdown.isVisible() && AutocompleteDropdown.selectActive()) { e.preventDefault(); return; } // Second: navigate search results if visible const results = document.querySelectorAll(".search-result-item"); if (results.length > 0 && _searchResultIdx >= 0) { const el = results[_searchResultIdx]; if (el) { const vault = el.dataset.vault; const path = el.dataset.path; if (vault && path) { TabManager.openPreview(vault, path); e.preventDefault(); return; } } } // Third: execute search AutocompleteDropdown.hide(); const q = input.value.trim(); if (q) { SearchHistory.add(q); clearTimeout(state.searchTimeout); state.advancedSearchOffset = 0; const vault = document.getElementById("vault-filter").value; const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null; performAdvancedSearch(q, vault, tagFilter); } e.preventDefault(); } else if (e.key === "ArrowDown" && !AutocompleteDropdown.isVisible()) { // Navigate search results when dropdown is closed if (window.navigateSearchResults) { window.navigateSearchResults(1); e.preventDefault(); } } else if (e.key === "ArrowUp" && !AutocompleteDropdown.isVisible()) { if (window.navigateSearchResults) { window.navigateSearchResults(-1); e.preventDefault(); } } else if (e.key === "Escape") { AutocompleteDropdown.hide(); e.stopPropagation(); } } else if (e.key === "Enter") { if (AutocompleteDropdown.isVisible() && AutocompleteDropdown.selectActive()) { e.preventDefault(); return; } const q = input.value.trim(); if (q) { SearchHistory.add(q); clearTimeout(state.searchTimeout); state.advancedSearchOffset = 0; const vault = document.getElementById("vault-filter").value; const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null; performAdvancedSearch(q, vault, tagFilter); } e.preventDefault(); } }); clearBtn.addEventListener("click", () => { input.value = ""; if (clearBtn) clearBtn.style.display = "none"; state.searchCaseSensitive = false; state.searchWholeWord = false; state.searchRegex = false; _updateToggleUI(); SearchChips.clear(); AutocompleteDropdown.hide(); showWelcome(); }); // Global keyboard shortcuts document.addEventListener("keydown", (e) => { // Ctrl+K or Cmd+K: focus search if ((e.ctrlKey || e.metaKey) && e.key === "k") { e.preventDefault(); input.focus(); input.select(); } // "/" key: focus search (when not in an input/textarea) if (e.key === "/" && !_isInputFocused()) { e.preventDefault(); input.focus(); } // Escape: blur search input and close dropdown if (e.key === "Escape" && document.activeElement === input) { AutocompleteDropdown.hide(); input.blur(); } }); } // ========================================================================= // showWelcome // ========================================================================= export function showWelcome() { hideProgressBar(); // Restore or rebuild the dashboard with tabbed sections const area = document.getElementById("content-area"); const home = document.getElementById("dashboard-home"); if (area && !home) { area.innerHTML = `
Chargement...
Aucun bookmark

Épinglez des fichiers pour les retrouver ici.

Aucun document partagé

Partagez un document pour le voir apparaître ici

`; // Re-initialize widgets and dashboard tabs DashboardRecentWidget.init(); initDashboardTabs(); safeCreateIcons(); } else if (home) { // Dashboard already exists, show it with default tab home.style.display = ""; // Reset tabs to default document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active")); document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active")); const defaultTab = document.querySelector('.dashboard-tab[data-tab="stats"]'); const defaultPanel = document.getElementById("dashboard-panel-stats"); if (defaultTab) defaultTab.classList.add("active"); if (defaultPanel) defaultPanel.classList.add("active"); } // Load all widgets (they handle missing elements gracefully) if (typeof DashboardStatsWidget !== "undefined") { DashboardStatsWidget.load(); } if (typeof DashboardConflictsWidget !== "undefined") { DashboardConflictsWidget.load(); } DashboardRecentWidget.load(state.selectedContextVault); if (typeof DashboardBookmarkWidget !== "undefined") { DashboardBookmarkWidget.load(state.selectedContextVault); } if (typeof DashboardSharedWidget !== "undefined") { DashboardSharedWidget.load(); } // Load saved searches sidebar loadSavedSearches(); } // ========================================================================= // goHome // ========================================================================= export function goHome() { const searchInput = document.getElementById("search-input"); if (searchInput) searchInput.value = ""; document.querySelectorAll(".tree-item.active").forEach((el) => el.classList.remove("active")); state.currentVault = null; state.currentPath = null; state.showingSource = false; state.cachedRawSource = null; closeMobileSidebar(); showWelcome(); } // ========================================================================= // loadSavedSearches (needed by showWelcome) // ========================================================================= async function loadSavedSearches() { const list = document.getElementById("saved-searches-list"); const empty = document.getElementById("saved-searches-empty"); if (!list) return; try { const searches = await api("/api/saved-searches"); if (!searches.length) { list.innerHTML = ""; if (empty) empty.style.display = ""; return; } if (empty) empty.style.display = "none"; list.innerHTML = searches.map(s => { const badges = []; if (s.case_sensitive) badges.push('Aa'); if (s.whole_word) badges.push('wd'); if (s.regex) badges.push('.*'); const pathFilters = []; if (s.include_paths) pathFilters.push(`\u{1F4E5} ${escapeHtml(s.include_paths)}`); if (s.exclude_paths) pathFilters.push(`\u{1F4E4} ${escapeHtml(s.exclude_paths)}`); const vaultStr = s.vault && s.vault !== "all" ? `\u{1F4C1} ${escapeHtml(s.vault)}` : ""; return `
${escapeHtml(s.query)}
${badges.join("")} ${vaultStr}
${pathFilters.length ? '
' + pathFilters.join(" ") + '
' : ""}
`}).join(""); list.querySelectorAll(".saved-search-item").forEach(item => { item.addEventListener("click", (e) => { if (e.target.classList.contains("saved-search-delete")) return; const idx = Array.from(list.children).indexOf(item); const s = searches[idx]; if (!s) return; // Apply the saved search const input = document.getElementById("search-input"); if (input) input.value = s.query; state.searchCaseSensitive = s.case_sensitive || false; state.searchWholeWord = s.whole_word || false; state.searchRegex = s.regex || false; if (typeof _updateToggleUI === "function") _updateToggleUI(); if (s.include_paths) { const incl = document.getElementById("search-include-input"); if (incl) incl.value = s.include_paths; } if (s.exclude_paths) { const excl = document.getElementById("search-exclude-input"); if (excl) excl.value = s.exclude_paths; } // Execute the search AutocompleteDropdown.hide(); AutocompleteDropdown._suppressNext = true; const vault = s.vault || "all"; if (input) { input.dispatchEvent(new Event("input")); } clearTimeout(state.searchTimeout); state.advancedSearchOffset = 0; performAdvancedSearch(s.query, vault, null); }); }); list.querySelectorAll(".saved-search-delete").forEach(b => b.addEventListener("click", async (e) => { e.stopPropagation(); await api(`/api/saved-searches/${b.dataset.id}`, { method: "DELETE" }); loadSavedSearches(); })); safeCreateIcons(); } catch (err) { /* silently ignore */ } } // ========================================================================= // initDashboardTabs (needed by showWelcome) // ========================================================================= function initDashboardTabs() { document.querySelectorAll(".dashboard-tab").forEach(tab => { // Remove existing listeners by cloning const newTab = tab.cloneNode(true); tab.parentNode.replaceChild(newTab, tab); newTab.addEventListener("click", function() { document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active")); document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active")); this.classList.add("active"); const panel = document.getElementById("dashboard-panel-" + this.dataset.tab); if (panel) panel.classList.add("active"); }); }); } // ========================================================================= // Exports // ========================================================================= export { loadVaultSettings, initSearch, showWelcome, goHome, };