ES module imports are read-only live bindings — can't reassign
imported let/const variables. Replace individual 'export let' with
single 'export const state = {...}' mutable object.
All modules updated: import { state } from './state.js'
All state access changed to state.xxx pattern.
Fixes cascade of 'Assignment to constant variable' errors.
561 lines
26 KiB
JavaScript
561 lines
26 KiB
JavaScript
1|/* ObsiGate — Legacy module: remaining functions for the orchestrator
|
|
2| Extracted from the monolithic frontend/app.js IIFE.
|
|
3| Functions already in other modules are re-exported. */
|
|
4|
|
|
5|// --- State imports ---
|
|
6|import {
|
|
7| state.currentVault,
|
|
8| state.currentPath,
|
|
9| state.showingSource,
|
|
10| state.cachedRawSource,
|
|
11| state.searchTimeout,
|
|
12| state.searchCaseSensitive,
|
|
13| state.searchWholeWord,
|
|
14| state.searchRegex,
|
|
15| state.searchFilterVisible,
|
|
16| state.advancedSearchOffset,
|
|
17| state.selectedTags,
|
|
18| state.selectedContextVault,
|
|
19| state.vaultSettings,
|
|
20| state.allVaults,
|
|
21| state.MIN_SEARCH_LENGTH,
|
|
22|} from './state.js';
|
|
23|
|
|
24|// --- Search imports ---
|
|
25|import {
|
|
26| AutocompleteDropdown,
|
|
27| SearchChips,
|
|
28| SearchHistory,
|
|
29| performAdvancedSearch,
|
|
30|} from './search.js';
|
|
31|
|
|
32|// --- Dashboard imports ---
|
|
33|import {
|
|
34| DashboardRecentWidget,
|
|
35| DashboardStatsWidget,
|
|
36| DashboardBookmarkWidget,
|
|
37| DashboardSharedWidget,
|
|
38|} from './dashboard.js';
|
|
39|
|
|
40|// --- Re-exports from modules that already have these functions ---
|
|
41|export { initSidebarTabs, initConfigModal, initHelpModal, initRecentTab } from './config.js';
|
|
42|export { initSyncStatus } from './sync.js';
|
|
43|export { DashboardRecentWidget } from './dashboard.js';
|
|
44|
|
|
45|// =========================================================================
|
|
46|// Progress bar helpers (used by showWelcome)
|
|
47|// =========================================================================
|
|
48|
|
|
49|function showProgressBar() {
|
|
50| const bar = document.getElementById("search-progress-bar");
|
|
51| if (bar) bar.classList.add("active");
|
|
52|}
|
|
53|
|
|
54|function hideProgressBar() {
|
|
55| const bar = document.getElementById("search-progress-bar");
|
|
56| if (bar) bar.classList.remove("active");
|
|
57|}
|
|
58|
|
|
59|// =========================================================================
|
|
60|// loadVaultSettings
|
|
61|// =========================================================================
|
|
62|
|
|
63|async function loadVaultSettings() {
|
|
64| try {
|
|
65| const settings = await api("/api/vaults/settings/all");
|
|
66| state.vaultSettings = settings;
|
|
67| } catch (err) {
|
|
68| console.error("Failed to load vault settings:", err);
|
|
69| state.vaultSettings = {};
|
|
70| }
|
|
71|}
|
|
72|
|
|
73|// =========================================================================
|
|
74|// Config helpers (needed by initSearch)
|
|
75|// =========================================================================
|
|
76|
|
|
77|const _FRONTEND_CONFIG_KEY = "obsigate-perf-config";
|
|
78|
|
|
79|function _getFrontendConfig() {
|
|
80| try {
|
|
81| return JSON.parse(localStorage.getItem(_FRONTEND_CONFIG_KEY) || "{}");
|
|
82| } catch {
|
|
83| return {};
|
|
84| }
|
|
85|}
|
|
86|
|
|
87|function _getEffective(key, fallback) {
|
|
88| const cfg = _getFrontendConfig();
|
|
89| return cfg[key] !== undefined ? cfg[key] : fallback;
|
|
90|}
|
|
91|
|
|
92|/** Check if user is focused on an input/textarea/contenteditable */
|
|
93|function _isInputFocused() {
|
|
94| const tag = document.activeElement?.tagName;
|
|
95| if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
|
|
96| return document.activeElement?.isContentEditable === true;
|
|
97|}
|
|
98|
|
|
99|// =========================================================================
|
|
100|// initSearch
|
|
101|// =========================================================================
|
|
102|
|
|
103|function initSearch() {
|
|
104| const input = document.getElementById("search-input");
|
|
105| if (!input) return;
|
|
106| const caseBtn = document.getElementById("search-case-btn");
|
|
107| const wordBtn = document.getElementById("search-word-btn");
|
|
108| const regexBtn = document.getElementById("search-regex-btn");
|
|
109| const filterBtn = document.getElementById("search-filter-btn");
|
|
110| const clearBtn = document.getElementById("search-clear-btn");
|
|
111| const filterRow = document.getElementById("search-filter-row");
|
|
112| const prevBtn = document.getElementById("search-prev-btn");
|
|
113| const nextBtn = document.getElementById("search-next-btn");
|
|
114| const countEl = document.getElementById("search-match-count");
|
|
115|
|
|
116| function _updateToggleUI() {
|
|
117| caseBtn.classList.toggle("active", state.searchCaseSensitive);
|
|
118| wordBtn.classList.toggle("active", state.searchWholeWord);
|
|
119| regexBtn.classList.toggle("active", state.searchRegex);
|
|
120| filterBtn.classList.toggle("active", state.searchFilterVisible);
|
|
121| }
|
|
122|
|
|
123| // Toggle buttons
|
|
124| caseBtn.addEventListener("click", () => { state.searchCaseSensitive = !state.searchCaseSensitive; _updateToggleUI(); _research(); });
|
|
125| if (wordBtn) wordBtn.addEventListener("click", () => { state.searchWholeWord = !state.searchWholeWord; _updateToggleUI(); _research(); });
|
|
126| if (regexBtn) regexBtn.addEventListener("click", () => { state.searchRegex = !state.searchRegex; _updateToggleUI(); _research(); });
|
|
127| if (filterBtn) filterBtn.addEventListener("click", () => { state.searchFilterVisible = !state.searchFilterVisible; if (filterRow) filterRow.style.display = searchFilterVisible ? "flex" : "none"; _updateToggleUI(); });
|
|
128|
|
|
129| // Result navigation (up/down arrows + Enter)
|
|
130| let _searchResultIdx = -1;
|
|
131| let _searchResultItems = [];
|
|
132|
|
|
133| function _updateResultHighlight() {
|
|
134| _searchResultItems.forEach((el, i) => {
|
|
135| el.classList.toggle("search-result-active", i === _searchResultIdx);
|
|
136| });
|
|
137| if (_searchResultIdx >= 0 && _searchResultIdx < _searchResultItems.length) {
|
|
138| _searchResultItems[_searchResultIdx].scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
139| }
|
|
140| const countEl = document.getElementById("search-match-count");
|
|
141| if (countEl) countEl.textContent = _searchResultIdx >= 0 ? `${_searchResultIdx + 1}/${_searchResultItems.length}` : `0/${_searchResultItems.length}`;
|
|
142| }
|
|
143|
|
|
144| function _refreshResultItems() {
|
|
145| _searchResultItems = Array.from(document.querySelectorAll(".search-result-item"));
|
|
146| _searchResultIdx = _searchResultItems.length > 0 ? 0 : -1;
|
|
147| _updateResultHighlight();
|
|
148| }
|
|
149|
|
|
150| window.navigateSearchResults = function(delta) {
|
|
151| _searchResultItems = Array.from(document.querySelectorAll(".search-result-item"));
|
|
152| if (_searchResultItems.length === 0) return;
|
|
153| _searchResultIdx = Math.max(0, Math.min(_searchResultItems.length - 1, _searchResultIdx + delta));
|
|
154| _updateResultHighlight();
|
|
155| };
|
|
156|
|
|
157| if (prevBtn) prevBtn.addEventListener("click", () => navigateSearchResults(-1));
|
|
158| if (nextBtn) nextBtn.addEventListener("click", () => navigateSearchResults(1));
|
|
159|
|
|
160| function _research() {
|
|
161| const q = input.value.trim();
|
|
162| if (q.length >= _getEffective("min_query_length", 2)) {
|
|
163| clearTimeout(state.searchTimeout);
|
|
164| state.searchTimeout = setTimeout(() => {
|
|
165| const vault = document.getElementById("vault-filter").value;
|
|
166| const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null;
|
|
167| state.advancedSearchOffset = 0;
|
|
168| performAdvancedSearch(q, vault, tagFilter);
|
|
169| }, _getEffective("debounce_ms", 300));
|
|
170| }
|
|
171| }
|
|
172|
|
|
173| // Keyboard shortcuts
|
|
174| document.addEventListener("keydown", (e) => {
|
|
175| if (e.altKey && !e.ctrlKey && !e.metaKey) {
|
|
176| if (e.key === "c" || e.key === "C") { e.preventDefault(); caseBtn.click(); }
|
|
177| else if (e.key === "w" || e.key === "W") { e.preventDefault(); if (wordBtn) wordBtn.click(); }
|
|
178| else if (e.key === "r" || e.key === "R") { e.preventDefault(); if (regexBtn) regexBtn.click(); }
|
|
179| else if (e.key === "f" || e.key === "F") { e.preventDefault(); if (filterBtn) filterBtn.click(); input.focus(); }
|
|
180| }
|
|
181| });
|
|
182|
|
|
183| // Initialize sub-controllers
|
|
184| AutocompleteDropdown.init();
|
|
185| SearchChips.init();
|
|
186|
|
|
187| // Initially hide clear button
|
|
188| if (clearBtn) clearBtn.style.display = "none";
|
|
189|
|
|
190| // Input handler: debounced search + autocomplete dropdown
|
|
191| input.addEventListener("input", () => {
|
|
192| const hasText = input.value.length > 0;
|
|
193| clearBtn.style.display = hasText ? "flex" : "none";
|
|
194|
|
|
195| // Show autocomplete dropdown while typing
|
|
196| AutocompleteDropdown.populate(input.value, input.selectionStart);
|
|
197|
|
|
198| // Debounced search execution
|
|
199| clearTimeout(state.searchTimeout);
|
|
200| state.searchTimeout = setTimeout(
|
|
201| () => {
|
|
202| const q = input.value.trim();
|
|
203| const vault = document.getElementById("vault-filter").value;
|
|
204| const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null;
|
|
205| state.advancedSearchOffset = 0;
|
|
206| if (q.length >= _getEffective("min_query_length", state.MIN_SEARCH_LENGTH) || tagFilter) {
|
|
207| performAdvancedSearch(q, vault, tagFilter);
|
|
208| } else if (q.length === 0) {
|
|
209| SearchChips.clear();
|
|
210| showWelcome();
|
|
211| }
|
|
212| },
|
|
213| _getEffective("debounce_ms", 300),
|
|
214| );
|
|
215| });
|
|
216|
|
|
217| // Focus handler: show history dropdown
|
|
218| input.addEventListener("focus", () => {
|
|
219| if (input.value.length === 0) {
|
|
220| const historyItems = SearchHistory.filter("").slice(0, 5);
|
|
221| if (historyItems.length > 0) {
|
|
222| AutocompleteDropdown.populate("", 0);
|
|
223| }
|
|
224| }
|
|
225| });
|
|
226|
|
|
227| // Keyboard navigation in dropdown
|
|
228| input.addEventListener("keydown", (e) => {
|
|
229| if (AutocompleteDropdown.isVisible()) {
|
|
230| if (e.key === "ArrowDown") {
|
|
231| e.preventDefault();
|
|
232| AutocompleteDropdown.navigateDown();
|
|
233| } else if (e.key === "ArrowUp") {
|
|
234| e.preventDefault();
|
|
235| AutocompleteDropdown.navigateUp();
|
|
236| } else if (e.key === "Enter") {
|
|
237| // First: check dropdown suggestions (higher priority than search results)
|
|
238| if (AutocompleteDropdown.isVisible() && AutocompleteDropdown.selectActive()) {
|
|
239| e.preventDefault();
|
|
240| return;
|
|
241| }
|
|
242| // Second: navigate search results if visible
|
|
243| const results = document.querySelectorAll(".search-result-item");
|
|
244| if (results.length > 0 && _searchResultIdx >= 0) {
|
|
245| const el = results[_searchResultIdx];
|
|
246| if (el) {
|
|
247| const vault = el.dataset.vault;
|
|
248| const path = el.dataset.path;
|
|
249| if (vault && path) { TabManager.openPreview(vault, path); e.preventDefault(); return; }
|
|
250| }
|
|
251| }
|
|
252| // Third: execute search
|
|
253| AutocompleteDropdown.hide();
|
|
254| const q = input.value.trim();
|
|
255| if (q) {
|
|
256| SearchHistory.add(q);
|
|
257| clearTimeout(state.searchTimeout);
|
|
258| state.advancedSearchOffset = 0;
|
|
259| const vault = document.getElementById("vault-filter").value;
|
|
260| const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null;
|
|
261| performAdvancedSearch(q, vault, tagFilter);
|
|
262| }
|
|
263| e.preventDefault();
|
|
264| } else if (e.key === "ArrowDown" && !AutocompleteDropdown.isVisible()) {
|
|
265| // Navigate search results when dropdown is closed
|
|
266| if (window.navigateSearchResults) { window.navigateSearchResults(1); e.preventDefault(); }
|
|
267| } else if (e.key === "ArrowUp" && !AutocompleteDropdown.isVisible()) {
|
|
268| if (window.navigateSearchResults) { window.navigateSearchResults(-1); e.preventDefault(); }
|
|
269| } else if (e.key === "Escape") {
|
|
270| AutocompleteDropdown.hide();
|
|
271| e.stopPropagation();
|
|
272| }
|
|
273| } else if (e.key === "Enter") {
|
|
274| if (AutocompleteDropdown.isVisible() && AutocompleteDropdown.selectActive()) {
|
|
275| e.preventDefault();
|
|
276| return;
|
|
277| }
|
|
278| const q = input.value.trim();
|
|
279| if (q) {
|
|
280| SearchHistory.add(q);
|
|
281| clearTimeout(state.searchTimeout);
|
|
282| state.advancedSearchOffset = 0;
|
|
283| const vault = document.getElementById("vault-filter").value;
|
|
284| const tagFilter = state.selectedTags.length > 0 ? state.selectedTags.join(",") : null;
|
|
285| performAdvancedSearch(q, vault, tagFilter);
|
|
286| }
|
|
287| e.preventDefault();
|
|
288| }
|
|
289| });
|
|
290|
|
|
291| clearBtn.addEventListener("click", () => {
|
|
292| input.value = "";
|
|
293| if (clearBtn) clearBtn.style.display = "none";
|
|
294| state.searchCaseSensitive = false;
|
|
295| state.searchWholeWord = false;
|
|
296| state.searchRegex = false;
|
|
297| _updateToggleUI();
|
|
298| SearchChips.clear();
|
|
299| AutocompleteDropdown.hide();
|
|
300| showWelcome();
|
|
301| });
|
|
302|
|
|
303| // Global keyboard shortcuts
|
|
304| document.addEventListener("keydown", (e) => {
|
|
305| // Ctrl+K or Cmd+K: focus search
|
|
306| if ((e.ctrlKey || e.metaKey) && e.key === "k") {
|
|
307| e.preventDefault();
|
|
308| input.focus();
|
|
309| input.select();
|
|
310| }
|
|
311| // "/" key: focus search (when not in an input/textarea)
|
|
312| if (e.key === "/" && !_isInputFocused()) {
|
|
313| e.preventDefault();
|
|
314| input.focus();
|
|
315| }
|
|
316| // Escape: blur search input and close dropdown
|
|
317| if (e.key === "Escape" && document.activeElement === input) {
|
|
318| AutocompleteDropdown.hide();
|
|
319| input.blur();
|
|
320| }
|
|
321| });
|
|
322|}
|
|
323|
|
|
324|// =========================================================================
|
|
325|// showWelcome
|
|
326|// =========================================================================
|
|
327|
|
|
328|function showWelcome() {
|
|
329| hideProgressBar();
|
|
330|
|
|
331| // Restore or rebuild the dashboard with tabbed sections
|
|
332| const area = document.getElementById("content-area");
|
|
333| const home = document.getElementById("dashboard-home");
|
|
334|
|
|
335| if (area && !home) {
|
|
336| area.innerHTML = `
|
|
337| <div id="dashboard-home" class="dashboard-home" role="region" aria-label="Tableau de bord">
|
|
338| <!-- Dashboard Tabs -->
|
|
339| <div class="dashboard-tabs">
|
|
340| <button class="dashboard-tab active" data-tab="stats">
|
|
341| <i data-lucide="bar-chart-3" style="width:14px;height:14px"></i> Statistiques
|
|
342| </button>
|
|
343| <button class="dashboard-tab" data-tab="bookmarks">
|
|
344| <i data-lucide="bookmark" style="width:14px;height:14px"></i> Bookmarks
|
|
345| </button>
|
|
346| <button class="dashboard-tab" data-tab="recent">
|
|
347| <i data-lucide="clock" style="width:14px;height:14px"></i> Récents
|
|
348| </button>
|
|
349| <button class="dashboard-tab" data-tab="shared">
|
|
350| <i data-lucide="share-2" style="width:14px;height:14px"></i> Partagés
|
|
351| </button>
|
|
352| </div>
|
|
353|
|
|
354| <!-- Stats Panel -->
|
|
355| <div id="dashboard-panel-stats" class="dashboard-panel active">
|
|
356| <div id="dashboard-stats-grid" class="dashboard-stats-grid">
|
|
357| <div class="dashboard-stats-loading">Chargement...</div>
|
|
358| </div>
|
|
359| <div id="dashboard-conflicts-container" style="margin-top:16px"></div>
|
|
360| </div>
|
|
361|
|
|
362| <!-- Bookmarks Panel -->
|
|
363| <div id="dashboard-panel-bookmarks" class="dashboard-panel">
|
|
364| <div id="dashboard-bookmarks-grid" class="dashboard-recent-grid"></div>
|
|
365| <div id="dashboard-bookmarks-empty" class="dashboard-recent-empty">
|
|
366| <i data-lucide="pin"></i>
|
|
367| <span>Aucun bookmark</span>
|
|
368| <p>Épinglez des fichiers pour les retrouver ici.</p>
|
|
369| </div>
|
|
370| </div>
|
|
371|
|
|
372| <!-- Recent Panel -->
|
|
373| <div id="dashboard-panel-recent" class="dashboard-panel">
|
|
374| <div class="dashboard-header">
|
|
375| <div class="dashboard-title-row">
|
|
376| <span id="dashboard-count" class="dashboard-badge"></span>
|
|
377| </div>
|
|
378| </div>
|
|
379| <div id="dashboard-recent-grid" class="dashboard-recent-grid"></div>
|
|
380| <div id="dashboard-loading" class="dashboard-loading">
|
|
381| <div class="skeleton-card"></div><div class="skeleton-card"></div><div class="skeleton-card"></div>
|
|
382| <div class="skeleton-card"></div><div class="skeleton-card"></div><div class="skeleton-card"></div>
|
|
383| </div>
|
|
384| <div id="dashboard-recent-empty" class="dashboard-recent-empty hidden">
|
|
385| <i data-lucide="inbox"></i>
|
|
386| <span>Aucun fichier récent</span>
|
|
387| <p>Ouvrez un fichier pour le voir apparaître ici</p>
|
|
388| </div>
|
|
389| </div>
|
|
390|
|
|
391| <!-- Shared Panel -->
|
|
392| <div id="dashboard-panel-shared" class="dashboard-panel">
|
|
393| <div id="dashboard-shared-grid" class="dashboard-recent-grid"></div>
|
|
394| <div id="dashboard-shared-empty" class="dashboard-recent-empty">
|
|
395| <i data-lucide="share-2"></i>
|
|
396| <span>Aucun document partagé</span>
|
|
397| <p>Partagez un document pour le voir apparaître ici</p>
|
|
398| </div>
|
|
399| </div>
|
|
400| </div>`;
|
|
401|
|
|
402| // Re-initialize widgets and dashboard tabs
|
|
403| DashboardRecentWidget.init();
|
|
404| initDashboardTabs();
|
|
405| safeCreateIcons();
|
|
406| } else if (home) {
|
|
407| // Dashboard already exists, show it with default tab
|
|
408| home.style.display = "";
|
|
409| // Reset tabs to default
|
|
410| document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active"));
|
|
411| document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active"));
|
|
412| const defaultTab = document.querySelector('.dashboard-tab[data-tab="stats"]');
|
|
413| const defaultPanel = document.getElementById("dashboard-panel-stats");
|
|
414| if (defaultTab) defaultTab.classList.add("active");
|
|
415| if (defaultPanel) defaultPanel.classList.add("active");
|
|
416| }
|
|
417|
|
|
418| // Load all widgets (they handle missing elements gracefully)
|
|
419| if (typeof DashboardStatsWidget !== "undefined") {
|
|
420| DashboardStatsWidget.load();
|
|
421| }
|
|
422| if (typeof DashboardConflictsWidget !== "undefined") {
|
|
423| DashboardConflictsWidget.load();
|
|
424| }
|
|
425| DashboardRecentWidget.load(state.selectedContextVault);
|
|
426| if (typeof DashboardBookmarkWidget !== "undefined") {
|
|
427| DashboardBookmarkWidget.load(state.selectedContextVault);
|
|
428| }
|
|
429| if (typeof DashboardSharedWidget !== "undefined") {
|
|
430| DashboardSharedWidget.load();
|
|
431| }
|
|
432|
|
|
433| // Load saved searches sidebar
|
|
434| loadSavedSearches();
|
|
435|}
|
|
436|
|
|
437|// =========================================================================
|
|
438|// goHome
|
|
439|// =========================================================================
|
|
440|
|
|
441|function goHome() {
|
|
442| const searchInput = document.getElementById("search-input");
|
|
443| if (searchInput) searchInput.value = "";
|
|
444|
|
|
445| document.querySelectorAll(".tree-item.active").forEach((el) => el.classList.remove("active"));
|
|
446|
|
|
447| state.currentVault = null;
|
|
448| state.currentPath = null;
|
|
449| state.showingSource = false;
|
|
450| state.cachedRawSource = null;
|
|
451|
|
|
452| closeMobileSidebar();
|
|
453| showWelcome();
|
|
454|}
|
|
455|
|
|
456|// =========================================================================
|
|
457|// loadSavedSearches (needed by showWelcome)
|
|
458|// =========================================================================
|
|
459|
|
|
460|async function loadSavedSearches() {
|
|
461| const list = document.getElementById("saved-searches-list");
|
|
462| const empty = document.getElementById("saved-searches-empty");
|
|
463| if (!list) return;
|
|
464| try {
|
|
465| const searches = await api("/api/saved-searches");
|
|
466| if (!searches.length) {
|
|
467| list.innerHTML = "";
|
|
468| if (empty) empty.style.display = "";
|
|
469| return;
|
|
470| }
|
|
471| if (empty) empty.style.display = "none";
|
|
472| list.innerHTML = searches.map(s => {
|
|
473| const badges = [];
|
|
474| if (s.case_sensitive) badges.push('<span class="search-filter-badge">Aa</span>');
|
|
475| if (s.whole_word) badges.push('<span class="search-filter-badge">wd</span>');
|
|
476| if (s.regex) badges.push('<span class="search-filter-badge">.*</span>');
|
|
477| const pathFilters = [];
|
|
478| if (s.include_paths) pathFilters.push(`<span class="saved-search-path" title="Inclure: ${escapeHtml(s.include_paths)}">\u{1F4E5} ${escapeHtml(s.include_paths)}</span>`);
|
|
479| if (s.exclude_paths) pathFilters.push(`<span class="saved-search-path" title="Exclure: ${escapeHtml(s.exclude_paths)}">\u{1F4E4} ${escapeHtml(s.exclude_paths)}</span>`);
|
|
480| const vaultStr = s.vault && s.vault !== "all" ? `<span class="saved-search-vault">\u{1F4C1} ${escapeHtml(s.vault)}</span>` : "";
|
|
481| return `
|
|
482| <div class="saved-search-item">
|
|
483| <div class="saved-search-query">${escapeHtml(s.query)}</div>
|
|
484| <div class="saved-search-meta">
|
|
485| ${badges.join("")}
|
|
486| ${vaultStr}
|
|
487| </div>
|
|
488| ${pathFilters.length ? '<div class="saved-search-filters">' + pathFilters.join(" ") + '</div>' : ""}
|
|
489| <button class="saved-search-delete" data-id="${s.id}" title="Supprimer"><E2><9C><95></button>
|
|
490| </div>
|
|
491| `}).join("");
|
|
492| list.querySelectorAll(".saved-search-item").forEach(item => {
|
|
493| item.addEventListener("click", (e) => {
|
|
494| if (e.target.classList.contains("saved-search-delete")) return;
|
|
495| const idx = Array.from(list.children).indexOf(item);
|
|
496| const s = searches[idx];
|
|
497| if (!s) return;
|
|
498| // Apply the saved search
|
|
499| const input = document.getElementById("search-input");
|
|
500| if (input) input.value = s.query;
|
|
501| state.searchCaseSensitive = s.case_sensitive || false;
|
|
502| state.searchWholeWord = s.whole_word || false;
|
|
503| state.searchRegex = s.regex || false;
|
|
504| if (typeof _updateToggleUI === "function") _updateToggleUI();
|
|
505| if (s.include_paths) {
|
|
506| const incl = document.getElementById("search-include-input");
|
|
507| if (incl) incl.value = s.include_paths;
|
|
508| }
|
|
509| if (s.exclude_paths) {
|
|
510| const excl = document.getElementById("search-exclude-input");
|
|
511| if (excl) excl.value = s.exclude_paths;
|
|
512| }
|
|
513| // Execute the search
|
|
514| AutocompleteDropdown.hide();
|
|
515| AutocompleteDropdown._suppressNext = true;
|
|
516| const vault = s.vault || "all";
|
|
517| if (input) { input.dispatchEvent(new Event("input")); }
|
|
518| clearTimeout(state.searchTimeout);
|
|
519| state.advancedSearchOffset = 0;
|
|
520| performAdvancedSearch(s.query, vault, null);
|
|
521| });
|
|
522| });
|
|
523| list.querySelectorAll(".saved-search-delete").forEach(b => b.addEventListener("click", async (e) => {
|
|
524| e.stopPropagation();
|
|
525| await api(`/api/saved-searches/${b.dataset.id}`, { method: "DELETE" });
|
|
526| loadSavedSearches();
|
|
527| }));
|
|
528| safeCreateIcons();
|
|
529| } catch (err) { /* silently ignore */ }
|
|
530|}
|
|
531|
|
|
532|// =========================================================================
|
|
533|// initDashboardTabs (needed by showWelcome)
|
|
534|// =========================================================================
|
|
535|
|
|
536|function initDashboardTabs() {
|
|
537| document.querySelectorAll(".dashboard-tab").forEach(tab => {
|
|
538| // Remove existing listeners by cloning
|
|
539| const newTab = tab.cloneNode(true);
|
|
540| tab.parentNode.replaceChild(newTab, tab);
|
|
541| newTab.addEventListener("click", function() {
|
|
542| document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active"));
|
|
543| document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active"));
|
|
544| this.classList.add("active");
|
|
545| const panel = document.getElementById("dashboard-panel-" + this.dataset.tab);
|
|
546| if (panel) panel.classList.add("active");
|
|
547| });
|
|
548| });
|
|
549|}
|
|
550|
|
|
551|// =========================================================================
|
|
552|// Exports
|
|
553|// =========================================================================
|
|
554|
|
|
555|export {
|
|
556| loadVaultSettings,
|
|
557| initSearch,
|
|
558| showWelcome,
|
|
559| goHome,
|
|
560|};
|
|
561| |