web: nicer tables
This commit is contained in:
@@ -50,18 +50,20 @@ export function ChatMessagesPanel({ messages, isLoading, isSending }: Props) {
|
|||||||
const iconKind = getToolIconName(toolLogMetadata.toolName ?? message.name);
|
const iconKind = getToolIconName(toolLogMetadata.toolName ?? message.name);
|
||||||
const Icon = iconKind === "search" ? Globe2 : iconKind === "fetch" ? Link2 : Wrench;
|
const Icon = iconKind === "search" ? Globe2 : iconKind === "fetch" ? Link2 : Wrench;
|
||||||
const isFailed = toolLogMetadata.status === "failed";
|
const isFailed = toolLogMetadata.status === "failed";
|
||||||
|
const toolSummary = getToolSummary(message, toolLogMetadata);
|
||||||
return (
|
return (
|
||||||
<div key={message.id} className="flex justify-start">
|
<div key={message.id} className="flex justify-start">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex max-w-[85%] items-center gap-3 rounded-lg border px-3.5 py-2 text-sm leading-5 shadow-[inset_0_1px_0_hsl(180_100%_88%_/_0.06)]",
|
"inline-flex max-w-[85%] min-w-0 items-center gap-3 overflow-hidden rounded-lg border px-3.5 py-2 text-sm leading-5 shadow-[inset_0_1px_0_hsl(180_100%_88%_/_0.06)]",
|
||||||
isFailed
|
isFailed
|
||||||
? "border-rose-500/40 bg-rose-950/18 text-rose-200"
|
? "border-rose-500/40 bg-rose-950/18 text-rose-200"
|
||||||
: "border-cyan-400/34 bg-cyan-950/18 text-cyan-100"
|
: "border-cyan-400/34 bg-cyan-950/18 text-cyan-100"
|
||||||
)}
|
)}
|
||||||
|
title={toolSummary}
|
||||||
>
|
>
|
||||||
<Icon className="h-4 w-4 shrink-0 text-cyan-300" />
|
<Icon className="h-4 w-4 shrink-0 text-cyan-300" />
|
||||||
<span>{getToolSummary(message, toolLogMetadata)}</span>
|
<span className="min-w-0 flex-1 truncate">{toolSummary}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useMemo } from "preact/hooks";
|
import { useMemo } from "preact/hooks";
|
||||||
import DOMPurify from "dompurify";
|
import DOMPurify from "dompurify";
|
||||||
import { marked } from "marked";
|
import { marked, Renderer } from "marked";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
type MarkdownMode = "default" | "citationTokens";
|
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 `<div class="md-table-scroll">${renderTable(token)}</div>`;
|
||||||
|
};
|
||||||
|
|
||||||
function renderMarkdown(markdown: string) {
|
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"] });
|
return DOMPurify.sanitize(rawHtml, { ADD_ATTR: ["class", "target", "rel"] });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,77 @@ textarea {
|
|||||||
word-break: break-word;
|
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 {
|
.md-content p + p {
|
||||||
margin-top: 0.85rem;
|
margin-top: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user