feat: Phase 7.2 — wire highlightTerms into ResponsePanel + mark CSS

- Add HighlightMark component rendering <mark class="bg-yellow-200...">
- Call highlightTerms() in SubQuestionSection and FlatResponse before ReactMarkdown
- Add mark: HighlightMark to ReactMarkdown components in both paths
- Add .prose mark CSS rule (yellow-200 bg, rounded, px-0.5)
- Tests: 56/56 pass (citation + highlight + ResponsePanel)
This commit is contained in:
Woody 2026-05-15 10:51:08 +08:00
parent 534559b2e0
commit e78f53b687
2 changed files with 16 additions and 5 deletions

View File

@ -3,7 +3,7 @@ import { MessageSquare, AlertCircle, Copy, ChevronDown, ChevronRight } from 'luc
import ReactMarkdown from 'react-markdown'
import type { SourceMetadata, SubQuestionSources } from '../types'
import { getPdfViewerUrl } from '../lib/api'
import { processCitations, processCitationsForSubq, extractCitedSources } from '../utils/citationParser'
import { processCitations, processCitationsForSubq, extractCitedSources, highlightTerms } from '../utils/citationParser'
import { bulletizeMarkdown } from '../utils/citationParser'
function getHighlightUrl(document_id: string, chunk_index: number, sub_question: string): string {
@ -32,6 +32,10 @@ const CitationLink = ({ href, children }: { href?: string; children?: React.Reac
</a>
)
const HighlightMark = ({ children }: { children?: React.ReactNode }) => (
<mark className="bg-yellow-200 rounded px-0.5">{children}</mark>
)
function parseAnswerSections(answer: string): string[] {
const sections = answer.split(/## Sub-question \d+:[^\n]*\n/)
if (sections.length <= 1) return [bulletizeMarkdown(answer)]
@ -107,6 +111,7 @@ function SubQuestionSection({
sq.sources.map(s => ({ ...s, sub_question_text: sq.sub_question_text }))
)
const processedAnswer = processCitations(answerSection, allSources, highlightReadyKeys)
const highlightedAnswer = highlightTerms(processedAnswer)
return (
<div
@ -120,8 +125,8 @@ function SubQuestionSection({
</div>
<div className="prose prose-sm max-w-none text-gray-800">
<ReactMarkdown components={{ a: CitationLink }}>
{processedAnswer}
<ReactMarkdown components={{ a: CitationLink, mark: HighlightMark }}>
{highlightedAnswer}
</ReactMarkdown>
</div>
@ -353,6 +358,7 @@ function FlatResponse({
const safeSources = sources ?? []
const processedAnswer = answer ? processCitations(answer, safeSources) : answer ?? ''
const highlightedAnswer = processedAnswer ? highlightTerms(processedAnswer) : processedAnswer
const handleCopyAnswer = async (): Promise<void> => {
if (answer) {
@ -454,9 +460,9 @@ function FlatResponse({
</div>
<div className="prose prose-sm max-w-none text-gray-800">
<ReactMarkdown
components={{ a: CitationLink }}
components={{ a: CitationLink, mark: HighlightMark }}
>
{processedAnswer}
{highlightedAnswer}
</ReactMarkdown>
</div>
</div>

View File

@ -10,3 +10,8 @@
list-style: decimal !important;
padding-left: 1.5rem !important;
}
.prose mark {
background-color: #FEF08A;
border-radius: 0.125rem;
padding: 0 0.125rem;
}