- Extend AppLanguage to 7 locales: zh-CN, zh-TW, en, ja, ko, vi, ru - Auto-detect system language via navigator.languages; fallback to English - Add BCP47 locale mapping for date formatting and sorting - Create 50 translation JSON files (5 languages × 10 namespaces, 1137 keys each) - Update language selector with native language names - Extend locale key parity test to cover all 7 languages (70 assertions) - Add VNC/RDP desktop, serial terminal, AI agent, port forwarding, and SFTP features - Add credential encryption, host metrics, session backup crypto, and zmodem bridge - Update Rust backend: desktop services, host metrics, SSH/SFTP enhancements
104 lines
3.9 KiB
TypeScript
104 lines
3.9 KiB
TypeScript
import ReactMarkdown from 'react-markdown'
|
|
import remarkGfm from 'remark-gfm'
|
|
|
|
interface AgentMarkdownProps {
|
|
content: string
|
|
className?: string
|
|
}
|
|
|
|
/** 简单检测是否含 Markdown 结构(标题/表格/引用/列表/粗体) */
|
|
export function looksLikeMarkdown(text: string): boolean {
|
|
const t = text.trim()
|
|
if (!t) return false
|
|
return (
|
|
/^#{1,3}\s/m.test(t) ||
|
|
/^\|.+\|/m.test(t) ||
|
|
/^>\s/m.test(t) ||
|
|
/^[-*]\s/m.test(t) ||
|
|
/^\d+\.\s/m.test(t) ||
|
|
/\*\*[^*]+\*\*/.test(t) ||
|
|
/```/.test(t)
|
|
)
|
|
}
|
|
|
|
/** Agent 结论富文本:对齐 aiops AgentMarkdown,使用本产品色板 */
|
|
export function AgentMarkdown({ content, className = '' }: AgentMarkdownProps) {
|
|
return (
|
|
<div className={`agent-md min-w-0 ${className}`.trim()}>
|
|
<ReactMarkdown
|
|
remarkPlugins={[remarkGfm]}
|
|
components={{
|
|
h1: ({ children }) => (
|
|
<h1 className="mb-2 mt-3 text-base font-bold text-content-primary first:mt-0">{children}</h1>
|
|
),
|
|
h2: ({ children }) => (
|
|
<h2 className="mb-1.5 mt-3 text-sm font-semibold text-content-primary first:mt-0">{children}</h2>
|
|
),
|
|
h3: ({ children }) => (
|
|
<h3 className="mb-1 mt-2 text-sm font-medium text-content-primary first:mt-0">{children}</h3>
|
|
),
|
|
p: ({ children }) => (
|
|
<p className="my-1.5 text-xs leading-relaxed text-content-secondary">{children}</p>
|
|
),
|
|
ul: ({ children }) => (
|
|
<ul className="my-1.5 list-disc space-y-0.5 pl-5 text-xs text-content-secondary">{children}</ul>
|
|
),
|
|
ol: ({ children }) => (
|
|
<ol className="my-1.5 list-decimal space-y-0.5 pl-5 text-xs text-content-secondary">{children}</ol>
|
|
),
|
|
li: ({ children }) => <li className="leading-relaxed">{children}</li>,
|
|
strong: ({ children }) => (
|
|
<strong className="font-semibold text-content-primary">{children}</strong>
|
|
),
|
|
blockquote: ({ children }) => (
|
|
<blockquote className="my-2 border-l-2 border-accent-blue/50 pl-3 text-xs italic text-txt-muted">
|
|
{children}
|
|
</blockquote>
|
|
),
|
|
table: ({ children }) => (
|
|
<div className="border-dark-700/60 my-2 overflow-x-auto rounded-md border">
|
|
<table className="w-full border-collapse text-[11px]">{children}</table>
|
|
</div>
|
|
),
|
|
thead: ({ children }) => <thead className="bg-dark-900/80">{children}</thead>,
|
|
th: ({ children }) => (
|
|
<th className="border-dark-700/60 whitespace-nowrap border-b px-2.5 py-1.5 text-left font-medium text-content-primary">
|
|
{children}
|
|
</th>
|
|
),
|
|
td: ({ children }) => (
|
|
<td className="border-dark-700/40 border-b px-2.5 py-1.5 text-content-secondary">{children}</td>
|
|
),
|
|
tr: ({ children }) => <tr className="hover:bg-dark-800/40">{children}</tr>,
|
|
code: ({ className: codeClass, children }) => {
|
|
const isBlock = Boolean(codeClass?.includes('language-'))
|
|
if (isBlock) {
|
|
return (
|
|
<pre className="bg-dark-950/60 my-2 overflow-x-auto rounded px-2 py-1.5 text-[11px] text-content-secondary">
|
|
<code>{children}</code>
|
|
</pre>
|
|
)
|
|
}
|
|
return (
|
|
<code className="bg-dark-800 rounded px-1 py-0.5 text-[11px] text-content-primary">{children}</code>
|
|
)
|
|
},
|
|
hr: () => <hr className="border-dark-700/60 my-3" />,
|
|
a: ({ href, children }) => (
|
|
<a
|
|
href={href}
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="text-accent-blue underline-offset-2 hover:underline"
|
|
>
|
|
{children}
|
|
</a>
|
|
),
|
|
}}
|
|
>
|
|
{content}
|
|
</ReactMarkdown>
|
|
</div>
|
|
)
|
|
}
|