-
+
{skill.displayName}
diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json
index 3a41bc87..edc527fb 100644
--- a/web/src/i18n/locales/en.json
+++ b/web/src/i18n/locales/en.json
@@ -1,9 +1,13 @@
{
"nav": {
- "home": "Home",
+ "landing": "Home",
+ "home": "Skill Center",
+ "search": "Search",
+ "skillDetail": "Skill Detail",
"explore": "Explore Skills",
- "search": "Search Skills",
"dashboard": "Dashboard",
+ "mySkills": "My Skills",
+ "publish": "Publish",
"login": "Login"
},
"landing": {
@@ -848,7 +852,7 @@
"community": "Community",
"privacy": "Privacy Policy",
"terms": "Terms of Service",
- "copyright": "© 2024 SkillHub. All rights reserved."
+ "copyright": "© 2026 SkillHub. All rights reserved."
},
"publish": {
"title": "Publish Skill",
diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json
index e1080cfa..988e106c 100644
--- a/web/src/i18n/locales/zh.json
+++ b/web/src/i18n/locales/zh.json
@@ -1,9 +1,13 @@
{
"nav": {
- "home": "首页",
+ "landing": "首页",
+ "home": "技能中心",
+ "search": "搜索",
+ "skillDetail": "技能详情",
"explore": "探索",
- "search": "搜索技能",
"dashboard": "控制台",
+ "mySkills": "我的技能",
+ "publish": "发布",
"login": "登录"
},
"landing": {
@@ -848,7 +852,7 @@
"community": "社区",
"privacy": "隐私政策",
"terms": "服务条款",
- "copyright": "© 2024 SkillHub. 保留所有权利。"
+ "copyright": "© 2026 SkillHub. 保留所有权利。"
},
"publish": {
"title": "发布技能",
diff --git a/web/src/index.css b/web/src/index.css
index bd6e84d5..b99f6a03 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -4,63 +4,76 @@
@layer base {
:root {
- /* Aurora Tech — light theme (default) */
- --background: 210 40% 97%;
- --foreground: 222 47% 11%;
+ /* SkillHub — 浅色主题 (indigo-violet brand) */
+ --background: 210 20% 98%;
+ --foreground: 220 26% 14%;
--card: 0 0% 100%;
- --card-foreground: 222 47% 11%;
+ --card-foreground: 220 26% 14%;
--popover: 0 0% 100%;
- --popover-foreground: 222 47% 11%;
- /* Cyan primary */
- --primary: 192 80% 42%;
+ --popover-foreground: 220 26% 14%;
+ /* Indigo primary (#6A6DFF) */
+ --primary: 239 100% 71%;
--primary-foreground: 0 0% 100%;
/* Surface tones */
- --secondary: 210 30% 93%;
- --secondary-foreground: 222 47% 11%;
- --muted: 210 25% 92%;
- --muted-foreground: 215 16% 42%;
- /* Violet accent */
- --accent: 263 60% 58%;
+ --secondary: 210 30% 96%;
+ --secondary-foreground: 220 26% 14%;
+ --muted: 210 25% 95%;
+ --muted-foreground: 215 14% 46%;
+ /* Violet accent (#B85EFF) */
+ --accent: 271 100% 68%;
--accent-foreground: 0 0% 100%;
--destructive: 0 72% 55%;
--destructive-foreground: 0 0% 100%;
- --border: 214 20% 88%;
- --input: 214 20% 88%;
- --ring: 192 80% 42%;
+ --border: 214 32% 91%;
+ --input: 214 32% 91%;
+ --ring: 239 100% 71%;
--radius: 0.75rem;
/* Extended palette */
- --surface-glass: 210 30% 95%;
- --glow-primary: 192 80% 42%;
- --glow-accent: 263 60% 58%;
+ --surface-glass: 210 30% 97%;
+ --glow-primary: 239 100% 71%;
+ --glow-accent: 271 100% 68%;
--success: 160 60% 45%;
--warning: 38 92% 58%;
+
+ /* Brand */
+ --brand-start: #6A6DFF;
+ --brand-end: #B85EFF;
+ --brand-gradient: linear-gradient(135deg, #6A6DFF 0%, #B85EFF 100%);
+
+ /* Text semantic */
+ --text-secondary: 215 19% 35%;
+ --text-muted: 215 14% 46%;
+ --text-placeholder: 213 12% 63%;
+
+ /* Border semantic */
+ --border-card: 220 26% 94%;
}
.dark {
- /* Aurora Tech — deep navy dark theme */
+ /* Dark theme (preserved, not default) */
--background: 222 47% 6%;
--foreground: 210 40% 96%;
--card: 222 40% 9%;
--card-foreground: 210 40% 96%;
--popover: 222 40% 9%;
--popover-foreground: 210 40% 96%;
- --primary: 192 91% 56%;
+ --primary: 239 100% 75%;
--primary-foreground: 222 47% 6%;
--secondary: 222 30% 13%;
--secondary-foreground: 210 30% 85%;
--muted: 222 25% 15%;
--muted-foreground: 215 20% 55%;
- --accent: 263 70% 70%;
+ --accent: 271 100% 72%;
--accent-foreground: 0 0% 100%;
--destructive: 0 72% 55%;
--destructive-foreground: 0 0% 100%;
--border: 222 20% 18%;
--input: 222 20% 18%;
- --ring: 192 91% 56%;
+ --ring: 239 100% 75%;
--surface-glass: 222 35% 11%;
- --glow-primary: 192 91% 56%;
- --glow-accent: 263 70% 70%;
+ --glow-primary: 239 100% 75%;
+ --glow-accent: 271 100% 72%;
}
}
@@ -75,7 +88,7 @@
body {
@apply bg-background text-foreground antialiased;
- font-family: 'IBM Plex Sans', system-ui, sans-serif;
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
h1, h2, h3, h4, h5, h6 {
@@ -216,21 +229,21 @@
/* ─── Gradient text ─── */
.text-gradient-primary {
- background: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--primary) / 0.7));
+ background: linear-gradient(135deg, hsl(var(--primary)), hsl(var(--accent)));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.text-gradient-hero {
- background: linear-gradient(135deg, hsl(192 80% 38%), hsl(192 80% 42%), hsl(263 60% 52%));
+ background: var(--brand-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.dark .text-gradient-hero {
- background: linear-gradient(135deg, hsl(192 95% 65%), hsl(192 91% 56%), hsl(263 70% 70%));
+ background: linear-gradient(135deg, #8183FF, #C77EFF);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -279,3 +292,120 @@
.focus-ring {
@apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background;
}
+
+/* ─── Brand gradient utilities ─── */
+.text-brand-gradient {
+ background: var(--brand-gradient);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+}
+
+.bg-brand-gradient {
+ background: var(--brand-gradient);
+}
+
+/* ─── Handle tag (namespace @ pill) ─── */
+.handle-tag {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 10px;
+ font-size: 12px;
+ font-weight: 500;
+ color: #0369a1;
+ background: #e0f2fe;
+ border: 1px solid #7dd3fc;
+ border-radius: 9999px;
+}
+
+/* ─── Status pills ─── */
+.status-pill {
+ display: inline-flex;
+ align-items: center;
+ margin-left: 5px;
+ padding: 4px 18px;
+ font-size: 12px;
+ font-weight: 500;
+ border-radius: 9999px;
+ color: white;
+}
+
+.status-pill--published { background: #16a34a; }
+.status-pill--review { background: #ea580c; }
+.status-pill--archived { background: #6b7280; }
+
+/* ─── Role pill ─── */
+.role-pill {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 10px;
+ font-size: 12px;
+ font-weight: 500;
+ border-radius: 9999px;
+ border: 1px solid hsl(var(--border));
+ color: hsl(var(--text-secondary));
+}
+
+/* ─── Soft badges ─── */
+.badge-soft {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 10px;
+ border-radius: 9999px;
+ font-size: 12px;
+ font-weight: 500;
+}
+
+.badge-soft-green {
+ background: #dcfce7;
+ color: #166534;
+}
+
+.badge-soft-blue {
+ background: #dbeafe;
+ color: #1d4ed8;
+}
+
+/* ─── Tab active ─── */
+.tab-active {
+ border-bottom: 2px solid #3b82f6;
+ color: #1d4ed8;
+}
+
+/* ─── Code block ─── */
+.code-block {
+ background: #1A202C;
+ color: #e5e7eb;
+ border-radius: 12px;
+}
+
+/* ─── Upload zone ─── */
+.upload-zone {
+ border: 2px dashed hsl(var(--border));
+ background: hsl(var(--background));
+ transition: border-color 0.2s ease, background 0.2s ease;
+}
+
+.upload-zone:hover {
+ border-color: var(--brand-start);
+ background: rgba(106, 109, 255, 0.04);
+}
+
+.upload-zone .upload-zone-icon {
+ color: hsl(var(--muted-foreground));
+ transition: color 0.2s ease;
+}
+
+.upload-zone:hover .upload-zone-icon {
+ color: var(--brand-start);
+}
+
+/* ─── Feature icon shadow ─── */
+.feature-icon {
+ box-shadow: 0 14px 30px rgba(106, 109, 255, 0.45);
+}
+
+/* ─── Hero input placeholder ─── */
+.hero-input::placeholder {
+ color: hsl(var(--text-placeholder));
+}
diff --git a/web/src/pages/dashboard.tsx b/web/src/pages/dashboard.tsx
index 896ec209..4102396b 100644
--- a/web/src/pages/dashboard.tsx
+++ b/web/src/pages/dashboard.tsx
@@ -20,8 +20,8 @@ export function DashboardPage() {
return (
-
{t('dashboard.title')}
-
+
{t('dashboard.title')}
+
{t('dashboard.subtitle')}
@@ -56,7 +56,7 @@ export function DashboardPage() {
{user.platformRoles.map((role: string) => (
{role}
diff --git a/web/src/pages/dashboard/my-skills.tsx b/web/src/pages/dashboard/my-skills.tsx
index 9ec97540..08cd98f9 100644
--- a/web/src/pages/dashboard/my-skills.tsx
+++ b/web/src/pages/dashboard/my-skills.tsx
@@ -59,15 +59,15 @@ export function MySkillsPage() {
const resolveStatusClassName = (status?: string) => {
if (status === 'ARCHIVED') {
- return 'bg-slate-500/10 text-slate-500 border-slate-500/20'
+ return 'status-pill status-pill--archived'
}
if (status === 'PENDING_REVIEW') {
- return 'bg-amber-500/10 text-amber-500 border-amber-500/20'
+ return 'status-pill status-pill--review'
}
if (status === 'PUBLISHED') {
- return 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20'
+ return 'status-pill status-pill--published'
}
- return 'bg-secondary/60 text-muted-foreground border-border/40'
+ return 'status-pill'
}
const handleArchiveSkill = async () => {
@@ -201,28 +201,16 @@ export function MySkillsPage() {
{skill.summary && (
{skill.summary}
)}
-
-
@{skill.namespace}
- {skill.latestVersion && (
-
v{skill.latestVersion}
- )}
- {skill.status ? (
-
- {resolveStatusLabel(skill.status)}
-
- ) : null}
- {skill.latestVersionStatus ? (
-
- {resolveStatusLabel(skill.latestVersionStatus)}
-
- ) : null}
-
-
- {formatCompactCount(skill.downloadCount)}
+ {skill.status ? (
+
+ {resolveStatusLabel(skill.status)}
-
+ ) : null}
+ {skill.latestVersionStatus ? (
+
+ {resolveStatusLabel(skill.latestVersionStatus)}
+
+ ) : null}
{skill.latestVersionStatus === 'PENDING_REVIEW' && skill.latestVersion ? (
diff --git a/web/src/pages/home.tsx b/web/src/pages/home.tsx
index 3b1ef4c9..ec79abb3 100644
--- a/web/src/pages/home.tsx
+++ b/web/src/pages/home.tsx
@@ -3,147 +3,10 @@ import { useTranslation } from 'react-i18next'
import { SearchBar } from '@/features/search/search-bar'
import { SkillCard } from '@/features/skill/skill-card'
import { SkeletonList } from '@/shared/components/skeleton-loader'
+import { QuickStartSection } from '@/shared/components/quick-start'
import { useSearchSkills } from '@/shared/hooks/use-skill-queries'
import { normalizeSearchQuery } from '@/shared/lib/search-query'
import { Button } from '@/shared/ui/button'
-import { Check, Copy, Terminal, Settings, PackageOpen } from 'lucide-react'
-import { useState, useMemo } from 'react'
-
-function getAppBaseUrl(): string {
- if (typeof window === 'undefined') {
- return 'https://skill.xfyun.cn'
- }
- const runtimeConfig = window.__SKILLHUB_RUNTIME_CONFIG__
- if (runtimeConfig?.appBaseUrl) {
- return runtimeConfig.appBaseUrl
- }
- return `${window.location.protocol}//${window.location.host}`
-}
-
-function CopyButton({ text }: { text: string }) {
- const { t } = useTranslation()
- const [copied, setCopied] = useState(false)
-
- const handleCopy = async () => {
- try {
- await navigator.clipboard.writeText(text)
- setCopied(true)
- window.setTimeout(() => setCopied(false), 2000)
- } catch (err) {
- console.error('Failed to copy:', err)
- }
- }
-
- return (
-
- )
-}
-
-function CodeBlock({ code }: { code: string }) {
- return (
-
-
- bash
-
-
-
- {code}
-
-
- )
-}
-
-function QuickStartSection() {
- const { t } = useTranslation()
- const baseUrl = useMemo(() => getAppBaseUrl(), [])
-
- const steps = [
- {
- icon:
,
- title: t('home.quickStart.steps.configureEnv.title'),
- description: t('home.quickStart.steps.configureEnv.description'),
- code: `# Linux/macOS
-export CLAWHUB_SITE=${baseUrl}
-export CLAWHUB_REGISTRY=${baseUrl}
-
-# Windows PowerShell
-$env:CLAWHUB_SITE = '${baseUrl}'
-$env:CLAWHUB_REGISTRY = '${baseUrl}'`,
- },
- {
- icon:
,
- title: t('home.quickStart.steps.installSkills.title'),
- description: t('home.quickStart.steps.installSkills.description'),
- code: t('home.quickStart.steps.installSkills.code'),
- },
- {
- icon:
,
- title: t('home.quickStart.steps.publishSkills.title'),
- description: t('home.quickStart.steps.publishSkills.description'),
- code: t('home.quickStart.steps.publishSkills.code'),
- },
- ]
-
- return (
-
-
-
- {t('home.quickStart.title')}
-
- {t('home.quickStart.subtitle')}
-
-
-
- {t('home.quickStart.description')}
-
-
-
-
- {steps.map((step, idx) => (
-
-
-
- {step.icon}
-
-
-
-
- {step.title}
-
-
- {step.description}
-
-
-
-
-
-
- ))}
-
-
-
-
-
- {t('home.quickStart.tip')}
-
-
-
-
- )
-}
export function HomePage() {
const { t } = useTranslation()
@@ -172,13 +35,13 @@ export function HomePage() {
{/* Hero Section */}
-
+
SkillHub
-
+
{t('home.subtitle')}
-
+
{t('home.description')}
@@ -188,12 +51,19 @@ export function HomePage() {
-
@@ -201,8 +71,10 @@ export function HomePage() {
-
{t('home.popularTitle')}
-
{t('home.popularDescription')}
+
+ {t('home.popularTitle')}
+
+
{t('home.popularDescription')}
-
{t('home.latestTitle')}
-
{t('home.latestDescription')}
+
+ {t('home.latestTitle')}
+
+
{t('home.latestDescription')}
{/* Quick Start Section */}
-
+
)
}
diff --git a/web/src/pages/landing.tsx b/web/src/pages/landing.tsx
index adc5b89f..8dca53fb 100644
--- a/web/src/pages/landing.tsx
+++ b/web/src/pages/landing.tsx
@@ -1,444 +1,174 @@
import { Link, useNavigate } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
-import { SearchBar } from '@/features/search/search-bar'
-import { useAuth } from '@/features/auth/use-auth'
-import { LanguageSwitcher } from '@/shared/components/language-switcher'
import { normalizeSearchQuery } from '@/shared/lib/search-query'
-import { UserMenu } from '@/shared/components/user-menu'
-import { Button } from '@/shared/ui/button'
-import { Check, Copy, Terminal, Settings, PackageOpen } from 'lucide-react'
-import { useEffect, useRef, useState, useMemo } from 'react'
-
-function getAppBaseUrl(): string {
- if (typeof window === 'undefined') {
- return 'https://skill.xfyun.cn'
- }
- const runtimeConfig = window.__SKILLHUB_RUNTIME_CONFIG__
- if (runtimeConfig?.appBaseUrl) {
- return runtimeConfig.appBaseUrl
- }
- return `${window.location.protocol}//${window.location.host}`
-}
-
-function CopyButton({ text }: { text: string }) {
- const { t } = useTranslation()
- const [copied, setCopied] = useState(false)
-
- const handleCopy = async () => {
- try {
- await navigator.clipboard.writeText(text)
- setCopied(true)
- window.setTimeout(() => setCopied(false), 2000)
- } catch (err) {
- console.error('Failed to copy:', err)
- }
- }
-
- return (
-
- {copied ? : }
-
- )
-}
-
-function CodeBlock({ code }: { code: string }) {
- return (
-
-
- bash
-
-
-
- {code}
-
-
- )
-}
-
-function QuickStartSection() {
- const { t } = useTranslation()
- const baseUrl = useMemo(() => getAppBaseUrl(), [])
-
- const steps = [
- {
- icon: ,
- title: t('landing.quickStart.steps.configureEnv.title'),
- description: t('landing.quickStart.steps.configureEnv.description'),
- code: `# Linux/macOS
-export CLAWHUB_SITE=${baseUrl}
-export CLAWHUB_REGISTRY=${baseUrl}
-
-# Windows PowerShell
-$env:CLAWHUB_SITE = '${baseUrl}'
-$env:CLAWHUB_REGISTRY = '${baseUrl}'`,
- },
- {
- icon: ,
- title: t('landing.quickStart.steps.installSkills.title'),
- description: t('landing.quickStart.steps.installSkills.description'),
- code: t('landing.quickStart.steps.installSkills.code'),
- },
- {
- icon: ,
- title: t('landing.quickStart.steps.publishSkills.title'),
- description: t('landing.quickStart.steps.publishSkills.description'),
- code: t('landing.quickStart.steps.publishSkills.code'),
- },
- ]
-
- return (
-
-
-
- {t('landing.quickStart.title')}
-
- {t('landing.quickStart.subtitle')}
-
-
-
- {t('landing.quickStart.description')}
-
-
-
-
- {steps.map((step, idx) => (
-
-
-
- {step.icon}
-
-
-
-
- {step.title}
-
-
- {step.description}
-
-
-
-
-
-
- ))}
-
-
-
-
-
- {t('landing.quickStart.tip')}
-
-
-
-
- )
-}
+import { PackageOpen, Terminal, Shield, Users, GitBranch, Search as SearchIcon, Settings } from 'lucide-react'
+import { QuickStartSection } from '@/shared/components/quick-start'
export function LandingPage() {
const { t } = useTranslation()
const navigate = useNavigate()
- const { user, isLoading } = useAuth()
- const canvasRef = useRef(null)
- const [stats] = useState({
- skills: '1000+',
- downloads: '50K+',
- teams: '200+',
- })
-
- useEffect(() => {
- const canvas = canvasRef.current
- if (!canvas) return
-
- const ctx = canvas.getContext('2d')
- if (!ctx) return
-
- const resize = () => {
- if (canvas) {
- canvas.width = window.innerWidth
- canvas.height = window.innerHeight
- }
- }
- resize()
- window.addEventListener('resize', resize)
-
- class Particle {
- x: number
- y: number
- vx: number
- vy: number
- radius: number
-
- constructor(canvasWidth: number, canvasHeight: number) {
- this.x = Math.random() * canvasWidth
- this.y = Math.random() * canvasHeight
- this.vx = (Math.random() - 0.5) * 0.3
- this.vy = (Math.random() - 0.5) * 0.3
- this.radius = Math.random() * 1.5 + 0.5
- }
-
- update(canvasWidth: number, canvasHeight: number) {
- this.x += this.vx
- this.y += this.vy
- if (this.x < 0 || this.x > canvasWidth) this.vx *= -1
- if (this.y < 0 || this.y > canvasHeight) this.vy *= -1
- }
-
- draw(context: CanvasRenderingContext2D) {
- context.beginPath()
- context.arc(this.x, this.y, this.radius, 0, Math.PI * 2)
- context.fillStyle = 'rgba(56, 189, 248, 0.6)'
- context.fill()
- }
- }
-
- const particles: Particle[] = []
- const particleCount = 80
-
- for (let i = 0; i < particleCount; i++) {
- particles.push(new Particle(canvas.width, canvas.height))
- }
-
- const connectParticles = () => {
- for (let i = 0; i < particles.length; i++) {
- for (let j = i + 1; j < particles.length; j++) {
- const dx = particles[i].x - particles[j].x
- const dy = particles[i].y - particles[j].y
- const distance = Math.sqrt(dx * dx + dy * dy)
-
- if (distance < 120) {
- ctx.beginPath()
- const opacity = 0.15 * (1 - distance / 120)
- ctx.strokeStyle = 'rgba(56, 189, 248, ' + opacity + ')'
- ctx.lineWidth = 0.5
- ctx.moveTo(particles[i].x, particles[i].y)
- ctx.lineTo(particles[j].x, particles[j].y)
- ctx.stroke()
- }
- }
- }
- }
-
- const animate = () => {
- if (!canvas) return
- ctx.clearRect(0, 0, canvas.width, canvas.height)
-
- particles.forEach(particle => {
- particle.update(canvas.width, canvas.height)
- particle.draw(ctx)
- })
-
- connectParticles()
- requestAnimationFrame(animate)
- }
-
- animate()
-
- return () => {
- window.removeEventListener('resize', resize)
- }
- }, [])
const handleSearch = (query: string) => {
- navigate({ to: '/search', search: { q: normalizeSearchQuery(query), sort: 'relevance', page: 0, starredOnly: false } })
+ const normalized = normalizeSearchQuery(query)
+ navigate({
+ to: '/search',
+ search: { q: normalized, sort: 'relevance', page: 0, starredOnly: false },
+ })
}
const features = [
{
- icon: '🔒',
- title: t('landing.featuresList.privateDeploy.title'),
- description: t('landing.featuresList.privateDeploy.description'),
+ icon: ,
+ title: t('landing.features.secure.title'),
+ description: t('landing.features.secure.description'),
},
{
- icon: '📦',
- title: t('landing.featuresList.versionControl.title'),
- description: t('landing.featuresList.versionControl.description'),
+ icon: ,
+ title: t('landing.features.community.title'),
+ description: t('landing.features.community.description'),
},
{
- icon: '🔍',
- title: t('landing.featuresList.smartSearch.title'),
- description: t('landing.featuresList.smartSearch.description'),
+ icon: ,
+ title: t('landing.features.integration.title'),
+ description: t('landing.features.integration.description'),
},
{
- icon: '👥',
- title: t('landing.featuresList.teamwork.title'),
- description: t('landing.featuresList.teamwork.description'),
+ icon: ,
+ title: t('landing.features.versionControl.title', { defaultValue: '版本控制' }),
+ description: t('landing.features.versionControl.description', { defaultValue: '完善的版本管理和发布流程,确保技能包的质量和可追溯性。' }),
},
{
- icon: '✅',
- title: t('landing.featuresList.governance.title'),
- description: t('landing.featuresList.governance.description'),
+ icon: ,
+ title: t('landing.features.cli.title', { defaultValue: 'CLI 工具' }),
+ description: t('landing.features.cli.description', { defaultValue: '强大的命令行工具,支持快速发布、安装和管理技能包。' }),
},
{
- icon: '⚡',
- title: t('landing.featuresList.cliFirst.title'),
- description: t('landing.featuresList.cliFirst.description'),
+ icon: ,
+ title: t('landing.features.governance.title', { defaultValue: '审核治理' }),
+ description: t('landing.features.governance.description', { defaultValue: '内置审核流程和权限管理,保障企业级技能质量。' }),
},
]
+ const stats = [
+ { value: '1000+', label: t('landing.stats.skills', { defaultValue: '项目库' }) },
+ { value: '50K+', label: t('landing.stats.downloads', { defaultValue: '下载量' }) },
+ { value: '200+', label: t('landing.stats.teams', { defaultValue: '团队' }) },
+ ]
+
return (
-