Skip to content

Commit 0156265

Browse files
authored
Render markdown in questions (#184)
1 parent 2d8211a commit 0156265

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
"react-markdown": "^10.1.0",
6464
"react-use": "^17.6.0",
6565
"recharts": "^2.15.3",
66+
"rehype-sanitize": "^6.0.0",
6667
"remark-breaks": "^4.0.0",
6768
"require-in-the-middle": "^7.5.2",
6869
"shiki": "^3.7.0",

apps/web/src/app/(authenticated)/usage/Messages.tsx

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMemo, useEffect, useState } from 'react';
22
import ReactMarkdown from 'react-markdown';
33
import remarkBreaks from 'remark-breaks';
4+
import rehypeSanitize from 'rehype-sanitize';
45
import { Link2 } from 'lucide-react';
56
import { toast } from 'sonner';
67

@@ -284,8 +285,17 @@ export const Messages = ({
284285
{isQuestion && questionData ? (
285286
<div className="space-y-4">
286287
{questionData.question && (
287-
<div className="text-sm leading-relaxed">
288-
{questionData.question}
288+
<div className="text-sm leading-relaxed markdown-prose">
289+
<ReactMarkdown
290+
remarkPlugins={[remarkBreaks]}
291+
rehypePlugins={[rehypeSanitize]}
292+
components={{
293+
a: PlainTextLink,
294+
code: CodeBlock,
295+
}}
296+
>
297+
{questionData.question}
298+
</ReactMarkdown>
289299
</div>
290300
)}
291301
{questionData.suggestions &&
@@ -314,6 +324,7 @@ export const Messages = ({
314324
<div className="text-sm leading-relaxed markdown-prose">
315325
<ReactMarkdown
316326
remarkPlugins={[remarkBreaks]}
327+
rehypePlugins={[rehypeSanitize]}
317328
components={{
318329
a: PlainTextLink,
319330
code: CodeBlock,

apps/web/src/components/ui/__tests__/Messages.test.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,45 @@ describe('Messages Component - Newline Handling', () => {
9494
expect(commandContainer).toBeInTheDocument();
9595
expect(commandContainer.closest('.font-mono')).toBeInTheDocument();
9696
});
97+
98+
it('should handle question data with newlines in the question text', () => {
99+
const questionMessage: Message = {
100+
id: '1',
101+
orgId: null,
102+
userId: 'test-user',
103+
taskId: 'test-task',
104+
text: JSON.stringify({
105+
question:
106+
'What would you like to do?\nPlease choose from the options below:\n\n1. Option A\n2. Option B',
107+
suggest: ['Option A', 'Option B'],
108+
}),
109+
timestamp: Date.now(),
110+
ts: Date.now(),
111+
type: 'ask',
112+
say: null,
113+
ask: 'followup',
114+
mode: 'code',
115+
reasoning: null,
116+
partial: null,
117+
};
118+
119+
render(<Messages messages={[questionMessage]} />);
120+
121+
// Question text should be present and properly formatted
122+
expect(screen.getByText(/What would you like to do/)).toBeInTheDocument();
123+
expect(
124+
screen.getByText(/Please choose from the options below/),
125+
).toBeInTheDocument();
126+
127+
// Should find both the list items and suggestion buttons (multiple instances expected)
128+
const optionAElements = screen.getAllByText(/Option A/);
129+
const optionBElements = screen.getAllByText(/Option B/);
130+
131+
// Should have at least 2 instances of each option (one in markdown list, one in suggestion button)
132+
expect(optionAElements.length).toBeGreaterThanOrEqual(2);
133+
expect(optionBElements.length).toBeGreaterThanOrEqual(2);
134+
135+
// Verify the markdown rendered the numbered list properly
136+
expect(screen.getByRole('list')).toBeInTheDocument();
137+
});
97138
});

pnpm-lock.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)