feat(web): add CLI install tab on landing quick start

Add a third peer tab 'CLI' to LandingQuickStartSection that surfaces the
official install command 'npm i -g @astron-team/skillhub'. Layout uses
grid-cols-1 md:grid-cols-3 so mobile shows tabs stacked and desktop
shows three equal-width columns.

Addresses iflytek/skillhub#419 (homepage Quick Start part only).
This commit is contained in:
dongmucat 2026-05-12 11:05:02 +08:00
parent 15e55e8055
commit 8931f6d241
5 changed files with 81 additions and 6 deletions

View file

@ -0,0 +1,50 @@
import { expect, test } from '@playwright/test'
import { setEnglishLocale } from './helpers/auth-fixtures'
test.describe('Landing Quick Start CLI Tab (Real API)', () => {
test.beforeEach(async ({ page }) => {
await setEnglishLocale(page)
})
test('renders three peer tabs and exposes the CLI install command', async ({ page }) => {
await page.goto('/')
const agentTab = page.getByRole('button', { name: 'I am Agent', exact: true })
const humanTab = page.getByRole('button', { name: 'I am Human', exact: true })
const cliTab = page.getByRole('button', { name: 'CLI', exact: true })
await expect(agentTab).toBeVisible()
await expect(humanTab).toBeVisible()
await expect(cliTab).toBeVisible()
await expect(agentTab).toHaveAttribute('aria-pressed', 'true')
await cliTab.click()
await expect(cliTab).toHaveAttribute('aria-pressed', 'true')
await expect(agentTab).toHaveAttribute('aria-pressed', 'false')
await expect(humanTab).toHaveAttribute('aria-pressed', 'false')
await expect(
page.getByText('Install the SkillHub CLI locally to run skillhub install for skills.'),
).toBeVisible()
await expect(page.getByText('npm i -g @astron-team/skillhub', { exact: true })).toBeVisible()
})
test('agent and human tabs keep their original commands', async ({ page }) => {
await page.goto('/')
const agentTab = page.getByRole('button', { name: 'I am Agent', exact: true })
const humanTab = page.getByRole('button', { name: 'I am Human', exact: true })
await expect(
page.getByText(/Read .+\/registry\/skill\.md and follow the instructions/),
).toBeVisible()
await humanTab.click()
await expect(humanTab).toHaveAttribute('aria-pressed', 'true')
await expect(page.getByText('npx clawhub search <keyword>', { exact: true })).toBeVisible()
await agentTab.click()
await expect(agentTab).toHaveAttribute('aria-pressed', 'true')
})
})

View file

@ -12,4 +12,13 @@ describe('landing quick start locales', () => {
expect(zh.landing.quickStart.agent.commandTemplate).toBe('阅读 {{url}},并按照说明完成 SkillHub Skills Registry 的配置')
expect(en.landing.quickStart.agent.commandTemplate).toBe('Read {{url}} and follow the instructions to setup SkillHub Skills Registry')
})
it('exposes CLI install command in both locales', () => {
expect(zh.landing.quickStart.tabs.cli).toBe('CLI')
expect(zh.landing.quickStart.cli.command).toBe('npm i -g @astron-team/skillhub')
expect(zh.landing.quickStart.cli.description).toBe('安装 SkillHub CLI 到本地,后续可运行 skillhub install 安装技能')
expect(en.landing.quickStart.tabs.cli).toBe('CLI')
expect(en.landing.quickStart.cli.command).toBe('npm i -g @astron-team/skillhub')
expect(en.landing.quickStart.cli.description).toBe('Install the SkillHub CLI locally to run skillhub install for skills.')
})
})

View file

@ -103,7 +103,12 @@
"tip": "💡 Tip: Visit the skill detail page for one-click install commands with environment variables",
"tabs": {
"agent": "I am Agent",
"human": "I am Human"
"human": "I am Human",
"cli": "CLI"
},
"cli": {
"description": "Install the SkillHub CLI locally to run skillhub install for skills.",
"command": "npm i -g @astron-team/skillhub"
},
"agent": {
"description": "Send a prompt to your Agent to set up the SkillHub Registry",

View file

@ -103,7 +103,12 @@
"tip": "💡 提示:访问技能详情页可获取带环境变量的一键复制安装命令",
"tabs": {
"agent": "我是 Agent",
"human": "我是 Human"
"human": "我是 Human",
"cli": "CLI"
},
"cli": {
"description": "安装 SkillHub CLI 到本地,后续可运行 skillhub install 安装技能",
"command": "npm i -g @astron-team/skillhub"
},
"agent": {
"description": "发送提示词给你的 Agent,以设置SkillHub Registry",

View file

@ -1,9 +1,9 @@
import { useState, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Bot, Check, Copy, UserRound } from 'lucide-react'
import { Bot, Check, Copy, Terminal, UserRound } from 'lucide-react'
import { useCopyToClipboard } from '@/shared/lib/clipboard'
type LandingQuickStartTabId = 'agent' | 'human'
type LandingQuickStartTabId = 'agent' | 'human' | 'cli'
interface LandingQuickStartTab {
id: LandingQuickStartTabId
@ -83,6 +83,12 @@ export function LandingQuickStartSection() {
description: t('landing.quickStart.human.description'),
command: t('landing.quickStart.human.command'),
},
{
id: 'cli',
label: t('landing.quickStart.tabs.cli'),
description: t('landing.quickStart.cli.description'),
command: t('landing.quickStart.cli.command'),
},
]
const currentTab = tabs.find((tab) => tab.id === activeTab) ?? tabs[0]
@ -104,12 +110,12 @@ export function LandingQuickStartSection() {
style={{ borderColor: 'hsl(var(--border-card))' }}
>
<div
className="grid grid-cols-2 gap-2 rounded-2xl p-1.5"
className="grid grid-cols-1 gap-2 rounded-2xl p-1.5 md:grid-cols-3"
style={{ background: 'linear-gradient(180deg, rgba(248,250,252,0.98) 0%, rgba(241,245,249,0.92) 100%)' }}
>
{tabs.map((tab) => {
const isActive = tab.id === currentTab.id
const Icon = tab.id === 'agent' ? Bot : UserRound
const Icon = tab.id === 'agent' ? Bot : tab.id === 'human' ? UserRound : Terminal
return (
<button