fix(comments): unify rendering with RichTextEditor, fix mention/link colors
- Comment display: replace <Markdown> with <RichTextEditor editable={false}>
- Link color: primary → brand (blue)
- Mention color: brand → primary + semibold
- Add MentionHoverCard component with HoverCardTrigger render={<span />}
- Markdown.tsx: sync mention style to text-primary font-semibold
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9f03b73809
commit
b9ea10c89d
4 changed files with 83 additions and 8 deletions
70
apps/web/components/common/mention-hover-card.tsx
Normal file
70
apps/web/components/common/mention-hover-card.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { Bot } from "lucide-react";
|
||||
import { HoverCard, HoverCardTrigger, HoverCardContent } from "@/components/ui/hover-card";
|
||||
import { ActorAvatar } from "@/components/common/actor-avatar";
|
||||
import { useWorkspaceStore } from "@/features/workspace";
|
||||
|
||||
interface MentionHoverCardProps {
|
||||
type: string;
|
||||
id: string;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
function MentionHoverCard({ type, id, children }: MentionHoverCardProps) {
|
||||
const members = useWorkspaceStore((s) => s.members);
|
||||
const agents = useWorkspaceStore((s) => s.agents);
|
||||
|
||||
if (type === "member") {
|
||||
const member = members.find((m) => m.user_id === id);
|
||||
if (!member) return <>{children}</>;
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger render={<span />} className="cursor-default">
|
||||
{children}
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-auto min-w-48 max-w-72">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<ActorAvatar actorType="member" actorId={id} size={32} />
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate">{member.name}</p>
|
||||
<p className="text-xs text-muted-foreground truncate">{member.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
||||
if (type === "agent") {
|
||||
const agent = agents.find((a) => a.id === id);
|
||||
if (!agent) return <>{children}</>;
|
||||
|
||||
return (
|
||||
<HoverCard>
|
||||
<HoverCardTrigger render={<span />} className="cursor-default">
|
||||
{children}
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-auto min-w-48 max-w-72">
|
||||
<div className="flex items-center gap-2.5">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-muted">
|
||||
<Bot className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate">{agent.name}</p>
|
||||
{agent.description && (
|
||||
<p className="text-xs text-muted-foreground truncate">{agent.description}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
export { MentionHoverCard };
|
||||
|
|
@ -126,7 +126,7 @@
|
|||
|
||||
/* Links */
|
||||
.rich-text-editor a {
|
||||
color: var(--primary);
|
||||
color: var(--brand);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
|
@ -137,8 +137,8 @@
|
|||
|
||||
/* Mentions */
|
||||
.rich-text-editor .mention {
|
||||
color: var(--brand);
|
||||
font-weight: 500;
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
margin: 0 0.125rem;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import ReactMarkdown, { type Components } from 'react-markdown'
|
|||
import rehypeRaw from 'rehype-raw'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { MentionHoverCard } from '@/components/common/mention-hover-card'
|
||||
import { CodeBlock, InlineCode } from './CodeBlock'
|
||||
import { preprocessLinks } from './linkify'
|
||||
|
||||
|
|
@ -60,10 +61,15 @@ function createComponents(
|
|||
a: ({ href, children }) => {
|
||||
// Mention links: mention://member/id or mention://agent/id
|
||||
if (href?.startsWith('mention://')) {
|
||||
const parts = href.replace('mention://', '').split('/')
|
||||
const mentionType = parts[0] ?? 'member'
|
||||
const mentionId = parts[1] ?? ''
|
||||
return (
|
||||
<span className="text-brand font-medium mx-0.5">
|
||||
{children}
|
||||
</span>
|
||||
<MentionHoverCard type={mentionType} id={mentionId}>
|
||||
<span className="text-primary font-semibold mx-0.5">
|
||||
{children}
|
||||
</span>
|
||||
</MentionHoverCard>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
||||
import { ActorAvatar } from "@/components/common/actor-avatar";
|
||||
import { ReactionBar } from "@/components/common/reaction-bar";
|
||||
import { Markdown } from "@/components/markdown";
|
||||
import { useActorName } from "@/features/workspace";
|
||||
import { timeAgo } from "@/shared/utils";
|
||||
import { RichTextEditor, type RichTextEditorRef } from "@/components/common/rich-text-editor";
|
||||
|
|
@ -162,7 +161,7 @@ function CommentRow({
|
|||
) : (
|
||||
<>
|
||||
<div className="mt-1.5 pl-8 text-sm leading-relaxed text-foreground/85">
|
||||
<Markdown mode="minimal">{entry.content ?? ""}</Markdown>
|
||||
<RichTextEditor defaultValue={entry.content ?? ""} editable={false} />
|
||||
</div>
|
||||
{!isTemp && (
|
||||
<ReactionBar
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue