diff --git a/realtime-factcheck/src/content/overlay.js b/realtime-factcheck/src/content/overlay.js index 558249a..2220482 100644 --- a/realtime-factcheck/src/content/overlay.js +++ b/realtime-factcheck/src/content/overlay.js @@ -2,6 +2,15 @@ console.log('[overlay] content script loaded'); +function escapeHtml(s) { + return String(s) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); +} + let panel = null; let transcriptFeedEl = null; let interimEl = null; @@ -73,6 +82,17 @@ function parseSpeakersFromTitle(title) { let lastActiveSpeaker = null; // track most recently labeled speaker +function normalizeSpeakerName(name) { + if (!name) return name; + // if name matches a known speaker's last name or full name, return the canonical last name + for (const speaker of speakers) { + const lastName = speaker.trim().split(' ').pop().toLowerCase(); + if (name.toLowerCase() === speaker.toLowerCase()) return speaker; // exact match + if (name.toLowerCase().includes(lastName)) return speaker; // last name match + } + return name; // unknown speaker — return as-is +} + function getClaimSpeaker(claimText) { if (!speakers.length) return 'Other'; const lower = claimText.toLowerCase(); @@ -113,10 +133,10 @@ function showSpeakerBanner(speakerId, sample) { banner.className = 'rtfc-speaker-banner'; banner.innerHTML = '
' + - '' + + '' + ''; @@ -197,7 +217,7 @@ function renderSpeakerEditor() { el.innerHTML = speakers.map((name, i) => { const color = getSpeakerColor(name); return '' + - '' + + '' + ''; }).join(''); @@ -247,7 +267,7 @@ function showError(message) { toast.className = 'rtfc-error-toast'; toast.innerHTML = '' + - '' + message + '' + + '' + escapeHtml(message) + '' + ''; toast.querySelector('.rtfc-error-close').addEventListener('click', () => toast.remove()); @@ -442,8 +462,8 @@ function buildCard(result) { const sourcesHTML = (result.sources ?? []).map((url, i) => { const isUrl = url.startsWith('http://') || url.startsWith('https://'); return isUrl - ? 'Source ' + (i + 1) + '' - : '' + url + ''; + ? 'Source ' + (i + 1) + '' + : '' + escapeHtml(url) + ''; }).join(''); const lexicalRows = buildLexicalRows(result.lexical); @@ -462,24 +482,24 @@ function buildCard(result) { const speakerName = (normalizedName && !normalizedName.match(/^Speaker\s*\d+$/i)) ? normalizedName : null; const speakerColor = speakerName ? getSpeakerColor(speakerName) : null; if (speakerColor) { - speakerTag = '"' + result.claim + '"
', - '' + result.explanation + '
', + '"' + escapeHtml(result.claim) + '"
', + '' + escapeHtml(result.explanation) + '
', '