feat(i18n): add multi-language support (ja/ko/vi/ru/zh-TW) with system auto-detection
- 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
This commit is contained in:
1 parent
fea6d8db04
commit
b0d0e3a5f3
301 files changed
+28002
-2705
No files matched your search
@@ -0,0 +1,103 @@
|
||||
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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user