double quotes inclusion

This commit is contained in:
gosangam
2026-05-30 00:53:50 +05:30
parent a22fcf360d
commit 3767a3a9b6
+34 -14
View File
@@ -49,6 +49,26 @@ function normalizeQuotes(s: string): string {
.replace(/[‘’‚‛′]/g, "'");
}
// Strip IAST / Vedic diacritics for matching. Decomposes via NFD then drops
// codepoints in the Combining Diacritical Marks block. Necessary because
// Typesense returns snippets from the `_ascii` shadow field (e.g. "Krsna")
// while the rendered DOM uses the original IAST form ("Kṛṣṇa") — without
// this normalization, a search-result link to KB 1.34 with prefix "Krsna is
// so beautiful" never matches the "Kṛṣṇa is so beautiful" in the DOM.
//
// Length-preserving for NFC input (one NFC char → one ASCII char): NFD
// expands an accented char into base + combining mark, the regex deletes
// only the combining mark, and the base is left. So position maps built
// from the original collapsed string remain valid after this transform.
function stripDiacritics(s: string): string {
return s.normalize("NFD").replace(/[̀-ͯ]/g, "");
}
// Combined normalization used on both sides of every match.
function normalizeForMatch(s: string): string {
return stripDiacritics(normalizeQuotes(s.toLowerCase()));
}
/**
* Decode a text fragment from URL params.
* Supports `§` (current), `|` (v2 legacy), and `,` (v1 legacy) delimiters.
@@ -103,10 +123,10 @@ export function extractMatchedText(
// Build a single string with paragraph breaks
const fullText = plainParagraphs.join(" ");
const normalized = fullText.replace(/\s+/g, " ");
const normalizedLower = normalizeQuotes(normalized.toLowerCase());
const normalizedLower = normalizeForMatch(normalized);
const normalizedPrefix = normalizeQuotes(
fragment.prefix.replace(/\s+/g, " ").toLowerCase(),
const normalizedPrefix = normalizeForMatch(
fragment.prefix.replace(/\s+/g, " "),
);
let searchFrom = 0;
let matchStart = -1;
@@ -116,8 +136,8 @@ export function extractMatchedText(
if (idx === -1) break;
if (fragment.suffix) {
const normalizedSuffix = normalizeQuotes(
fragment.suffix.replace(/\s+/g, " ").toLowerCase(),
const normalizedSuffix = normalizeForMatch(
fragment.suffix.replace(/\s+/g, " "),
);
const expectedEnd = idx + fragment.length;
const suffixSearchStart = Math.max(
@@ -397,15 +417,15 @@ export function findAndHighlightText(fragment: {
}
}
// Quote-normalize for matching so the prefix/suffix and the DOM text agree
// even when one side uses straight ASCII quotes and the other uses curly
// typographical quotes. Length-preserving, so collapsedPosMap indices stay
// valid against rawText.
const normalizedFullLower = normalizeQuotes(collapsedNorm.toLowerCase());
// Normalize for matching so prefix/suffix and DOM text agree on quote style
// and diacritics (search snippets come from the `_ascii` shadow field, the
// DOM renders the original IAST form). Length-preserving for NFC input, so
// collapsedPosMap indices remain valid against rawText.
const normalizedFullLower = normalizeForMatch(collapsedNorm);
// Search for the prefix in the full text (case-insensitive)
const normalizedPrefix = normalizeQuotes(
fragment.prefix.replace(/\s+/g, " ").toLowerCase(),
const normalizedPrefix = normalizeForMatch(
fragment.prefix.replace(/\s+/g, " "),
);
let searchFrom = 0;
let matchStartNorm = -1;
@@ -416,8 +436,8 @@ export function findAndHighlightText(fragment: {
// If we have a suffix, verify it appears near the expected length
if (fragment.suffix) {
const normalizedSuffix = normalizeQuotes(
fragment.suffix.replace(/\s+/g, " ").toLowerCase(),
const normalizedSuffix = normalizeForMatch(
fragment.suffix.replace(/\s+/g, " "),
);
// Search for suffix near the expected end position in the normalized string.
// Use a wider tolerance window (±50) since normalization can shift positions.