feat: Introduce agent operating rules and internal documentation, and refactor UI styling with design tokens.
This commit is contained in:
parent
9a9f8f2ec5
commit
a96da5d1d0
40
agent.md
Normal file
40
agent.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Agent Operating Rules
|
||||
|
||||
You are an AI agent working inside this repository.
|
||||
|
||||
## Role
|
||||
|
||||
- Act as a senior engineer and automation assistant.
|
||||
- Prioritize correctness, minimal changes, and reproducibility.
|
||||
- agent.md mirrors AGENTS.md; when in doubt, follow AGENTS.md as the source of truth.
|
||||
|
||||
## Global Rules
|
||||
|
||||
- Do not introduce new dependencies unless explicitly requested.
|
||||
- Do not change API contracts without explicit instruction.
|
||||
- Do not add new environment variables without approval.
|
||||
- Keep changes scoped to the request; avoid unrelated refactors.
|
||||
- Prefer minimal edits that preserve existing behavior and style.
|
||||
|
||||
## Repository Constraints (Quick View)
|
||||
|
||||
- App layer: src/app/**, src/components/**, src/lib/**, src/state/**, src/modules/\*\*
|
||||
- Library layer: packages/\*\* (no @/ aliases, no global state)
|
||||
- Global state: Zustand only, in src/state/** or src/app/store/**
|
||||
- URL-synced state must live in Zustand slices
|
||||
|
||||
## Testing Policy
|
||||
|
||||
- Follow mcp/testing.md for minimal verification.
|
||||
- Always run the minimal verification after a coherent change-set.
|
||||
|
||||
## Output Format
|
||||
|
||||
Always respond with:
|
||||
|
||||
1. Summary of changes
|
||||
2. Commands executed
|
||||
3. Result (success/failure)
|
||||
4. Next step
|
||||
|
||||
If these rules conflict with user instructions, ask once for clarification and proceed conservatively.
|
||||
20
mcp/architecture.md
Normal file
20
mcp/architecture.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Architecture & Stack
|
||||
|
||||
## Layers
|
||||
|
||||
- Application layer: src/app/**, src/components/**, src/lib/**, src/state/**, src/modules/\*\*
|
||||
- Library layer: packages/\*\* (vendored libraries, no app dependencies)
|
||||
- Build/runtime glue: scripts/**, config/**, public/\*\*
|
||||
|
||||
## Tech Stack
|
||||
|
||||
- Framework: Next.js App Router
|
||||
- Language: TypeScript (strict mode)
|
||||
- Styling: Tailwind CSS
|
||||
- UI: Radix UI
|
||||
- Content: Contentlayer
|
||||
|
||||
## State Management
|
||||
|
||||
- Global state uses Zustand only, confined to src/state/** or src/app/store/**.
|
||||
- URL-synced state must live in Zustand slices.
|
||||
9
mcp/constraints.md
Normal file
9
mcp/constraints.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Hard Constraints
|
||||
|
||||
- Do not introduce production-only services.
|
||||
- Do not add analytics or tracking.
|
||||
- Do not store personal data.
|
||||
- Do not change API contracts without explicit instruction.
|
||||
- Do not add new environment variables without approval.
|
||||
- Do not add dependencies unless explicitly requested.
|
||||
- packages/\*\* must remain app-agnostic and avoid @/ aliases.
|
||||
26
mcp/conventions.md
Normal file
26
mcp/conventions.md
Normal file
@ -0,0 +1,26 @@
|
||||
# Naming, Directory, Style
|
||||
|
||||
## Naming
|
||||
|
||||
- Components: PascalCase (UserProfile.tsx)
|
||||
- Utilities: kebab-case (user-utils.ts)
|
||||
- Variables: camelCase
|
||||
- Constants: UPPER_SNAKE_CASE
|
||||
- Types: PascalCase with T prefix for generics (TUser)
|
||||
|
||||
## Imports
|
||||
|
||||
- src/\*\* may use @/ aliases.
|
||||
- packages/\*\* must use relative imports or package exports.
|
||||
- Group imports: React, third-party, local types, local components.
|
||||
|
||||
## TypeScript
|
||||
|
||||
- Use type for type definitions.
|
||||
- Use interface for object shapes.
|
||||
- Prefer explicit return types for public APIs.
|
||||
|
||||
## Formatting
|
||||
|
||||
- Use Prettier via yarn format.
|
||||
- Avoid reformatting unrelated code.
|
||||
16
mcp/project.md
Normal file
16
mcp/project.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Project Overview
|
||||
|
||||
## Project Type
|
||||
|
||||
- Personal, non-commercial demo project.
|
||||
- No production deployments.
|
||||
- No real user data.
|
||||
|
||||
## Product Summary
|
||||
|
||||
- console.svc.plus is the Open Cloud Control Panel for the Cloud Neutral Toolkit.
|
||||
- Next.js App Router dashboard that connects multiple internal services.
|
||||
|
||||
## Target Environments
|
||||
|
||||
- Local development and demo environments only.
|
||||
15
mcp/testing.md
Normal file
15
mcp/testing.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Minimal Verification
|
||||
|
||||
## Next.js Dashboard
|
||||
|
||||
Required:
|
||||
|
||||
- yarn lint
|
||||
- yarn typecheck
|
||||
- yarn build
|
||||
|
||||
Best effort:
|
||||
|
||||
- yarn dev
|
||||
|
||||
Do not ask the user to run these unless manual interaction is required.
|
||||
2
next-env.d.ts
vendored
2
next-env.d.ts
vendored
@ -1,6 +1,6 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/dev/types/routes.d.ts";
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
||||
20
skills/coding.modify.md
Normal file
20
skills/coding.modify.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Skill: Modify Code Safely
|
||||
|
||||
## When to Use
|
||||
|
||||
- Bug fixes
|
||||
- Feature tweaks
|
||||
- Small refactors
|
||||
|
||||
## Steps
|
||||
|
||||
1. Identify affected files and confirm scope.
|
||||
2. Make minimal necessary changes.
|
||||
3. Run minimal verification (see mcp/testing.md).
|
||||
4. Report results concisely.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Do not reformat unrelated code.
|
||||
- Do not introduce new dependencies unless asked.
|
||||
- Do not ask the user to self-test unless required.
|
||||
18
skills/deps.upgrade.md
Normal file
18
skills/deps.upgrade.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Skill: Upgrade Dependencies
|
||||
|
||||
## When to Use
|
||||
|
||||
- Explicit dependency upgrade requests
|
||||
- Security or compatibility fixes
|
||||
|
||||
## Steps
|
||||
|
||||
1. Identify the target packages and version constraints.
|
||||
2. Upgrade with minimal scope and document rationale.
|
||||
3. Validate build and typecheck (see mcp/testing.md).
|
||||
4. Note any breaking changes or follow-ups.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Do not upgrade unrelated dependencies.
|
||||
- Do not change lockfile format unless required by the tool.
|
||||
19
skills/docs.write.md
Normal file
19
skills/docs.write.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Skill: Write Docs
|
||||
|
||||
## When to Use
|
||||
|
||||
- README updates
|
||||
- New developer guidance
|
||||
- Process documentation
|
||||
|
||||
## Steps
|
||||
|
||||
1. Locate the relevant documentation file.
|
||||
2. Update content with minimal scope.
|
||||
3. Keep formatting consistent with existing style.
|
||||
4. Run minimal verification only if code changes occur.
|
||||
|
||||
## Do Not
|
||||
|
||||
- Do not add marketing language.
|
||||
- Do not change code samples unrelated to the request.
|
||||
19
skills/refactor.safe.md
Normal file
19
skills/refactor.safe.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Skill: Safe Refactor
|
||||
|
||||
## When to Use
|
||||
|
||||
- Simplifying existing logic
|
||||
- Reducing duplication
|
||||
- Renaming symbols without behavior changes
|
||||
|
||||
## Steps
|
||||
|
||||
1. Confirm no behavior change is intended.
|
||||
2. Refactor in small, reversible steps.
|
||||
3. Keep interfaces stable unless instructed.
|
||||
4. Run minimal verification (see mcp/testing.md).
|
||||
|
||||
## Do Not
|
||||
|
||||
- Do not mix refactors with feature changes.
|
||||
- Do not reformat unrelated code.
|
||||
19
skills/testing.write.md
Normal file
19
skills/testing.write.md
Normal file
@ -0,0 +1,19 @@
|
||||
# Skill: Write Tests
|
||||
|
||||
## When to Use
|
||||
|
||||
- New features
|
||||
- Bug fixes with reproducible behavior
|
||||
- Refactors that change logic
|
||||
|
||||
## Steps
|
||||
|
||||
1. Identify test scope and location.
|
||||
2. Add or update unit tests under \*.test.ts/tsx.
|
||||
3. Mock external dependencies in test setup.
|
||||
4. Run minimal verification (see mcp/testing.md).
|
||||
|
||||
## Do Not
|
||||
|
||||
- Do not add E2E tests unless requested.
|
||||
- Do not change production code to fit tests.
|
||||
@ -11,8 +11,8 @@ export default function AboutPage() {
|
||||
const t = translations[language].about
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 text-slate-100">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(56,189,248,0.18),transparent_35%),radial-gradient(circle_at_80%_0,rgba(168,85,247,0.15),transparent_30%),radial-gradient(circle_at_50%_60%,rgba(52,211,153,0.08),transparent_35%)]" aria-hidden />
|
||||
<div className="min-h-screen bg-background text-text transition-colors duration-150">
|
||||
<div className="absolute inset-0 bg-gradient-app-from opacity-20" aria-hidden />
|
||||
|
||||
<div className="relative mx-auto max-w-7xl px-6 pb-20">
|
||||
<Navbar />
|
||||
@ -22,23 +22,23 @@ export default function AboutPage() {
|
||||
|
||||
{/* Header */}
|
||||
<div className="space-y-4 text-center">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-white sm:text-5xl">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-heading sm:text-5xl">
|
||||
{t.title}
|
||||
</h1>
|
||||
<p className="text-lg text-slate-300">
|
||||
<p className="text-lg text-text-muted">
|
||||
{t.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Disclaimer Section */}
|
||||
<div className="rounded-2xl border border-yellow-500/20 bg-yellow-500/5 p-8 shadow-inner shadow-yellow-500/10">
|
||||
<div className="rounded-2xl border border-warning/20 bg-warning/5 p-8 shadow-inner shadow-warning/10">
|
||||
<div className="flex gap-4">
|
||||
<div className="mt-1 shrink-0 text-yellow-500">
|
||||
<div className="mt-1 shrink-0 text-warning">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-alert-triangle"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" /><path d="M12 9v4" /><path d="M12 17h.01" /></svg>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-semibold text-yellow-200">Disclaimer</h3>
|
||||
<p className="text-sm leading-relaxed text-yellow-100/80">
|
||||
<h3 className="font-semibold text-warning-foreground">Disclaimer</h3>
|
||||
<p className="text-sm leading-relaxed text-warning-foreground/80">
|
||||
{t.disclaimer}
|
||||
</p>
|
||||
</div>
|
||||
@ -46,33 +46,40 @@ export default function AboutPage() {
|
||||
</div>
|
||||
|
||||
{/* Acknowledgments */}
|
||||
<div className="space-y-8 rounded-3xl border border-white/10 bg-white/5 p-8 lg:p-12 shadow-2xl backdrop-blur-sm">
|
||||
<div className="space-y-8 rounded-3xl border border-surface-border bg-surface p-8 lg:p-12 shadow-2xl backdrop-blur-sm">
|
||||
<div className="space-y-6">
|
||||
<p className="text-lg leading-relaxed text-slate-300">
|
||||
<p className="text-lg leading-relaxed text-text-muted">
|
||||
{t.acknowledgments}
|
||||
</p>
|
||||
|
||||
<div className="space-y-4">
|
||||
<h3 className="text-sm font-semibold uppercase tracking-wider text-indigo-400">
|
||||
<h3 className="text-sm font-semibold uppercase tracking-wider text-primary">
|
||||
{t.toolsTitle}
|
||||
</h3>
|
||||
<ul className="grid gap-3 sm:grid-cols-2">
|
||||
{t.tools.map((tool, index) => (
|
||||
<li key={index} className="flex items-center gap-2 text-sm text-slate-300">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-indigo-500" />
|
||||
{tool}
|
||||
<li key={index} className="flex items-center gap-2 text-sm text-text-muted">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-primary" />
|
||||
<a
|
||||
href={tool.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="transition-colors hover:text-text hover:underline hover:decoration-primary"
|
||||
>
|
||||
{tool.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<p className="text-xs text-slate-500">
|
||||
<p className="text-xs text-text-subtle">
|
||||
{t.toolsNote}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative overflow-hidden rounded-xl bg-indigo-500/10 p-6">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-indigo-500/20 to-purple-500/20 opacity-50" />
|
||||
<div className="relative flex items-center gap-4 text-indigo-200">
|
||||
<div className="relative overflow-hidden rounded-xl bg-primary/10 p-6">
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-primary/20 to-accent/20 opacity-50" />
|
||||
<div className="relative flex items-center gap-4 text-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className="lucide lucide-heart h-6 w-6"><path d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z" /></svg>
|
||||
<p className="font-medium">
|
||||
{t.opensource}
|
||||
|
||||
234
src/app/page.tsx
234
src/app/page.tsx
@ -20,24 +20,43 @@ import {
|
||||
} from 'lucide-react'
|
||||
import Footer from '../components/Footer'
|
||||
import Navbar from '../components/Navbar'
|
||||
import { useUserStore } from '../lib/userStore'
|
||||
import { useLanguage } from '../i18n/LanguageProvider'
|
||||
import { translations } from '../i18n/translations'
|
||||
|
||||
const heroCards = [
|
||||
{
|
||||
title: 'Create your app',
|
||||
description: 'Add your application and configure the client details to start integrating quickly.',
|
||||
icon: PlusCircle,
|
||||
},
|
||||
{
|
||||
title: 'Register your app',
|
||||
description: 'Register your application to manage access and configure redirect URIs.',
|
||||
icon: ShieldCheck,
|
||||
},
|
||||
{
|
||||
title: 'Deploy your app',
|
||||
description: 'Manage your application and users within Cloud-Neutral Toolkit for secure access.',
|
||||
icon: Users,
|
||||
},
|
||||
]
|
||||
const iconMap: Record<string, any> = {
|
||||
// English keys
|
||||
'Create your app': PlusCircle,
|
||||
'Register your app': ShieldCheck,
|
||||
'Deploy your app': Users,
|
||||
'Add a new user to your project': Users,
|
||||
'Register a new application': AppWindow,
|
||||
'Deploy your application': Command,
|
||||
'Invite a user': MousePointerClick,
|
||||
'Get started': Sparkles,
|
||||
'Creating your application': AppWindow,
|
||||
'More about Authentication': ShieldCheck,
|
||||
'Understanding Authorization': Lock,
|
||||
'Machine-to-Machine': Layers,
|
||||
'Connect via CLI': Terminal,
|
||||
'REST & Admin APIs': Link,
|
||||
// Chinese keys
|
||||
'创建您的应用': PlusCircle,
|
||||
'注册您的应用': ShieldCheck,
|
||||
'部署您的应用': Users,
|
||||
'向项目添加新用户': Users,
|
||||
'注册新应用程序': AppWindow,
|
||||
'部署您的应用程序': Command,
|
||||
'邀请用户': MousePointerClick,
|
||||
'开始使用': Sparkles,
|
||||
'创建您的应用程序': AppWindow,
|
||||
'关于身份验证': ShieldCheck,
|
||||
'了解授权': Lock,
|
||||
'机器对机器': Layers,
|
||||
'通过 CLI 连接': Terminal,
|
||||
}
|
||||
|
||||
const getIcon = (key: string, fallback: any) => iconMap[key] || fallback
|
||||
|
||||
const nextSteps = [
|
||||
{ title: 'Add a new user to your project', status: 'NEW', icon: Users },
|
||||
@ -76,8 +95,8 @@ const shortcuts = [
|
||||
|
||||
export default function HomePage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-950 text-slate-100">
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_20%_20%,rgba(56,189,248,0.18),transparent_35%),radial-gradient(circle_at_80%_0,rgba(168,85,247,0.15),transparent_30%),radial-gradient(circle_at_50%_60%,rgba(52,211,153,0.08),transparent_35%)]" aria-hidden />
|
||||
<div className="min-h-screen bg-background text-text transition-colors duration-150">
|
||||
<div className="absolute inset-0 bg-gradient-app-from opacity-20" aria-hidden />
|
||||
<div className="relative mx-auto max-w-7xl px-6 pb-20">
|
||||
<Navbar />
|
||||
<main className="space-y-12 pt-10">
|
||||
@ -93,46 +112,42 @@ export default function HomePage() {
|
||||
}
|
||||
|
||||
function HeroSection() {
|
||||
const { user } = useUserStore()
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language].marketing.home
|
||||
|
||||
return (
|
||||
<section className="grid gap-10 lg:grid-cols-[1.1fr_0.9fr]">
|
||||
<div className="space-y-8">
|
||||
<div className="flex items-center gap-2 text-sm font-medium text-slate-300">
|
||||
<span className="inline-flex rounded-full border border-indigo-500/30 bg-indigo-500/10 px-3 py-1 text-indigo-100">Signed in</span>
|
||||
<span>example.tenant.zitadel.cloud</span>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center space-y-8">
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-slate-400">Create, authenticate, deploy</p>
|
||||
<h1 className="text-4xl font-bold leading-tight text-white sm:text-5xl">Get started with Cloud-Neutral Toolkit</h1>
|
||||
<p className="max-w-2xl text-lg text-slate-300">
|
||||
Integrate Cloud-Neutral Toolkit into your application or use one of our samples to get started quickly.
|
||||
</p>
|
||||
<p className="font-semibold uppercase tracking-wider text-text-subtle">{t.hero.eyebrow}</p>
|
||||
<h1 className="text-4xl font-bold tracking-tight text-heading sm:text-6xl">{t.hero.title}</h1>
|
||||
<p className="text-lg text-text-muted">{t.hero.subtitle}</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3 text-sm font-semibold">
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center gap-2 rounded-full bg-indigo-500 px-4 py-2 text-white shadow-lg shadow-indigo-500/25 transition hover:translate-y-[-1px] hover:bg-indigo-400"
|
||||
>
|
||||
<PlusCircle className="h-4 w-4" aria-hidden />
|
||||
Create Application
|
||||
<ArrowRight className="h-4 w-4" aria-hidden />
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-4 py-2 text-white transition hover:bg-white/10"
|
||||
>
|
||||
<Play className="h-4 w-4" aria-hidden />
|
||||
Try Samples in Playground
|
||||
</a>
|
||||
<a
|
||||
href="#"
|
||||
className="inline-flex items-center gap-2 rounded-full border border-white/10 px-4 py-2 text-white transition hover:border-white/40"
|
||||
>
|
||||
<BookOpen className="h-4 w-4" aria-hidden />
|
||||
View Tutorials
|
||||
</a>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{user ? (
|
||||
<div className="flex items-center gap-2 rounded-full border border-success/30 bg-success/10 px-4 py-1.5 text-sm font-medium text-success">
|
||||
<div className="h-2 w-2 rounded-full bg-success animate-pulse" />
|
||||
{t.signedIn} as {user.username}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3 text-sm text-slate-300">
|
||||
<span className="font-semibold text-white">Trusted by your dev team</span>
|
||||
) : (
|
||||
<button className="flex items-center gap-2 rounded-full bg-primary px-6 py-2.5 text-sm font-semibold text-white transition hover:bg-primary-hover">
|
||||
<PlusCircle className="h-4 w-4" />
|
||||
{t.heroButtons.create}
|
||||
</button>
|
||||
)}
|
||||
<button className="flex items-center gap-2 rounded-full border border-surface-border bg-surface px-6 py-2.5 text-sm font-semibold text-text transition hover:bg-surface-hover">
|
||||
<Play className="h-4 w-4" />
|
||||
{t.heroButtons.playground}
|
||||
</button>
|
||||
<button className="flex items-center gap-2 rounded-full border border-surface-border bg-surface px-6 py-2.5 text-sm font-semibold text-text transition hover:bg-surface-hover">
|
||||
<BookOpen className="h-4 w-4" />
|
||||
{t.heroButtons.tutorials}
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 text-sm text-text-muted">
|
||||
<p>{t.trustedBy}</p>
|
||||
<div className="flex gap-2">
|
||||
<LogoPill label="Vue" />
|
||||
<LogoPill label="Svelte" />
|
||||
<LogoPill label="Node" />
|
||||
@ -140,70 +155,77 @@ function HeroSection() {
|
||||
<LogoPill label="Laravel" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-3 rounded-2xl border border-white/10 bg-white/5 p-5 shadow-xl shadow-slate-900/40 backdrop-blur">
|
||||
{heroCards.map((card) => (
|
||||
<div key={card.title} className="group relative overflow-hidden rounded-xl border border-white/10 bg-slate-900/70 p-4 transition hover:border-indigo-400/50 hover:bg-slate-900">
|
||||
<div className="absolute inset-0 opacity-0 transition group-hover:opacity-100" aria-hidden>
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-indigo-500/10 via-transparent to-sky-400/10" />
|
||||
</div>
|
||||
<div className="relative flex gap-3">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-white/5 text-indigo-200">
|
||||
<card.icon className="h-5 w-5" aria-hidden />
|
||||
<div className="flex flex-col gap-4">
|
||||
{t.heroCards.map((card, index: number) => {
|
||||
const Icon = getIcon(card.title, PlusCircle)
|
||||
return (
|
||||
<div key={card.title} className="group flex items-start gap-4 rounded-2xl border border-surface-border bg-surface p-6 transition hover:border-primary/50 hover:bg-surface-hover">
|
||||
<div className="mt-1 rounded-full border border-surface-border bg-surface-muted p-2 group-hover:border-primary/50 group-hover:text-primary">
|
||||
<Icon className="h-5 w-5" />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<h3 className="text-base font-semibold text-white">{card.title}</h3>
|
||||
<p className="text-sm text-slate-300">{card.description}</p>
|
||||
<button className="mt-2 inline-flex items-center gap-1 text-xs font-semibold text-indigo-200 transition hover:text-white">
|
||||
Go to action
|
||||
<ArrowRight className="h-4 w-4" aria-hidden />
|
||||
<h3 className="font-semibold text-heading">{card.title}</h3>
|
||||
<p className="text-sm text-text-muted">{card.description}</p>
|
||||
<button className="mt-2 flex items-center gap-1 text-xs font-semibold text-primary transition group-hover:text-primary-hover">
|
||||
Go to action <ArrowRight className="h-3 w-3" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function NextStepsSection() {
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language].marketing.home
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<header className="flex items-center gap-3 text-sm text-slate-300">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-400">Your Next Steps</p>
|
||||
<span className="rounded-full bg-white/5 px-3 py-1 text-xs font-semibold text-indigo-200">Data detected</span>
|
||||
<header className="flex items-center gap-3 text-sm text-text-muted">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-text-subtle">{t.nextSteps.title}</p>
|
||||
<span className="rounded-full bg-surface-muted px-3 py-1 text-xs font-semibold text-primary">{t.nextSteps.badge}</span>
|
||||
</header>
|
||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-4">
|
||||
{nextSteps.map((item) => (
|
||||
<div key={item.title} className="flex items-start gap-3 rounded-xl border border-white/10 bg-white/5 p-4 shadow-lg shadow-slate-900/30">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/15 text-indigo-200">
|
||||
<item.icon className="h-5 w-5" aria-hidden />
|
||||
{t.nextSteps.items.map((item, index: number) => {
|
||||
const Icon = getIcon(item.title, Users)
|
||||
return (
|
||||
<div key={index} className="flex items-start gap-3 rounded-xl border border-surface-border bg-surface p-4 shadow-lg shadow-shadow-sm">
|
||||
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/15 text-primary">
|
||||
<Icon className="h-5 w-5" aria-hidden />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-wide text-indigo-100">
|
||||
<span className="rounded-full bg-indigo-500/20 px-2 py-0.5">{item.status}</span>
|
||||
<div className="flex items-center gap-2 text-[11px] font-semibold uppercase tracking-wide text-primary-muted">
|
||||
<span className="rounded-full bg-primary/20 px-2 py-0.5">{item.status}</span>
|
||||
</div>
|
||||
<p className="text-sm font-semibold text-white">{item.title}</p>
|
||||
<button className="inline-flex items-center gap-1 text-xs font-semibold text-indigo-200 transition hover:text-white">
|
||||
<p className="text-sm font-semibold text-heading">{item.title}</p>
|
||||
<button className="inline-flex items-center gap-1 text-xs font-semibold text-primary transition hover:text-primary-hover">
|
||||
Learn more
|
||||
<ArrowRight className="h-4 w-4" aria-hidden />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function StatsSection() {
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language].marketing.home
|
||||
|
||||
return (
|
||||
<section className="rounded-2xl border border-white/10 bg-gradient-to-r from-white/5 via-white/0 to-white/5 p-6 shadow-inner shadow-slate-900/50">
|
||||
<section className="rounded-2xl border border-surface-border bg-gradient-to-r from-surface-muted via-surface/0 to-surface-muted p-6 shadow-inner shadow-shadow-sm">
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{stats.map((stat) => (
|
||||
<div key={stat.label} className="space-y-1 text-center md:text-left">
|
||||
<div className="text-3xl font-semibold text-white">{stat.value}</div>
|
||||
<p className="text-sm text-slate-300">{stat.label}</p>
|
||||
{t.stats.map((stat, index: number) => (
|
||||
<div key={index} className="space-y-1 text-center md:text-left">
|
||||
<div className="text-3xl font-semibold text-heading">{stat.value}</div>
|
||||
<p className="text-sm text-text-muted">{stat.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -212,36 +234,42 @@ function StatsSection() {
|
||||
}
|
||||
|
||||
function ShortcutsSection() {
|
||||
const { language } = useLanguage()
|
||||
const t = translations[language].marketing.home
|
||||
|
||||
return (
|
||||
<section className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-400">More shortcuts</p>
|
||||
<p className="text-sm text-slate-300">Save time when integrating Cloud-Neutral Toolkit</p>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-text-subtle">{t.shortcuts.title}</p>
|
||||
<p className="text-sm text-text-muted">{t.shortcuts.subtitle}</p>
|
||||
</div>
|
||||
<div className="flex gap-2 text-xs font-semibold text-indigo-200">
|
||||
<button className="rounded-full border border-white/10 bg-white/5 px-3 py-1 transition hover:bg-white/10">Get Started</button>
|
||||
<button className="rounded-full border border-white/10 bg-white/5 px-3 py-1 transition hover:bg-white/10">Docs</button>
|
||||
<button className="rounded-full border border-white/10 bg-white/5 px-3 py-1 transition hover:bg-white/10">Guides</button>
|
||||
<div className="flex gap-2 text-xs font-semibold text-primary">
|
||||
<button className="rounded-full border border-surface-border bg-surface-muted px-3 py-1 transition hover:bg-surface-hover">{t.shortcuts.buttons.start}</button>
|
||||
<button className="rounded-full border border-surface-border bg-surface-muted px-3 py-1 transition hover:bg-surface-hover">{t.shortcuts.buttons.docs}</button>
|
||||
<button className="rounded-full border border-surface-border bg-surface-muted px-3 py-1 transition hover:bg-surface-hover">{t.shortcuts.buttons.guides}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
||||
{shortcuts.map((item) => (
|
||||
{t.shortcuts.items.map((item, index: number) => {
|
||||
const Icon = getIcon(item.title, Sparkles)
|
||||
return (
|
||||
<a
|
||||
key={item.title}
|
||||
key={index}
|
||||
href="#"
|
||||
className="group flex items-start gap-3 rounded-xl border border-white/10 bg-white/5 p-4 transition hover:-translate-y-[1px] hover:border-indigo-400/50 hover:bg-slate-900/60"
|
||||
className="group flex items-start gap-3 rounded-xl border border-surface-border bg-surface p-4 transition hover:-translate-y-[1px] hover:border-primary/50 hover:bg-surface-hover"
|
||||
>
|
||||
<div className="mt-1 flex h-10 w-10 items-center justify-center rounded-full bg-indigo-500/15 text-indigo-200">
|
||||
<item.icon className="h-5 w-5" aria-hidden />
|
||||
<div className="mt-1 flex h-10 w-10 items-center justify-center rounded-full bg-primary/15 text-primary">
|
||||
<Icon className="h-5 w-5" aria-hidden />
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<div className="text-sm font-semibold text-white">{item.title}</div>
|
||||
<p className="text-sm text-slate-300">{item.description}</p>
|
||||
<div className="text-sm font-semibold text-heading">{item.title}</div>
|
||||
<p className="text-sm text-text-muted">{item.description}</p>
|
||||
</div>
|
||||
<ArrowRight className="ml-auto h-4 w-4 text-slate-400 transition group-hover:text-indigo-200" aria-hidden />
|
||||
<ArrowRight className="ml-auto h-4 w-4 text-text-subtle transition group-hover:text-primary" aria-hidden />
|
||||
</a>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
@ -249,8 +277,8 @@ function ShortcutsSection() {
|
||||
|
||||
function LogoPill({ label }: { label: string }) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs font-semibold text-white">
|
||||
<div className="h-2 w-2 rounded-full bg-emerald-400" />
|
||||
<span className="inline-flex items-center gap-2 rounded-full border border-surface-border bg-surface-muted px-3 py-1 text-xs font-semibold text-text">
|
||||
<div className="h-2 w-2 rounded-full bg-success" />
|
||||
{label}
|
||||
</span>
|
||||
)
|
||||
|
||||
@ -156,6 +156,7 @@ export default function Navbar() {
|
||||
docs: isChinese ? '文档' : 'Docs',
|
||||
download: isChinese ? '博客' : 'blog',
|
||||
openSource: isChinese ? '开源项目' : 'Open source',
|
||||
about: isChinese ? '关于' : 'About',
|
||||
moreServices: isChinese ? '更多服务' : 'More services',
|
||||
}
|
||||
|
||||
@ -216,12 +217,12 @@ export default function Navbar() {
|
||||
<>
|
||||
<nav
|
||||
ref={navRef}
|
||||
className="sticky top-0 z-50 w-full border-b border-white/10 bg-slate-950/95 text-slate-100 backdrop-blur"
|
||||
className="sticky top-0 z-50 w-full border-b border-surface-border bg-background/95 text-text backdrop-blur transition-colors duration-150"
|
||||
>
|
||||
<div className="mx-auto w-full max-w-7xl px-6 sm:px-8">
|
||||
<div className="flex items-center gap-5 py-3">
|
||||
<div className="flex flex-1 items-center gap-5">
|
||||
<Link href="/" className="flex items-center gap-2 rounded-md border border-white/5 bg-slate-900/60 px-2.5 py-1.5 text-sm font-medium text-white/90 transition hover:bg-slate-800/60">
|
||||
<Link href="/" className="flex items-center gap-2 rounded-md border border-surface-border bg-surface-muted/60 px-2.5 py-1.5 text-sm font-medium text-text/90 transition hover:bg-surface-hover/60">
|
||||
<Image
|
||||
src="/icons/cloudnative_32.png"
|
||||
alt="logo"
|
||||
@ -230,14 +231,14 @@ export default function Navbar() {
|
||||
className="h-[20px] w-[20px] opacity-90"
|
||||
unoptimized
|
||||
/>
|
||||
<span className="text-sm font-medium opacity-90">Cloud-Neutral</span>
|
||||
<span className="text-sm font-medium opacity-90 text-text">Cloud-Neutral</span>
|
||||
</Link>
|
||||
<div className="hidden items-center gap-5 text-sm font-medium text-slate-200 lg:flex">
|
||||
<div className="hidden items-center gap-5 text-sm font-medium text-text-muted lg:flex">
|
||||
{mainLinks.map((link) => (
|
||||
<Link
|
||||
key={link.key}
|
||||
href={link.href}
|
||||
className="text-sm opacity-80 transition hover:text-white hover:opacity-100"
|
||||
className="text-sm opacity-80 transition hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
@ -245,15 +246,15 @@ export default function Navbar() {
|
||||
<Link
|
||||
key={downloadLink.key}
|
||||
href={downloadLink.href}
|
||||
className="text-sm opacity-80 transition hover:text-white hover:opacity-100"
|
||||
className="text-sm opacity-80 transition hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{downloadLink.label}
|
||||
</Link>
|
||||
<div className="group relative">
|
||||
<button className="flex items-center gap-1 text-sm opacity-80 transition hover:text-white hover:opacity-100">
|
||||
<button className="flex items-center gap-1 text-sm opacity-80 transition hover:text-primary hover:opacity-100">
|
||||
<span>{labels.openSource}</span>
|
||||
<svg
|
||||
className="h-4 w-4 text-slate-400 transition group-hover:text-indigo-200"
|
||||
className="h-4 w-4 text-text-subtle transition group-hover:text-primary"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
@ -263,22 +264,28 @@ export default function Navbar() {
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</button>
|
||||
<div className="absolute left-0 top-full hidden min-w-[200px] translate-y-1 rounded-lg border border-white/10 bg-slate-900/95 py-2 text-sm text-slate-100 opacity-0 shadow-[0_12px_32px_rgba(0,0,0,0.35)] transition-all duration-200 group-hover:block group-hover:translate-y-2 group-hover:opacity-100 group-focus-within:block group-focus-within:translate-y-2 group-focus-within:opacity-100">
|
||||
<div className="absolute left-0 top-full hidden min-w-[200px] translate-y-1 rounded-lg border border-surface-border bg-surface/95 py-2 text-sm text-text opacity-0 shadow-shadow-md transition-all duration-200 group-hover:block group-hover:translate-y-2 group-hover:opacity-100 group-focus-within:block group-focus-within:translate-y-2 group-focus-within:opacity-100">
|
||||
{openSourceProjects.map((project) => (
|
||||
<Link
|
||||
key={project.key}
|
||||
href={project.href}
|
||||
className="block px-4 py-2 text-sm opacity-80 transition hover:bg-indigo-500/10 hover:text-white hover:opacity-100"
|
||||
className="block px-4 py-2 text-sm opacity-80 transition hover:bg-primary/10 hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{project.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href="/about"
|
||||
className="text-sm opacity-80 transition hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{labels.about}
|
||||
</Link>
|
||||
<Link
|
||||
key={servicesLink.key}
|
||||
href={servicesLink.href}
|
||||
className="text-sm opacity-80 transition hover:text-white hover:opacity-100"
|
||||
className="text-sm opacity-80 transition hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{servicesLink.label}
|
||||
</Link>
|
||||
@ -292,29 +299,29 @@ export default function Navbar() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setAccountMenuOpen((prev) => !prev)}
|
||||
className="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-sky-500 text-sm font-semibold text-white shadow-[0_10px_24px_rgba(37,99,235,0.28)] transition hover:from-indigo-400 hover:to-sky-400 focus:outline-none focus:ring-2 focus:ring-indigo-300/60 focus:ring-offset-2 focus:ring-offset-slate-900"
|
||||
className="flex h-10 w-10 items-center justify-center rounded-full bg-gradient-to-br from-primary to-accent text-sm font-semibold text-white shadow-shadow-sm transition hover:from-primary-hover hover:to-accent focus:outline-none focus:ring-2 focus:ring-primary/60 focus:ring-offset-2 focus:ring-offset-background"
|
||||
aria-haspopup="menu"
|
||||
aria-expanded={accountMenuOpen}
|
||||
>
|
||||
{accountInitial}
|
||||
</button>
|
||||
{accountMenuOpen ? (
|
||||
<div className="absolute right-0 mt-2 w-56 overflow-hidden rounded-xl border border-white/10 bg-slate-900/95 shadow-[0_12px_32px_rgba(0,0,0,0.35)]">
|
||||
<div className="border-b border-white/10 bg-white/5 px-4 py-3">
|
||||
<p className="text-sm font-semibold text-white">{user.username}</p>
|
||||
<p className="text-xs text-slate-300">{user.email}</p>
|
||||
<div className="absolute right-0 mt-2 w-56 overflow-hidden rounded-xl border border-surface-border bg-surface/95 shadow-shadow-md">
|
||||
<div className="border-b border-surface-border bg-surface-muted px-4 py-3">
|
||||
<p className="text-sm font-semibold text-text">{user.username}</p>
|
||||
<p className="text-xs text-text-muted">{user.email}</p>
|
||||
</div>
|
||||
<div className="py-1 text-sm text-slate-100">
|
||||
<div className="py-1 text-sm text-text">
|
||||
<Link
|
||||
href="/panel"
|
||||
className="block px-4 py-2 text-sm opacity-80 transition hover:bg-indigo-500/10 hover:opacity-100"
|
||||
className="block px-4 py-2 text-sm opacity-80 transition hover:bg-primary/10 hover:opacity-100"
|
||||
onClick={() => setAccountMenuOpen(false)}
|
||||
>
|
||||
{accountCopy.userCenter}
|
||||
</Link>
|
||||
<Link
|
||||
href="/logout"
|
||||
className="flex w-full items-center px-4 py-2 text-left text-sm text-rose-300 opacity-80 transition hover:bg-rose-500/10 hover:opacity-100"
|
||||
className="flex w-full items-center px-4 py-2 text-left text-sm text-danger-foreground opacity-80 transition hover:bg-danger/10 hover:opacity-100"
|
||||
onClick={() => setAccountMenuOpen(false)}
|
||||
>
|
||||
{accountCopy.logout}
|
||||
@ -324,17 +331,17 @@ export default function Navbar() {
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-3 text-sm font-medium text-slate-200">
|
||||
<div className="flex items-center gap-3 text-sm font-medium text-text-muted">
|
||||
<Link
|
||||
href="/login"
|
||||
className="text-sm opacity-80 transition hover:text-white hover:opacity-100"
|
||||
className="text-sm opacity-80 transition hover:text-primary hover:opacity-100"
|
||||
>
|
||||
{nav.account.login}
|
||||
</Link>
|
||||
<span className="h-3 w-px bg-white/20" aria-hidden="true" />
|
||||
<span className="h-3 w-px bg-surface-border" aria-hidden="true" />
|
||||
<Link
|
||||
href="/register"
|
||||
className="rounded-md border border-white/10 px-3 py-1 text-indigo-100 transition hover:border-indigo-300/40 hover:bg-white/5"
|
||||
className="rounded-md border border-surface-border px-3 py-1 text-primary transition hover:border-primary/40 hover:bg-surface-muted"
|
||||
>
|
||||
{nav.account.register}
|
||||
</Link>
|
||||
@ -350,7 +357,7 @@ export default function Navbar() {
|
||||
</div>
|
||||
|
||||
<button
|
||||
className="flex items-center text-slate-100 focus:outline-none lg:hidden"
|
||||
className="flex items-center text-text focus:outline-none lg:hidden"
|
||||
onClick={() => setMenuOpen((prev) => !prev)}
|
||||
aria-label="Toggle menu"
|
||||
>
|
||||
@ -391,6 +398,13 @@ export default function Navbar() {
|
||||
{link.label}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="/about"
|
||||
className="py-2 text-sm opacity-80 transition hover:opacity-100"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
>
|
||||
{labels.about}
|
||||
</Link>
|
||||
<Link
|
||||
key={servicesLink.key}
|
||||
href={servicesLink.href}
|
||||
|
||||
@ -485,7 +485,7 @@ type AboutTranslation = {
|
||||
acknowledgments: string
|
||||
toolsTitle: string
|
||||
toolsNote: string
|
||||
tools: string[]
|
||||
tools: { label: string; url: string }[]
|
||||
opensource: string
|
||||
}
|
||||
|
||||
@ -555,7 +555,31 @@ export type Translation = {
|
||||
auth: AuthTranslation
|
||||
userCenter: UserCenterTranslation
|
||||
marketing: {
|
||||
home: MarketingHomeTranslation
|
||||
home: MarketingHomeTranslation & {
|
||||
heroCards: { title: string; description: string }[]
|
||||
nextSteps: {
|
||||
title: string
|
||||
badge: string
|
||||
items: { title: string; status: string }[]
|
||||
}
|
||||
stats: { value: string; label: string }[]
|
||||
shortcuts: {
|
||||
title: string
|
||||
subtitle: string
|
||||
buttons: { start: string; docs: string; guides: string }
|
||||
items: { title: string; description: string }[]
|
||||
}
|
||||
trustedBy: string
|
||||
heroButtons: {
|
||||
create: string
|
||||
playground: string
|
||||
tutorials: string
|
||||
}
|
||||
signedIn: string
|
||||
hero: {
|
||||
eyebrow: string
|
||||
}
|
||||
}
|
||||
}
|
||||
about: AboutTranslation
|
||||
}
|
||||
@ -1043,20 +1067,63 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
marketing: {
|
||||
home: {
|
||||
hero: {
|
||||
eyebrow: 'Cloud-Native Suite',
|
||||
title: 'A Unified Toolkit for Cloud-Native Environments',
|
||||
subtitle:
|
||||
'Unify asset management, access control, observability, and automated runbooks in a responsive workspace.',
|
||||
highlights: [
|
||||
'Unified governance across clusters and clouds',
|
||||
'Policy-driven security and compliance automation',
|
||||
'Template-driven workflows accelerate delivery',
|
||||
'Modular capabilities you can enable on demand',
|
||||
eyebrow: 'Create, authenticate, deploy',
|
||||
title: 'Get started with Cloud-Neutral Toolkit',
|
||||
subtitle: 'Integrate Cloud-Neutral Toolkit into your application or use one of our samples to get started quickly.',
|
||||
},
|
||||
heroButtons: {
|
||||
create: 'Create Application',
|
||||
playground: 'Try Samples in Playground',
|
||||
tutorials: 'View Tutorials',
|
||||
},
|
||||
signedIn: 'Signed in',
|
||||
trustedBy: 'Trusted by your dev team',
|
||||
heroCards: [
|
||||
{
|
||||
title: 'Create your app',
|
||||
description: 'Add your application and configure the client details to start integrating quickly.',
|
||||
},
|
||||
{
|
||||
title: 'Register your app',
|
||||
description: 'Register your application to manage access and configure redirect URIs.',
|
||||
},
|
||||
{
|
||||
title: 'Deploy your app',
|
||||
description: 'Manage your application and users within Cloud-Neutral Toolkit for secure access.',
|
||||
},
|
||||
],
|
||||
nextSteps: {
|
||||
title: 'Your Next Steps',
|
||||
badge: 'Data detected',
|
||||
items: [
|
||||
{ title: 'Add a new user to your project', status: 'NEW' },
|
||||
{ title: 'Register a new application', status: 'NEW' },
|
||||
{ title: 'Deploy your application', status: 'READY' },
|
||||
{ title: 'Invite a user', status: 'READY' },
|
||||
],
|
||||
},
|
||||
stats: [
|
||||
{ value: '~150k', label: 'Applications integrated with Cloud-Neutral Toolkit' },
|
||||
{ value: '~330k', label: 'Daily active users' },
|
||||
{ value: '7', label: 'Go check out our examples & guides' },
|
||||
],
|
||||
shortcuts: {
|
||||
title: 'More shortcuts',
|
||||
subtitle: 'Save time when integrating Cloud-Neutral Toolkit',
|
||||
buttons: {
|
||||
start: 'Get Started',
|
||||
docs: 'Docs',
|
||||
guides: 'Guides',
|
||||
},
|
||||
items: [
|
||||
{ title: 'Get started', description: 'An overview of using Cloud-Neutral Toolkit' },
|
||||
{ title: 'Creating your application', description: 'Integrate Cloud-Neutral Toolkit into your application' },
|
||||
{ title: 'More about Authentication', description: 'Understand all about authenticating with Cloud-Neutral Toolkit' },
|
||||
{ title: 'Understanding Authorization', description: 'Scope out all about authorization using Cloud-Neutral Toolkit' },
|
||||
{ title: 'Machine-to-Machine', description: 'Integrate Cloud-Neutral Toolkit into your services' },
|
||||
{ title: 'Connect via CLI', description: 'Connect Cloud-Neutral Toolkit with your application via CLI' },
|
||||
{ title: 'REST & Admin APIs', description: 'Programmatically integrate Cloud-Neutral Toolkit into your application' },
|
||||
],
|
||||
bodyHtml:
|
||||
'<p>XControl uses a modular architecture so you can add observability, identity, and orchestration packages without disrupting the core platform. Open APIs and event streams connect seamlessly with the DevOps toolchain you already rely on.</p>',
|
||||
primaryCtaLabel: 'Try it now',
|
||||
secondaryCtaLabel: 'Product docs',
|
||||
},
|
||||
tabsLabel: 'Product Matrix',
|
||||
tabsAriaLabel: 'XControl product suite',
|
||||
@ -1234,13 +1301,13 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
toolsTitle: 'Acknowledged tools and platforms used (in no particular order):',
|
||||
toolsNote: '(The platform names above are listed for technical acknowledgment only and do not imply any official partnership or endorsement.)',
|
||||
tools: [
|
||||
'GitHub — https://github.com',
|
||||
'Cloudflare — https://www.cloudflare.com',
|
||||
'ChatGPT (OpenAI)',
|
||||
'Google AI Suite',
|
||||
'NVIDIA AI Platform',
|
||||
'Vercel — https://vercel.com',
|
||||
'Google Cloud Run',
|
||||
{ label: 'GitHub', url: 'https://github.com/cloud-neutral-toolkit' },
|
||||
{ label: 'Cloudflare', url: 'https://www.cloudflare.com' },
|
||||
{ label: 'ChatGPT (OpenAI)', url: 'https://chatgpt.com/codex' },
|
||||
{ label: 'Google AI Suite', url: 'https://google.com' },
|
||||
{ label: 'NVIDIA AI Platform', url: 'https://build.nvidia.com/' },
|
||||
{ label: 'Vercel', url: 'https://vercel.com/svc-designs-projects' },
|
||||
{ label: 'Google Cloud Run', url: 'https://cloud.google.com/' },
|
||||
],
|
||||
opensource: 'We embrace open source. Human progress is made possible through continuous sharing and collaboration.',
|
||||
},
|
||||
@ -1706,7 +1773,65 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
},
|
||||
marketing: {
|
||||
home: {
|
||||
hero: {},
|
||||
hero: {
|
||||
eyebrow: '构建、认证、部署',
|
||||
title: '开始使用 Cloud-Neutral Toolkit',
|
||||
subtitle: '将 Cloud-Neutral Toolkit 集成到您的应用中,或使用我们的示例快速入门。',
|
||||
},
|
||||
heroButtons: {
|
||||
create: '创建应用',
|
||||
playground: '在 Playground 中试用',
|
||||
tutorials: '查看教程',
|
||||
},
|
||||
signedIn: '已登录',
|
||||
trustedBy: '被您的开发团队信赖',
|
||||
heroCards: [
|
||||
{
|
||||
title: '创建您的应用',
|
||||
description: '添加您的应用程序并配置客户端详细信息,以便快速开始集成。',
|
||||
},
|
||||
{
|
||||
title: '注册您的应用',
|
||||
description: '注册您的应用程序以管理访问权限并配置重定向 URI。',
|
||||
},
|
||||
{
|
||||
title: '部署您的应用',
|
||||
description: '在 Cloud-Neutral Toolkit 中管理您的应用程序和用户,以确保访问安全。',
|
||||
},
|
||||
],
|
||||
nextSteps: {
|
||||
title: '后续步骤',
|
||||
badge: '检测到数据',
|
||||
items: [
|
||||
{ title: '向项目添加新用户', status: 'NEW' },
|
||||
{ title: '注册新应用程序', status: 'NEW' },
|
||||
{ title: '部署您的应用程序', status: 'READY' },
|
||||
{ title: '邀请用户', status: 'READY' },
|
||||
],
|
||||
},
|
||||
stats: [
|
||||
{ value: '~150k', label: '集成 Cloud-Neutral Toolkit 的应用程序' },
|
||||
{ value: '~330k', label: '日活跃用户' },
|
||||
{ value: '7', label: '查看我们的示例和指南' },
|
||||
],
|
||||
shortcuts: {
|
||||
title: '更多快捷方式',
|
||||
subtitle: '在集成 Cloud-Neutral Toolkit 时节省时间',
|
||||
buttons: {
|
||||
start: '开始使用',
|
||||
docs: '文档',
|
||||
guides: '指南',
|
||||
},
|
||||
items: [
|
||||
{ title: '开始使用', description: 'Cloud-Neutral Toolkit 使用概览' },
|
||||
{ title: '创建您的应用程序', description: '将 Cloud-Neutral Toolkit 集成到您的应用程序中' },
|
||||
{ title: '关于身份验证', description: '了解有关使用 Cloud-Neutral Toolkit 进行身份验证的所有信息' },
|
||||
{ title: '了解授权', description: '了解有关使用 Cloud-Neutral Toolkit 进行授权的所有信息' },
|
||||
{ title: '机器对机器', description: '将 Cloud-Neutral Toolkit 集成到您的服务中' },
|
||||
{ title: '通过 CLI 连接', description: '通过 CLI 将 Cloud-Neutral Toolkit 连接到您的应用程序' },
|
||||
{ title: 'REST & Admin APIs', description: '通过编程将 Cloud-Neutral Toolkit 集成到您的应用程序中' },
|
||||
],
|
||||
},
|
||||
tabsLabel: '产品矩阵',
|
||||
tabsAriaLabel: 'XControl 产品套件',
|
||||
productMatrix: {
|
||||
@ -1751,13 +1876,13 @@ export const translations: Record<'en' | 'zh', Translation> = {
|
||||
toolsTitle: '致谢与使用的工具与平台(不分先后):',
|
||||
toolsNote: '以上平台名称仅用于技术致谢,不代表任何官方合作或背书!',
|
||||
tools: [
|
||||
'ChatGPT (OpenAI)',
|
||||
'Google AI Suite',
|
||||
'NVIDIA AI Platform',
|
||||
'GitHub — https://github.com',
|
||||
'Cloudflare — https://www.cloudflare.com',
|
||||
'Vercel — https://vercel.com',
|
||||
'Google Cloud Run',
|
||||
{ label: 'ChatGPT (OpenAI)', url: 'https://chatgpt.com/codex' },
|
||||
{ label: 'Google AI Suite', url: 'https://google.com' },
|
||||
{ label: 'NVIDIA AI Platform', url: 'https://build.nvidia.com/' },
|
||||
{ label: 'GitHub', url: 'https://github.com/cloud-neutral-toolkit' },
|
||||
{ label: 'Cloudflare', url: 'https://www.cloudflare.com' },
|
||||
{ label: 'Vercel', url: 'https://vercel.com/svc-designs-projects' },
|
||||
{ label: 'Google Cloud Run', url: 'https://cloud.google.com/' },
|
||||
],
|
||||
opensource: '我们拥抱开源。人类正是因为持续的共享与协作,才得以彼此成就。',
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user