feat: add Markdown rendering support

- Add react-markdown dependency
- Render message content as Markdown
- Style Markdown elements (code blocks, headings, lists, tables, blockquotes, etc.)
- Code blocks use monospace font with dark background
- Keep export functionality as-is (already preserves Markdown)
- All 52 tests passing
This commit is contained in:
2026-02-26 01:58:46 +01:00
parent e8968ee87b
commit 183241536d
3 changed files with 760 additions and 15 deletions
+2 -1
View File
@@ -16,7 +16,8 @@
"@capacitor/cli": "^5.0.0",
"@capacitor/core": "^5.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-markdown": "^10.1.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.17.0",
+678
View File
File diff suppressed because it is too large Load Diff
+80 -14
View File
@@ -1,4 +1,5 @@
import { useState, useRef, useEffect } from 'react'
import ReactMarkdown from 'react-markdown'
import { sendMessage, sendMessageStream } from '../utils/llmApi'
import type { Session, Config } from '../services/sessionService'
@@ -154,6 +155,81 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
return session.messages.length
}
// Markdown content renderer
const MarkdownContent = ({ content }: { content: string }) => (
<div style={{
fontSize: '14px',
lineHeight: 1.7,
'& pre': {
backgroundColor: 'var(--bg-primary)',
padding: '12px',
borderRadius: 'var(--radius-sm)',
overflowX: 'auto',
border: '1px solid var(--border)'
},
'& code': {
backgroundColor: 'var(--bg-primary)',
padding: '2px 4px',
borderRadius: '4px',
fontSize: '13px',
fontFamily: 'Monaco, Consolas, "Courier New", monospace'
},
'& p': {
marginBottom: '12px',
lineHeight: 1.7
},
'& p:last-child': {
marginBottom: 0
},
'& ul, & ol': {
marginBottom: '12px',
paddingLeft: '20px'
},
'& li': {
marginBottom: '4px'
},
'& h1, & h2, & h3, & h4, & h5, & h6': {
marginTop: '16px',
marginBottom: '8px',
fontWeight: 600,
lineHeight: 1.3
},
'& blockquote': {
borderLeft: '4px solid var(--accent)',
paddingLeft: '12px',
margin: '12px 0',
color: 'var(--text-secondary)',
fontStyle: 'italic'
},
'& a': {
color: 'var(--accent-light)',
textDecoration: 'underline'
},
'& strong': {
fontWeight: 600
},
'& em': {
fontStyle: 'italic'
},
'& table': {
borderCollapse: 'collapse',
width: '100%',
marginBottom: '12px'
},
'& th, & td': {
border: '1px solid var(--border)',
padding: '8px 12px',
textAlign: 'left'
},
'& th': {
backgroundColor: 'var(--bg-tertiary)',
fontWeight: 600
}
} as React.CSSProperties}>
<ReactMarkdown>{content}</ReactMarkdown>
</div>
)
return (
<div style={{
display: 'flex',
@@ -314,13 +390,8 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
}}>
{msg.role === 'user' ? 'You' : 'Assistant'}
</div>
<div style={{
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
fontSize: '14px',
lineHeight: 1.6
}}>
{msg.content}
<div style={{ fontSize: '14px', lineHeight: 1.6 }}>
<MarkdownContent content={msg.content} />
</div>
</div>
</div>
@@ -364,13 +435,8 @@ export function ChatWindow({ session, config, onAddMessage }: ChatWindowProps) {
borderRadius: '50%'
}} />
</div>
<div style={{
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
fontSize: '14px',
lineHeight: 1.6
}}>
{streamingContent}
<div style={{ fontSize: '14px', lineHeight: 1.6 }}>
<MarkdownContent content={streamingContent} />
</div>
</div>
</div>