ObsiViewer/src/core/search/search-preferences.service.ts

194 lines
4.8 KiB
TypeScript

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<Map<string, SearchPreferences>>(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<SearchPreferences>): 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<SearchPreferences, 'caseSensitive' | 'regexMode' | 'collapseResults' | 'showMoreContext' | 'explainSearchTerms'>
): 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<string, SearchPreferences>();
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<string, SearchPreferences> = {};
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<string, SearchPreferences> = {};
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<string, SearchPreferences>();
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;
}
}
}