fix: og image generation

This commit is contained in:
gosangam
2026-05-06 01:45:47 +05:30
parent de374eab8b
commit 7361f9c14a
28 changed files with 221 additions and 45 deletions
+65
View File
@@ -1,5 +1,9 @@
// ---------------------------------------------------------------------------
// Text Fragment Highlight — compact URL encoding for selected text
//
// These utilities work in both browser and server environments.
// The DOM-based findAndHighlightText() is browser-only.
// The string-based extractMatchedText() is server-safe (for OG image generation).
// ---------------------------------------------------------------------------
/**
@@ -52,6 +56,67 @@ export function decodeTextFragment(
return { prefix, suffix, length };
}
/**
* Server-side: extract the full matched text from an array of paragraph strings
* using a decoded text fragment (prefix, suffix, length).
*
* This is the server equivalent of findAndHighlightText() — instead of
* walking the DOM, it searches a flat array of HTML paragraph strings.
* HTML tags are stripped before matching.
*
* Returns the matched text string, or null if not found.
*/
export function extractMatchedText(
fragment: { prefix: string; suffix: string; length: number },
paragraphs: string[],
): string | null {
// Strip HTML tags to get plain text
const plainParagraphs = paragraphs.map((p) =>
p.replace(/<[^>]*>/g, "").replace(/&[^;]+;/g, " "),
);
// Build a single string with paragraph breaks
const fullText = plainParagraphs.join(" ");
const normalized = fullText.replace(/\s+/g, " ");
const normalizedPrefix = fragment.prefix.replace(/\s+/g, " ");
let searchFrom = 0;
let matchStart = -1;
while (searchFrom < normalized.length) {
const idx = normalized.indexOf(normalizedPrefix, searchFrom);
if (idx === -1) break;
if (fragment.suffix) {
const normalizedSuffix = fragment.suffix.replace(/\s+/g, " ");
const expectedEnd = idx + fragment.length;
const suffixSearchStart = Math.max(
idx,
expectedEnd - normalizedSuffix.length - 20,
);
const suffixSearchEnd = Math.min(normalized.length, expectedEnd + 20);
const searchWindow = normalized.slice(suffixSearchStart, suffixSearchEnd);
if (searchWindow.includes(normalizedSuffix)) {
matchStart = idx;
break;
}
} else {
matchStart = idx;
break;
}
searchFrom = idx + 1;
}
if (matchStart === -1) return null;
const matchEnd = Math.min(matchStart + fragment.length, normalized.length);
const matched = normalized.slice(matchStart, matchEnd).trim();
if (matched.length === 0) return null;
return matched;
}
/**
* Build the full URL with highlight fragment for the selected text.
*/