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;
}