import { Injectable, signal, computed } from '@angular/core'; /** * Search preferences per context */ export interface SearchPreferences { /** Case sensitive search */ caseSensitive: boolean; /** Regex mode enabled */ regexMode: boolean; /** Collapse all results by default */ collapseResults: boolean; /** Show more context around matches */ showMoreContext: boolean; /** Number of context lines (when showMoreContext is true) */ contextLines: number; /** Explain search terms */ explainSearchTerms: boolean; } /** * Default preferences */ const DEFAULT_PREFERENCES: SearchPreferences = { caseSensitive: false, regexMode: false, collapseResults: false, showMoreContext: false, contextLines: 2, explainSearchTerms: false }; /** * Search preferences service * Manages persistent search preferences per context (vault, header, graph) */ @Injectable({ providedIn: 'root' }) export class SearchPreferencesService { private readonly STORAGE_KEY = 'obsiviewer_search_preferences'; // Preferences by context private preferencesMap = signal>(new Map()); constructor() { this.loadFromStorage(); } /** * Get preferences for a specific context */ getPreferences(context: string = 'vault'): SearchPreferences { const prefs = this.preferencesMap().get(context); return prefs ? { ...prefs } : { ...DEFAULT_PREFERENCES }; } /** * Get preferences signal for a specific context */ getPreferencesSignal(context: string = 'vault') { return computed(() => { const prefs = this.preferencesMap().get(context); return prefs ? { ...prefs } : { ...DEFAULT_PREFERENCES }; }); } /** * Update preferences for a specific context */ updatePreferences(context: string, updates: Partial): void { this.preferencesMap.update(map => { const current = map.get(context) || { ...DEFAULT_PREFERENCES }; const updated = { ...current, ...updates }; map.set(context, updated); return new Map(map); }); this.saveToStorage(); } /** * Toggle a boolean preference */ togglePreference( context: string, key: keyof Pick ): void { const current = this.getPreferences(context); this.updatePreferences(context, { [key]: !current[key] }); } /** * Set context lines */ setContextLines(context: string, lines: number): void { this.updatePreferences(context, { contextLines: Math.max(0, Math.min(10, lines)) }); } /** * Reset preferences for a context */ resetPreferences(context: string): void { this.preferencesMap.update(map => { map.set(context, { ...DEFAULT_PREFERENCES }); return new Map(map); }); this.saveToStorage(); } /** * Reset all preferences */ resetAllPreferences(): void { this.preferencesMap.set(new Map()); this.saveToStorage(); } /** * Load preferences from localStorage */ private loadFromStorage(): void { try { const stored = localStorage.getItem(this.STORAGE_KEY); if (stored) { const data = JSON.parse(stored); const map = new Map(); for (const [context, prefs] of Object.entries(data)) { map.set(context, { ...DEFAULT_PREFERENCES, ...(prefs as any) }); } this.preferencesMap.set(map); } } catch (e) { console.error('Failed to load search preferences:', e); } } /** * Save preferences to localStorage */ private saveToStorage(): void { try { const data: Record = {}; this.preferencesMap().forEach((prefs, context) => { data[context] = prefs; }); localStorage.setItem(this.STORAGE_KEY, JSON.stringify(data)); } catch (e) { console.error('Failed to save search preferences:', e); } } /** * Export preferences as JSON */ exportPreferences(): string { const data: Record = {}; this.preferencesMap().forEach((prefs, context) => { data[context] = prefs; }); return JSON.stringify(data, null, 2); } /** * Import preferences from JSON */ importPreferences(json: string): boolean { try { const data = JSON.parse(json); const map = new Map(); for (const [context, prefs] of Object.entries(data)) { map.set(context, { ...DEFAULT_PREFERENCES, ...(prefs as any) }); } this.preferencesMap.set(map); this.saveToStorage(); return true; } catch (e) { console.error('Failed to import search preferences:', e); return false; } } }