better markdown formatting in chat and widgets
This commit is contained in:
parent
fcd300d6c2
commit
5c1e3fd17a
4 changed files with 209 additions and 6 deletions
|
|
@ -20,7 +20,9 @@
|
||||||
"cors": "^2.8.6",
|
"cors": "^2.8.6",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0"
|
"react-dom": "^19.2.0",
|
||||||
|
"react-markdown": "^10.1.0",
|
||||||
|
"remark-gfm": "^4.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.39.1",
|
"@eslint/js": "^9.39.1",
|
||||||
|
|
|
||||||
|
|
@ -267,13 +267,198 @@ input, textarea {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Briefing Widget Styles */
|
/* Briefing Widget Styles */
|
||||||
.briefing-text {
|
.briefing-markdown {
|
||||||
white-space: pre-line;
|
|
||||||
line-height: 1.7;
|
|
||||||
font-size: 1.05rem;
|
font-size: 1.05rem;
|
||||||
|
line-height: 1.7;
|
||||||
margin: 8px 0 0 0;
|
margin: 8px 0 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.briefing-markdown p {
|
||||||
|
margin: 0 0 0.75em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown h1,
|
||||||
|
.briefing-markdown h2,
|
||||||
|
.briefing-markdown h3,
|
||||||
|
.briefing-markdown h4 {
|
||||||
|
margin: 1em 0 0.35em 0;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown h1 { font-size: 1.5rem; }
|
||||||
|
.briefing-markdown h2 { font-size: 1.3rem; }
|
||||||
|
.briefing-markdown h3 { font-size: 1.15rem; }
|
||||||
|
.briefing-markdown h4 { font-size: 1.05rem; }
|
||||||
|
|
||||||
|
.briefing-markdown ul,
|
||||||
|
.briefing-markdown ol {
|
||||||
|
margin: 0.4em 0 0.75em 0;
|
||||||
|
padding-left: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown li {
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown code {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.9em;
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
padding: 0.1em 0.3em;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .briefing-markdown code {
|
||||||
|
background-color: rgba(0, 0, 0, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.briefing-markdown hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
margin: 0.75em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .briefing-markdown hr {
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Chat Markdown - condensed for the chat bubble context */
|
||||||
|
.chat-markdown {
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown p {
|
||||||
|
margin: 0 0 0.4em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown h1,
|
||||||
|
.chat-markdown h2,
|
||||||
|
.chat-markdown h3,
|
||||||
|
.chat-markdown h4 {
|
||||||
|
margin: 0.5em 0 0.2em 0;
|
||||||
|
line-height: 1.2;
|
||||||
|
font-size: 1em;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown ul,
|
||||||
|
.chat-markdown ol {
|
||||||
|
margin: 0.2em 0 0.4em 0;
|
||||||
|
padding-left: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown li {
|
||||||
|
margin-bottom: 0.15em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown li p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown strong {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown code {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 0.85em;
|
||||||
|
background-color: rgba(0, 0, 0, 0.2);
|
||||||
|
padding: 0.1em 0.25em;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown pre {
|
||||||
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 0.5em 0.75em;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 0.3em 0 0.4em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown pre code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.82em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown blockquote {
|
||||||
|
border-left: 3px solid rgba(255, 255, 255, 0.35);
|
||||||
|
margin: 0.3em 0;
|
||||||
|
padding: 0 0 0 0.6em;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown code {
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown pre {
|
||||||
|
background-color: rgba(0, 0, 0, 0.07);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown blockquote {
|
||||||
|
border-left-color: rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown hr {
|
||||||
|
border-top-color: rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85em;
|
||||||
|
margin: 0.3em 0 0.4em 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown th,
|
||||||
|
.chat-markdown td {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
|
padding: 0.2em 0.5em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-markdown th {
|
||||||
|
font-weight: 700;
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown th,
|
||||||
|
.app-light .chat-markdown td {
|
||||||
|
border-color: rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-light .chat-markdown th {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
/* Weather Widget Styles */
|
/* Weather Widget Styles */
|
||||||
.weather-error-message {
|
.weather-error-message {
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { useState, useRef, useEffect } from 'react';
|
import { useState, useRef, useEffect } from 'react';
|
||||||
import { Box, TextField, IconButton, Paper, CircularProgress } from '@mui/material';
|
import { Box, TextField, IconButton, Paper, CircularProgress } from '@mui/material';
|
||||||
import { Send, ExpandLess, ExpandMore } from '@mui/icons-material';
|
import { Send, ExpandLess, ExpandMore } from '@mui/icons-material';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
import { sendChatMessage } from '../services/claudeService';
|
import { sendChatMessage } from '../services/claudeService';
|
||||||
|
|
||||||
function ChatInterface({ briefing, sourceData }) {
|
function ChatInterface({ briefing, sourceData }) {
|
||||||
|
|
@ -86,7 +88,15 @@ function ChatInterface({ briefing, sourceData }) {
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={msg.role === 'user' ? 'chat-message-user' : 'chat-message-assistant'}>
|
<div className={msg.role === 'user' ? 'chat-message-user' : 'chat-message-assistant'}>
|
||||||
{msg.content}
|
{msg.role === 'user' ? (
|
||||||
|
msg.content
|
||||||
|
) : (
|
||||||
|
<div className="chat-markdown">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{msg.content}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { Skeleton, Box, IconButton, Collapse, FormControlLabel, Switch, ToggleButtonGroup, ToggleButton, Select, MenuItem, FormControl, InputLabel, Typography } from '@mui/material';
|
import { Skeleton, Box, IconButton, Collapse, FormControlLabel, Switch, ToggleButtonGroup, ToggleButton, Select, MenuItem, FormControl, InputLabel, Typography } from '@mui/material';
|
||||||
import { VolumeUp, PlayArrow, Pause, Settings, Stop } from '@mui/icons-material';
|
import { VolumeUp, PlayArrow, Pause, Settings, Stop } from '@mui/icons-material';
|
||||||
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
import GenericWidget from './GenericWidget';
|
import GenericWidget from './GenericWidget';
|
||||||
|
|
||||||
function BriefingWidget({ briefing, sx }) {
|
function BriefingWidget({ briefing, sx }) {
|
||||||
|
|
@ -319,7 +321,11 @@ function BriefingWidget({ briefing, sx }) {
|
||||||
{/* Briefing Content */}
|
{/* Briefing Content */}
|
||||||
<Box sx={{ mt: 1 }}>
|
<Box sx={{ mt: 1 }}>
|
||||||
{briefing ? (
|
{briefing ? (
|
||||||
<p className="briefing-text">{briefing}</p>
|
<div className="briefing-markdown">
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]}>
|
||||||
|
{briefing}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Skeleton variant="text" sx={{ fontSize: '1rem' }} />
|
<Skeleton variant="text" sx={{ fontSize: '1rem' }} />
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue