better markdown formatting in chat and widgets

This commit is contained in:
Drew Peifer 2026-02-23 02:14:48 -05:00
parent fcd300d6c2
commit 5c1e3fd17a
4 changed files with 209 additions and 6 deletions

View file

@ -20,7 +20,9 @@
"cors": "^2.8.6",
"express": "^5.2.1",
"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": {
"@eslint/js": "^9.39.1",

View file

@ -267,13 +267,198 @@ input, textarea {
}
/* Briefing Widget Styles */
.briefing-text {
white-space: pre-line;
line-height: 1.7;
.briefing-markdown {
font-size: 1.05rem;
line-height: 1.7;
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-error-message {
color: rgba(255, 255, 255, 0.6);

View file

@ -1,6 +1,8 @@
import { useState, useRef, useEffect } from 'react';
import { Box, TextField, IconButton, Paper, CircularProgress } from '@mui/material';
import { Send, ExpandLess, ExpandMore } from '@mui/icons-material';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import { sendChatMessage } from '../services/claudeService';
function ChatInterface({ briefing, sourceData }) {
@ -86,7 +88,15 @@ function ChatInterface({ briefing, sourceData }) {
}}
>
<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>
</Box>
))}

View file

@ -1,6 +1,8 @@
import { useState, useEffect, useRef } from 'react';
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 ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import GenericWidget from './GenericWidget';
function BriefingWidget({ briefing, sx }) {
@ -319,7 +321,11 @@ function BriefingWidget({ briefing, sx }) {
{/* Briefing Content */}
<Box sx={{ mt: 1 }}>
{briefing ? (
<p className="briefing-text">{briefing}</p>
<div className="briefing-markdown">
<ReactMarkdown remarkPlugins={[remarkGfm]}>
{briefing}
</ReactMarkdown>
</div>
) : (
<>
<Skeleton variant="text" sx={{ fontSize: '1rem' }} />