Files
chatpapers/src/modules/lecture/application/matchPreparedSentence.ts
T

88 lines
2.6 KiB
TypeScript

import type {
StoredParagraph,
StoredSentence,
} from "../infrastructure/storage/lectureStore";
export function normalizeSelectionText(text: string): string {
return text
.replace(/[\u0000-\u001f\f]/g, " ")
.replace(/\s+/g, " ")
.trim()
.toLowerCase();
}
export interface PreparedSentenceMatch {
paragraph: StoredParagraph;
sentence: StoredSentence;
}
function overlapScore(a: string, b: string): number {
const na = normalizeSelectionText(a);
const nb = normalizeSelectionText(b);
if (!na || !nb) return 0;
if (na === nb) return 100;
if (na.includes(nb) || nb.includes(na)) {
const shorter = Math.min(na.length, nb.length);
const longer = Math.max(na.length, nb.length);
return Math.round((shorter / longer) * 90);
}
const aWords = new Set(na.split(/\s+/).filter((w) => w.length > 2));
const bWords = nb.split(/\s+/).filter((w) => w.length > 2);
if (!aWords.size || !bWords.length) return 0;
let shared = 0;
for (const w of bWords) {
if (aWords.has(w)) shared += 1;
}
return Math.round((shared / bWords.length) * 70);
}
export function findMatchingPreparedSentence(
paragraphs: StoredParagraph[] | undefined,
selectedText: string,
options?: { requireTts?: boolean },
): PreparedSentenceMatch | undefined {
if (!paragraphs?.length || !selectedText.trim()) return undefined;
const requireTts = options?.requireTts !== false;
let best: PreparedSentenceMatch | undefined;
let bestScore = 0;
for (const paragraph of paragraphs) {
if (paragraph.genStatus !== "ready") continue;
for (const sentence of paragraph.sentences ?? []) {
if (requireTts && sentence.ttsZhStatus !== "ready") continue;
const score = overlapScore(selectedText, sentence.text);
if (score > bestScore) {
bestScore = score;
best = { paragraph, sentence };
}
}
if (!paragraph.sentences?.length) {
const score = overlapScore(selectedText, paragraph.originalText);
if (score > bestScore && (!requireTts || paragraph.ttsZhStatus === "ready")) {
bestScore = score;
best = {
paragraph,
sentence: {
id: `${paragraph.id}-whole`,
orderIndex: 0,
text: paragraph.originalText,
translation: paragraph.translation,
page: paragraph.page,
searchText: paragraph.searchText,
audioPathEn: paragraph.audioPathEn,
audioPathZh: paragraph.audioPathZh,
ttsEnStatus: paragraph.ttsEnStatus,
ttsZhStatus: paragraph.ttsZhStatus,
},
};
}
}
}
return bestScore >= 55 ? best : undefined;
}