Fix markdown mention chips

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
dotta 2026-03-21 14:48:10 -05:00
parent cd7c6ee751
commit 8232456ce8
14 changed files with 527 additions and 264 deletions

View file

@ -1,9 +1,9 @@
import { isValidElement, useEffect, useId, useState, type CSSProperties, type ReactNode } from "react";
import { isValidElement, useEffect, useId, useState, type ReactNode } from "react";
import Markdown, { type Components } from "react-markdown";
import remarkGfm from "remark-gfm";
import { parseProjectMentionHref } from "@paperclipai/shared";
import { cn } from "../lib/utils";
import { useTheme } from "../context/ThemeContext";
import { mentionChipInlineStyle, parseMentionChipHref } from "../lib/mention-chips";
interface MarkdownBodyProps {
children: string;
@ -36,29 +36,6 @@ function extractMermaidSource(children: ReactNode): string | null {
return flattenText(childProps.children).replace(/\n$/, "");
}
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
const match = /^#([0-9a-f]{6})$/i.exec(hex.trim());
if (!match) return null;
const value = match[1];
return {
r: parseInt(value.slice(0, 2), 16),
g: parseInt(value.slice(2, 4), 16),
b: parseInt(value.slice(4, 6), 16),
};
}
function mentionChipStyle(color: string | null): CSSProperties | undefined {
if (!color) return undefined;
const rgb = hexToRgb(color);
if (!rgb) return undefined;
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
return {
borderColor: color,
backgroundColor: `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, 0.22)`,
color: luminance > 0.55 ? "#111827" : "#f8fafc",
};
}
function MermaidDiagramBlock({ source, darkMode }: { source: string; darkMode: boolean }) {
const renderId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
const [svg, setSvg] = useState<string | null>(null);
@ -125,16 +102,23 @@ export function MarkdownBody({ children, className, resolveImageSrc }: MarkdownB
return <pre {...preProps}>{preChildren}</pre>;
},
a: ({ href, children: linkChildren }) => {
const parsed = href ? parseProjectMentionHref(href) : null;
const parsed = href ? parseMentionChipHref(href) : null;
if (parsed) {
const label = linkChildren;
const targetHref = parsed.kind === "project"
? `/projects/${parsed.projectId}`
: `/agents/${parsed.agentId}`;
return (
<a
href={`/projects/${parsed.projectId}`}
className="paperclip-project-mention-chip"
style={mentionChipStyle(parsed.color)}
href={targetHref}
className={cn(
"paperclip-mention-chip",
`paperclip-mention-chip--${parsed.kind}`,
parsed.kind === "project" && "paperclip-project-mention-chip",
)}
data-mention-kind={parsed.kind}
style={mentionChipInlineStyle(parsed)}
>
{label}
{linkChildren}
</a>
);
}
@ -160,7 +144,7 @@ export function MarkdownBody({ children, className, resolveImageSrc }: MarkdownB
className,
)}
>
<Markdown remarkPlugins={[remarkGfm]} components={components}>
<Markdown remarkPlugins={[remarkGfm]} components={components} urlTransform={(url) => url}>
{children}
</Markdown>
</div>