feat(frontend): search results show deleted, edited and earlier version marks with highlighted words, filter chips live in the url, results load on scroll

This commit is contained in:
hh committed 2026-10-19 03:41:44 +00:00
1 parent d45c96c259
commit 9a6bd8a714
15 files changed
+1430 -228

No files matched your search

+58
View File
@@ -0,0 +1,58 @@
export interface Segment {
hit: boolean;
text: string;
}
const WORD = /[\p{L}\p{N}_]+/gu;
const SPACE = /\s/;
const PREFIX_MIN = 3;
const REACH = 20;
const LEAD = 10;
function fold(word: string): string {
return word.toLowerCase().replaceAll("ё", "е");
}
function matches(word: string, terms: string[]): boolean {
const folded = fold(word);
return terms.some((term) =>
term.length >= PREFIX_MIN ? folded.startsWith(term) : folded === term
);
}
function windowed(text: string, terms: string[]): string {
for (const match of text.matchAll(WORD)) {
if (!matches(match[0], terms)) {
continue;
}
if (match.index <= REACH) {
return text;
}
let start = match.index - LEAD;
while (start > 0 && !SPACE.test(text[start - 1])) {
start -= 1;
}
return `…${text.slice(start)}`;
}
return text;
}
export function highlight(text: string, terms: string[]): Segment[] {
const shown = windowed(text, terms);
const segments: Segment[] = [];
let last = 0;
for (const match of shown.matchAll(WORD)) {
if (!matches(match[0], terms)) {
continue;
}
if (match.index > last) {
segments.push({ hit: false, text: shown.slice(last, match.index) });
}
segments.push({ hit: true, text: match[0] });
last = match.index + match[0].length;
}
if (last < shown.length) {
segments.push({ hit: false, text: shown.slice(last) });
}
return segments;
}