diff --git a/claudash/package.json b/claudash/package.json index dfda0cc..4a37f74 100644 --- a/claudash/package.json +++ b/claudash/package.json @@ -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", diff --git a/claudash/src/App.css b/claudash/src/App.css index e230d2b..1ec17bc 100644 --- a/claudash/src/App.css +++ b/claudash/src/App.css @@ -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); diff --git a/claudash/src/components/ChatInterface.jsx b/claudash/src/components/ChatInterface.jsx index 47e8f01..e8b23f8 100644 --- a/claudash/src/components/ChatInterface.jsx +++ b/claudash/src/components/ChatInterface.jsx @@ -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 }) { }} >
{briefing}
+