From a8a7de372de27a503e5f873916db973b29304ca8 Mon Sep 17 00:00:00 2001 From: S9063561 Date: Wed, 6 May 2026 12:15:51 +0800 Subject: [PATCH] feat: change homepage style and add homepage quick install script --- .github/skills/awesome-design-md/SKILL.md | 22 + i18n/en/code.json | 58 +- i18n/zh-cn/code.json | 58 +- src/pages/index.css | 1842 +++++++++++++++------ src/pages/index.tsx | 597 +++---- src/theme/Layout/index.tsx | 2 +- static/install.sh | 276 +++ 7 files changed, 1993 insertions(+), 862 deletions(-) create mode 100644 .github/skills/awesome-design-md/SKILL.md create mode 100755 static/install.sh diff --git a/.github/skills/awesome-design-md/SKILL.md b/.github/skills/awesome-design-md/SKILL.md new file mode 100644 index 0000000..b756df2 --- /dev/null +++ b/.github/skills/awesome-design-md/SKILL.md @@ -0,0 +1,22 @@ +--- +name: awesome-design-md +user-invocable: true +summary: Provides a reference to the VoltAgent awesome-design-md collection for design and markdown best practices. +description: | + Use when the user asks for markdown design patterns, documentation design guidance, or references to the VoltAgent awesome-design-md repository. + Includes a direct link to https://github.com/VoltAgent/awesome-design-md. +--- + +# Awesome Design MD Skill + +This skill exposes the VoltAgent `awesome-design-md` repository as a reference asset. + +## Repository + +- https://github.com/VoltAgent/awesome-design-md + +## Use Cases + +- Answering questions about markdown design patterns. +- Providing documentation design best practices. +- Referring to the curated external resource when asked about design-centered markdown examples. diff --git a/i18n/en/code.json b/i18n/en/code.json index 374b8df..ffd521e 100644 --- a/i18n/en/code.json +++ b/i18n/en/code.json @@ -1,14 +1,14 @@ { "homepage.title": { - "message": "Curvine - High-Performance Distributed Caching System", + "message": "Curvine - High-Performance Distributed Caching FileSystem", "description": "The homepage title" }, "homepage.hero.title": { - "message": "High-Performance Distributed Caching System", + "message": "High-Performance Distributed Caching FileSystem", "description": "The hero title on the homepage" }, "homepage.hero.subtitle": { - "message": "Curvine is a high-performance distributed caching system implemented in Rust, designed for low-latency and high-throughput workloads with powerful data governance capabilities.", + "message": "Curvine is a high-performance distributed caching filesystem implemented in Rust, designed for low-latency and high-throughput workloads with powerful data governance capabilities.", "description": "The hero subtitle on the homepage" }, "homepage.hero.getStarted": { @@ -96,7 +96,7 @@ "description": "Performance section title" }, "homepage.performance.subtitle": { - "message": "Curvine delivers industry-leading performance metrics that set new standards for distributed caching systems.", + "message": "Curvine keeps 100K concurrent clients stably connected with low-latency metadata operations and around 1 GB of connection memory overhead.", "description": "Performance section subtitle" }, "homepage.performance.latency": { @@ -104,7 +104,7 @@ "description": "Latency performance metric" }, "homepage.performance.ops": { - "message": "Operations/sec", + "message": "Stable QPS", "description": "Operations per second metric" }, "homepage.performance.uptime": { @@ -116,7 +116,7 @@ "description": "Getting started section title" }, "homepage.gettingstarted.subtitle": { - "message": "Deploy Curvine quickly with our comprehensive documentation and tools.", + "message": "Start a local Curvine Docker demo with one command, then move to production deployment when you are ready.", "description": "Getting started section subtitle" }, "homepage.gettingstarted.step1.title": { @@ -148,7 +148,7 @@ "description": "CTA section title" }, "homepage.cta.subtitle": { - "message": "Download now and experience the high-performance distributed caching system that Curvine brings.", + "message": "Download now and experience the high-performance distributed caching filesystem that Curvine brings.", "description": "CTA section subtitle" }, "homepage.cta.download": { @@ -481,5 +481,49 @@ "theme.tags.tagsPageTitle": { "message": "Tags", "description": "The title of the tag list page" + }, + "homepage.hero.kicker": { + "message": "Rust systems software for data acceleration", + "description": "Hero eyebrow text" + }, + "homepage.visual.chip.label": { + "message": "DISTRIBUTED CACHE", + "description": "Hero chip label" + }, + "homepage.visual.modules.agentic": { + "message": "Agentic FileSystem", + "description": "Agentic filesystem module label" + }, + "homepage.visual.modules.nodes": { + "message": "AI/ML Pipelines Accelerate", + "description": "AI and ML pipelines acceleration module label" + }, + "homepage.visual.modules.inference": { + "message": "LLM Inference Accelerate", + "description": "LLM inference acceleration module label" + }, + "homepage.visual.modules.aiNative": { + "message": "AI-Native FileSystem", + "description": "AI-native filesystem module label" + }, + "homepage.visual.modules.pipeline": { + "message": "Cloud-Native Storage", + "description": "Cloud-native storage module label" + }, + "homepage.gettingstarted.command.eyebrow": { + "message": "Run this in your terminal", + "description": "homepage.gettingstarted.command.eyebrow" + }, + "homepage.gettingstarted.command.copied": { + "message": "Copied", + "description": "Copied install command button feedback" + }, + "homepage.gettingstarted.command.copy": { + "message": "Copy", + "description": "homepage.gettingstarted.command.copy" + }, + "homepage.gettingstarted.command.note": { + "message": "If Docker is available on your machine, run the following command in your terminal, then follow the on-screen instructions to start a local Curvine demo cluster.", + "description": "homepage.gettingstarted.command.note" } } diff --git a/i18n/zh-cn/code.json b/i18n/zh-cn/code.json index fe81734..e4781c2 100644 --- a/i18n/zh-cn/code.json +++ b/i18n/zh-cn/code.json @@ -323,15 +323,15 @@ "description": "The title of the tag list page" }, "homepage.title": { - "message": "Curvine - 高性能分布式缓存系统", + "message": "Curvine - 高性能分布式缓存文件系统", "description": "The homepage title" }, "homepage.hero.title": { - "message": "高性能分布式缓存系统", + "message": "高性能分布式缓存文件系统", "description": "The hero title on the homepage" }, "homepage.hero.subtitle": { - "message": "Curvine 是一个用 Rust 实现的高性能分布式缓存系统,专为低延迟和高吞吐量工作负载设计,具有强大的数据治理能力。", + "message": "Curvine 是一个用 Rust 实现的高性能分布式缓存文件系统,专为低延迟和高吞吐量工作负载设计,具有强大的数据治理能力。", "description": "The hero subtitle on the homepage" }, "homepage.hero.getStarted": { @@ -379,7 +379,7 @@ "description": "CTA section title" }, "homepage.cta.subtitle": { - "message": "立即下载并体验 Curvine 带来的高性能分布式缓存系统。", + "message": "立即下载并体验 Curvine 带来的高性能分布式缓存文件系统。", "description": "CTA section subtitle" }, "homepage.cta.downloadNow": { @@ -451,7 +451,7 @@ "description": "Performance section title" }, "homepage.performance.subtitle": { - "message": "Curvine 在各项性能指标上都表现卓越。", + "message": "Curvine 可支撑 10 万客户端并发稳定连接,元数据操作保持低延迟,连接内存开销仅约 1G,适合大规模缓存文件系统场景。", "description": "Performance section subtitle" }, "homepage.performance.latency": { @@ -459,7 +459,7 @@ "description": "Latency performance metric" }, "homepage.performance.ops": { - "message": "QPS", + "message": "稳定 QPS", "description": "Operations per second metric" }, "homepage.performance.uptime": { @@ -471,7 +471,7 @@ "description": "Getting started section title" }, "homepage.gettingstarted.subtitle": { - "message": "三步即可开始使用 Curvine 分布式缓存系统。", + "message": "复制一条命令即可启动本地 Curvine Docker 体验集群,验证通过后再进入生产部署。", "description": "Getting started section subtitle" }, "homepage.gettingstarted.step1.title": { @@ -505,5 +505,49 @@ "homepage.cta.github": { "message": "查看 GitHub", "description": "GitHub button text" + }, + "homepage.hero.kicker": { + "message": "面向数据加速的 Rust 系统软件", + "description": "Hero eyebrow text" + }, + "homepage.visual.chip.label": { + "message": "分布式缓存", + "description": "Hero chip label" + }, + "homepage.visual.modules.agentic": { + "message": "智能体文件系统", + "description": "Agentic filesystem module label" + }, + "homepage.visual.modules.nodes": { + "message": "AI/ML 流水线加速", + "description": "AI and ML pipelines acceleration module label" + }, + "homepage.visual.modules.inference": { + "message": "大模型推理加速", + "description": "LLM inference acceleration module label" + }, + "homepage.visual.modules.aiNative": { + "message": "AI 原生文件系统", + "description": "AI-native filesystem module label" + }, + "homepage.visual.modules.pipeline": { + "message": "云原生存储", + "description": "Cloud-native storage module label" + }, + "homepage.gettingstarted.command.eyebrow": { + "message": "在终端中运行以下命令", + "description": "homepage.gettingstarted.command.eyebrow" + }, + "homepage.gettingstarted.command.copied": { + "message": "已复制", + "description": "Copied install command button feedback" + }, + "homepage.gettingstarted.command.copy": { + "message": "复制", + "description": "homepage.gettingstarted.command.copy" + }, + "homepage.gettingstarted.command.note": { + "message": "如果你的机器已经安装并可访问 Docker,请在终端运行以下命令,然后根据屏幕提示启动本地 Curvine 体验集群。", + "description": "homepage.gettingstarted.command.note" } } diff --git a/src/pages/index.css b/src/pages/index.css index f39d5df..92bc41e 100644 --- a/src/pages/index.css +++ b/src/pages/index.css @@ -1,719 +1,1559 @@ -/** - * Homepage styles for Curvine documentation site - * Converted from HTML with modern design and Docusaurus integration - */ - :root { - --primary-color: #0a0f0a; /* 深绿黑 */ - --secondary-color: #1a241a; /* 深绿背景 */ - --accent-color: #4ade80; /* 现代绿色主题 */ - --accent-dark: #22c55e; /* 深绿色 */ - --accent-light: #86efac; /* 浅绿色 */ - --text-color: #f0f9f0; /* 淡绿白色文本 */ - --light-bg: #1e2a1e; /* 深绿浅色背景 */ - --dark-bg: #0f1b0f; /* 更深的绿色背景 */ - --success-color: #22c55e; /* 成功色调 */ - --neutral-color: #6b7280; /* 中性色调 */ - --border-color: #2d3a2d; /* 绿色边框 */ - --hover-color: #1f2d1f; /* 悬停效果颜色 */ - --card-bg: #1a2a1a; /* 卡片背景 */ -} - -/* Global homepage wrapper */ + --font-sans: "Aptos", "Segoe UI Variable Text", "Segoe UI", "HarmonyOS Sans SC", "MiSans", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; + --font-display: "Bahnschrift", "Aptos Display", "Segoe UI Variable Display", "Segoe UI", "HarmonyOS Sans SC", "MiSans", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif; + --font-mono: "Cascadia Mono", "IBM Plex Mono", "SFMono-Regular", "JetBrains Mono", ui-monospace, monospace; + --page-bg: #f5fbf7; + --ink: #112018; + --muted: #52675b; + --blue: #16a34a; + --blue-deep: #15803d; + --blue-soft: #86efac; + --line: rgba(22, 163, 74, 0.16); + --line-strong: rgba(22, 163, 74, 0.28); + --card: rgba(255, 255, 255, 0.95); + --panel: rgba(255, 255, 255, 0.82); + --panel-strong: rgba(255, 255, 255, 0.95); + --chip-surface: #ffffff; + --shadow: 0 18px 50px rgba(20, 128, 63, 0.12); +} + +[data-theme='dark'] { + --page-bg: #07130c; + --ink: #effff4; + --muted: #a7c8b0; + --blue: #4ade80; + --blue-deep: #86efac; + --blue-soft: #6ee7a0; + --line: rgba(74, 222, 128, 0.2); + --line-strong: rgba(74, 222, 128, 0.34); + --card: rgba(9, 20, 37, 0.9); + --panel: rgba(11, 24, 44, 0.82); + --panel-strong: rgba(15, 31, 55, 0.94); + --chip-surface: #091b10; + --shadow: 0 22px 58px rgba(0, 0, 0, 0.34); +} + .curvine-homepage { - color: var(--text-color); - line-height: 1.6; - background-color: var(--primary-color); - position: relative; + min-height: 100vh; + color: var(--ink); + font-family: var(--font-sans); + font-feature-settings: 'kern' 1, 'liga' 1; + text-rendering: optimizeLegibility; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(245, 251, 247, 0.94) 26%, var(--page-bg)), + radial-gradient(circle at 12% 10%, rgba(134, 239, 172, 0.22), transparent 28%), + radial-gradient(circle at 88% 18%, rgba(22, 163, 74, 0.13), transparent 26%), + var(--page-bg); overflow-x: hidden; } -/* Global decorative background effects */ -.curvine-homepage::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: - linear-gradient(45deg, transparent 48%, rgba(74, 222, 128, 0.03) 49%, rgba(74, 222, 128, 0.03) 51%, transparent 52%), - linear-gradient(-45deg, transparent 48%, rgba(34, 197, 94, 0.02) 49%, rgba(34, 197, 94, 0.02) 51%, transparent 52%); - background-size: 200px 200px, 150px 150px; - animation: globalLineFlow 20s linear infinite; - pointer-events: none; - z-index: -1; +.curvine-homepage a { + text-decoration: none; } -@keyframes globalLineFlow { - 0% { - background-position: 0 0, 0 0; - } - 100% { - background-position: 200px 200px, -150px 150px; - } +[data-theme='dark'] .curvine-homepage { + background: + linear-gradient(180deg, rgba(3, 10, 20, 0.96), rgba(7, 19, 12, 0.98) 32%, var(--page-bg)), + radial-gradient(circle at 12% 10%, rgba(74, 222, 128, 0.16), transparent 28%), + radial-gradient(circle at 88% 18%, rgba(22, 163, 74, 0.18), transparent 26%), + var(--page-bg); } -/* Edge glow effects */ -.curvine-homepage::after { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: - radial-gradient(circle at 10% 20%, rgba(74, 222, 128, 0.1) 0%, transparent 30%), - radial-gradient(circle at 90% 80%, rgba(34, 197, 94, 0.08) 0%, transparent 25%), - radial-gradient(circle at 30% 90%, rgba(22, 163, 74, 0.06) 0%, transparent 20%); - animation: edgeGlow 15s ease-in-out infinite; - pointer-events: none; - z-index: -1; +[data-theme='dark'] .hero::before, +[data-theme='dark'] .section-band::before { + background-image: + linear-gradient(90deg, transparent 0, transparent calc(100% - 1px), rgba(74, 222, 128, 0.08) calc(100% - 1px)), + linear-gradient(180deg, transparent 0, transparent calc(100% - 1px), rgba(74, 222, 128, 0.07) calc(100% - 1px)); } -@keyframes edgeGlow { - 0%, 100% { - opacity: 0.3; - transform: scale(1); - } - 50% { - opacity: 0.6; - transform: scale(1.1); - } +[data-theme='dark'] .btn-secondary { + border-color: rgba(74, 222, 128, 0.36); + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.18); +} + +[data-theme='dark'] .circuit-board { + background: + radial-gradient(circle at center, rgba(74, 222, 128, 0.17), transparent 44%), + linear-gradient(135deg, rgba(12, 50, 24, 0.78), rgba(7, 19, 12, 0.32)); +} + +[data-theme='dark'] .circuit-board::before { + background-image: + linear-gradient(90deg, transparent 48%, rgba(74, 222, 128, 0.13) 49%, rgba(74, 222, 128, 0.13) 51%, transparent 52%), + linear-gradient(180deg, transparent 48%, rgba(74, 222, 128, 0.11) 49%, rgba(74, 222, 128, 0.11) 51%, transparent 52%); +} + +[data-theme='dark'] .trace::before, +[data-theme='dark'] .trace::after { + background: var(--chip-surface); + border-color: rgba(74, 222, 128, 0.5); +} + +[data-theme='dark'] .chip-base { + background: linear-gradient(145deg, #14532d, #16a34a 52%, #4ade80); + border-color: rgba(134, 239, 172, 0.42); + box-shadow: inset 0 0 0 8px rgba(255, 255, 255, 0.12); +} + +[data-theme='dark'] .chip-shadow { + background: rgba(22, 163, 74, 0.26); +} + +[data-theme='dark'] .chip-base::before, +[data-theme='dark'] .chip-top::before { + opacity: 0.42; +} + +[data-theme='dark'] .module-card { + border-color: rgba(74, 222, 128, 0.24); + box-shadow: 0 20px 44px rgba(0, 0, 0, 0.28), inset 0 0 0 1px rgba(134, 239, 172, 0.12); +} + +[data-theme='dark'] .module-card::before { + border-color: rgba(134, 239, 172, 0.18); +} + +[data-theme='dark'] .feature-card, +[data-theme='dark'] .stat-card, +[data-theme='dark'] .step-card { + border-color: rgba(74, 222, 128, 0.18); +} + +[data-theme='dark'] .feature-card:hover, +[data-theme='dark'] .stat-card:hover, +[data-theme='dark'] .step-card:hover { + border-color: rgba(74, 222, 128, 0.34); + box-shadow: 0 26px 60px rgba(0, 0, 0, 0.4); +} + +[data-theme='dark'] .feature-icon, +[data-theme='dark'] .stat-icon, +[data-theme='dark'] .step-icon { + background: linear-gradient(145deg, rgba(17, 69, 35, 0.94), rgba(20, 94, 45, 0.82)); + border-color: rgba(74, 222, 128, 0.24); + box-shadow: inset 0 0 0 1px rgba(134, 239, 172, 0.12); } -/* Container */ .container { - width: 100%; - max-width: 1200px; + width: min(100% - 48px, 1240px); margin: 0 auto; - padding: 0 20px; } -/* Hero Section */ .hero { - padding: 200px 0 80px 0; - background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); - text-align: center; - color: white; position: relative; overflow: hidden; - min-height: 700px; - display: flex; - align-items: flex-end; + padding: 86px 0 72px; } -.hero-background { +.hero::before, +.section-band::before { + content: ''; position: absolute; - top: -300px; - left: -100px; - right: -100px; - bottom: 200px; - display: flex; - justify-content: center; - align-items: flex-start; - z-index: 1; /* 改为1,确保在hero::before之上 */ - opacity: 0.8; /* 提高透明度,让图片更明显 */ -} - -.hero-image { - width: 120%; - max-width: 1200px; - height: auto; - filter: brightness(0.8) contrast(1.1); /* 提高亮度 */ - animation: heroFloat 6s ease-in-out infinite; + inset: 0; + pointer-events: none; + background-image: + linear-gradient(90deg, transparent 0, transparent calc(100% - 1px), rgba(22, 163, 74, 0.06) calc(100% - 1px)), + linear-gradient(180deg, transparent 0, transparent calc(100% - 1px), rgba(22, 163, 74, 0.05) calc(100% - 1px)); + background-size: 86px 86px; + mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.72), transparent 78%); } -.hero::before { +.hero::after, +.section-title::before, +.section-title::after { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; + height: 1px; + background: linear-gradient(90deg, transparent, rgba(22, 163, 74, 0.32), rgba(134, 239, 172, 0.18), transparent); +} + +.hero::after { + left: 5%; + right: 5%; bottom: 0; - background: radial-gradient(circle at 30% 20%, rgba(74, 222, 128, 0.04) 0%, transparent 60%), - radial-gradient(circle at 70% 80%, rgba(34, 197, 94, 0.03) 0%, transparent 60%); - pointer-events: none; - z-index: 0; /* 改为0,确保在hero-background之下 */ } -.hero .container { +.hero-grid { position: relative; - z-index: 2; - width: 100%; - padding-bottom: 20px; + z-index: 1; + display: grid; + grid-template-columns: minmax(0, 1.16fr) minmax(420px, 0.84fr); + align-items: center; + gap: 36px; } -.hero h1 { - font-size: 48px; - margin-bottom: 40px; - font-weight: 700; - color: white; - line-height: 1.3; - position: relative; - padding: 0 20px; +.hero-content { + max-width: 680px; + padding: 34px 0; +} + +.hero-copy { + max-width: 680px; +} + +.hero-kicker { + display: inline-flex; + align-items: center; + gap: 10px; + margin-bottom: 22px; + color: var(--blue); + font-family: var(--font-display); + font-size: 0.92rem; + font-weight: 740; + letter-spacing: 0.025em; + text-transform: uppercase; } -.hero h1::after { +.hero-kicker::before { content: ''; - position: absolute; - bottom: -20px; - left: 50%; - transform: translateX(-50%); - width: 100px; - height: 3px; - background: linear-gradient(90deg, transparent, var(--accent-color), transparent); - border-radius: 2px; + width: 11px; + height: 11px; + border: 2px solid var(--blue); + box-shadow: 18px 0 0 -3px rgba(22, 163, 74, 0.38); + transform: rotate(45deg); } -.hero p { - font-size: 20px; - max-width: 700px; - margin: 40px auto 60px; - color: #b0b0b0; - line-height: 1.6; - padding: 0 20px; +.hero h1 { + margin: 0 0 24px; + color: var(--ink); + font-family: var(--font-display); + font-size: 4.45rem; + font-weight: 760; + line-height: 1.02; + letter-spacing: 0; } -/* Button Styles */ -.btn { - display: inline-block; - padding: 8px 20px; - background: linear-gradient(135deg, var(--accent-color), var(--accent-dark)); - color: white; - text-decoration: none; - border-radius: 6px; - font-weight: 500; - font-size: 14px; - transition: all 0.3s ease; - margin: 6px 12px; - position: relative; - overflow: hidden; - border: none; - min-width: 120px; - max-width: 200px; +.title-keep { white-space: nowrap; } -.btn-container { + +html[lang="zh-CN"] .hero-grid, +html[lang="zh-cn"] .hero-grid { + align-items: stretch; +} + +html[lang="zh-CN"] .hero-content, +html[lang="zh-cn"] .hero-content { + min-height: 440px; display: flex; + flex-direction: column; justify-content: center; - flex-wrap: wrap; - gap: 12px; - margin-top: 20px; + gap: 40px; + padding: 0; } -.btn::before { - content: ''; - position: absolute; +html[lang="zh-CN"] .hero-kicker, +html[lang="zh-cn"] .hero-kicker { + margin-bottom: 30px; +} + +html[lang="zh-CN"] .hero h1, +html[lang="zh-cn"] .hero h1 { + margin-bottom: 30px; + font-size: 3.95rem; + line-height: 1.08; + white-space: normal; +} + +html[lang="zh-CN"] .hero p, +html[lang="zh-cn"] .hero p { + max-width: 520px; + margin-bottom: 0; + font-size: 1.14rem; + line-height: 1.98; +} + +html[lang="zh-CN"] .hero-visual, +html[lang="zh-CN"] .circuit-board, +html[lang="zh-cn"] .hero-visual, +html[lang="zh-cn"] .circuit-board { + min-height: 440px; +} + +html[lang="zh-CN"] .chip-stack, +html[lang="zh-cn"] .chip-stack { + width: 218px; + height: 218px; +} + +html[lang="zh-CN"] .chip, +html[lang="zh-cn"] .chip { + width: 160px; + height: 160px; +} + +html[lang="zh-CN"] .module-card, +html[lang="zh-cn"] .module-card { + width: 146px; + min-height: 106px; + gap: 10px; + padding: 14px 12px; +} + +html[lang="zh-CN"] .module-agentic, +html[lang="zh-cn"] .module-agentic { + width: 178px; top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(135deg, var(--accent-light), var(--accent-color)); - transition: left 0.3s ease; - z-index: -1; } -.btn:hover::before { - left: 0; +html[lang="zh-CN"] .module-nodes, +html[lang="zh-cn"] .module-nodes { left: 2%; top: 22%; } +html[lang="zh-CN"] .module-inference, +html[lang="zh-cn"] .module-inference { left: 4%; bottom: 7%; } +html[lang="zh-CN"] .module-ai-native, +html[lang="zh-cn"] .module-ai-native { right: 2%; top: 22%; } +html[lang="zh-CN"] .module-pipeline, +html[lang="zh-cn"] .module-pipeline { right: 4%; bottom: 7%; } + +html[lang="zh-CN"] .node-grid, +html[lang="zh-CN"] .app-grid, +html[lang="zh-CN"] .memory-stack, +html[lang="zh-CN"] .pipeline-bars, +html[lang="zh-CN"] .filesystem-rings, +html[lang="zh-cn"] .node-grid, +html[lang="zh-cn"] .app-grid, +html[lang="zh-cn"] .memory-stack, +html[lang="zh-cn"] .pipeline-bars, +html[lang="zh-cn"] .filesystem-rings { + width: 46px; + height: 36px; +} + +.hero p { + max-width: 650px; + margin: 0 0 34px; + color: var(--muted); + font-size: 1.1rem; + line-height: 1.66; +} + +.btn-container { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 16px; +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 54px; + min-width: 154px; + padding: 0 24px; + border-radius: 8px; + font-family: var(--font-display); + font-weight: 720; + letter-spacing: 0; + transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; } .btn:hover { transform: translateY(-2px); - box-shadow: 0 8px 25px rgba(74, 222, 128, 0.4); - color: white; text-decoration: none; } +.btn-primary { + color: white; + background: linear-gradient(135deg, #16a34a, #15803d); + box-shadow: 0 16px 34px rgba(22, 163, 74, 0.28); +} + +.btn-primary:hover { + color: white; + box-shadow: 0 20px 42px rgba(22, 163, 74, 0.34); +} + .btn-secondary { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.04)); - border: 1px solid var(--border-color); - color: var(--text-color); - box-shadow: none; + color: var(--blue); + background: var(--panel); + border: 1px solid rgba(22, 163, 74, 0.36); + box-shadow: 0 12px 28px rgba(20, 128, 63, 0.08); +} + +.btn-secondary:hover { + color: var(--blue-deep); + border-color: rgba(22, 163, 74, 0.56); +} + +.btn-icon { position: relative; - overflow: hidden; - border-radius: 8px; + width: 18px; + height: 18px; + flex: 0 0 18px; } -.btn-secondary::before { +.btn-icon-download::before { content: ''; position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(135deg, rgba(74, 222, 128, 0.15), rgba(34, 197, 94, 0.1)); - transition: left 0.3s ease; - z-index: -1; + left: 8px; + top: 1px; + width: 2px; + height: 11px; + background: currentColor; } -.btn-secondary:hover::before { - left: 0; +.btn-icon-download::after { + content: ''; + position: absolute; + left: 4px; + top: 7px; + width: 10px; + height: 10px; + border-right: 2px solid currentColor; + border-bottom: 2px solid currentColor; + transform: rotate(45deg); } -.btn-secondary:hover { - color: var(--accent-color); - border-color: var(--accent-color); - box-shadow: 0 4px 15px rgba(74, 222, 128, 0.2); - text-decoration: none; +.btn-icon-arrow::before { + content: ''; + position: absolute; + inset: 4px 5px 4px 3px; + border-top: 2px solid currentColor; + border-right: 2px solid currentColor; + transform: rotate(45deg); +} + +.hero-visual { + min-height: 520px; + position: relative; } -/* Section Styles */ -.features, -.use-cases, -.performance, -.architecture, -.getting-started { - padding: 60px 0; +.circuit-board { position: relative; + min-height: 520px; + border-radius: 10px; + background: + radial-gradient(circle at center, rgba(134, 239, 172, 0.18), transparent 42%), + linear-gradient(135deg, rgba(255, 255, 255, 0.76), rgba(255, 255, 255, 0.3)); + overflow: visible; } -/* Section decorative lines */ -.features::before, -.use-cases::before, -.performance::before, -.architecture::before, -.getting-started::before { +.circuit-board::before { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; + inset: 12px -18px; + background-image: + linear-gradient(90deg, transparent 48%, rgba(22, 163, 74, 0.12) 49%, rgba(22, 163, 74, 0.12) 51%, transparent 52%), + linear-gradient(180deg, transparent 48%, rgba(22, 163, 74, 0.1) 49%, rgba(22, 163, 74, 0.1) 51%, transparent 52%); + background-size: 58px 58px; + mask-image: radial-gradient(circle at center, #000 0, transparent 72%); +} + +.trace { + position: absolute; height: 2px; - background: linear-gradient(90deg, transparent, rgba(74, 222, 128, 0.3), transparent); - animation: sectionLineGlow 3s ease-in-out infinite; - z-index: 1; + background: linear-gradient(90deg, transparent, rgba(22, 163, 74, 0.36), rgba(134, 239, 172, 0.42), transparent); } -@keyframes sectionLineGlow { - 0%, 100% { - opacity: 0.3; - transform: scaleX(0.8); - } - 50% { - opacity: 0.8; - transform: scaleX(1.2); - } +.trace::before, +.trace::after { + content: ''; + position: absolute; + top: -4px; + width: 10px; + height: 10px; + border: 2px solid rgba(22, 163, 74, 0.42); + border-radius: 50%; + background: var(--chip-surface); +} + +.trace::before { + left: 22%; +} + +.trace::after { + right: 14%; +} + +.trace-a { left: -18%; right: 48%; top: 28%; transform: rotate(12deg); } +.trace-b { left: 42%; right: -18%; top: 30%; transform: rotate(-16deg); } +.trace-c { left: -12%; right: 44%; bottom: 25%; transform: rotate(-13deg); } +.trace-d { left: 46%; right: -16%; bottom: 28%; transform: rotate(13deg); } + +.chip-stack { + position: absolute; + left: 50%; + top: 51%; + width: 260px; + height: 260px; + transform: translate(-50%, -50%) rotate(-8deg); + filter: drop-shadow(0 28px 38px rgba(4, 46, 124, 0.26)); +} + +.chip { + position: absolute; + left: 50%; + top: 50%; + width: 188px; + height: 188px; + border-radius: 24px; + transform: translate(-50%, -50%); +} + +.chip-shadow { + top: 62%; + background: rgba(21, 128, 61, 0.18); + filter: blur(18px); +} + +.chip-base { + top: 55%; + background: linear-gradient(145deg, #dcfce7, #86efac 52%, #16a34a); + border: 1px solid rgba(255, 255, 255, 0.8); + box-shadow: inset 0 0 0 8px rgba(255, 255, 255, 0.36); } -/* Section side decorations */ -.features::after, -.use-cases::after, -.performance::after, -.architecture::after, -.getting-started::after { +.chip-base::before, +.chip-top::before { content: ''; position: absolute; - top: 20%; - right: -50px; - width: 100px; - height: 60%; - background: linear-gradient(180deg, transparent, rgba(74, 222, 128, 0.1), transparent); - border-radius: 50px; - animation: sideDecorFlow 8s ease-in-out infinite; - z-index: 1; + inset: -18px; + border-radius: 28px; + background: + repeating-linear-gradient(90deg, transparent 0 11px, rgba(22, 163, 74, 0.48) 11px 14px, transparent 14px 20px), + repeating-linear-gradient(0deg, transparent 0 11px, rgba(22, 163, 74, 0.48) 11px 14px, transparent 14px 20px); + z-index: -1; + opacity: 0.58; } -@keyframes sideDecorFlow { - 0%, 100% { - transform: translateY(0) rotate(0deg); - opacity: 0.4; - } - 50% { - transform: translateY(-20px) rotate(5deg); - opacity: 0.8; - } +.chip-top { + top: 45%; + display: grid; + place-items: center; + align-content: center; + gap: 4px; + color: white; + background: linear-gradient(145deg, #16a34a, #15803d); + border: 1px solid rgba(255, 255, 255, 0.58); + box-shadow: + inset 0 0 0 9px rgba(255, 255, 255, 0.08), + inset 0 -26px 38px rgba(0, 0, 0, 0.18); +} + +.chip-top strong { + font-family: var(--font-mono); + font-size: 1.22rem; + font-weight: 800; + letter-spacing: 0.05em; +} + +.chip-top small { + color: rgba(255, 255, 255, 0.82); + font-family: var(--font-mono); + font-size: 0.58rem; + font-weight: 700; + letter-spacing: 0.08em; +} + +.chip-mark { + display: grid; + place-items: center; + width: 34px; + height: 34px; + border: 2px solid rgba(255, 255, 255, 0.78); + border-radius: 9px; + font-family: var(--font-mono); + font-weight: 800; +} + +.module-card { + position: absolute; + width: 156px; + min-height: 122px; + display: grid; + align-content: center; + justify-items: center; + gap: 12px; + padding: 18px 14px; + border: 1px solid rgba(22, 163, 74, 0.18); + border-radius: 8px; + color: var(--ink); + background: var(--panel); + box-shadow: 0 18px 38px rgba(20, 128, 63, 0.12), inset 0 0 0 1px rgba(255, 255, 255, 0.76); + backdrop-filter: blur(16px); +} + +.module-card::before { + content: ''; + position: absolute; + inset: 8px; + border: 1px solid rgba(134, 239, 172, 0.24); + border-radius: 6px; + pointer-events: none; +} + +.module-title { + max-width: 128px; + font-family: var(--font-display); + font-size: 0.82rem; + font-weight: 740; + line-height: 1.16; + text-align: center; + letter-spacing: 0.015em; +} + +.module-agentic { + left: 50%; + top: 2%; + width: 188px; + min-height: 112px; + transform: translateX(-50%); +} +.module-agentic .module-title { max-width: 154px; } +.module-nodes { left: 0; top: 22%; } +.module-inference { left: 4%; bottom: 10%; } +.module-ai-native { right: 0; top: 22%; } +.module-pipeline { right: 4%; bottom: 10%; } + +.node-grid, +.app-grid, +.memory-stack, +.pipeline-bars, +.filesystem-rings { + position: relative; + width: 50px; + height: 40px; + display: block; +} + +.node-grid::before { + content: ''; + position: absolute; + inset: 5px 4px; + background: + radial-gradient(circle, #16a34a 0 4px, transparent 5px) 0 0 / 22px 22px, + linear-gradient(90deg, transparent 4px, #16a34a 5px 34px, transparent 35px) 3px 10px / 40px 2px no-repeat, + linear-gradient(90deg, transparent 4px, #16a34a 5px 34px, transparent 35px) 3px 32px / 40px 2px no-repeat, + linear-gradient(180deg, transparent 4px, #16a34a 5px 28px, transparent 29px) 11px 8px / 2px 30px no-repeat, + linear-gradient(180deg, transparent 4px, #16a34a 5px 28px, transparent 29px) 33px 8px / 2px 30px no-repeat; +} + +.app-grid::before { + content: ''; + position: absolute; + inset: 4px 8px; + background: repeating-linear-gradient(90deg, #16a34a 0 12px, transparent 12px 20px), repeating-linear-gradient(180deg, #16a34a 0 12px, transparent 12px 20px); + mask-image: linear-gradient(#000, #000); +} + +.memory-stack::before, +.memory-stack::after { + content: ''; + position: absolute; + left: 9px; + width: 32px; + height: 18px; + border: 3px solid #16a34a; + transform: rotate(45deg) skew(-8deg, -8deg); +} + +.memory-stack::before { top: 4px; } +.memory-stack::after { top: 16px; opacity: 0.62; } + +.pipeline-bars::before { + content: ''; + position: absolute; + left: 4px; + top: 6px; + width: 42px; + height: 28px; + background: + linear-gradient(#16a34a, #16a34a) 0 0 / 42px 5px no-repeat, + linear-gradient(#86efac, #86efac) 0 11px / 32px 5px no-repeat, + linear-gradient(#16a34a, #16a34a) 0 22px / 42px 5px no-repeat; +} + +.filesystem-rings::before, +.filesystem-rings::after { + content: ''; + position: absolute; + border: 3px solid #16a34a; + border-radius: 10px; +} + +.filesystem-rings::before { + left: 7px; + top: 5px; + width: 36px; + height: 28px; + box-shadow: inset 0 0 0 5px rgba(134, 239, 172, 0.22); +} + +.filesystem-rings::after { + left: 16px; + top: 13px; + width: 28px; + height: 22px; + border-color: #86efac; + background: rgba(22, 163, 74, 0.08); +} + +.section-band { + position: relative; + padding: 72px 0; } .section-title { + position: relative; + max-width: 720px; + margin: 0 auto 34px; text-align: center; - margin-bottom: 60px; } +.section-title::before, +.section-title::after { + top: 24px; + width: 190px; +} + +.section-title::before { right: calc(100% + 22px); } +.section-title::after { left: calc(100% + 22px); } + .section-title h2 { - font-size: 36px; - margin-bottom: 15px; - position: relative; - display: inline-block; - color: var(--text-color); - font-weight: 700; + margin: 0 0 14px; + color: var(--ink); + font-family: var(--font-display); + font-size: 2.55rem; + font-weight: 740; + letter-spacing: 0; } .section-title p { - color: #b0b0b0; - max-width: 600px; margin: 0 auto; + color: var(--muted); + font-size: 1rem; + line-height: 1.62; } -/* Feature Cards */ -.feature-cards { +.feature-cards, +.performance-stats, +.getting-started-steps { + position: relative; + z-index: 1; display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 30px; - margin-top: 40px; - width: 100%; - justify-content: space-between; + gap: 22px; } -.use-case-cards { - display: flex; - flex-wrap: nowrap; - gap: 30px; - max-width: 1400px; - margin: 0 auto; - width: 100%; - justify-content: center; - padding: 0 20px; +.three-columns { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.feature-card { - background-color: var(--card-bg); - border-radius: 12px; - overflow: hidden; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); - transition: all 0.3s ease; - border: 1px solid var(--border-color); - padding: 25px 40px; +.four-columns { + grid-template-columns: repeat(4, minmax(0, 1fr)); +} + +.feature-card, +.stat-card, +.step-card { position: relative; - min-width: 280px; - width: calc(25% - 23px); - flex: 1 1 0; + overflow: hidden; + min-width: 0; + border: 1px solid rgba(22, 163, 74, 0.14); + border-radius: 8px; + background: var(--card); + box-shadow: var(--shadow); + backdrop-filter: blur(16px); + transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; } -.feature-card::before { +.feature-card::before, +.stat-card::before, +.step-card::before { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - linear-gradient(45deg, transparent 30%, rgba(74, 222, 128, 0.02) 50%, transparent 70%), - linear-gradient(-45deg, transparent 30%, rgba(34, 197, 94, 0.01) 50%, transparent 70%); - background-size: 60px 60px, 40px 40px; - animation: cardLineFlow 12s linear infinite; + inset: 0; pointer-events: none; - border-radius: 12px; + background: + linear-gradient(90deg, rgba(22, 163, 74, 0.08), transparent 34%), + linear-gradient(180deg, rgba(134, 239, 172, 0.08), transparent 40%); + opacity: 0.72; } -@keyframes cardLineFlow { - 0% { - background-position: 0 0, 0 0; - } - 100% { - background-position: 60px 60px, -40px 40px; - } +.feature-card:hover, +.stat-card:hover, +.step-card:hover { + transform: translateY(-5px); + border-color: rgba(22, 163, 74, 0.28); + box-shadow: 0 24px 54px rgba(20, 128, 63, 0.16); } -.feature-card:hover { - transform: translateY(-5px); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); - border-color: var(--accent-color); +.feature-card { + padding: 30px 28px; + min-height: 230px; } -.feature-card:hover::before { - animation-duration: 6s; +.compact-cards .feature-card { + min-height: 258px; + padding: 28px 24px; } -.feature-icon { - font-size: 28px; - margin-bottom: 15px; - color: var(--accent-color); - display: flex; - align-items: center; - justify-content: center; - width: 56px; - height: 56px; - border-radius: 14px; - background: linear-gradient(135deg, rgba(74, 222, 128, 0.08), rgba(74, 222, 128, 0.04)); - border: 1px solid rgba(74, 222, 128, 0.15); - transition: all 0.3s ease; +.feature-content, +.feature-card h3, +.feature-card p, +.stat-card > *, +.step-card > * { position: relative; - backdrop-filter: blur(10px); + z-index: 1; } -.feature-icon::before { +.feature-icon, +.stat-icon, +.step-icon { + position: relative; + z-index: 1; + width: 64px; + height: 64px; + margin-bottom: 20px; + border: 1px solid rgba(22, 163, 74, 0.18); + border-radius: 8px; + background: linear-gradient(145deg, var(--panel-strong), rgba(225, 255, 235, 0.76)); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.86); +} + +.feature-icon::before, +.feature-icon::after, +.stat-icon::before, +.stat-icon::after, +.step-icon::before, +.step-icon::after { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 16px; - background: linear-gradient(135deg, rgba(74, 222, 128, 0.1), rgba(34, 197, 94, 0.05)); - opacity: 0; - transition: all 0.3s ease; - z-index: -1; } -.feature-card:hover .feature-icon { - transform: translateY(-2px); - border-color: rgba(74, 222, 128, 0.3); - box-shadow: 0 8px 25px rgba(74, 222, 128, 0.15); +.icon-performance::before { + inset: 14px; + border: 3px solid var(--blue); + border-radius: 8px; +} + +.icon-performance::after { + inset: 25px; + background: var(--blue); + border-radius: 3px; + box-shadow: -22px 0 0 -8px var(--blue), 22px 0 0 -8px var(--blue), 0 -22px 0 -8px var(--blue), 0 22px 0 -8px var(--blue); +} + +.icon-distributed::before { + left: 16px; + top: 16px; + width: 32px; + height: 32px; + background: + linear-gradient(var(--blue), var(--blue)) 0 7px / 32px 3px no-repeat, + linear-gradient(var(--blue), var(--blue)) 0 22px / 32px 3px no-repeat, + linear-gradient(90deg, var(--blue), var(--blue)) 7px 0 / 3px 32px no-repeat, + linear-gradient(90deg, var(--blue), var(--blue)) 22px 0 / 3px 32px no-repeat; +} + +.icon-distributed::after { + inset: 12px; + background: radial-gradient(circle, var(--blue) 0 4px, transparent 5px) 0 0 / 20px 20px; +} + +.icon-memory::before { + left: 13px; + top: 18px; + width: 38px; + height: 28px; + border: 3px solid var(--blue); + border-radius: 5px; +} + +.icon-memory::after { + left: 20px; + top: 27px; + width: 24px; + height: 3px; + background: var(--blue); + box-shadow: 0 8px 0 var(--blue); +} + +.icon-training::before, +.icon-inference::before, +.icon-olap::before, +.icon-bigdata::before { + inset: 15px; + border: 3px solid var(--blue); +} + +.icon-training::before { border-radius: 50%; border-top-color: transparent; } +.icon-inference::before { border-radius: 8px; box-shadow: 0 0 0 6px rgba(22, 163, 74, 0.08); } +.icon-bigdata::before { transform: rotate(45deg); border-radius: 5px; } + +.icon-training::after, +.icon-inference::after, +.icon-bigdata::after { + left: 28px; + top: 16px; + width: 8px; + height: 32px; + background: var(--blue); + border-radius: 4px; +} + +.icon-inference::after { transform: rotate(90deg); } +.icon-bigdata::after { box-shadow: -12px 12px 0 -2px var(--blue), 12px -12px 0 -2px var(--blue); } + +.icon-olap::before { + left: 13px; + top: 13px; + right: auto; + bottom: auto; + width: 25px; + height: 25px; + border: 3px solid var(--blue); + border-radius: 50%; + background: + linear-gradient(var(--blue-soft), var(--blue-soft)) 7px 8px / 11px 2px no-repeat, + linear-gradient(var(--blue-soft), var(--blue-soft)) 7px 15px / 7px 2px no-repeat; } -.feature-card:hover .feature-icon::before { - opacity: 1; +.icon-olap::after { + left: 36px; + top: 37px; + width: 20px; + height: 4px; + background: var(--blue); + border-radius: 999px; + transform: rotate(45deg); + transform-origin: left center; } -.feature-content h3 { - color: var(--accent-color); - margin-bottom: 10px; - font-size: 18px; +.feature-card h3, +.step-card h3 { + margin: 0 0 12px; + color: var(--ink); + font-family: var(--font-display); + font-size: 1.12rem; + line-height: 1.28; + font-weight: 720; } -.feature-content p { - color: #b0b0b0; - font-size: 14px; - line-height: 1.4; +.feature-card p, +.step-card p { + margin: 0; + color: var(--muted); + font-size: 0.94rem; + line-height: 1.62; } -/* Performance Stats */ .performance-stats { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); - gap: 30px; - margin-top: 40px; + grid-template-columns: repeat(3, minmax(0, 1fr)); } .stat-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 40px 20px; - text-align: center; - transition: all 0.3s ease; + min-height: 188px; + padding: 30px 26px; + display: grid; + grid-template-columns: 72px 1fr; + align-items: center; + column-gap: 22px; + text-align: left; +} + +.stat-icon { + grid-row: span 2; + margin-bottom: 0; +} + +.stat-latency::before { + left: 12px; + top: 16px; + width: 40px; + height: 40px; + border: 3px solid var(--blue-soft); + border-bottom-color: transparent; + border-left-color: transparent; + border-radius: 50%; + transform: rotate(-45deg); +} + +.stat-latency::after { + left: 16px; + top: 16px; + width: 32px; + height: 32px; + background: + radial-gradient(circle at 50% 82%, var(--blue) 0 4px, transparent 5px), + linear-gradient(var(--blue), var(--blue)) 50% 18% / 3px 22px no-repeat; + border-radius: 50%; + transform: rotate(42deg); + transform-origin: 50% 82%; +} + +.stat-throughput::before { + left: 14px; + bottom: 15px; + width: 8px; + height: 18px; + background: var(--blue); + border-radius: 3px 3px 1px 1px; + box-shadow: 14px -8px 0 var(--blue), 28px -18px 0 var(--blue); +} + +.stat-throughput::after { + left: 17px; + top: 14px; + width: 38px; + height: 32px; + background: var(--blue); + clip-path: polygon(0 66%, 9% 56%, 34% 34%, 50% 47%, 78% 17%, 70% 10%, 100% 0, 94% 31%, 87% 22%, 52% 60%, 35% 47%, 15% 66%, 22% 75%, 0 75%); +} + +.stat-uptime::before { + left: 16px; + top: 10px; + width: 32px; + height: 42px; + border: 4px solid var(--blue); + border-radius: 18px 18px 10px 10px; +} + +.stat-uptime::after { + left: 27px; + top: 28px; + width: 18px; + height: 10px; + border-left: 4px solid var(--blue); + border-bottom: 4px solid var(--blue); + transform: rotate(-45deg); +} + +.stat-number { + color: var(--blue); + font-family: var(--font-display); + font-size: 3.15rem; + font-weight: 760; + line-height: 1; +} + +.stat-label { + color: var(--ink); + font-family: var(--font-display); + font-size: 1.1rem; + line-height: 1.28; + font-weight: 720; +} + +.install-command-card { position: relative; + z-index: 1; + width: 100%; + margin: 28px auto 0; + padding: 18px; + color: var(--ink); + border: 1px solid rgba(22, 163, 74, 0.18); + border-radius: 10px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.96), rgba(237, 255, 243, 0.82)), + radial-gradient(circle at 10% 20%, rgba(134, 239, 172, 0.22), transparent 34%); + box-shadow: var(--shadow); overflow: hidden; } -.stat-card::before { +.install-command-card::before { content: ''; position: absolute; + inset: 0; + pointer-events: none; + background-image: + linear-gradient(90deg, transparent 0 58px, rgba(22, 163, 74, 0.08) 59px, transparent 60px), + linear-gradient(180deg, transparent 0 58px, rgba(22, 163, 74, 0.06) 59px, transparent 60px); + background-size: 78px 78px; + mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent); + opacity: 0.58; +} + +.install-command-card::after { + content: ''; + position: absolute; + left: 18px; + right: 18px; top: 0; - left: 0; - right: 0; - bottom: 0; - background: linear-gradient(135deg, rgba(74, 222, 128, 0.02), rgba(34, 197, 94, 0.01)); - opacity: 0; - transition: all 0.3s ease; + height: 3px; + background: linear-gradient(90deg, transparent, rgba(22, 163, 74, 0.58), rgba(134, 239, 172, 0.42), transparent); +} + +.install-command-copy { + position: relative; + z-index: 1; + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 14px; + padding: 14px; + border: 1px solid rgba(22, 163, 74, 0.2); + border-radius: 8px; + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(247, 255, 250, 0.82)); + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.82), 0 12px 28px rgba(20, 128, 63, 0.08); } -.stat-card:hover::before { - opacity: 1; +.install-command-eyebrow { + display: block; + margin: 0 0 8px; + color: var(--blue); + font-family: var(--font-display); + font-size: 0.75rem; + font-weight: 780; + letter-spacing: 0.08em; + text-transform: uppercase; } -.stat-card:hover { - transform: translateY(-5px); - border-color: var(--accent-color); - box-shadow: 0 10px 30px rgba(74, 222, 128, 0.1); +.install-command-line { + display: flex; + align-items: center; + min-width: 0; + padding: 12px 14px; + border: 1px solid rgba(22, 163, 74, 0.13); + border-radius: 7px; + background: rgba(245, 255, 249, 0.86); +} + +.install-command-copy code { + display: block; + min-width: 0; + overflow-x: auto; + color: var(--ink); + font-family: var(--font-mono); + font-size: 1rem; + line-height: 1.45; + white-space: nowrap; } -.stat-number { - font-size: 48px; - font-weight: 700; - color: var(--accent-color); - margin-bottom: 10px; - position: relative; - z-index: 1; +.install-command-button { + min-width: 108px; + min-height: 50px; + padding: 0 22px; + border: 1px solid rgba(22, 163, 74, 0.32); + border-radius: 7px; + color: white; + background: linear-gradient(135deg, #16a34a, #15803d); + box-shadow: 0 14px 28px rgba(22, 163, 74, 0.24); + font-family: var(--font-display); + font-weight: 780; + cursor: pointer; + transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease; } -.stat-label { - font-size: 16px; - color: #b0b0b0; +.install-command-button:hover { + transform: translateY(-1px); + border-color: rgba(22, 163, 74, 0.48); + box-shadow: 0 18px 36px rgba(22, 163, 74, 0.3); +} + + +.install-command-button--copied, +.install-command-button--copied:hover { + border-color: rgba(21, 128, 61, 0.58); + background: linear-gradient(135deg, #15803d, #16a34a); + box-shadow: 0 18px 36px rgba(22, 163, 74, 0.34); +} + +.install-command-card > p { position: relative; z-index: 1; + max-width: 820px; + margin: 12px auto 0; + color: var(--muted); + font-size: 0.94rem; + line-height: 1.58; + text-align: center; } -/* Getting Started Steps */ -.getting-started-steps { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 30px; - margin-top: 40px; +[data-theme='dark'] .install-command-card { + border-color: rgba(74, 222, 128, 0.22); + background: + linear-gradient(135deg, rgba(9, 37, 18, 0.92), rgba(14, 57, 26, 0.82)), + radial-gradient(circle at 10% 20%, rgba(74, 222, 128, 0.16), transparent 34%); } -.step-card { - background: var(--card-bg); - border: 1px solid var(--border-color); - border-radius: 12px; - padding: 30px; - text-align: center; - transition: all 0.3s ease; - position: relative; +[data-theme='dark'] .install-command-card::before { + background-image: + linear-gradient(90deg, transparent 0 58px, rgba(74, 222, 128, 0.09) 59px, transparent 60px), + linear-gradient(180deg, transparent 0 58px, rgba(74, 222, 128, 0.07) 59px, transparent 60px); } -.step-card:hover { - transform: translateY(-5px); - border-color: var(--accent-color); - box-shadow: 0 10px 30px rgba(74, 222, 128, 0.1); +[data-theme='dark'] .install-command-copy { + border-color: rgba(74, 222, 128, 0.22); + background: linear-gradient(180deg, rgba(12, 50, 24, 0.86), rgba(7, 31, 14, 0.78)); + box-shadow: inset 0 0 0 1px rgba(134, 239, 172, 0.08), 0 14px 34px rgba(0, 0, 0, 0.22); +} + +[data-theme='dark'] .install-command-line { + border-color: rgba(74, 222, 128, 0.16); + background: rgba(5, 27, 12, 0.72); +} + +.getting-started-steps { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.step-card { + min-height: 216px; + padding: 28px 28px 30px 98px; } .step-number { - width: 60px; - height: 60px; + position: absolute; + left: 28px; + top: 28px; + z-index: 2; + display: grid; + place-items: center; + width: 34px; + height: 34px; border-radius: 50%; - background: linear-gradient(135deg, var(--accent-color), var(--accent-dark)); color: white; - font-size: 24px; - font-weight: 700; - display: flex; - align-items: center; - justify-content: center; - margin: 0 auto 20px; - transition: all 0.3s ease; + background: var(--blue); + font-family: var(--font-mono); + font-weight: 800; + box-shadow: 0 12px 24px rgba(22, 163, 74, 0.22); } -.step-card:hover .step-number { - transform: scale(1.1); - box-shadow: 0 8px 25px rgba(74, 222, 128, 0.3); +.step-icon { + margin-bottom: 18px; } -.step-card h3 { - color: var(--accent-color); - margin-bottom: 15px; - font-size: 20px; +.step-install::before { + left: 15px; + top: 17px; + width: 34px; + height: 30px; + border: 3px solid var(--blue); + border-radius: 5px; } -.step-card p { - color: #b0b0b0; - font-size: 15px; +.step-install::after { + left: 25px; + top: 26px; + width: 14px; + height: 14px; + border-right: 3px solid var(--blue); + border-bottom: 3px solid var(--blue); + transform: rotate(-45deg); +} + +.step-configure::before { + inset: 16px; + border: 4px solid var(--blue); + border-radius: 50%; + box-shadow: 0 -14px 0 -9px var(--blue), 0 14px 0 -9px var(--blue), 14px 0 0 -9px var(--blue), -14px 0 0 -9px var(--blue); +} + +.step-configure::after { + inset: 26px; + background: var(--blue); + border-radius: 50%; +} + +.step-deploy::before { + left: 14px; + top: 25px; + width: 36px; + height: 20px; + border: 3px solid var(--blue); + border-top: 0; + border-radius: 0 0 16px 16px; +} + +.step-deploy::after { + left: 22px; + top: 17px; + width: 24px; + height: 24px; + border-top: 3px solid var(--blue); + border-left: 3px solid var(--blue); + border-radius: 18px 0 0 0; + transform: rotate(45deg); } -/* CTA Section */ .cta { - background: linear-gradient(135deg, var(--secondary-color), var(--primary-color)); - padding: 80px 0; - text-align: center; position: relative; overflow: hidden; + margin: 54px 0 0; + padding: 72px 0; + color: white; + background: + linear-gradient(135deg, #06358f 0%, #16a34a 56%, #1d73ff 100%); } .cta::before { content: ''; position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 20% 30%, rgba(74, 222, 128, 0.08) 0%, transparent 50%), - radial-gradient(circle at 80% 70%, rgba(34, 197, 94, 0.06) 0%, transparent 50%); - pointer-events: none; + inset: 0; + background-image: + linear-gradient(90deg, transparent 0 60px, rgba(255, 255, 255, 0.12) 61px, transparent 62px), + linear-gradient(180deg, transparent 0 60px, rgba(255, 255, 255, 0.1) 61px, transparent 62px); + background-size: 80px 80px; + mask-image: linear-gradient(90deg, transparent, #000 30%, #000 75%, transparent); } -.cta .container { +.cta-grid { position: relative; z-index: 1; + display: grid; + grid-template-columns: minmax(0, 1.2fr) 0.8fr; + align-items: center; + gap: 38px; } .cta h2 { - font-size: 36px; - margin-bottom: 20px; - color: var(--text-color); - font-weight: 700; + margin: 0 0 14px; + font-family: var(--font-display); + font-size: 2.75rem; + font-weight: 740; + line-height: 1.1; + color: white; } .cta p { - font-size: 18px; - color: #b0b0b0; - max-width: 600px; - margin: 0 auto 40px; + max-width: 620px; + margin: 0 0 30px; + color: rgba(255, 255, 255, 0.86); + font-size: 1.04rem; + line-height: 1.62; } -/* Responsive Design */ -@media (max-width: 768px) { - .hero { - padding: 120px 0 60px 0; +.btn-light { + color: var(--blue); + background: white; + box-shadow: 0 18px 36px rgba(0, 0, 0, 0.16); +} + +.btn-light:hover { + color: var(--blue-deep); +} + +.btn-outline-light { + color: white; + border: 1px solid rgba(255, 255, 255, 0.48); + background: rgba(255, 255, 255, 0.08); +} + +.btn-outline-light:hover { + color: white; + border-color: rgba(255, 255, 255, 0.76); +} + +.cta-chip { + position: relative; + justify-self: end; + width: min(100%, 380px); + aspect-ratio: 1.5; +} + +.cta-chip::before { + content: ''; + position: absolute; + inset: 28px 38px; + border-radius: 18px; + background: linear-gradient(145deg, rgba(255, 255, 255, 0.28), rgba(134, 239, 172, 0.18)); + border: 1px solid rgba(255, 255, 255, 0.38); + transform: rotate(-8deg); + box-shadow: 0 24px 44px rgba(0, 0, 0, 0.2), inset 0 0 0 8px rgba(255, 255, 255, 0.08); +} + +.cta-chip::after { + content: ''; + position: absolute; + left: 18px; + right: -40px; + top: 50%; + height: 2px; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.54), transparent); +} + +.cta-chip span { + position: absolute; + left: 48%; + top: 50%; + width: 48px; + height: 48px; + border: 3px solid rgba(255, 255, 255, 0.82); + border-radius: 12px; + transform: translate(-50%, -50%) rotate(-8deg); +} + +@media (max-width: 1100px) { + .hero-grid { + grid-template-columns: 1fr; + gap: 26px; + } + + .hero-content { + min-height: auto; + max-width: 860px; + gap: 30px; + padding: 0; + } + + html[lang="zh-CN"] .hero-content, + html[lang="zh-cn"] .hero-content { + min-height: auto; + max-width: 860px; + gap: 30px; + padding: 0; + } + + .hero-visual { min-height: 500px; } - - .hero h1 { - font-size: 32px; - margin-bottom: 20px; + + html[lang="zh-CN"] .hero-visual, + html[lang="zh-CN"] .circuit-board, + html[lang="zh-cn"] .hero-visual, + html[lang="zh-cn"] .circuit-board { + min-height: 430px; } - - .hero p { - font-size: 16px; - margin: 20px auto 40px; + + .four-columns { + grid-template-columns: repeat(2, minmax(0, 1fr)); } - - .section-title h2 { - font-size: 28px; + + .section-title::before, + .section-title::after { + display: none; } - - .feature-cards { - grid-template-columns: 1fr; - gap: 25px; - max-width: 600px; +} + +@media (max-width: 860px) { + .container { + width: min(100% - 32px, 1240px); } - - .use-case-cards { - grid-template-columns: repeat(2, 1fr); - gap: 20px; - max-width: 800px; + + .hero { + padding: 60px 0 48px; + } + + .hero h1, + html[lang="zh-CN"] .hero h1, + html[lang="zh-cn"] .hero h1 { + font-size: 3.35rem; + white-space: normal; } - - .performance-stats { + + + .install-command-copy { grid-template-columns: 1fr; } - - .getting-started-steps { + + .install-command-button { + min-height: 44px; + } + + .three-columns, + .performance-stats, + .getting-started-steps, + .cta-grid { grid-template-columns: 1fr; } - + + .stat-card { + min-height: 160px; + } + + .cta-chip { + justify-self: center; + width: min(100%, 320px); + } +} + +@media (max-width: 620px) { + .hero h1, + html[lang="zh-CN"] .hero h1, + html[lang="zh-cn"] .hero h1 { + font-size: 2.7rem; + white-space: normal; + } + + .section-title h2, + .cta h2 { + font-size: 2rem; + } + + .stat-number { + font-size: 2.45rem; + } + .btn-container { + align-items: stretch; flex-direction: column; - align-items: center; } - + .btn { - margin: 6px 0; - min-width: 200px; - } - - .stat-number { - font-size: 36px; + width: 100%; } - - .cta h2 { - font-size: 28px; + + .hero-visual, + .circuit-board { + min-height: 430px; } - - .cta p { - font-size: 16px; + + .chip-stack { + width: 220px; + height: 220px; } -} -@media (max-width: 480px) { - .hero h1 { - font-size: 24px; + .chip { + width: 150px; + height: 150px; + border-radius: 20px; } - - .hero p { - font-size: 14px; + + .module-card { + width: 132px; + min-height: 104px; + padding: 14px 10px; } - - .section-title h2 { - font-size: 24px; + + .module-title { + font-size: 0.72rem; } - - .feature-card { - padding: 20px; + + .module-agentic { + top: 0; + width: 144px; + min-height: 92px; } - - .feature-cards { + + .module-nodes { left: 0; top: 16%; } + .module-inference { left: 0; bottom: 0; } + .module-ai-native { right: 0; top: 16%; } + .module-pipeline { right: 0; bottom: 0; } + + .four-columns { grid-template-columns: 1fr; - gap: 20px; } - - .use-case-cards { + + .section-band { + padding: 54px 0; + } + + .feature-card, + .compact-cards .feature-card, + .stat-card, + .step-card { + min-height: auto; + padding: 24px; + } + + .stat-card { grid-template-columns: 1fr; - gap: 20px; + text-align: center; + justify-items: center; + row-gap: 10px; } - - .stat-number { - font-size: 28px; + + .step-card { + padding-left: 86px; } -} \ No newline at end of file + + .step-number { + left: 24px; + top: 24px; + } +} diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 7dbe402..f22b0d0 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,459 +1,364 @@ -import React from 'react'; -import Layout from '@theme/Layout'; -import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; -import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Translate, {translate} from '@docusaurus/Translate'; -import './index.css'; +import React from "react"; +import Layout from "@theme/Layout"; +import Link from "@docusaurus/Link"; +import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import Translate, {translate} from "@docusaurus/Translate"; +import "./index.css"; -/** - * Main homepage component for Curvine documentation site - * Converted from HTML to React with Docusaurus i18n support - */ function HomePage() { - const {siteConfig} = useDocusaurusContext(); + const {siteConfig, i18n} = useDocusaurusContext(); + const installCommand = "curl -fsSL https://curvineio.github.io/install.sh | bash"; + const [isInstallCommandCopied, setIsInstallCommandCopied] = React.useState(false); + + const markInstallCommandCopied = () => { + setIsInstallCommandCopied(true); + window.setTimeout(() => setIsInstallCommandCopied(false), 1800); + }; + + const handleCopyInstallCommand = async () => { + try { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(installCommand); + markInstallCommandCopied(); + return; + } + } catch { + // Fall back for non-secure origins or browsers that block clipboard access. + } + + const textArea = document.createElement("textarea"); + textArea.value = installCommand; + textArea.setAttribute("readonly", ""); + textArea.style.position = "fixed"; + textArea.style.left = "-9999px"; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand("copy"); + document.body.removeChild(textArea); + markInstallCommandCopied(); + }; return (
-
- Curvine -
-
-

- - High-Performance Distributed Caching System - -

-

- - Curvine is a high-performance distributed caching system implemented in Rust, designed for low-latency and high-throughput workloads with powerful data governance capabilities. - -

-
- - - Get Started - - - - - Learn More - - +
+
+
+
Rust systems software for data acceleration
+

+ {i18n.currentLocale === "zh-cn" ? ( + <> + 高性能分布式
缓存文件系统 + + ) : ( + <> + High-Performance Distributed Caching FileSystem + + )} +

+

+ + Curvine is a high-performance distributed caching filesystem implemented in Rust, designed for low-latency and high-throughput workloads with powerful data governance capabilities. + +

+
+
+ +
+
+ +
- {/* Features Section */} -
+

- + Core Features

- + Curvine delivers exceptional performance through innovative architecture and cutting-edge technology.

-
- {/* High Performance Card */} -
-
+
+
+

- + High Performance

- + Built with Rust for maximum performance, delivering microsecond-level latency and millions of operations per second.

-
- - {/* Distributed Architecture Card */} -
-
🌐
+
+
+

- + Distributed Architecture

- + Horizontally scalable architecture with automatic sharding and replication for high availability.

-
- - {/* Memory Efficiency Card */} -
-
💾
+
+
+

- + Memory Efficiency

- + Advanced memory management with intelligent caching strategies and automatic garbage collection.

-
+
- {/* Use Cases Section */} -
+

- + Why Choose Curvine

- - With the development of big data and AI, the performance requirements for large-scale data processing are increasing. Curvine is designed to solve large-scale IO acceleration and break through single-machine memory cache capacity bottlenecks. + + Curvine is designed to solve large-scale IO acceleration and break through single-machine memory cache capacity bottlenecks.

-
- {/* AI Training Acceleration Card */} -
-
🤖
-
-

- - AI Training Acceleration - -

-

- - Provides high-speed data access for deep learning training, significantly reducing data loading time, improving GPU utilization, and accelerating model training processes. - -

-
-
- - {/* Large Model Inference Card */} -
-
🧠
-
-

- - Large Model Inference Acceleration - -

-

- - Optimizes data access for large language model inference scenarios, reducing inference latency and improving model service response speed and throughput. - -

-
-
- - {/* OLAP Engine Card */} -
-
📊
-
-

- - OLAP Engine Query Acceleration - -

-

- - Provides high-speed caching for analytical databases and OLAP engines, significantly improving complex query performance and reducing data analysis time. - -

-
-
- - {/* Big Data Computing Card */} -
-
🔢
-
-

- - Big Data Computing - -

-

- - Provides hot data caching and shuffle acceleration for big data offline computing, significantly improving data processing efficiency. - -

-
-
+
+
+
+

AI Training Acceleration

+

Provides high-speed data access for deep learning training, significantly reducing data loading time, improving GPU utilization, and accelerating model training processes.

+
+
+
+

Large Model Inference Acceleration

+

Optimizes data access for large language model inference scenarios, reducing inference latency and improving model service response speed and throughput.

+
+
+
+

OLAP Engine Query Acceleration

+

Provides high-speed caching for analytical databases and OLAP engines, significantly improving complex query performance and reducing data analysis time.

+
+
+
+

Big Data Computing

+

Provides hot data caching and shuffle acceleration for big data offline computing, significantly improving data processing efficiency.

+
- {/* Performance Section */} -
+

- + Exceptional Performance

- - Curvine delivers industry-leading performance metrics that set new standards for distributed caching systems. + + Curvine keeps 100K concurrent clients stably connected with low-latency metadata operations and around 1 GB of connection memory overhead.

-
+
+
< 1ms
-
- - Average Latency - -
-
-
-
10M+
-
- - Operations/sec - -
-
-
+
Average Latency
+
+
+
+
{i18n.currentLocale === "zh-cn" ? "10w+" : "100K+"}
+
Operations/sec
+
+
+
99.99%
-
- - Uptime SLA - -
-
+
Uptime SLA
+
- {/* Getting Started Section */} -
+

- + Get Started in Minutes

- + Deploy Curvine quickly with our comprehensive documentation and tools.

-
-
1
-

- - Install - -

-

- - Download and install Curvine using our simple installation script. - -

-
-
-
2
-

- - Configure - -

-

- - Set up your cluster configuration with our intuitive configuration files. - -

-
-
-
3
-

- - Deploy - -

-

- - Launch your high-performance caching cluster and start serving requests. - -

+
+ 1 +
+

Install

+

Download and install Curvine using our simple installation script.

+
+
+ 2 +
+

Configure

+

Set up your cluster configuration with our intuitive configuration files.

+
+
+ 3 +
+

Deploy

+

Launch your high-performance caching cluster and start serving requests.

+
+
+
+
+
+ + + Run this in your terminal + + +
+ {installCommand} +
+
+
+

+ + If Docker is available on your machine, run the following command in your terminal, then follow the on-screen instructions to start a local Curvine demo cluster. + +

- {/* CTA Section */}
-
-

- - Ready to Get Started with Curvine? - -

-

- - Download now and experience the high-performance distributed caching system that Curvine brings. - -

-
- - - Download Now +
+
+

+ + Ready to Get Started with Curvine? - - - - View Source +

+

+ + Download now and experience the high-performance distributed caching filesystem that Curvine brings. - +

+
+ + Download Now + + + View Source + +
+
+
@@ -461,4 +366,4 @@ function HomePage() { ); } -export default HomePage; \ No newline at end of file +export default HomePage; diff --git a/src/theme/Layout/index.tsx b/src/theme/Layout/index.tsx index 276d3ed..2cf10f3 100644 --- a/src/theme/Layout/index.tsx +++ b/src/theme/Layout/index.tsx @@ -5,7 +5,7 @@ import type { WrapperProps } from '@docusaurus/types'; type Props = WrapperProps; -export default function LayoutWrapper(props: Props): JSX.Element { +export default function LayoutWrapper(props: Props): React.ReactElement { const [zoomedSvg, setZoomedSvg] = useState(null); const [scale, setScale] = useState(1); const [position, setPosition] = useState({ x: 0, y: 0 }); diff --git a/static/install.sh b/static/install.sh new file mode 100755 index 0000000..275cc71 --- /dev/null +++ b/static/install.sh @@ -0,0 +1,276 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Curvine one-command Docker demo installer. +# Usage: +# curl -fsSL https://curvineio.github.io/install.sh | bash +# Optional: +# CURVINE_IMAGE=ghcr.io/curvineio/curvine:latest bash install.sh +# CURVINE_MASTER_WEB_PORT=19000 bash install.sh +# CURVINE_RESET=false bash install.sh + +CURVINE_IMAGE="${CURVINE_IMAGE:-ghcr.io/curvineio/curvine:latest}" +CURVINE_PROJECT="${CURVINE_PROJECT:-curvine-demo}" +CURVINE_HOME_DIR="${CURVINE_HOME_DIR:-$HOME/.curvine/$CURVINE_PROJECT}" +NETWORK_NAME="${CURVINE_PROJECT}-net" +MASTER_NAME="${CURVINE_PROJECT}-master" +WORKER_NAME="${CURVINE_PROJECT}-worker" +MASTER_DATA_VOLUME="${CURVINE_PROJECT}-master-data" +WORKER_DATA_VOLUME="${CURVINE_PROJECT}-worker-data" +MASTER_LOG_VOLUME="${CURVINE_PROJECT}-master-logs" +WORKER_LOG_VOLUME="${CURVINE_PROJECT}-worker-logs" +MASTER_RPC_HOST_PORT="${CURVINE_MASTER_RPC_PORT:-18995}" +MASTER_JOURNAL_HOST_PORT="${CURVINE_MASTER_JOURNAL_PORT:-18996}" +WORKER_RPC_HOST_PORT="${CURVINE_WORKER_RPC_PORT:-18997}" +MASTER_WEB_HOST_PORT="${CURVINE_MASTER_WEB_PORT:-9000}" +WORKER_WEB_HOST_PORT="${CURVINE_WORKER_WEB_PORT:-9001}" +CURVINE_RESET="${CURVINE_RESET:-true}" + +info() { printf '[curvine] %s\n' "$*"; } +warn() { printf '[curvine] WARN: %s\n' "$*" >&2; } +die() { printf '[curvine] ERROR: %s\n' "$*" >&2; exit 1; } + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || die "'$1' is required. Please install it and retry." +} + +select_docker() { + if [ -n "${CURVINE_DOCKER:-}" ]; then + DOCKER_CMD="$CURVINE_DOCKER" + return + fi + + need_cmd docker + if docker info >/dev/null 2>&1; then + DOCKER_CMD="docker" + return + fi + + if command -v sudo >/dev/null 2>&1 && sudo -n docker info >/dev/null 2>&1; then + DOCKER_CMD="sudo docker" + return + fi + + die "Docker is installed but not accessible. Add your user to the docker group, start Docker Desktop, or set CURVINE_DOCKER='sudo docker' if passwordless sudo is enabled." +} + +run_docker() { + # shellcheck disable=SC2086 + $DOCKER_CMD "$@" +} + +write_config() { + mkdir -p "$CURVINE_HOME_DIR/conf" + cat > "$CURVINE_HOME_DIR/conf/curvine-cluster.toml" <<'EOF' +format_master = true +format_worker = true +cluster_id = "curvine-demo" + +[master] +rpc_port = 8995 +web_port = 9000 +meta_dir = "./data/meta" +audit_logging_enabled = true +log = { level = "info", log_dir = "./logs", file_name = "master.log" } + +[journal] +rpc_port = 8996 +journal_addrs = [ + { id = 1, hostname = "curvine-demo-master", port = 8996 } +] +journal_dir = "./data/journal" + +[worker] +rpc_port = 8997 +web_port = 9001 +dir_reserved = "1GB" +data_dir = [ + "[SSD]/data/data1", +] +log = { level = "info", log_dir = "./logs", file_name = "worker.log" } + +[client] +master_addrs = [ + { hostname = "curvine-demo-master", port = 8995 } +] + +[fuse] +debug = false + +[log] +level = "info" +log_dir = "./logs" +file_name = "client.log" +EOF + + cat > "$CURVINE_HOME_DIR/conf/curvine-env.sh" <<'EOF' +#!/usr/bin/env bash +export CURVINE_HOME=/app/curvine +export CURVINE_MASTER_HOSTNAME=curvine-demo-master +export CURVINE_WORKER_HOSTNAME=curvine-demo-worker +export CURVINE_CLIENT_HOSTNAME=curvine-demo-master +export CURVINE_CONF_FILE=/app/curvine/conf/curvine-cluster.toml +EOF +} + +cleanup_containers() { + for name in "$WORKER_NAME" "$MASTER_NAME"; do + if run_docker ps -a --format '{{.Names}}' | grep -qx "$name"; then + info "Removing existing container: $name" + run_docker rm -f "$name" >/dev/null + fi + done +} + +reset_demo_volumes() { + if [ "$CURVINE_RESET" != "true" ]; then + return + fi + for volume in "$MASTER_DATA_VOLUME" "$WORKER_DATA_VOLUME" "$MASTER_LOG_VOLUME" "$WORKER_LOG_VOLUME"; do + if run_docker volume inspect "$volume" >/dev/null 2>&1; then + info "Removing existing demo volume: $volume" + run_docker volume rm -f "$volume" >/dev/null + fi + done +} + +ensure_network() { + if ! run_docker network inspect "$NETWORK_NAME" >/dev/null 2>&1; then + info "Creating Docker network: $NETWORK_NAME" + run_docker network create "$NETWORK_NAME" >/dev/null + fi +} + +wait_for_app_log() { + local name="$1" + local log_glob="$2" + local pattern="$3" + local timeout_seconds="${4:-90}" + local elapsed=0 + while [ "$elapsed" -lt "$timeout_seconds" ]; do + local status + status="$(run_docker inspect -f '{{.State.Status}}' "$name" 2>/dev/null || true)" + if [ "$status" = "exited" ] || [ "$status" = "dead" ]; then + warn "Container $name exited before becoming ready. Recent logs:" + run_docker logs --tail 100 "$name" >&2 || true + return 1 + fi + if [ "$status" = "running" ] && run_docker exec "$name" bash -lc "grep -q '$pattern' $log_glob" >/dev/null 2>&1; then + return 0 + fi + sleep 2 + elapsed=$((elapsed + 2)) + done + warn "Timed out waiting for $name readiness pattern: $pattern" + run_docker logs --tail 100 "$name" >&2 || true + run_docker exec "$name" bash -lc "tail -120 $log_glob" >&2 || true + return 1 +} + + +wait_for_live_worker() { + local timeout_seconds="${1:-60}" + local elapsed=0 + while [ "$elapsed" -lt "$timeout_seconds" ]; do + if run_docker exec "$MASTER_NAME" bash -lc "cv report | grep -Eq 'live_worker_num:[[:space:]]*[1-9]'" >/dev/null 2>&1; then + return 0 + fi + sleep 2 + elapsed=$((elapsed + 2)) + done + warn "Timed out waiting for at least one live worker in cv report." + return 1 +} + +wait_for_http() { + local url="$1" + local timeout_seconds="${2:-60}" + local elapsed=0 + while [ "$elapsed" -lt "$timeout_seconds" ]; do + if curl -fsS "$url" >/dev/null 2>&1; then + return 0 + fi + sleep 2 + elapsed=$((elapsed + 2)) + done + return 1 +} + +main() { + need_cmd curl + select_docker + + info "Using image: $CURVINE_IMAGE" + info "Pulling Curvine image..." + run_docker pull "$CURVINE_IMAGE" + + write_config + cleanup_containers + reset_demo_volumes + ensure_network + + info "Starting Curvine master..." + run_docker run -d \ + --name "$MASTER_NAME" \ + --hostname "$MASTER_NAME" \ + --network "$NETWORK_NAME" \ + -e CURVINE_MASTER_HOSTNAME="$MASTER_NAME" \ + -e CURVINE_CLIENT_HOSTNAME="$MASTER_NAME" \ + -e CURVINE_CONF_FILE=/app/curvine/conf/curvine-cluster.toml \ + -p "$MASTER_RPC_HOST_PORT:8995" \ + -p "$MASTER_JOURNAL_HOST_PORT:8996" \ + -p "$MASTER_WEB_HOST_PORT:9000" \ + -v "$CURVINE_HOME_DIR/conf:/app/curvine/conf:ro" \ + -v "$MASTER_DATA_VOLUME:/app/curvine/data" \ + -v "$MASTER_LOG_VOLUME:/app/curvine/logs" \ + "$CURVINE_IMAGE" master start >/dev/null + wait_for_app_log "$MASTER_NAME" "/app/curvine/logs/master.log.*" "Rpc server .* start successfully" 120 + + info "Starting Curvine worker..." + run_docker run -d \ + --name "$WORKER_NAME" \ + --hostname "$WORKER_NAME" \ + --network "$NETWORK_NAME" \ + -e CURVINE_MASTER_HOSTNAME="$MASTER_NAME" \ + -e CURVINE_WORKER_HOSTNAME="$WORKER_NAME" \ + -e CURVINE_CLIENT_HOSTNAME="$MASTER_NAME" \ + -e CURVINE_CONF_FILE=/app/curvine/conf/curvine-cluster.toml \ + -p "$WORKER_RPC_HOST_PORT:8997" \ + -p "$WORKER_WEB_HOST_PORT:9001" \ + -v "$CURVINE_HOME_DIR/conf:/app/curvine/conf:ro" \ + -v "$WORKER_DATA_VOLUME:/data/data1" \ + -v "$WORKER_LOG_VOLUME:/app/curvine/logs" \ + "$CURVINE_IMAGE" worker start >/dev/null + wait_for_app_log "$WORKER_NAME" "/app/curvine/logs/worker.log.*" "worker register success" 120 + + if wait_for_http "http://127.0.0.1:$MASTER_WEB_HOST_PORT" 60; then + info "Master Web UI is ready: http://127.0.0.1:$MASTER_WEB_HOST_PORT" + else + warn "Master Web UI did not respond yet. Check logs with: $DOCKER_CMD logs $MASTER_NAME" + fi + + info "Waiting for worker registration to appear in cv report..." + if wait_for_live_worker 60; then + info "Running a quick CLI check..." + run_docker exec "$MASTER_NAME" bash -lc 'cv report' + else + warn "CLI check did not find a live worker yet. The containers are running; inspect logs if needed." + fi + + cat <