+
{item.label} {item.value}
{item.suffix}
diff --git a/web/src/index.css b/web/src/index.css
index 9961db7..752d8a0 100644
--- a/web/src/index.css
+++ b/web/src/index.css
@@ -2,100 +2,104 @@
/* 这些变量叠加在 Semi Design 之上,供新的 shadcn 风格组件使用 */
:root {
- /* shadcn/ui 基础色板 — Slate 中性色调 (220° hue) */
--background: 0 0% 100%;
- --foreground: 240 10% 3.9%;
+ --foreground: 222 47% 11%;
--card: 0 0% 100%;
- --card-foreground: 240 10% 3.9%;
+ --card-foreground: 222 47% 11%;
--popover: 0 0% 100%;
- --popover-foreground: 240 10% 3.9%;
- --primary: 0 0% 9%;
- --primary-foreground: 0 0% 98%;
- --secondary: 220 5% 96%;
- --secondary-foreground: 220 6% 10%;
- --muted: 220 5% 96%;
- --muted-foreground: 220 4% 56%;
- --accent: 220 5% 96%;
- --accent-foreground: 220 6% 10%;
+ --popover-foreground: 222 47% 11%;
+ --primary: 221 83% 53%;
+ --primary-foreground: 0 0% 100%;
+ --secondary: 210 40% 96%;
+ --secondary-foreground: 222 47% 11%;
+ --muted: 210 40% 96%;
+ --muted-foreground: 215 16% 47%;
+ --accent: 221 83% 53%;
+ --accent-foreground: 0 0% 100%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
- --border: 220 13% 91%;
- --input: 220 13% 91%;
- --ring: 0 0% 9%;
+ --border: 214 32% 91%;
+ --input: 214 32% 91%;
+ --ring: 221 83% 53%;
--radius: 0.5rem;
- --link: 0 0% 9%;
- --link-hover: 0 0% 20%;
- --chart-1: 160 60% 45%;
- --chart-2: 217 91% 60%;
+ --link: 221 83% 53%;
+ --link-hover: 221 83% 40%;
+ --chart-1: 221 83% 53%;
+ --chart-2: 160 60% 45%;
--chart-3: 43 96% 56%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
.dark {
- --background: 0 0% 5.5%;
- --foreground: 0 0% 90%;
- --card: 0 0% 10%;
- --card-foreground: 0 0% 90%;
- --popover: 0 0% 8%;
- --popover-foreground: 0 0% 90%;
- --primary: 236 52% 62%;
+ --background: 210 60% 97%;
+ --foreground: 222 47% 11%;
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+ --primary: 217 91% 60%;
--primary-foreground: 0 0% 100%;
- --secondary: 0 0% 13%;
- --secondary-foreground: 0 0% 90%;
- --muted: 0 0% 9%;
- --muted-foreground: 0 0% 56%;
- --accent: 236 52% 62%;
+ --secondary: 210 40% 93%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+ --muted: 210 40% 93%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+ --accent: 217 91% 60%;
--accent-foreground: 0 0% 100%;
--destructive: 0 62% 45%;
--destructive-foreground: 0 0% 100%;
- --border: 0 0% 100% / 0.08;
- --input: 0 0% 100% / 0.06;
- --ring: 236 52% 62%;
- --link: 236 52% 72%;
- --link-hover: 236 52% 62%;
+ --border: 214.3 31.8% 85%;
+ --input: 214.3 31.8% 85%;
+ --ring: 217 91% 60%;
+ --link: 217 91% 60%;
+ --link-hover: 217 91% 50%;
--radius: 0.375rem;
- --chart-1: 236 52% 62%;
+ --chart-1: 217 91% 60%;
--chart-2: 160 60% 50%;
--chart-3: 43 96% 56%;
--chart-4: 280 65% 60%;
--chart-5: 340 75% 55%;
}
-/* === Linear Dark: Semi Design 暗色覆盖 === */
-.dark {
- --semi-color-primary: rgba(94, 106, 210, 1);
- --semi-color-primary-hover: rgba(114, 126, 230, 1);
- --semi-color-primary-active: rgba(78, 90, 190, 1);
- --semi-color-primary-disabled: rgba(94, 106, 210, 0.35);
- --semi-color-primary-light-default: rgba(94, 106, 210, 0.12);
- --semi-color-primary-light-hover: rgba(94, 106, 210, 0.18);
- --semi-color-primary-light-active: rgba(94, 106, 210, 0.24);
- --semi-color-bg-0: rgba(14, 14, 14, 1);
- --semi-color-bg-1: rgba(19, 19, 19, 1);
- --semi-color-bg-2: rgba(25, 25, 25, 1);
- --semi-color-bg-3: rgba(32, 32, 32, 1);
- --semi-color-bg-4: rgba(40, 40, 40, 1);
- --semi-color-text-0: rgba(230, 230, 230, 1);
- --semi-color-text-1: rgba(190, 190, 190, 1);
- --semi-color-text-2: rgba(140, 140, 140, 1);
- --semi-color-text-3: rgba(100, 100, 100, 1);
- --semi-color-border: rgba(255, 255, 255, 0.08);
- --semi-color-fill-0: rgba(255, 255, 255, 0.04);
- --semi-color-fill-1: rgba(255, 255, 255, 0.06);
- --semi-color-fill-2: rgba(255, 255, 255, 0.08);
- --semi-color-link: rgba(130, 143, 255, 1);
- --semi-color-link-hover: rgba(158, 168, 255, 1);
- --semi-color-link-active: rgba(110, 122, 235, 1);
- --semi-color-link-visited: rgba(130, 143, 255, 0.8);
- --semi-color-tertiary: rgba(255, 255, 255, 0.06);
- --semi-color-tertiary-hover: rgba(255, 255, 255, 0.10);
- --semi-color-tertiary-active: rgba(255, 255, 255, 0.14);
- --semi-shadow-elevated: 0 0 0 1px rgba(255,255,255,0.06), 0 2px 8px rgba(0,0,0,0.3);
+/* === Linear Dark: Semi Design 暗色覆盖 — 蓝白渐变主题 === */
+body[theme-mode='dark'] {
+ --semi-color-primary: rgba(59, 130, 246, 1);
+ --semi-color-primary-hover: rgba(79, 150, 255, 1);
+ --semi-color-primary-active: rgba(37, 99, 235, 1);
+ --semi-color-primary-disabled: rgba(59, 130, 246, 0.35);
+ --semi-color-primary-light-default: rgba(59, 130, 246, 0.12);
+ --semi-color-primary-light-hover: rgba(59, 130, 246, 0.18);
+ --semi-color-primary-light-active: rgba(59, 130, 246, 0.24);
+ --semi-color-bg-0: rgba(240, 247, 255, 1);
+ --semi-color-bg-1: rgba(235, 244, 255, 1);
+ --semi-color-bg-2: rgba(224, 240, 253, 1);
+ --semi-color-bg-3: rgba(214, 232, 248, 1);
+ --semi-color-bg-4: rgba(204, 224, 244, 1);
+ --semi-color-nav-bg: rgba(240, 247, 255, 1);
+ --semi-color-text-0: rgba(15, 23, 42, 1);
+ --semi-color-text-1: rgba(51, 65, 85, 1);
+ --semi-color-text-2: rgba(100, 116, 139, 1);
+ --semi-color-text-3: rgba(148, 163, 184, 1);
+ --semi-color-border: rgba(191, 219, 254, 1);
+ --semi-color-fill-0: rgba(59, 130, 246, 0.04);
+ --semi-color-fill-1: rgba(59, 130, 246, 0.06);
+ --semi-color-fill-2: rgba(59, 130, 246, 0.08);
+ --semi-color-link: rgba(59, 130, 246, 1);
+ --semi-color-link-hover: rgba(37, 99, 235, 1);
+ --semi-color-link-active: rgba(29, 78, 216, 1);
+ --semi-color-link-visited: rgba(59, 130, 246, 0.8);
+ --semi-color-tertiary: rgba(59, 130, 246, 0.06);
+ --semi-color-tertiary-hover: rgba(59, 130, 246, 0.10);
+ --semi-color-tertiary-active: rgba(59, 130, 246, 0.14);
+ --semi-color-overlay-bg: rgba(22, 22, 26, .4);
+ --semi-shadow-elevated: 0 0 0 1px rgba(0,0,0,0.06), 0 2px 8px rgba(0,0,0,0.1);
--semi-color-success: rgba(52, 211, 153, 1);
--semi-color-danger: rgba(248, 113, 113, 1);
--semi-color-warning: rgba(251, 191, 36, 1);
- --semi-color-info: rgba(94, 106, 210, 1);
+ --semi-color-info: rgba(59, 130, 246, 1);
+ --semi-color-default: rgba(59, 130, 246, 0.04);
+ --semi-color-default-hover: rgba(59, 130, 246, 0.06);
+ --semi-color-default-active: rgba(59, 130, 246, 0.08);
}
/* 确保按钮圆角在暗色模式下生效(覆盖 Semi 库默认值) */
@@ -108,17 +112,25 @@
border-radius: 8px !important;
}
-/* 全局基础样式 — 使用新字体族 */
+/* 全局基础样式 — 蓝白渐变主题 */
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans SC', 'Microsoft YaHei', sans-serif;
color: hsl(var(--foreground));
- background-color: hsl(var(--background));
+ background: linear-gradient(135deg, #e0f0ff 0%, #edf5ff 20%, #f5faff 40%, #ffffff 60%, #f0f7ff 80%, #e8f4fd 100%);
+ background-attachment: fixed;
+ min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* ==================== Tailwind CSS 配置 ==================== */
+/* 暗色模式 body 背景也使用蓝白渐变 */
+html.dark body {
+ background: linear-gradient(135deg, #e0f0ff 0%, #edf5ff 20%, #f5faff 40%, #ffffff 60%, #f0f7ff 80%, #e8f4fd 100%);
+ background-attachment: fixed;
+}
+
/* ==================== Tailwind CSS 閰嶇疆 ==================== */
@layer tailwind-base, semi, tailwind-components, tailwind-utils;
@@ -1610,183 +1622,598 @@ html.dark .with-pastel-balls::before {
padding-top: 0.75rem !important;
}
-/* ==================== 妯″瀷瀹氫环椤甸潰甯冨眬 ==================== */
-.pricing-layout {
- height: calc(100vh - 60px);
- overflow: hidden;
- margin-top: 60px;
+/* ==================== SiliconFlow-style Pricing Page (Cool Effects) ==================== */
+
+/* ─── Keyframe Animations ─── */
+@keyframes sf-gradient-shift {
+ 0%, 100% { background-position: 0% 50%; }
+ 50% { background-position: 100% 50%; }
}
-.pricing-sidebar {
- width: clamp(280px, 24vw, 520px) !important;
- min-width: clamp(280px, 24vw, 520px) !important;
- max-width: clamp(280px, 24vw, 520px) !important;
- height: calc(100vh - 60px);
- background-color: var(--semi-color-bg-0);
- overflow: auto;
+@keyframes sf-float {
+ 0%, 100% { transform: translateY(0) translateX(0); }
+ 25% { transform: translateY(-20px) translateX(10px); }
+ 50% { transform: translateY(-10px) translateX(-5px); }
+ 75% { transform: translateY(-25px) translateX(8px); }
}
-.pricing-content {
- height: calc(100vh - 60px);
- background-color: var(--semi-color-bg-0);
- display: flex;
- flex-direction: column;
+@keyframes sf-pulse-glow {
+ 0%, 100% { box-shadow: 0 0 20px rgba(25, 118, 210, 0.1); }
+ 50% { box-shadow: 0 0 40px rgba(25, 118, 210, 0.25); }
}
-.pricing-pagination-divider {
- border-color: var(--semi-color-border);
+@keyframes sf-card-in {
+ from { opacity: 0; transform: translateY(30px) scale(0.96); }
+ to { opacity: 1; transform: translateY(0) scale(1); }
}
-.pricing-content-mobile {
- height: 100%;
- display: flex;
- flex-direction: column;
- overflow: auto;
+@keyframes sf-shimmer {
+ 0% { background-position: -200% 0; }
+ 100% { background-position: 200% 0; }
}
-.pricing-search-header {
- padding: 0.5rem;
- background-color: var(--semi-color-bg-0);
- flex-shrink: 0;
- position: sticky;
+@keyframes sf-glow-ring {
+ 0%, 100% { opacity: 0.3; transform: scale(1); }
+ 50% { opacity: 0.8; transform: scale(1.02); }
+}
+
+@keyframes sf-fade-up {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+@keyframes sf-search-glow {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(25, 118, 210, 0); }
+ 50% { box-shadow: 0 0 0 8px rgba(25, 118, 210, 0.08); }
+}
+
+@keyframes sf-dot-float {
+ 0% { transform: translateY(100vh) scale(0); opacity: 0; }
+ 10% { opacity: 1; }
+ 90% { opacity: 1; }
+ 100% { transform: translateY(-10vh) scale(1); opacity: 0; }
+}
+
+@keyframes sf-title-reveal {
+ from { opacity: 0; transform: translateY(30px); filter: blur(8px); }
+ to { opacity: 1; transform: translateY(0); filter: blur(0); }
+}
+
+@keyframes sf-price-glow {
+ 0%, 100% { text-shadow: none; }
+ 50% { text-shadow: 0 0 8px rgba(25, 118, 210, 0.3); }
+}
+
+/* Page Container */
+.sf-pricing-page {
+ min-height: 100vh;
+ background: #f7f9fc;
+ position: relative;
+ overflow-x: hidden;
+}
+
+/* ─── Floating Particles Background ─── */
+.sf-particles {
+ position: fixed;
top: 0;
- z-index: 5;
-}
-
-.pricing-view-container {
- flex: 1;
- overflow: auto;
-}
-
-.pricing-view-container-mobile {
- flex: 1;
- overflow: auto;
- min-height: 0;
-}
-
-/* ─── Models 新布局 ─── */
-.models-page-layout {
- display: flex;
- max-width: 1440px;
- margin: 0 auto;
- padding: 56px 24px 0;
- gap: 32px;
- height: 100vh;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ pointer-events: none;
+ z-index: 0;
overflow: hidden;
+}
+
+.sf-particle {
+ position: absolute;
+ width: 4px;
+ height: 4px;
+ border-radius: 50%;
+ background: rgba(25, 118, 210, 0.15);
+ animation: sf-dot-float linear infinite;
+}
+.sf-particle:nth-child(1) { left: 10%; animation-duration: 12s; animation-delay: 0s; width: 6px; height: 6px; background: rgba(25, 118, 210, 0.1); }
+.sf-particle:nth-child(2) { left: 25%; animation-duration: 15s; animation-delay: 2s; width: 4px; height: 4px; }
+.sf-particle:nth-child(3) { left: 40%; animation-duration: 10s; animation-delay: 4s; width: 8px; height: 8px; background: rgba(66, 165, 245, 0.12); }
+.sf-particle:nth-child(4) { left: 55%; animation-duration: 18s; animation-delay: 1s; width: 3px; height: 3px; }
+.sf-particle:nth-child(5) { left: 70%; animation-duration: 13s; animation-delay: 3s; width: 5px; height: 5px; background: rgba(21, 101, 192, 0.1); }
+.sf-particle:nth-child(6) { left: 85%; animation-duration: 16s; animation-delay: 5s; width: 4px; height: 4px; }
+.sf-particle:nth-child(7) { left: 15%; animation-duration: 14s; animation-delay: 6s; width: 7px; height: 7px; background: rgba(25, 118, 210, 0.08); }
+.sf-particle:nth-child(8) { left: 60%; animation-duration: 11s; animation-delay: 7s; width: 3px; height: 3px; }
+
+.sf-pricing-inner {
+ max-width: 1400px;
+ margin: 0 auto;
+ padding: 0 32px 60px;
+ position: relative;
+ z-index: 1;
+}
+
+/* ─── Hero Section ─── */
+.sf-hero {
+ text-align: center;
+ padding: 70px 0 50px;
+ position: relative;
+}
+
+.sf-hero::before {
+ content: '';
+ position: absolute;
+ top: -100px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 800px;
+ height: 500px;
+ background: radial-gradient(ellipse, rgba(25, 118, 210, 0.06) 0%, transparent 70%);
+ pointer-events: none;
+ animation: sf-pulse-glow 6s ease-in-out infinite;
+}
+
+.sf-hero-title {
+ font-size: 48px;
+ font-weight: 800;
+ margin: 0 0 16px;
+ line-height: 1.2;
+ background: linear-gradient(135deg, #0d47a1 0%, #1976d2 25%, #42a5f5 50%, #1976d2 75%, #0d47a1 100%);
+ background-size: 200% 200%;
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ animation: sf-gradient-shift 4s ease infinite, sf-title-reveal 0.8s ease-out;
+ text-shadow: none;
+}
+
+.sf-hero-subtitle {
+ font-size: 17px;
+ color: #546e7a;
+ margin: 0 0 36px;
+ animation: sf-fade-up 0.8s ease-out 0.2s both;
+}
+
+/* ─── Search Bar ─── */
+.sf-search-bar {
+ position: relative;
+ max-width: 620px;
+ margin: 0 auto 28px;
+ animation: sf-fade-up 0.8s ease-out 0.4s both;
+}
+
+.sf-search-icon {
+ position: absolute;
+ left: 18px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: #90a4ae;
+ transition: color 0.3s;
+}
+
+.sf-search-input {
+ width: 100%;
+ height: 52px;
+ padding: 0 18px 0 48px;
+ border: 2px solid #e3f2fd;
+ border-radius: 14px;
+ background: #fff;
+ font-size: 15px;
+ color: #0a1929;
+ outline: none;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ box-shadow: 0 4px 16px rgba(25, 118, 210, 0.06);
box-sizing: border-box;
}
-
-.models-sidebar {
- width: 280px;
- flex-shrink: 0;
- overflow-y: auto;
- padding: 0 16px 24px 0;
- border-right: 1px solid var(--border-color, #e5e7eb);
+.sf-search-input::placeholder {
+ color: #b0bec5;
+ transition: color 0.3s;
+}
+.sf-search-input:focus {
+ border-color: #1976d2;
+ box-shadow: 0 4px 24px rgba(25, 118, 210, 0.18), 0 0 0 4px rgba(25, 118, 210, 0.06);
+ animation: sf-search-glow 2s ease-in-out infinite;
+}
+.sf-search-input:focus ~ .sf-search-icon {
+ color: #1976d2;
}
-.models-sidebar::-webkit-scrollbar {
- width: 4px;
-}
-.models-sidebar::-webkit-scrollbar-thumb {
- background: transparent;
-}
-.models-sidebar:hover::-webkit-scrollbar-thumb {
- background: rgba(0,0,0,0.15);
- border-radius: 2px;
+/* ─── Hot Tags ─── */
+.sf-hot-tags {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ flex-wrap: wrap;
+ animation: sf-fade-up 0.8s ease-out 0.6s both;
}
-.models-content {
- flex: 1;
- min-width: 0;
+.sf-hot-label {
+ font-size: 13px;
+ font-weight: 600;
+ color: #546e7a;
+}
+
+.sf-hot-tag {
+ padding: 5px 14px;
+ border: 1px solid #e3f2fd;
+ border-radius: 20px;
+ background: #fff;
+ font-size: 12px;
+ color: #374151;
+ cursor: pointer;
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+ position: relative;
+ overflow: hidden;
+}
+.sf-hot-tag::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(135deg, rgba(25, 118, 210, 0.08), rgba(66, 165, 245, 0.08));
+ opacity: 0;
+ transition: opacity 0.25s;
+}
+.sf-hot-tag:hover {
+ border-color: #1976d2;
+ color: #1976d2;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(25, 118, 210, 0.15);
+}
+.sf-hot-tag:hover::before {
+ opacity: 1;
+}
+
+/* ─── Filter Section ─── */
+.sf-filter-section {
+ padding: 24px 0;
display: flex;
flex-direction: column;
+ gap: 16px;
+ animation: sf-fade-up 0.8s ease-out 0.7s both;
+}
+
+.sf-filter-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ flex-wrap: wrap;
+}
+
+.sf-filter-label {
+ font-size: 14px;
+ font-weight: 600;
+ color: #374151;
+ min-width: 70px;
+ flex-shrink: 0;
+}
+
+.sf-filter-pills {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+
+.sf-filter-pill {
+ padding: 7px 18px;
+ border: 1.5px solid #e3f2fd;
+ border-radius: 10px;
+ background: #fff;
+ font-size: 13px;
+ color: #546e7a;
+ cursor: pointer;
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
+ position: relative;
+}
+.sf-filter-pill:hover {
+ border-color: #1976d2;
+ color: #1976d2;
+ transform: translateY(-1px);
+ box-shadow: 0 3px 10px rgba(25, 118, 210, 0.1);
+}
+.sf-filter-pill.active {
+ border-color: #1976d2;
+ background: linear-gradient(135deg, #e3f2fd, #bbdefb);
+ color: #1976d2;
+ font-weight: 600;
+ box-shadow: 0 2px 12px rgba(25, 118, 210, 0.2);
+}
+
+/* ─── Results Header ─── */
+.sf-results-header {
+ padding: 16px 0 8px;
+ animation: sf-fade-up 0.8s ease-out 0.8s both;
+}
+
+.sf-results-title {
+ font-size: 22px;
+ font-weight: 700;
+ color: #0a1929;
+ margin: 0;
+}
+
+.sf-results-count {
+ color: #1976d2;
+ font-weight: 800;
+ animation: sf-price-glow 3s ease-in-out infinite;
+}
+
+/* ─── Card Grid ─── */
+.sf-card-grid {
+ display: grid;
+ gap: 22px;
+ padding: 20px 0;
+}
+
+/* ─── Model Card ─── */
+.sf-model-card {
+ position: relative;
+ background: #fff;
+ border: 1.5px solid #e3f2fd;
+ border-radius: 16px;
+ padding: 24px;
+ height: 230px;
+ display: flex;
+ flex-direction: column;
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ overflow: hidden;
+ cursor: pointer;
+ animation: sf-card-in 0.6s ease-out both;
+ /* Staggered entrance */
+}
+.sf-model-card:nth-child(1) { animation-delay: 0.1s; }
+.sf-model-card:nth-child(2) { animation-delay: 0.15s; }
+.sf-model-card:nth-child(3) { animation-delay: 0.2s; }
+.sf-model-card:nth-child(4) { animation-delay: 0.25s; }
+.sf-model-card:nth-child(5) { animation-delay: 0.3s; }
+.sf-model-card:nth-child(6) { animation-delay: 0.35s; }
+.sf-model-card:nth-child(7) { animation-delay: 0.4s; }
+.sf-model-card:nth-child(8) { animation-delay: 0.45s; }
+.sf-model-card:nth-child(9) { animation-delay: 0.5s; }
+
+/* Glow border on hover */
+.sf-model-card::before {
+ content: '';
+ position: absolute;
+ inset: -2px;
+ border-radius: 18px;
+ background: linear-gradient(135deg, #1976d2, #42a5f5, #1976d2);
+ opacity: 0;
+ z-index: -1;
+ transition: opacity 0.4s ease;
+}
+.sf-model-card::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border-radius: 16px;
+ background: #fff;
+ z-index: -1;
+}
+.sf-model-card:hover {
+ transform: translateY(-6px) scale(1.01);
+ border-color: transparent;
+ box-shadow: 0 12px 40px rgba(25, 118, 210, 0.2), 0 0 60px rgba(25, 118, 210, 0.06);
+}
+.sf-model-card:hover::before {
+ opacity: 1;
+ animation: sf-glow-ring 2s ease-in-out infinite;
+}
+
+/* Shimmer effect on hover */
+.sf-model-card .sf-shimmer-overlay {
+ position: absolute;
+ inset: 0;
+ border-radius: 16px;
+ background: linear-gradient(90deg, transparent 0%, rgba(25, 118, 210, 0.04) 50%, transparent 100%);
+ background-size: 200% 100%;
+ opacity: 0;
+ transition: opacity 0.3s;
+ pointer-events: none;
+}
+.sf-model-card:hover .sf-shimmer-overlay {
+ opacity: 1;
+ animation: sf-shimmer 2s linear infinite;
+}
+
+/* Card Header */
+.sf-card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+
+.sf-card-provider {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.sf-card-icon-wrap {
+ width: 40px;
+ height: 40px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, #e3f2fd, #bbdefb);
+ flex-shrink: 0;
+ transition: all 0.3s;
+}
+.sf-model-card:hover .sf-card-icon-wrap {
+ transform: scale(1.08);
+ box-shadow: 0 4px 12px rgba(25, 118, 210, 0.2);
+}
+
+.sf-card-icon {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.sf-card-provider-name {
+ font-size: 13px;
+ color: #546e7a;
+ font-weight: 500;
+}
+
+.sf-card-type-tag {
+ font-size: 11px;
+ padding: 3px 10px;
+ border-radius: 6px;
+ background: linear-gradient(135deg, #e3f2fd, #bbdefb);
+ color: #1976d2;
+ font-weight: 600;
+ letter-spacing: 0.02em;
+}
+
+/* Default View */
+.sf-card-default-view {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ transition: opacity 0.4s ease;
+}
+
+.sf-card-model-name {
+ font-size: 17px;
+ font-weight: 700;
+ color: #0a1929;
+ margin: 0 0 10px;
+ line-height: 1.3;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ transition: color 0.3s;
+}
+.sf-model-card:hover .sf-card-model-name {
+ color: #1976d2;
+}
+
+.sf-card-tags {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+ margin-bottom: 12px;
+}
+
+.sf-card-tag {
+ font-size: 11px;
+ padding: 3px 10px;
+ border-radius: 6px;
+ background: #f0f7ff;
+ color: #1976d2;
+ font-weight: 500;
+ transition: all 0.25s;
+}
+.sf-model-card:hover .sf-card-tag {
+ background: #e3f2fd;
+}
+
+/* Pricing */
+.sf-card-pricing {
+ margin-top: auto;
+ display: flex;
+ gap: 16px;
+}
+
+.sf-price-item {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+}
+
+.sf-price-label {
+ font-size: 11px;
+ color: #90a4ae;
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+}
+
+.sf-price-value {
+ font-size: 15px;
+ font-weight: 700;
+ color: #1976d2;
+ transition: all 0.3s;
+}
+.sf-model-card:hover .sf-price-value {
+ animation: sf-price-glow 2s ease-in-out infinite;
+}
+
+/* Hover View */
+.sf-card-hover-view {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 24px;
+ background: linear-gradient(transparent, rgba(255,255,255,0.97) 15%);
+ opacity: 0;
+ transform: translateY(12px);
+ transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
+ pointer-events: none;
+}
+.sf-model-card:hover .sf-card-hover-view {
+ opacity: 1;
+ transform: translateY(0);
+}
+.sf-model-card:hover .sf-card-default-view {
+ opacity: 0.2;
+}
+
+.sf-card-description {
+ font-size: 13px;
+ color: #546e7a;
+ line-height: 1.6;
+ margin: 0;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
overflow: hidden;
}
-.models-tabs-bar {
+/* ─── Pagination ─── */
+.sf-pagination {
display: flex;
- gap: 8px;
- padding: 16px 0;
- flex-shrink: 0;
- border-bottom: 1px solid var(--border-color, #e5e7eb);
+ justify-content: center;
+ padding: 36px 0 16px;
}
-.models-tab-btn {
- padding: 6px 20px;
- border-radius: 20px;
- border: 1px solid var(--border-color, #d1d5db);
- background: transparent;
- cursor: pointer;
- font-size: 14px;
- color: var(--text-muted, #6b7280);
- transition: all 0.2s;
- white-space: nowrap;
-}
-.models-tab-btn:hover {
- border-color: var(--primary-color, #3b82f6);
- color: var(--primary-color, #3b82f6);
-}
-.models-tab-btn.active {
- background: var(--primary-color, #3b82f6);
- color: white;
- border-color: var(--primary-color, #3b82f6);
+/* ─── Responsive ─── */
+@media (max-width: 1024px) {
+ .sf-pricing-inner {
+ padding: 0 20px 40px;
+ }
+ .sf-hero-title {
+ font-size: 36px;
+ }
}
-.models-card-grid {
- display: grid;
- grid-template-columns: repeat(2, 1fr);
- gap: 16px;
- padding: 16px 0;
- overflow-y: auto;
- flex: 1;
-}
-
-.models-card-grid::-webkit-scrollbar {
- width: 6px;
-}
-.models-card-grid::-webkit-scrollbar-thumb {
- background: rgba(0,0,0,0.12);
- border-radius: 3px;
-}
-
-/* 筛选面板标题 */
-.models-filter-section {
- margin-bottom: 24px;
-}
-.models-filter-title {
- font-size: 13px;
- font-weight: 600;
- text-transform: uppercase;
- letter-spacing: 0.05em;
- color: var(--text-muted, #6b7280);
- margin-bottom: 12px;
- padding-bottom: 8px;
- border-bottom: 1px solid var(--border-color, #e5e7eb);
-}
-
-.models-filter-item {
- display: flex;
- align-items: center;
- padding: 6px 10px;
- border-radius: 8px;
- cursor: pointer;
- transition: all 0.15s;
- font-size: 14px;
- color: var(--text-color, #374151);
- gap: 8px;
-}
-.models-filter-item:hover {
- background: var(--hover-bg, #f3f4f6);
-}
-.models-filter-item.active {
- background: var(--primary-light, #eff6ff);
- color: var(--primary-color, #3b82f6);
- font-weight: 500;
-}
-.models-filter-item .count {
- margin-left: auto;
- font-size: 12px;
- color: var(--text-muted, #9ca3af);
+@media (max-width: 768px) {
+ .sf-pricing-inner {
+ padding: 0 16px 32px;
+ }
+ .sf-hero {
+ padding: 40px 0 24px;
+ }
+ .sf-hero-title {
+ font-size: 28px;
+ }
+ .sf-hero-subtitle {
+ font-size: 14px;
+ }
+ .sf-filter-row {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 8px;
+ }
+ .sf-model-card {
+ height: auto;
+ min-height: 180px;
+ }
+ .sf-particles {
+ display: none;
+ }
}
/* ==================== semi-ui 缁勪欢鑷畾涔夋牱寮?==================== */
diff --git a/web/src/pages/Home/index.jsx b/web/src/pages/Home/index.jsx
index 1633638..91d9982 100644
--- a/web/src/pages/Home/index.jsx
+++ b/web/src/pages/Home/index.jsx
@@ -1,425 +1,255 @@
/*
Copyright (C) 2025 QuantumNous
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see
.
-
-For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useEffect, useCallback, useRef, useContext } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
-import { Copy, Check, Code, Terminal, FileCode, Box, Grid3X3, Play, Trophy, PieChart, BookOpen, Search, Sun, Moon, Monitor, Languages } from 'lucide-react';
+import {
+ Copy, Check, Box, Grid3X3, Trophy, BookOpen,
+ Zap, Shield, Globe, ArrowRight, Layers, Sparkles, ArrowUpRight,
+ Cpu, CreditCard, RefreshCw, MessageSquare, Send, Terminal,
+ Database, Workflow, Lock, BarChart3, Users, ChevronRight,
+ Cloud, Server, Activity, TrendingUp, Headphones, FileText,
+ Key, Settings, Monitor, Smartphone, BarChart2, Globe2,
+} from 'lucide-react';
import { UserContext } from '../../context/User';
-import { StatusContext } from '../../context/Status';
import { stringToColor, getSystemName } from '../../helpers';
-/* ==================================================================
- TokenDance-style Home Page
- Design: black/white minimal with serif headings
- ================================================================== */
-/* ─── Hero Marquee Carousel Texts ────────────────────────── */
-const CAROUSEL_TEXTS = [
- 'OpenAI 协议',
- 'Claude 协议',
- 'Gemini 协议',
- '智能路由',
- '统一计费',
- '模型丰富',
- '安全可靠',
- '开箱即用',
-];
-
-/* ─── Hero Section ──────────────────────────────────────────── */
-
-/* --- Helper: Theme Toggle --- */
-function HomeThemeToggle() {
- const { t } = useTranslation();
- const [open, setOpen] = useState(false);
- const ref = useRef(null);
- useEffect(() => {
- if (!open) return;
- const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
- document.addEventListener("mousedown", handler);
- return () => document.removeEventListener("mousedown", handler);
- }, [open]);
- const theme = localStorage.getItem("theme-mode") || "auto";
- const applyTheme = useCallback((val) => {
- const actualTheme = val === 'auto'
- ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
- : val;
- if (actualTheme === 'dark') {
- document.body.setAttribute('theme-mode', 'dark');
- document.documentElement.classList.add('dark');
- } else {
- document.body.removeAttribute('theme-mode');
- document.documentElement.classList.remove('dark');
- }
- }, []);
- const setTheme = (val) => {
- localStorage.setItem("theme-mode", val);
- applyTheme(val);
- setOpen(false);
- };
- useEffect(() => {
- applyTheme(theme);
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
- const handleChange = () => {
- const stored = localStorage.getItem("theme-mode") || "auto";
- applyTheme(stored);
- };
- mediaQuery.addEventListener('change', handleChange);
- return () => mediaQuery.removeEventListener('change', handleChange);
- }, [applyTheme]);
- const icons = { light:
, dark:
, auto:
};
- return (
-
-
- {open && (
-
- {[
- { key: "light", icon: , label: t("浅色模式") },
- { key: "dark", icon: , label: t("深色模式") },
- { key: "auto", icon: , label: t("自动模式") },
- ].map((opt) => (
-
- ))}
-
- )}
-
- );
-}
-
-/* --- Helper: Language Selector --- */
-function HomeLanguageSelector() {
- const { t, i18n } = useTranslation();
- const [open, setOpen] = useState(false);
- const ref = useRef(null);
- useEffect(() => {
- if (!open) return;
- const handler = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
- document.addEventListener("mousedown", handler);
- return () => document.removeEventListener("mousedown", handler);
- }, [open]);
- const current = (i18n.language || "").startsWith("zh") ? "zh-CN" : "en";
- const switchLang = (code) => { i18n.changeLanguage(code); setOpen(false); };
- const langLabels = { "zh-CN": "简体中文", "en": "English" };
- return (
-
-
- {open && (
-
- {Object.entries(langLabels).map(([code, label]) => (
-
- ))}
-
- )}
-
- );
-}
-
-function HeroSection() {
+/* ═══════════════ SECTION 1: HERO ═══════════════ */
+function Hero() {
const { t } = useTranslation();
const [userState] = useContext(UserContext);
- const [statusState] = useContext(StatusContext);
const [copied, setCopied] = useState(false);
- const navigate = useNavigate();
- // 读取管理员开关:默认 true(未加载 status 前也显示)
- const showThemeToggle = statusState?.status?.HeaderShowThemeToggle !== false;
- const showLanguageSelector = false;
- const copyCmd = useCallback(() => {
- navigator.clipboard.writeText("https://tokendance.space");
+ const copyUrl = useCallback(() => {
+ navigator.clipboard.writeText('https://api.turingtoken.ai');
setCopied(true);
setTimeout(() => setCopied(false), 1500);
}, []);
+
return (
-
-
-
-
-
-

-
TuringToken
-
BETA
-
-
-
-
- {showThemeToggle && }
- {showLanguageSelector && }
- {userState.user ? (
-
-
- {userState.user.username?.charAt(0).toUpperCase()}
-
-
- ) : (
-
- {t("登录")}
-
- )}
-
-
-
-
-
-
- {"// unified model API gateway"}
-
-
-
- {t("更快、更稳、更省")}
{t("Token服务平台")}
-
-
- {t("兼容 OpenAI / Claude / Gemini 等协议,覆盖文本、图像、视频、语音,智能路由,统一计费。")}
-
-
-
- {t("开始使用")}
-
-
- {t("查看价格")} →
-
-
-
-
-
-
-
-
-
-
-
-
terminal
-
-
-
- {`curl https://api.turingtoken.ai/v1/chat/completions \\
- -H "Authorization: Bearer \$YOUR_API_KEY" \\
- -H "Content-Type: application/json" \\
- -d '{
- "model": "gpt-4o",
- "messages": [{"role": "user", "content": "Hello"}]
- }'`}
-
-
-
-
+
+ {/* Background decorative elements */}
+
+
+
+ {/* Grid pattern */}
+
- {/* Hero brand wordmark — TURINGTOKEN */}
-
-
- {/* Feature marquee strip — 轮播: OpenAI 协议 / Claude 协议 / 智能路由 ... */}
-
-
- {[...CAROUSEL_TEXTS, ...CAROUSEL_TEXTS].map((text, i) => (
-
- {text}
-
+ {/* Nav */}
+
-
- );
-}const FEATURES = [
- {
- titleKey: '多协议兼容',
- descKey:
- '原生支持 OpenAI、Claude、Gemini 文本协议,覆盖图像 / 视频 / 文本转语音生成。无需修改代码,切换 Base URL 即可接入。',
- code: 'baseURL: "api.turingtoken.ai"',
- },
- {
- titleKey: '智能路由',
- descKey:
- '根据模型名称自动路由至对应供应商。一个入口,无需关心底层调度。',
- code: 'route(model) → provider',
- },
- {
- titleKey: '统一计费',
- descKey:
- '跨供应商统一 Token 消耗统计与账单。告别多平台分别充值的混乱。',
- code: 'billing.unified()',
- },
- {
- titleKey: '容错降级',
- descKey:
- '同一模型支持多供应商端点自动切换;单次请求可指定多个候选模型,逐级降级,保障服务持续可用。',
- code: 'fallback: model[] → provider[]',
- },
- {
- titleKey: '模型丰富',
- descKey:
- '接入 MiniMax、通义千问、Kimi、智谱、DeepSeek 等国内头部模型。持续扩展中。',
- code: 'models.list() → 80+',
- },
- {
- titleKey: '开箱即用',
- descKey:
- '一键登录,分钟级接入。兼容现有 SDK,零迁移成本。',
- code: 'import OpenAI from "openai"',
- },
-];
-
-function FeaturesSection() {
- const { t } = useTranslation();
- return (
-
-
-
- {/* features */}
-
-
- {t('为接入 AI 模型的开发者而造。')}
-
-
- {FEATURES.map((f, i) => (
-
-
- {f.code}
-
-
- {t(f.titleKey)}
-
-
- {t(f.descKey)}
-
-
- ))}
-
-
-
- );
-}
-
-/* ─── How It Works Section ──────────────────────────────────── */
-
-const STEPS = [
- {
- step: '01',
- titleKey: '注册账号',
- descKey: '通过邮箱一键注册,即刻开始使用。',
- link: '/register',
- },
- {
- step: '02',
- titleKey: '创建 API Key',
- descKey: '在控制台创建密钥,支持多 Key 管理与权限控制。',
- link: '/console/token',
- },
- {
- step: '03',
- titleKey: '发起请求',
- descKey: '使用你熟悉的 SDK 调用任意模型,完全兼容原生协议。',
- link: '/pricing',
- },
-];
-
-function HowItWorksSection() {
- const { t } = useTranslation();
- return (
-
-
-
-
- {'// how it works'}
-
-
- {t('三步接入,分钟级上线')}
-
-
- {STEPS.map((s, i) => (
-
-
-
-
- {s.step}
-
-
-
-
- {t(s.titleKey)}
-
-
- {t(s.descKey)}
-
-
-
- →
+
+ {userState.user ? (
+
+
+ {userState.user.username?.charAt(0).toUpperCase()}
+
+ ) : (
+
+ {t('登录')}
+
+ )}
+
+
+
+ {/* Content */}
+
+
+ {/* Left */}
+
+
+
+ Unified AI Gateway
-
- ))}
+
+
+ {t('更快、更稳、更省')}
+
+ {t('Token服务平台')}
+
+
+
+ {t('兼容 OpenAI / Claude / Gemini 等协议,覆盖文本、图像、视频、语音,智能路由,统一计费。')}
+
+
+
+
+ {t('开始使用')}
+
+
+ {t('查看价格')}
+
+
+
+ {/* Stats */}
+
+ {[
+ { n: '80+', l: 'AI 模型' },
+ { n: '40+', l: '供应商' },
+ { n: '99.9%', l: '可用率' },
+ ].map((s, i) => (
+
+ ))}
+
+
+
+ {/* Right — Code card */}
+
+
+ {/* Header */}
+
+
+
+
+
+
+
terminal
+
+
+ {/* Code */}
+
+
# Quick start with curl
+
+ curl{' '}
+ https://api.turingtoken.ai/v1/chat/completions \
+
+
{' '}-H "Authorization: Bearer $KEY" \
+
{' '}-H "Content-Type: application/json" \
+
{' '}-d '{'{'}
+
{' '}"model": "gpt-4o",
+
{' '}"messages": [{'{'}"role": "user", "content": "Hello"{'}'}]
+
{'}'}'
+
# Response
+
{'{'} "choices": [{'{'} "message": {'{'} "content": "Hello! How can I help?" {'}'} {'}'}] {'}'}
+
+
+
+
+
+
+ {/* Bottom marquee */}
+
+
+ {[...Array(3)].fill(null).map((_, gi) => (
+
+ {['OpenAI', 'Claude', 'Gemini', 'DeepSeek', 'Qwen', 'MiniMax', 'Kimi', '智谱', 'Groq', 'Azure'].map((name, i) => (
+
+ {name}
+
+ ))}
+
+ ))}
+
);
}
-/* ─── Quick Start Section (Code Tabs) ───────────────────────── */
+/* ═══════════════ SECTION 2: CAPABILITIES ═══════════════ */
+const CAPABILITIES = [
+ { icon: , title: '多协议兼容', desc: '原生支持 OpenAI、Claude、Gemini,切换 Base URL 即可接入' },
+ { icon: , title: '智能路由', desc: '根据模型名称自动路由至最优供应商,无需关心底层调度' },
+ { icon: , title: '统一计费', desc: '跨供应商统一 Token 消耗统计与账单,告别多平台充值' },
+ { icon: , title: '容错降级', desc: '多供应商端点自动切换,逐级降级保障服务持续可用' },
+ { icon: , title: '模型丰富', desc: '接入 80+ 主流模型,覆盖文本、图像、视频、语音全模态' },
+ { icon: , title: '开箱即用', desc: '一键登录,分钟级接入,兼容现有 SDK,零迁移成本' },
+];
-const PROVIDERS = ['openai', 'claude', 'gemini'];
-const PROVIDER_LABELS = { openai: 'OpenAI', claude: 'Claude', gemini: 'Gemini' };
-const LANGUAGES = ['curl', 'python', 'nodejs'];
+function Capabilities() {
+ const { t } = useTranslation();
+ return (
+
+
+
+
{t('平台核心能力')}
+
一个 API 网关,连接所有主流 AI 模型供应商
+
+
+ {CAPABILITIES.map((p, i) => (
+
+
+ {p.icon}
+
+
{t(p.title)}
+
{t(p.desc)}
+
+ ))}
+
+
+
+ );
+}
+
+/* ═══════════════ SECTION 3: HOW IT WORKS ═══════════════ */
+const STEPS = [
+ { n: '01', title: '注册账号', desc: '通过邮箱一键注册,即刻开始使用。', icon: , link: '/register' },
+ { n: '02', title: '创建 API Key', desc: '在控制台创建密钥,支持多 Key 管理。', icon: , link: '/console/token' },
+ { n: '03', title: '发起请求', desc: '使用你熟悉的 SDK 调用任意模型。', icon: , link: '/pricing' },
+];
+
+function HowItWorks() {
+ const { t } = useTranslation();
+ return (
+
+
+
+
{t('三步接入,分钟级上线')}
+
+
+ {STEPS.map((s, i) => (
+
+
+
+ {s.icon}
+
+
+ {s.n}
+
+
+
{t(s.title)}
+
{t(s.desc)}
+ {i < STEPS.length - 1 && (
+
+ )}
+
+ ))}
+
+
+
+ );
+}
+
+/* ═══════════════ SECTION 5: QUICK START ═══════════════ */
+const PROVS = ['openai', 'claude', 'gemini'];
+const PROV_LABELS = { openai: 'OpenAI', claude: 'Claude', gemini: 'Gemini' };
+const LANGS = ['curl', 'python', 'nodejs'];
const LANG_LABELS = { curl: 'cURL', python: 'Python', nodejs: 'Node.js' };
-
-const CODE_SAMPLES = {
+const SAMPLES = {
openai: {
curl: `curl https://api.turingtoken.ai/v1/chat/completions \\
-H "Authorization: Bearer $OPENAI_API_KEY" \\
@@ -499,289 +329,344 @@ console.log(response.content[0].text);`,
python: `import google.generativeai as genai
genai.configure(api_key="sk-your-key")
-
model = genai.GenerativeModel("gemini-2.0-flash")
response = model.generate_content("Hello!")
print(response.text)`,
nodejs: `import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI("sk-your-key");
-const model = genAI.getGenerativeModel({
- model: "gemini-2.0-flash",
-});
-
+const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
const result = await model.generateContent("Hello!");
console.log(result.response.text());`,
},
};
-function QuickStartSection() {
+function QuickStart() {
const { t } = useTranslation();
- const [provider, setProvider] = useState('openai');
+ const [prov, setProv] = useState('openai');
const [lang, setLang] = useState('curl');
const [copied, setCopied] = useState(false);
-
- const code = CODE_SAMPLES[provider]?.[lang] || '';
-
- const handleCopy = useCallback(() => {
- navigator.clipboard.writeText(code);
- setCopied(true);
- setTimeout(() => setCopied(false), 1500);
- }, [code]);
+ const code = SAMPLES[prov]?.[lang] || '';
+ const copy = useCallback(() => { navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 1500); }, [code]);
return (
-
-
-
- Quick Start
-
-
- {t('快速开始')}
-
-
- {/* Provider tabs */}
-
- {PROVIDERS.map((p) => (
-
- ))}
+
+
+
+
{t('快速开始')}
-
- {/* Code block */}
-
-
-
- {LANGUAGES.map((l) => (
-