diff --git a/web/src/components/chat/chat-messages-panel.tsx b/web/src/components/chat/chat-messages-panel.tsx index 1fb1b2c..4406f69 100644 --- a/web/src/components/chat/chat-messages-panel.tsx +++ b/web/src/components/chat/chat-messages-panel.tsx @@ -50,18 +50,20 @@ export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) { const iconKind = getToolIconName(toolLogMetadata.toolName ?? message.name); const Icon = iconKind === "search" ? Globe2 : iconKind === "fetch" ? Link2 : Wrench; const isFailed = toolLogMetadata.status === "failed"; + const toolSummary = getToolSummary(message, toolLogMetadata); return (
- {getToolSummary(message, toolLogMetadata)} + {toolSummary}
); diff --git a/web/src/components/markdown/markdown-content.tsx b/web/src/components/markdown/markdown-content.tsx index 05564b2..1a0e066 100644 --- a/web/src/components/markdown/markdown-content.tsx +++ b/web/src/components/markdown/markdown-content.tsx @@ -1,6 +1,6 @@ import { useMemo } from "preact/hooks"; import DOMPurify from "dompurify"; -import { marked } from "marked"; +import { marked, Renderer } from "marked"; import { cn } from "@/lib/utils"; type MarkdownMode = "default" | "citationTokens"; @@ -21,8 +21,15 @@ function replaceMarkdownLinksWithCitationTokens(markdown: string, resolveCitatio }); } +const markdownRenderer = new Renderer(); +const renderTable = markdownRenderer.table.bind(markdownRenderer); + +markdownRenderer.table = (token) => { + return `
${renderTable(token)}
`; +}; + function renderMarkdown(markdown: string) { - const rawHtml = marked.parse(markdown, { gfm: true, breaks: true }) as string; + const rawHtml = marked.parse(markdown, { gfm: true, breaks: true, renderer: markdownRenderer }) as string; return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] }); } diff --git a/web/src/index.css b/web/src/index.css index a8067ed..e7c982d 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -83,6 +83,77 @@ textarea { word-break: break-word; } +.md-table-scroll { + max-width: 100%; + margin: 0.35rem 0 1rem; + overflow-x: auto; + overflow-y: hidden; + border: 1px solid hsl(var(--border) / 0.86); + border-radius: 0.625rem; + background: hsl(246 34% 10% / 0.76); + box-shadow: inset 0 1px 0 hsl(258 80% 88% / 0.06); +} + +.md-content table { + width: max-content; + min-width: 100%; + border-collapse: separate; + border-spacing: 0; + font-size: 0.94em; + line-height: 1.48; +} + +.md-table-scroll::-webkit-scrollbar { + height: 0.45rem; +} + +.md-table-scroll::-webkit-scrollbar-thumb { + border-radius: 9999px; + background: hsl(263 78% 72% / 0.34); +} + +.md-content th, +.md-content td { + padding: 0.48rem 0.7rem; + border-right: 1px solid hsl(var(--border) / 0.72); + border-bottom: 1px solid hsl(var(--border) / 0.7); + text-align: left; + vertical-align: top; + word-break: normal; +} + +.md-content th:last-child, +.md-content td:last-child { + border-right: 0; +} + +.md-content tr:last-child td { + border-bottom: 0; +} + +.md-content th { + background: hsl(251 40% 15% / 0.92); + color: hsl(258 36% 98%); + font-weight: 700; + white-space: nowrap; +} + +.md-content td { + color: hsl(258 34% 94% / 0.96); +} + +.md-content tbody tr:nth-child(odd) td { + background: hsl(242 32% 10% / 0.58); +} + +.md-content tbody tr:nth-child(even) td { + background: hsl(252 36% 13% / 0.46); +} + +.md-content tbody tr:hover td { + background: hsl(263 46% 20% / 0.48); +} + .md-content p + p { margin-top: 0.85rem; }