Skip to content

Commit 045f308

Browse files
committed
Harden citation token and mermaid loose chart parsing
1 parent c52ada9 commit 045f308

1 file changed

Lines changed: 38 additions & 11 deletions

File tree

frontend/src/components/ui/RichText.tsx

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,13 @@ function parseMermaidXyChart(code: string): ParsedGraph | null {
6767

6868
const xAxisMatch = code.match(/^\s*x-axis\s+(.+)$/im)
6969
const yAxisMatch = code.match(/^\s*y-axis\s+"([^"]+)"/im)
70-
const barMatch = code.match(/^\s*bar\s+\[([^\]]+)\]/im)
71-
const lineMatch = code.match(/^\s*line\s+\[([^\]]+)\]/im)
70+
const barMatch = code.match(/^\s*bar(?:\s+"([^"]+)")?\s+\[([^\]]+)\]/im)
71+
const lineMatch = code.match(/^\s*line(?:\s+"([^"]+)")?\s+\[([^\]]+)\]/im)
7272

7373
const values = barMatch
74-
? parseMermaidNumbers(barMatch[1])
74+
? parseMermaidNumbers(barMatch[2])
7575
: lineMatch
76-
? parseMermaidNumbers(lineMatch[1])
76+
? parseMermaidNumbers(lineMatch[2])
7777
: []
7878
if (values.length === 0) return null
7979

@@ -86,7 +86,7 @@ function parseMermaidXyChart(code: string): ParsedGraph | null {
8686
type: barMatch ? "bar" : "line",
8787
labels: labels.slice(0, count),
8888
series: [{
89-
name: yAxisMatch?.[1]?.trim() || "Value",
89+
name: barMatch?.[1]?.trim() || lineMatch?.[1]?.trim() || yAxisMatch?.[1]?.trim() || "Value",
9090
values: values.slice(0, count),
9191
}],
9292
}
@@ -123,11 +123,21 @@ function parseMermaidGraph(code: string): ParsedGraph | null {
123123

124124
const OPENAI_CITATION_OPEN = "\uE200cite\uE202"
125125
const OPENAI_CITATION_CLOSE = "\uE201"
126+
const OPENAI_CITATION_OPEN_ALT = "\u{F0000}cite\u{F0002}"
127+
const OPENAI_CITATION_CLOSE_ALT = "\u{F0001}"
126128

127129
function parseOpenAICitationToken(text: string, from: number): { length: number; ids: number[] } | null {
128-
if (!text.startsWith(OPENAI_CITATION_OPEN, from)) return null
129-
const payloadStart = from + OPENAI_CITATION_OPEN.length
130-
const end = text.indexOf(OPENAI_CITATION_CLOSE, payloadStart)
130+
let payloadStart = -1
131+
let end = -1
132+
if (text.startsWith(OPENAI_CITATION_OPEN, from)) {
133+
payloadStart = from + OPENAI_CITATION_OPEN.length
134+
end = text.indexOf(OPENAI_CITATION_CLOSE, payloadStart)
135+
} else if (text.startsWith(OPENAI_CITATION_OPEN_ALT, from)) {
136+
payloadStart = from + OPENAI_CITATION_OPEN_ALT.length
137+
end = text.indexOf(OPENAI_CITATION_CLOSE_ALT, payloadStart)
138+
} else {
139+
return null
140+
}
131141
if (end === -1) return null
132142

133143
const payload = text.slice(payloadStart, end)
@@ -148,20 +158,34 @@ function parseOpenAICitationToken(text: string, from: number): { length: number;
148158

149159
function parseLooseMermaidChartBlock(lines: string[], startLine: number): { graph: ParsedGraph; endLine: number; snippet: string } | null {
150160
const start = lines[startLine]?.trim() ?? ""
151-
const maybeMermaidStart = /^xychart-beta\b/i.test(start) || /^pie(?:\s+showData)?$/i.test(start)
161+
const isXyStart = /^xychart-beta\b/i.test(start)
162+
const isPieStart = /^pie(?:\s+showData)?$/i.test(start)
163+
const maybeMermaidStart = isXyStart || isPieStart
152164
if (!maybeMermaidStart) return null
165+
const mode: "xy" | "pie" = isXyStart ? "xy" : "pie"
166+
167+
const isChartLine = (t: string) => {
168+
if (!t) return true
169+
if (mode === "xy") return /^(title|x-axis|y-axis|line|bar)\b/i.test(t)
170+
return /^(title\b|(?:"[^"]+"|'[^']+'|[^:]+)\s*:\s*-?\d+(?:\.\d+)?)$/i.test(t)
171+
}
153172

154173
const collected: string[] = [lines[startLine]]
155174
let endLine = startLine
156175
for (let i = startLine + 1; i < lines.length; i++) {
157176
const t = lines[i].trim()
158-
if (!t) break
177+
if (!t) {
178+
collected.push(lines[i])
179+
endLine = i
180+
continue
181+
}
159182
if (/^#{1,6}\s/.test(t)) break
160183
if (/^```/.test(t)) break
161184
if (/^[-*]\s+/.test(t)) break
162185
if (/^\d+[.)]\s+/.test(t)) break
163186
if (parseGraphTag(t)) break
164187
if (parseTableLine(lines[i])) break
188+
if (!isChartLine(t)) break
165189
collected.push(lines[i])
166190
endLine = i
167191
}
@@ -324,7 +348,10 @@ function renderInline(text: string, variant: Variant, COLORS: ThemeColors, VS: R
324348
let key = 0
325349

326350
while (cursor < text.length) {
327-
const nextOpenAICitation = findNextIndex(text, cursor, OPENAI_CITATION_OPEN)
351+
const nextOpenAICitation = Math.min(
352+
findNextIndex(text, cursor, OPENAI_CITATION_OPEN),
353+
findNextIndex(text, cursor, OPENAI_CITATION_OPEN_ALT),
354+
)
328355
const nextCode = findNextIndex(text, cursor, "`")
329356
const nextBoldItalic = allowBold ? findNextIndex(text, cursor, "***") : Number.POSITIVE_INFINITY
330357
const nextBold = allowBold ? findNextIndex(text, cursor, "**") : Number.POSITIVE_INFINITY

0 commit comments

Comments
 (0)