feat: add Meilisearch backend integration with Docker Compose setup and Excalidraw support

This commit is contained in:
2025-10-14 10:41:15 -04:00
parent b359e8ab8e
commit 32a9998b40
71 changed files with 11544 additions and 435 deletions
@@ -5,9 +5,10 @@ import {
EventEmitter,
signal,
computed,
effect,
ChangeDetectionStrategy,
inject
inject,
OnChanges,
SimpleChanges
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
@@ -197,7 +198,7 @@ type SortOption = 'relevance' | 'name' | 'modified';
}
`]
})
export class SearchResultsComponent {
export class SearchResultsComponent implements OnChanges {
@Input() set results(value: SearchResult[]) {
this._results.set(value);
this.buildGroups(value);
@@ -206,6 +207,7 @@ export class SearchResultsComponent {
@Input() collapseAll: boolean = false;
@Input() showMoreContext: boolean = false;
@Input() contextLines: number = 2;
@Input() highlight: boolean = true;
@Output() noteOpen = new EventEmitter<{ noteId: string; line?: number }>();
@@ -216,13 +218,12 @@ export class SearchResultsComponent {
private groups = signal<ResultGroup[]>([]);
sortBy: SortOption = 'relevance';
constructor() {
// Watch for collapseAll changes
effect(() => {
if (this.collapseAll !== undefined) {
this.applyCollapseAll(this.collapseAll);
}
});
constructor() {}
ngOnChanges(changes: SimpleChanges): void {
if (changes['collapseAll'] && !changes['collapseAll'].firstChange) {
this.applyCollapseAll(this.collapseAll);
}
}
/**
@@ -347,6 +348,19 @@ export class SearchResultsComponent {
return '';
}
// If highlight is disabled, strip any pre-highlighted HTML and return plain text
if (!this.highlight) {
return this.highlighter.stripHtml(match.context);
}
// If context already contains highlight markup from Meilisearch
// (e.g., <mark> or <em> tags), return it as-is to preserve server-side highlights.
// We purposely avoid escaping HTML here because it is produced by our backend with safe tags.
const hasPreHighlightedHtml = /<(mark|em)>/i.test(match.context);
if (hasPreHighlightedHtml) {
return match.context;
}
// If we have ranges, use them for precise highlighting
if (match.ranges && match.ranges.length > 0) {
return this.highlighter.highlightWithRanges(match.context, match.ranges);