in frontend/src/components/Answer/AnswerParser.tsx [35:109]
export function parseAnswerToHtml(
answer: string,
showSources: boolean,
onCitationClicked: (citationFilePath: string, filename: string) => void
): HtmlParsedAnswer {
const citations: string[] = [];
const followupQuestions: string[] = [];
// 1. Extract any follow-up questions enclosed in << >> and remove them from the answer.
let parsedAnswer = answer.replace(/<<([^>>]+)>>/g, (_, content) => {
followupQuestions.push(content.trim());
return "";
});
// 2. Trim any whitespace from the end of the answer after removing follow-up questions.
parsedAnswer = parsedAnswer.trim();
let processedAnswer = parsedAnswer;
if (showSources) {
// 3. Replace citations with unique placeholders and collect them.
// Citations are assumed to be in the format [citation].
processedAnswer = processedAnswer.replace(/\[([^\]]+)\]/g, (_, citation) => {
const trimmedCitation = citation.trim();
if (!citations.includes(trimmedCitation)) {
citations.push(trimmedCitation);
}
const citationIndex = citations.indexOf(trimmedCitation) + 1;
// Use a unique placeholder to identify citations later.
return `CITATION_MARKER_${citationIndex}`;
});
} else {
// 4. If sources are not to be shown, remove citations entirely.
processedAnswer = removeCitations(processedAnswer);
}
let htmlContent: string;
if (showSources) {
// 5. Use marked.parse to parse the Markdown with citation placeholders to HTML.
htmlContent = marked.parse(processedAnswer) as string; // Type Assertion Added
htmlContent = htmlContent.replace(/<\/?p>/g, '');
// 6. Replace citation placeholders with actual HTML links.
// These links include data attributes to store citation information.
htmlContent = htmlContent.replace(/CITATION_MARKER_(\d+)/g, (_: string, index: string) => {
const citationIndex = parseInt(index, 10);
const citation = citations[citationIndex - 1];
const path = getCitationFilePath(citation);
// Return an anchor tag with data attributes and a unique class for event handling.
return `<a class="supContainer citation-link" title="${DOMPurify.sanitize(
citation
)}" data-citation="${DOMPurify.sanitize(citation)}" data-path="${DOMPurify.sanitize(
path
)}" href="#"><sup>${citationIndex}</sup></a>`;
});
} else {
// 7. If not showing sources, simply parse the Markdown to HTML.
htmlContent = marked.parse(processedAnswer) as string; // Type Assertion Added
htmlContent = htmlContent.replace(/<\/?p>/g, '');
}
// 8. Sanitize the HTML to prevent XSS attacks and allow specific tags and attributes.
const sanitizedHtml = DOMPurify.sanitize(htmlContent, {
ADD_TAGS: ["sup", "a"],
ADD_ATTR: ["class", "title", "data-citation", "data-path", "href"],
});
return {
answerHtml: sanitizedHtml,
citations,
followupQuestions,
};
}