Files
ttstd-infinite-canvas/web/src/components/ui/announcement-content.tsx

44 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { cn } from "@/lib/utils";
type AnnouncementContentProps = {
content: string;
className?: string;
};
/**
* 公告由管理员输入但会在所有用户页面展示,因此不启用原始 HTML,避免直接注入 HTML。
* 链接强制新窗口打开,并且只允许 http/https 协议。
*/
export function AnnouncementContent({ content, className }: AnnouncementContentProps) {
return (
<div className={cn("break-words [&_a]:text-blue-600 [&_a]:underline [&_a]:decoration-blue-600/40 [&_a]:underline-offset-2 [&_a]:transition [&_a]:hover:text-blue-700 dark:[&_a]:text-blue-400 dark:[&_a]:decoration-blue-400/50 dark:[&_a]:hover:text-blue-300 [&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em] [&_h1]:mb-2 [&_h1]:mt-3 [&_h1]:text-lg [&_h1]:font-semibold [&_h2]:mb-2 [&_h2]:mt-3 [&_h2]:text-base [&_h2]:font-semibold [&_h3]:mb-1 [&_h3]:mt-2 [&_h3]:font-semibold [&_li]:ml-5 [&_li]:list-disc [&_ol_li]:list-decimal [&_ol]:my-2 [&_p]:my-2 [&_pre]:my-2 [&_pre]:overflow-x-auto [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_table]:my-2 [&_table]:border-collapse [&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1 [&_th]:border [&_th]:border-border [&_th]:bg-muted [&_th]:px-2 [&_th]:py-1 [&_ul]:my-2", className)}>
<ReactMarkdown
remarkPlugins={[remarkGfm]}
skipHtml
components={{
a: ({ children, href }) => {
const safeURL = safeHref(href);
return safeURL ? <a href={safeURL} target="_blank" rel="noopener noreferrer">{children}</a> : <>{children}</>;
},
table: ({ children }) => <div className="overflow-x-auto"><table>{children}</table></div>,
}}
>
{content}
</ReactMarkdown>
</div>
);
}
function safeHref(value?: string) {
if (!value || typeof window === "undefined") return null;
try {
const url = new URL(value, window.location.href);
return url.protocol === "http:" || url.protocol === "https:" ? url.href : null;
} catch {
return null;
}
}