Files
fusion/packages/dashboard/app/components/MailboxMessageContent.tsx
gsxdsm 6c779159c0 feat(dashboard): render mailbox messages as markdown
Adds GitHub-flavored markdown rendering to message bodies in both
MailboxView and MailboxModal via a shared MailboxMessageContent
component (ReactMarkdown + remark-gfm). Plain-text messages render
unchanged. Raw HTML is not executed.

- New MailboxMessageContent component with mailbox-scoped pre/table/link
  overrides; links open in a new tab with noopener noreferrer.
- Wired into the full message body and conversation thread bodies in
  both Mailbox surfaces (parity preserved).
- CSS for .mailbox-markdown, .mailbox-markdown-pre, .mailbox-markdown-table
  with horizontal scroll for code blocks/tables; removed white-space:
  pre-wrap on bodies since markdown owns its own whitespace.
- 10 new unit tests cover headings, lists, bold/italic, inline code,
  fenced code blocks, link target/rel, GFM tables, plain-text passthrough,
  raw-HTML safety, and testId forwarding. Existing 108 mailbox tests
  continue to pass.
2026-05-08 20:48:26 -07:00

61 lines
1.8 KiB
TypeScript

import { memo } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import type { Components } from "react-markdown";
const mailboxMarkdownComponents: Components = {
pre: ({ children, ...props }) => (
<pre {...props} className="mailbox-markdown-pre">
{children}
</pre>
),
table: ({ children, ...props }) => (
<table {...props} className="mailbox-markdown-table">
{children}
</table>
),
// Open links in a new tab. ReactMarkdown does not allow raw HTML by default,
// so the rendered output here is safe.
a: ({ children, ...props }) => (
<a {...props} target="_blank" rel="noopener noreferrer">
{children}
</a>
),
};
interface MailboxMessageContentProps {
/** Raw message body. Rendered as GitHub-flavored markdown. */
content: string;
/** Optional extra class for the wrapper. */
className?: string;
/** Optional data-testid for test selectors. */
testId?: string;
}
/**
* Renders a mailbox message body as GitHub-flavored markdown.
*
* Uses ReactMarkdown defaults (no raw HTML) so untrusted message content is
* safe. Plain-text messages render unchanged (markdown is a strict superset
* for the formatting we care about — bold, lists, code, links, tables).
*
* Memoized because mailbox detail panes can re-render on selection / SSE
* updates while the underlying message body is unchanged.
*/
export const MailboxMessageContent = memo(function MailboxMessageContent({
content,
className,
testId,
}: MailboxMessageContentProps) {
const wrapperClass = className
? `mailbox-markdown ${className}`
: "mailbox-markdown";
return (
<div className={wrapperClass} data-testid={testId}>
<ReactMarkdown remarkPlugins={[remarkGfm]} components={mailboxMarkdownComponents}>
{content}
</ReactMarkdown>
</div>
);
});