docs: remove deprecated integration checklists and documentation files

This commit is contained in:
2025-10-05 10:31:50 -04:00
parent 8c5394ed64
commit c0ebfcf5b9
30 changed files with 3976 additions and 259 deletions
@@ -4,17 +4,23 @@ import {
Output,
EventEmitter,
signal,
computed,
OnInit,
inject,
ChangeDetectionStrategy
ChangeDetectionStrategy,
effect,
untracked
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { SearchBarComponent } from '../search-bar/search-bar.component';
import { SearchResultsComponent } from '../search-results/search-results.component';
import { SearchEvaluatorService, SearchResult } from '../../core/search/search-evaluator.service';
import { SearchOrchestratorService, SearchResult } from '../../core/search/search-orchestrator.service';
import { SearchIndexService } from '../../core/search/search-index.service';
import { SearchPreferencesService } from '../../core/search/search-preferences.service';
import { SearchOptions } from '../../core/search/search-parser.types';
import { VaultService } from '../../services/vault.service';
import { ClientLoggingService } from '../../services/client-logging.service';
/**
* Complete search panel with bar and results
@@ -23,7 +29,7 @@ import { VaultService } from '../../services/vault.service';
@Component({
selector: 'app-search-panel',
standalone: true,
imports: [CommonModule, SearchBarComponent, SearchResultsComponent],
imports: [CommonModule, FormsModule, SearchBarComponent, SearchResultsComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="flex flex-col h-full bg-bg-primary dark:bg-gray-900">
@@ -39,6 +45,56 @@ import { VaultService } from '../../services/vault.service';
/>
</div>
<!-- Search options toggles -->
@if (hasSearched() && results().length > 0) {
<div class="flex flex-col gap-3 p-4 border-b border-border dark:border-gray-700 bg-bg-muted dark:bg-gray-800">
<!-- Collapse results toggle -->
<label class="flex items-center justify-between cursor-pointer group">
<span class="text-sm text-text-main dark:text-gray-200">Collapse results</span>
<div class="relative">
<input
type="checkbox"
[(ngModel)]="collapseResults"
(change)="onToggleCollapse()"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-300 dark:bg-gray-600 rounded-full peer peer-checked:bg-blue-500 dark:peer-checked:bg-blue-600 transition-colors"></div>
<div class="absolute left-1 top-1 w-4 h-4 bg-white rounded-full transition-transform peer-checked:translate-x-5"></div>
</div>
</label>
<!-- Show more context toggle -->
<label class="flex items-center justify-between cursor-pointer group">
<span class="text-sm text-text-main dark:text-gray-200">Show more context</span>
<div class="relative">
<input
type="checkbox"
[(ngModel)]="showMoreContext"
(change)="onToggleContext()"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-300 dark:bg-gray-600 rounded-full peer peer-checked:bg-blue-500 dark:peer-checked:bg-blue-600 transition-colors"></div>
<div class="absolute left-1 top-1 w-4 h-4 bg-white rounded-full transition-transform peer-checked:translate-x-5"></div>
</div>
</label>
<!-- Explain search terms toggle -->
<label class="flex items-center justify-between cursor-pointer group">
<span class="text-sm text-text-main dark:text-gray-200">Explain search terms</span>
<div class="relative">
<input
type="checkbox"
[(ngModel)]="explainSearchTerms"
(change)="onToggleExplain()"
class="sr-only peer"
/>
<div class="w-11 h-6 bg-gray-300 dark:bg-gray-600 rounded-full peer peer-checked:bg-blue-500 dark:peer-checked:bg-blue-600 transition-colors"></div>
<div class="absolute left-1 top-1 w-4 h-4 bg-white rounded-full transition-transform peer-checked:translate-x-5"></div>
</div>
</label>
</div>
}
<!-- Search results -->
<div class="flex-1 overflow-hidden">
@if (isSearching()) {
@@ -59,6 +115,9 @@ import { VaultService } from '../../services/vault.service';
} @else if (results().length > 0) {
<app-search-results
[results]="results()"
[collapseAll]="collapseResults"
[showMoreContext]="showMoreContext"
[contextLines]="contextLines()"
(noteOpen)="onNoteOpen($event)"
/>
} @else {
@@ -85,21 +144,51 @@ export class SearchPanelComponent implements OnInit {
@Output() noteOpen = new EventEmitter<{ noteId: string; line?: number }>();
private searchEvaluator = inject(SearchEvaluatorService);
private orchestrator = inject(SearchOrchestratorService);
private searchIndex = inject(SearchIndexService);
private vaultService = inject(VaultService);
private preferences = inject(SearchPreferencesService);
private logger = inject(ClientLoggingService);
results = signal<SearchResult[]>([]);
isSearching = signal(false);
hasSearched = signal(false);
currentQuery = signal('');
ngOnInit(): void {
// Build search index from vault notes
this.rebuildIndex();
private lastOptions: SearchOptions = {};
// Rebuild index when vault changes (could be optimized with incremental updates)
// For now, we'll rebuild on init
// UI toggles
collapseResults = false;
showMoreContext = false;
explainSearchTerms = false;
// Computed context lines based on showMoreContext
contextLines = computed(() => this.showMoreContext ? 5 : 2);
private syncIndexEffect = effect(() => {
const notes = this.vaultService.allNotes();
this.logger.info('SearchPanel', 'Detected notes change, rebuilding index', {
context: this.context,
noteCount: notes.length
});
this.searchIndex.rebuildIndex(notes);
const query = untracked(() => this.currentQuery());
if (query && query.trim()) {
this.logger.debug('SearchPanel', 'Re-running search after index rebuild', {
query,
context: this.context
});
this.executeSearch(query);
}
}, { allowSignalWrites: true });
ngOnInit(): void {
// Load preferences for this context
const prefs = this.preferences.getPreferences(this.context);
this.collapseResults = prefs.collapseResults;
this.showMoreContext = prefs.showMoreContext;
this.explainSearchTerms = prefs.explainSearchTerms;
}
/**
@@ -110,29 +199,46 @@ export class SearchPanelComponent implements OnInit {
this.searchIndex.rebuildIndex(notes);
}
/**
* Handle search execution
*/
onSearch(event: { query: string; options: SearchOptions }): void {
const { query, options } = event;
private executeSearch(query: string, options?: SearchOptions): void {
const trimmed = query?.trim() ?? '';
if (!query || !query.trim()) {
if (!trimmed) {
this.logger.debug('SearchPanel', 'Clearing search (empty query)');
this.isSearching.set(false);
this.results.set([]);
this.hasSearched.set(false);
return;
}
const baseOptions = options
? { ...this.lastOptions, ...options }
: { ...this.lastOptions };
this.logger.info('SearchPanel', 'Executing search', {
query: trimmed,
options: baseOptions,
contextLines: this.contextLines(),
context: this.context
});
this.isSearching.set(true);
this.currentQuery.set(query);
// Execute search asynchronously to avoid blocking UI
setTimeout(() => {
try {
const searchResults = this.searchEvaluator.search(query, options);
const searchResults = this.orchestrator.execute(trimmed, {
...baseOptions,
contextLines: this.contextLines()
});
this.logger.info('SearchPanel', 'Search completed', {
query: trimmed,
resultCount: searchResults.length
});
this.results.set(searchResults);
this.hasSearched.set(true);
} catch (error) {
console.error('Search error:', error);
this.logger.error('SearchPanel', 'Search execution error', {
error: error instanceof Error ? error.message : String(error)
});
this.results.set([]);
this.hasSearched.set(true);
} finally {
@@ -141,6 +247,17 @@ export class SearchPanelComponent implements OnInit {
}, 0);
}
/**
* Handle search execution
*/
onSearch(event: { query: string; options: SearchOptions }): void {
const { query, options } = event;
this.currentQuery.set(query);
this.lastOptions = { ...options };
this.executeSearch(query, options);
}
/**
* Handle query change (for live search if needed)
*/
@@ -166,10 +283,39 @@ export class SearchPanelComponent implements OnInit {
// Re-run current search if there is one
if (this.currentQuery()) {
this.onSearch({
query: this.currentQuery(),
options: {}
});
this.executeSearch(this.currentQuery());
}
}
/**
* Toggle collapse results
*/
onToggleCollapse(): void {
this.preferences.updatePreferences(this.context, {
collapseResults: this.collapseResults
});
}
/**
* Toggle show more context
*/
onToggleContext(): void {
this.preferences.updatePreferences(this.context, {
showMoreContext: this.showMoreContext
});
// Re-run search with new context lines
if (this.currentQuery()) {
this.executeSearch(this.currentQuery());
}
}
/**
* Toggle explain search terms
*/
onToggleExplain(): void {
this.preferences.updatePreferences(this.context, {
explainSearchTerms: this.explainSearchTerms
});
}
}
@@ -5,12 +5,14 @@ import {
EventEmitter,
signal,
computed,
effect,
ChangeDetectionStrategy,
inject
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { SearchResult, SearchMatch } from '../../core/search/search-evaluator.service';
import { SearchResult, SearchMatch } from '../../core/search/search-orchestrator.service';
import { SearchHighlighterService } from '../../core/search/search-highlighter.service';
import { VaultService } from '../../services/vault.service';
/**
@@ -24,6 +26,7 @@ interface ResultGroup {
matchCount: number;
isExpanded: boolean;
score: number;
allRanges: any[];
}
/**
@@ -67,15 +70,6 @@ type SortOption = 'relevance' | 'name' | 'modified';
<option value="name">Name</option>
<option value="modified">Modified</option>
</select>
<!-- Expand/Collapse all -->
<button
(click)="toggleAllGroups()"
class="btn-standard-xs"
title="Expand/Collapse all"
>
{{ allExpanded() ? 'Collapse all' : 'Expand all' }}
</button>
</div>
</div>
@@ -164,8 +158,8 @@ type SortOption = 'relevance' | 'name' | 'modified';
</div>
<!-- Match context with highlighting -->
<div class="text-sm text-text-main dark:text-gray-200 font-mono leading-relaxed">
<span [innerHTML]="highlightMatch(match.context, match.text)"></span>
<div class="text-sm text-text-main dark:text-gray-200 font-mono leading-relaxed whitespace-pre-wrap">
<span [innerHTML]="highlightMatch(match)"></span>
</div>
</div>
}
@@ -205,16 +199,32 @@ type SortOption = 'relevance' | 'name' | 'modified';
})
export class SearchResultsComponent {
@Input() set results(value: SearchResult[]) {
this._results.set(value);
this.buildGroups(value);
}
@Input() collapseAll: boolean = false;
@Input() showMoreContext: boolean = false;
@Input() contextLines: number = 2;
@Output() noteOpen = new EventEmitter<{ noteId: string; line?: number }>();
private vaultService = inject(VaultService);
private highlighter = inject(SearchHighlighterService);
private _results = signal<SearchResult[]>([]);
private groups = signal<ResultGroup[]>([]);
sortBy: SortOption = 'relevance';
constructor() {
// Watch for collapseAll changes
effect(() => {
if (this.collapseAll !== undefined) {
this.applyCollapseAll(this.collapseAll);
}
});
}
/**
* Total number of result files
*/
@@ -273,14 +283,24 @@ export class SearchResultsComponent {
filePath,
matches: result.matches,
matchCount: result.matches.length,
isExpanded: true, // Expand by default
score: result.score
isExpanded: !this.collapseAll, // Respect collapseAll setting
score: result.score,
allRanges: result.allRanges || []
};
});
this.groups.set(groups);
}
/**
* Apply collapse all setting to all groups
*/
private applyCollapseAll(collapse: boolean): void {
this.groups.update(groups => {
return groups.map(g => ({ ...g, isExpanded: !collapse }));
});
}
/**
* Toggle a result group
*/
@@ -295,7 +315,7 @@ export class SearchResultsComponent {
}
/**
* Toggle all groups
* Toggle all groups (not used when controlled by parent)
*/
toggleAllGroups(): void {
const shouldExpand = !this.allExpanded();
@@ -320,30 +340,24 @@ export class SearchResultsComponent {
}
/**
* Highlight matched text in context
* Highlight matched text in context using ranges
*/
highlightMatch(context: string, matchText: string): string {
if (!matchText || !context) {
return this.escapeHtml(context);
highlightMatch(match: SearchMatch): string {
if (!match || !match.context) {
return '';
}
const escapedContext = this.escapeHtml(context);
const escapedMatch = this.escapeHtml(matchText);
// Case-insensitive replacement with highlighting
const regex = new RegExp(`(${escapedMatch})`, 'gi');
return escapedContext.replace(
regex,
'<mark class="bg-yellow-200 dark:bg-yellow-600 text-text-main dark:text-gray-900 px-0.5 rounded">$1</mark>'
);
}
// If we have ranges, use them for precise highlighting
if (match.ranges && match.ranges.length > 0) {
return this.highlighter.highlightWithRanges(match.context, match.ranges);
}
/**
* Escape HTML to prevent XSS
*/
private escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
// Fallback to simple text-based highlighting
if (match.text) {
return this.highlighter.highlightMatches(match.context, [match.text], false);
}
// No highlighting needed
return this.highlighter.highlightMatches(match.context, [], false);
}
}