mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-07 08:26:51 +00:00
Render markdown in questions (#184)
This commit is contained in:
parent
2d8211aea7
commit
0156265963
4 changed files with 75 additions and 2 deletions
|
|
@ -63,6 +63,7 @@
|
|||
"react-markdown": "^10.1.0",
|
||||
"react-use": "^17.6.0",
|
||||
"recharts": "^2.15.3",
|
||||
"rehype-sanitize": "^6.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"require-in-the-middle": "^7.5.2",
|
||||
"shiki": "^3.7.0",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { useMemo, useEffect, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkBreaks from 'remark-breaks';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import { Link2 } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
|
|
@ -284,8 +285,17 @@ export const Messages = ({
|
|||
{isQuestion && questionData ? (
|
||||
<div className="space-y-4">
|
||||
{questionData.question && (
|
||||
<div className="text-sm leading-relaxed">
|
||||
{questionData.question}
|
||||
<div className="text-sm leading-relaxed markdown-prose">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkBreaks]}
|
||||
rehypePlugins={[rehypeSanitize]}
|
||||
components={{
|
||||
a: PlainTextLink,
|
||||
code: CodeBlock,
|
||||
}}
|
||||
>
|
||||
{questionData.question}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
)}
|
||||
{questionData.suggestions &&
|
||||
|
|
@ -314,6 +324,7 @@ export const Messages = ({
|
|||
<div className="text-sm leading-relaxed markdown-prose">
|
||||
<ReactMarkdown
|
||||
remarkPlugins={[remarkBreaks]}
|
||||
rehypePlugins={[rehypeSanitize]}
|
||||
components={{
|
||||
a: PlainTextLink,
|
||||
code: CodeBlock,
|
||||
|
|
|
|||
|
|
@ -94,4 +94,45 @@ describe('Messages Component - Newline Handling', () => {
|
|||
expect(commandContainer).toBeInTheDocument();
|
||||
expect(commandContainer.closest('.font-mono')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle question data with newlines in the question text', () => {
|
||||
const questionMessage: Message = {
|
||||
id: '1',
|
||||
orgId: null,
|
||||
userId: 'test-user',
|
||||
taskId: 'test-task',
|
||||
text: JSON.stringify({
|
||||
question:
|
||||
'What would you like to do?\nPlease choose from the options below:\n\n1. Option A\n2. Option B',
|
||||
suggest: ['Option A', 'Option B'],
|
||||
}),
|
||||
timestamp: Date.now(),
|
||||
ts: Date.now(),
|
||||
type: 'ask',
|
||||
say: null,
|
||||
ask: 'followup',
|
||||
mode: 'code',
|
||||
reasoning: null,
|
||||
partial: null,
|
||||
};
|
||||
|
||||
render(<Messages messages={[questionMessage]} />);
|
||||
|
||||
// Question text should be present and properly formatted
|
||||
expect(screen.getByText(/What would you like to do/)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Please choose from the options below/),
|
||||
).toBeInTheDocument();
|
||||
|
||||
// Should find both the list items and suggestion buttons (multiple instances expected)
|
||||
const optionAElements = screen.getAllByText(/Option A/);
|
||||
const optionBElements = screen.getAllByText(/Option B/);
|
||||
|
||||
// Should have at least 2 instances of each option (one in markdown list, one in suggestion button)
|
||||
expect(optionAElements.length).toBeGreaterThanOrEqual(2);
|
||||
expect(optionBElements.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
// Verify the markdown rendered the numbered list properly
|
||||
expect(screen.getByRole('list')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
20
pnpm-lock.yaml
generated
20
pnpm-lock.yaml
generated
|
|
@ -299,6 +299,9 @@ importers:
|
|||
recharts:
|
||||
specifier: ^2.15.3
|
||||
version: 2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
rehype-sanitize:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
remark-breaks:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
|
|
@ -4376,6 +4379,9 @@ packages:
|
|||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hast-util-sanitize@5.0.2:
|
||||
resolution: {integrity: sha512-3yTWghByc50aGS7JlGhk61SPenfE/p1oaFeNwkOOyrscaOkMGrcW9+Cy/QAIOBpZxP1yqDIzFMR0+Np0i0+usg==}
|
||||
|
||||
hast-util-to-html@9.0.5:
|
||||
resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==}
|
||||
|
||||
|
|
@ -5906,6 +5912,9 @@ packages:
|
|||
resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
rehype-sanitize@6.0.0:
|
||||
resolution: {integrity: sha512-CsnhKNsyI8Tub6L4sm5ZFsme4puGfc6pYylvXo1AeqaGbjOYyzNv3qZPwvs0oMJ39eryyeOdmxwUIo94IpEhqg==}
|
||||
|
||||
remark-breaks@4.0.0:
|
||||
resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
|
||||
|
||||
|
|
@ -11064,6 +11073,12 @@ snapshots:
|
|||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
hast-util-sanitize@5.0.2:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@ungap/structured-clone': 1.3.0
|
||||
unist-util-position: 5.0.0
|
||||
|
||||
hast-util-to-html@9.0.5:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
|
|
@ -12830,6 +12845,11 @@ snapshots:
|
|||
gopd: 1.2.0
|
||||
set-function-name: 2.0.2
|
||||
|
||||
rehype-sanitize@6.0.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
hast-util-sanitize: 5.0.2
|
||||
|
||||
remark-breaks@4.0.0:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue