docs: archive Claude Design v2 output [CI SKIP]
10 HTML pages + puro.css + HANDOFF.md + 2 images (~810KB total). Reference artifacts for Stage 3 Vue 3 translation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -130,6 +130,7 @@ docs/*
|
||||
!docs/PAYMENT.md
|
||||
!docs/PAYMENT_CN.md
|
||||
!docs/superpowers/
|
||||
!docs/design-drafts/
|
||||
.superpowers/
|
||||
.serena/
|
||||
.codex/
|
||||
|
||||
1183
docs/design-drafts/Landing.html
Normal file
1183
docs/design-drafts/Landing.html
Normal file
File diff suppressed because it is too large
Load Diff
689
docs/design-drafts/Login.html
Normal file
689
docs/design-drafts/Login.html
Normal file
@@ -0,0 +1,689 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>登录 — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg-0: #0a0e1a;
|
||||
--bg-1: #0f172a;
|
||||
--border: #1e293b;
|
||||
--border-2: #334155;
|
||||
--text-0: #f8fafc;
|
||||
--text-1: #cbd5e1;
|
||||
--text-2: #94a3b8;
|
||||
--text-3: #64748b;
|
||||
--cyan: #22d3ee;
|
||||
--purple: #a855f7;
|
||||
--amber: #fbbf24;
|
||||
--red: #f87171;
|
||||
--green: #34d399;
|
||||
}
|
||||
html, body {
|
||||
background: var(--bg-0);
|
||||
color: var(--text-0);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
|
||||
|
||||
/* ------ SPLIT LAYOUT ------ */
|
||||
.split {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ------ LEFT (NARRATIVE) ------ */
|
||||
.narrative {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 56px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(135deg, #0a0e1a 0%, #1e1b4b 100%);
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.narrative::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -200px; left: -200px;
|
||||
width: 700px; height: 700px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #22d3ee 0%, transparent 60%);
|
||||
filter: blur(100px);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.narrative::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -250px; right: -150px;
|
||||
width: 700px; height: 700px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #a855f7 0%, transparent 60%);
|
||||
filter: blur(100px);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* grid pattern */
|
||||
.narrative-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(148, 163, 184, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(148, 163, 184, 0.04) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
mask-image: radial-gradient(ellipse at center, black 30%, transparent 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.narrative-inner {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.n-top { padding-top: 4px; }
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.hex {
|
||||
width: 22px; height: 22px;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
.n-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
margin: auto 0;
|
||||
max-width: 520px;
|
||||
}
|
||||
.n-kicker {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--cyan);
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.n-headline {
|
||||
font-size: clamp(36px, 4.2vw, 52px);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
.n-headline .amber {
|
||||
color: var(--amber);
|
||||
font-variant-numeric: tabular-nums;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
.n-headline .cyan {
|
||||
color: var(--cyan);
|
||||
font-variant-numeric: tabular-nums;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
.n-headline .arrow {
|
||||
display: inline-block;
|
||||
margin: 0 14px;
|
||||
color: var(--text-2);
|
||||
font-weight: 400;
|
||||
}
|
||||
.n-sub {
|
||||
font-size: 17px;
|
||||
color: var(--text-1);
|
||||
line-height: 1.7;
|
||||
font-weight: 400;
|
||||
}
|
||||
.n-sub .line { display: block; }
|
||||
.n-sub .puro {
|
||||
color: var(--text-0);
|
||||
font-weight: 600;
|
||||
}
|
||||
.n-sub .mono-accent {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
color: var(--text-2);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Visual demo block — a mock "route" diagram */
|
||||
.route-demo {
|
||||
border: 1px solid rgba(148, 163, 184, 0.12);
|
||||
border-radius: 12px;
|
||||
padding: 18px 20px;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
max-width: 420px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
.route-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.route-row .head {
|
||||
width: 84px;
|
||||
color: var(--text-3);
|
||||
}
|
||||
.route-row .val {
|
||||
color: var(--text-0);
|
||||
}
|
||||
.route-row.status .val { color: var(--cyan); display: inline-flex; align-items: center; gap: 6px; }
|
||||
.route-row.status .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--cyan); box-shadow: 0 0 10px var(--cyan); }
|
||||
.route-row .chip {
|
||||
padding: 2px 8px;
|
||||
background: rgba(34, 211, 238, 0.08);
|
||||
border: 1px solid rgba(34, 211, 238, 0.2);
|
||||
border-radius: 4px;
|
||||
color: var(--cyan);
|
||||
font-size: 11px;
|
||||
}
|
||||
.route-row .chip.a { background: rgba(168, 85, 247, 0.08); border-color: rgba(168, 85, 247, 0.2); color: var(--purple); }
|
||||
|
||||
.n-bottom {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding-top: 24px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
.n-bottom .sep { color: var(--border-2); }
|
||||
.n-bottom .live {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.n-bottom .live .dot {
|
||||
width: 6px; height: 6px; border-radius: 50%; background: var(--green);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.4; }
|
||||
}
|
||||
|
||||
/* ------ RIGHT (FORM) ------ */
|
||||
.form-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px;
|
||||
background: var(--bg-0);
|
||||
position: relative;
|
||||
}
|
||||
.form-side::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 40%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 500px; height: 500px;
|
||||
background: radial-gradient(circle, rgba(34, 211, 238, 0.06) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.form-card h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.form-card .sub {
|
||||
color: var(--text-2);
|
||||
font-size: 14px;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
.field {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.field label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-1);
|
||||
margin-bottom: 8px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input-wrap .icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--text-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%;
|
||||
padding: 12px 14px 12px 42px;
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 8px;
|
||||
color: var(--text-0);
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: all .15s;
|
||||
}
|
||||
.input-wrap input::placeholder { color: var(--text-3); }
|
||||
.input-wrap input:hover { border-color: #475569; }
|
||||
.input-wrap input:focus {
|
||||
border-color: var(--cyan);
|
||||
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.12);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
}
|
||||
.input-wrap input.error {
|
||||
border-color: var(--red);
|
||||
box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.12);
|
||||
}
|
||||
.input-wrap .eye {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
color: var(--text-3);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
transition: color .15s;
|
||||
}
|
||||
.input-wrap .eye:hover { color: var(--text-1); }
|
||||
|
||||
.field-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
.field-meta .forgot {
|
||||
color: var(--text-2);
|
||||
transition: color .15s;
|
||||
margin-left: auto;
|
||||
}
|
||||
.field-meta .forgot:hover { color: var(--cyan); }
|
||||
.field-error {
|
||||
color: var(--red);
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.field-hint {
|
||||
color: var(--text-3);
|
||||
font-size: 12px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.pw-strength {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.pw-strength .bar {
|
||||
flex: 1;
|
||||
height: 3px;
|
||||
background: var(--border);
|
||||
border-radius: 2px;
|
||||
transition: background .2s;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 13px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
font-family: inherit;
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--cyan);
|
||||
color: #042f2e;
|
||||
}
|
||||
.btn-primary:hover { background: #67e8f9; }
|
||||
.btn-primary:active { transform: translateY(1px); }
|
||||
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-2);
|
||||
color: var(--text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-ghost:hover { border-color: #475569; color: var(--text-0); background: rgba(255,255,255,0.02); }
|
||||
|
||||
.btn-primary .spinner {
|
||||
width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.2);
|
||||
border-top-color: #042f2e;
|
||||
border-radius: 50%;
|
||||
animation: spin .7s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
.btn-primary.loading .spinner { display: inline-block; }
|
||||
.btn-primary.loading .label { opacity: 0.5; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin: 24px 0;
|
||||
color: var(--text-3);
|
||||
font-size: 11px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
letter-spacing: 0.15em;
|
||||
}
|
||||
.divider::before, .divider::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.foot {
|
||||
margin-top: 28px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.foot a {
|
||||
color: var(--cyan);
|
||||
font-weight: 500;
|
||||
transition: color .15s;
|
||||
}
|
||||
.foot a:hover { color: #67e8f9; }
|
||||
|
||||
.legal {
|
||||
margin-top: 18px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.legal a { color: var(--text-2); text-decoration: underline; text-decoration-color: var(--border-2); }
|
||||
.legal a:hover { color: var(--text-0); }
|
||||
|
||||
/* checkbox */
|
||||
.check {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
.check input { display: none; }
|
||||
.check .box {
|
||||
width: 14px; height: 14px;
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-1);
|
||||
transition: all .15s;
|
||||
}
|
||||
.check input:checked + .box {
|
||||
background: var(--cyan);
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.check input:checked + .box::after {
|
||||
content: "✓";
|
||||
color: #042f2e;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.back-home {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 32px;
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
transition: color .15s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
z-index: 10;
|
||||
}
|
||||
.back-home:hover { color: var(--text-0); }
|
||||
|
||||
/* LinuxDo logo */
|
||||
.linuxdo-ico {
|
||||
width: 16px; height: 16px;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(135deg, #f0a030, #f05050);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 900px) {
|
||||
.split { grid-template-columns: 1fr; min-height: auto; }
|
||||
.narrative {
|
||||
padding: 24px 24px 32px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
min-height: auto;
|
||||
}
|
||||
.narrative-inner { gap: 18px; display: block; }
|
||||
.n-top { margin-bottom: 16px; }
|
||||
.n-center { gap: 12px; margin: 0; }
|
||||
.n-headline { font-size: 28px; }
|
||||
.n-sub { font-size: 14px; }
|
||||
.n-sub .line { display: inline; }
|
||||
.route-demo, .n-bottom { display: none; }
|
||||
.form-side { padding: 32px 24px; }
|
||||
.back-home { top: 18px; right: 18px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="split">
|
||||
<!-- LEFT: NARRATIVE -->
|
||||
<div class="narrative">
|
||||
<div class="narrative-grid"></div>
|
||||
<div class="narrative-inner">
|
||||
<div class="n-top">
|
||||
<a href="Landing.html" class="brand">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="n-center">
|
||||
<div class="n-kicker">// 你的订阅,已经付过钱了</div>
|
||||
<h1 class="n-headline">
|
||||
<span class="amber">N</span> 个订阅
|
||||
<span class="arrow">→</span>
|
||||
<span class="cyan">1</span> 个 key
|
||||
</h1>
|
||||
<div class="n-sub">
|
||||
<span class="line">省去切换账号的繁琐,</span>
|
||||
<span class="line">省去为多个高昂订阅重复买单。</span>
|
||||
<span class="line"><span class="puro">PURO</span>(纯粹)—— 让 AI 调用回归本质。</span>
|
||||
</div>
|
||||
|
||||
<div class="route-demo">
|
||||
<div class="route-row"><span class="head">POST</span> <span class="val">/v1/chat/completions</span></div>
|
||||
<div class="route-row"><span class="head">model</span> <span class="chip">claude-sonnet-4-5</span></div>
|
||||
<div class="route-row"><span class="head">route →</span> <span class="chip a">claude-pool-03</span></div>
|
||||
<div class="route-row status"><span class="head">status</span> <span class="val"><span class="dot"></span>200 · 213ms · 42 tok</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-bottom">
|
||||
<span>Claude</span><span class="sep">·</span>
|
||||
<span>ChatGPT</span><span class="sep">·</span>
|
||||
<span>Codex</span><span class="sep">·</span>
|
||||
<span>Gemini</span>
|
||||
<span class="sep" style="margin: 0 4px;">|</span>
|
||||
<span class="live"><span class="dot"></span>ai.puro.im · operational</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: FORM -->
|
||||
<div class="form-side">
|
||||
<a href="Landing.html" class="back-home">← 返回首页</a>
|
||||
|
||||
<form class="form-card" id="login-form" autocomplete="off" novalidate>
|
||||
<h1>登录</h1>
|
||||
<p class="sub">用你的 PURO AI 账户继续</p>
|
||||
|
||||
<div class="field">
|
||||
<label for="email">邮箱</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
||||
<path d="M3 7l9 6 9-6"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="email" id="email" name="email" placeholder="you@puro.im" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password">密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password" name="password" placeholder="••••••••" required>
|
||||
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
||||
<svg id="eye-open" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
<svg id="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="display:none;">
|
||||
<path d="M9.88 9.88A3 3 0 0 0 14.12 14.12M10.73 5.08A10.94 10.94 0 0 1 12 5c6.5 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68M6.61 6.61A13.53 13.53 0 0 0 2 12s3.5 7 10 7a9.77 9.77 0 0 0 5.39-1.61M2 2l20 20"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="field-meta">
|
||||
<label class="check">
|
||||
<input type="checkbox" checked>
|
||||
<span class="box"></span>
|
||||
记住我
|
||||
</label>
|
||||
<a href="#" class="forgot">忘记密码?</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn">
|
||||
<span class="spinner"></span>
|
||||
<span class="label">登录 →</span>
|
||||
</button>
|
||||
|
||||
<div class="divider">OR</div>
|
||||
|
||||
<button type="button" class="btn btn-ghost">
|
||||
<span class="linuxdo-ico">L</span>
|
||||
使用 LinuxDO 登录
|
||||
</button>
|
||||
|
||||
<div class="foot">
|
||||
没有账户?<a href="Register.html">注册</a>
|
||||
</div>
|
||||
|
||||
<div class="legal">
|
||||
登录即表示你同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// eye toggle
|
||||
const pw = document.getElementById('password');
|
||||
const toggle = document.getElementById('toggle-pw');
|
||||
const eyeOpen = document.getElementById('eye-open');
|
||||
const eyeClosed = document.getElementById('eye-closed');
|
||||
toggle.addEventListener('click', () => {
|
||||
const show = pw.type === 'password';
|
||||
pw.type = show ? 'text' : 'password';
|
||||
eyeOpen.style.display = show ? 'none' : 'block';
|
||||
eyeClosed.style.display = show ? 'block' : 'none';
|
||||
});
|
||||
|
||||
// submit (mocked)
|
||||
const form = document.getElementById('login-form');
|
||||
const btn = document.getElementById('submit-btn');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (!form.email.value || !form.password.value) return;
|
||||
btn.classList.add('loading');
|
||||
btn.disabled = true;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('loading');
|
||||
btn.disabled = false;
|
||||
btn.querySelector('.label').textContent = '✓ 登录成功';
|
||||
btn.style.background = '#34d399';
|
||||
setTimeout(() => {
|
||||
btn.querySelector('.label').textContent = '登录 →';
|
||||
btn.style.background = '';
|
||||
}, 1500);
|
||||
}, 1200);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
734
docs/design-drafts/Register.html
Normal file
734
docs/design-drafts/Register.html
Normal file
@@ -0,0 +1,734 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>注册 — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
:root {
|
||||
--bg-0: #0a0e1a;
|
||||
--bg-1: #0f172a;
|
||||
--border: #1e293b;
|
||||
--border-2: #334155;
|
||||
--text-0: #f8fafc;
|
||||
--text-1: #cbd5e1;
|
||||
--text-2: #94a3b8;
|
||||
--text-3: #64748b;
|
||||
--cyan: #22d3ee;
|
||||
--purple: #a855f7;
|
||||
--amber: #fbbf24;
|
||||
--red: #f87171;
|
||||
--green: #34d399;
|
||||
}
|
||||
html, body {
|
||||
background: var(--bg-0);
|
||||
color: var(--text-0);
|
||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
min-height: 100vh;
|
||||
}
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
|
||||
|
||||
.split {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* ------ LEFT (NARRATIVE) ------ */
|
||||
.narrative {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 56px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
background: linear-gradient(135deg, #0a0e1a 0%, #1e1b4b 100%);
|
||||
border-right: 1px solid var(--border);
|
||||
}
|
||||
.narrative::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: -200px; left: -200px;
|
||||
width: 700px; height: 700px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #a855f7 0%, transparent 60%);
|
||||
filter: blur(100px);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.narrative::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -250px; right: -150px;
|
||||
width: 700px; height: 700px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, #22d3ee 0%, transparent 60%);
|
||||
filter: blur(100px);
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.narrative-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image:
|
||||
linear-gradient(rgba(148, 163, 184, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(148, 163, 184, 0.04) 1px, transparent 1px);
|
||||
background-size: 48px 48px;
|
||||
mask-image: radial-gradient(ellipse at center, black 30%, transparent 80%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.narrative-inner {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.brand {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.hex {
|
||||
width: 22px; height: 22px;
|
||||
color: var(--cyan);
|
||||
}
|
||||
|
||||
.n-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
margin: auto 0;
|
||||
max-width: 520px;
|
||||
}
|
||||
.n-kicker {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--cyan);
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.n-headline {
|
||||
font-size: clamp(36px, 4.2vw, 52px);
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.03em;
|
||||
}
|
||||
.n-headline .amber { color: var(--amber); font-variant-numeric: tabular-nums; display: inline-block; }
|
||||
.n-headline .cyan { color: var(--cyan); font-variant-numeric: tabular-nums; display: inline-block; }
|
||||
.n-headline .arrow {
|
||||
display: inline-block;
|
||||
margin: 0 14px;
|
||||
color: var(--text-2);
|
||||
font-weight: 400;
|
||||
}
|
||||
.n-sub {
|
||||
font-size: 17px;
|
||||
color: var(--text-1);
|
||||
line-height: 1.7;
|
||||
}
|
||||
.n-sub .line { display: block; }
|
||||
.n-sub .puro { color: var(--text-0); font-weight: 600; }
|
||||
|
||||
/* Step list */
|
||||
.steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.12);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
backdrop-filter: blur(10px);
|
||||
max-width: 420px;
|
||||
}
|
||||
.steps-title {
|
||||
font-size: 12px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
color: var(--text-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.15em;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.step-num {
|
||||
flex-shrink: 0;
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 50%;
|
||||
background: rgba(34, 211, 238, 0.1);
|
||||
border: 1px solid rgba(34, 211, 238, 0.25);
|
||||
color: var(--cyan);
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.step-text { color: var(--text-1); line-height: 1.5; padding-top: 2px; }
|
||||
.step-text b { color: var(--text-0); font-weight: 600; }
|
||||
.step-text .k {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.n-bottom {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
padding-top: 24px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
.n-bottom .sep { color: var(--border-2); }
|
||||
|
||||
/* ------ RIGHT (FORM) ------ */
|
||||
.form-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px;
|
||||
background: var(--bg-0);
|
||||
position: relative;
|
||||
}
|
||||
.form-side::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 40%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 500px; height: 500px;
|
||||
background: radial-gradient(circle, rgba(168, 85, 247, 0.05) 0%, transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.form-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.form-card h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.form-card .sub {
|
||||
color: var(--text-2);
|
||||
font-size: 14px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.field { margin-bottom: 18px; }
|
||||
.field label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-1);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.input-wrap .icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
color: var(--text-3);
|
||||
pointer-events: none;
|
||||
}
|
||||
.input-wrap input {
|
||||
width: 100%;
|
||||
padding: 12px 14px 12px 42px;
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 8px;
|
||||
color: var(--text-0);
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: all .15s;
|
||||
}
|
||||
.input-wrap input::placeholder { color: var(--text-3); }
|
||||
.input-wrap input:hover { border-color: #475569; }
|
||||
.input-wrap input:focus {
|
||||
border-color: var(--cyan);
|
||||
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.12);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
}
|
||||
.input-wrap input.ok { border-color: rgba(52, 211, 153, 0.4); }
|
||||
.input-wrap input.ok:focus { box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.12); }
|
||||
.input-wrap input.error { border-color: var(--red); }
|
||||
.input-wrap .eye {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
color: var(--text-3);
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.input-wrap .eye:hover { color: var(--text-1); }
|
||||
.input-wrap .valid-ico {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
color: var(--green);
|
||||
display: none;
|
||||
}
|
||||
.input-wrap input.ok ~ .valid-ico { display: flex; }
|
||||
|
||||
.pw-strength {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.pw-strength .bar {
|
||||
flex: 1;
|
||||
height: 3px;
|
||||
background: var(--border);
|
||||
border-radius: 2px;
|
||||
transition: background .2s;
|
||||
}
|
||||
.pw-strength[data-score="1"] .bar:nth-child(1) { background: var(--red); }
|
||||
.pw-strength[data-score="2"] .bar:nth-child(-n+2) { background: var(--amber); }
|
||||
.pw-strength[data-score="3"] .bar:nth-child(-n+3) { background: var(--cyan); }
|
||||
.pw-strength[data-score="4"] .bar { background: var(--green); }
|
||||
|
||||
.pw-hint {
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
}
|
||||
.pw-hint .label { color: var(--text-3); }
|
||||
.pw-hint .val { color: var(--text-1); }
|
||||
.pw-hint[data-score="1"] .val { color: var(--red); }
|
||||
.pw-hint[data-score="2"] .val { color: var(--amber); }
|
||||
.pw-hint[data-score="3"] .val { color: var(--cyan); }
|
||||
.pw-hint[data-score="4"] .val { color: var(--green); }
|
||||
|
||||
.match-hint {
|
||||
font-size: 11px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
margin-top: 6px;
|
||||
color: var(--text-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.match-hint.ok { color: var(--green); }
|
||||
.match-hint.bad { color: var(--red); }
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 13px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--cyan);
|
||||
color: #042f2e;
|
||||
}
|
||||
.btn-primary:hover { background: #67e8f9; }
|
||||
.btn-primary:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||
.btn-primary .spinner {
|
||||
width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.2);
|
||||
border-top-color: #042f2e;
|
||||
border-radius: 50%;
|
||||
animation: spin .7s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
.btn-primary.loading .spinner { display: inline-block; }
|
||||
.btn-primary.loading .label { opacity: 0.5; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.btn-ghost {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border-2);
|
||||
color: var(--text-1);
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-ghost:hover { border-color: #475569; color: var(--text-0); background: rgba(255,255,255,0.02); }
|
||||
|
||||
.divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
margin: 24px 0;
|
||||
color: var(--text-3);
|
||||
font-size: 11px;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
letter-spacing: 0.15em;
|
||||
}
|
||||
.divider::before, .divider::after {
|
||||
content: "";
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
|
||||
.foot {
|
||||
margin-top: 24px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.foot a {
|
||||
color: var(--cyan);
|
||||
font-weight: 500;
|
||||
}
|
||||
.foot a:hover { color: #67e8f9; }
|
||||
.legal {
|
||||
margin-top: 16px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
line-height: 1.6;
|
||||
}
|
||||
.legal a { color: var(--text-2); text-decoration: underline; text-decoration-color: var(--border-2); }
|
||||
|
||||
.check {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
margin-bottom: 20px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.check input { display: none; }
|
||||
.check .box {
|
||||
flex-shrink: 0;
|
||||
margin-top: 1px;
|
||||
width: 14px; height: 14px;
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 4px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: var(--bg-1);
|
||||
}
|
||||
.check input:checked + .box {
|
||||
background: var(--cyan);
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.check input:checked + .box::after {
|
||||
content: "✓";
|
||||
color: #042f2e;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.check a { color: var(--text-0); text-decoration: underline; text-decoration-color: var(--border-2); }
|
||||
|
||||
.back-home {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 32px;
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
z-index: 10;
|
||||
}
|
||||
.back-home:hover { color: var(--text-0); }
|
||||
|
||||
.linuxdo-ico {
|
||||
width: 16px; height: 16px;
|
||||
border-radius: 3px;
|
||||
background: linear-gradient(135deg, #f0a030, #f05050);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.split { grid-template-columns: 1fr; min-height: auto; }
|
||||
.narrative {
|
||||
padding: 24px 24px 32px;
|
||||
border-right: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.narrative-inner { gap: 18px; display: block; }
|
||||
.n-center { gap: 12px; margin: 16px 0 0; }
|
||||
.n-headline { font-size: 28px; }
|
||||
.n-sub { font-size: 14px; }
|
||||
.n-sub .line { display: inline; }
|
||||
.steps, .n-bottom { display: none; }
|
||||
.form-side { padding: 32px 24px; }
|
||||
.back-home { top: 18px; right: 18px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="split">
|
||||
<!-- LEFT: NARRATIVE -->
|
||||
<div class="narrative">
|
||||
<div class="narrative-grid"></div>
|
||||
<div class="narrative-inner">
|
||||
<div>
|
||||
<a href="Landing.html" class="brand">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="n-center">
|
||||
<div class="n-kicker">// 5 分钟开始用</div>
|
||||
<h1 class="n-headline">
|
||||
<span class="amber">N</span> 个订阅
|
||||
<span class="arrow">→</span>
|
||||
<span class="cyan">1</span> 个 key
|
||||
</h1>
|
||||
<div class="n-sub">
|
||||
<span class="line">省去切换账号的繁琐,</span>
|
||||
<span class="line">省去为多个高昂订阅重复买单。</span>
|
||||
<span class="line"><span class="puro">PURO</span>(纯粹)—— 让 AI 调用回归本质。</span>
|
||||
</div>
|
||||
|
||||
<div class="steps">
|
||||
<div class="steps-title">// 下一步</div>
|
||||
<div class="step">
|
||||
<div class="step-num">1</div>
|
||||
<div class="step-text"><b>创建账户</b> · 邮箱 + 密码,或用 LinuxDO OAuth</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">2</div>
|
||||
<div class="step-text"><b>绑定订阅</b> · OAuth 接入你现有的 Claude Pro / ChatGPT Plus</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">3</div>
|
||||
<div class="step-text"><b>生成 key</b> · 拿到 <span class="k">sk-puro-…</span> 换掉 SDK 的 <span class="k">base_url</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-bottom">
|
||||
<span>Claude</span><span class="sep">·</span>
|
||||
<span>ChatGPT</span><span class="sep">·</span>
|
||||
<span>Codex</span><span class="sep">·</span>
|
||||
<span>Gemini</span>
|
||||
<span class="sep" style="margin: 0 4px;">|</span>
|
||||
<span>无需信用卡 · 永久免费 Hobby 套餐</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT: FORM -->
|
||||
<div class="form-side">
|
||||
<a href="Landing.html" class="back-home">← 返回首页</a>
|
||||
|
||||
<form class="form-card" id="reg-form" autocomplete="off" novalidate>
|
||||
<h1>创建账户</h1>
|
||||
<p class="sub">5 分钟开始用 PURO AI</p>
|
||||
|
||||
<div class="field">
|
||||
<label for="email">邮箱</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
||||
<path d="M3 7l9 6 9-6"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="email" id="email" name="email" placeholder="you@puro.im" required>
|
||||
<span class="valid-ico">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12l5 5L20 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password">密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password" name="password" placeholder="至少 8 位,含字母与数字" required>
|
||||
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-strength" id="pw-strength" data-score="0">
|
||||
<span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
||||
</div>
|
||||
<div class="pw-hint" id="pw-hint" data-score="0">
|
||||
<span class="label">// strength</span>
|
||||
<span class="val">—</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password2">确认密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password2" name="password2" placeholder="再输入一次" required>
|
||||
<span class="valid-ico">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12l5 5L20 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="match-hint" id="match-hint"></div>
|
||||
</div>
|
||||
|
||||
<label class="check">
|
||||
<input type="checkbox" id="terms">
|
||||
<span class="box"></span>
|
||||
<span>我已阅读并同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a></span>
|
||||
</label>
|
||||
|
||||
<button type="submit" class="btn btn-primary" id="submit-btn" disabled>
|
||||
<span class="spinner"></span>
|
||||
<span class="label">创建账户 →</span>
|
||||
</button>
|
||||
|
||||
<div class="divider">OR</div>
|
||||
|
||||
<button type="button" class="btn btn-ghost">
|
||||
<span class="linuxdo-ico">L</span>
|
||||
使用 LinuxDO 注册
|
||||
</button>
|
||||
|
||||
<div class="foot">
|
||||
已有账户?<a href="Login.html">登录</a>
|
||||
</div>
|
||||
|
||||
<div class="legal">
|
||||
注册即可获得 Hobby 套餐 · 无需信用卡
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const pw = document.getElementById('password');
|
||||
const pw2 = document.getElementById('password2');
|
||||
const email = document.getElementById('email');
|
||||
const terms = document.getElementById('terms');
|
||||
const btn = document.getElementById('submit-btn');
|
||||
const strengthBars = document.getElementById('pw-strength');
|
||||
const strengthHint = document.getElementById('pw-hint');
|
||||
const matchHint = document.getElementById('match-hint');
|
||||
const toggle = document.getElementById('toggle-pw');
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
pw.type = pw.type === 'password' ? 'text' : 'password';
|
||||
});
|
||||
|
||||
const labels = ['—', '太弱', '一般', '良好', '强'];
|
||||
function scorePw(v) {
|
||||
let s = 0;
|
||||
if (v.length >= 8) s++;
|
||||
if (/[a-z]/.test(v) && /[A-Z]/.test(v)) s++;
|
||||
if (/\d/.test(v)) s++;
|
||||
if (/[^\w\s]/.test(v) || v.length >= 14) s++;
|
||||
if (v.length === 0) s = 0;
|
||||
return Math.min(s, 4);
|
||||
}
|
||||
|
||||
function update() {
|
||||
const s = scorePw(pw.value);
|
||||
strengthBars.dataset.score = s;
|
||||
strengthHint.dataset.score = s;
|
||||
strengthHint.querySelector('.val').textContent = labels[s];
|
||||
|
||||
// email validity
|
||||
const emailOk = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value);
|
||||
email.classList.toggle('ok', emailOk);
|
||||
|
||||
// match
|
||||
if (pw2.value.length === 0) {
|
||||
matchHint.textContent = '';
|
||||
matchHint.className = 'match-hint';
|
||||
pw2.classList.remove('ok', 'error');
|
||||
} else if (pw.value === pw2.value) {
|
||||
matchHint.innerHTML = '<span>✓</span> 两次密码一致';
|
||||
matchHint.className = 'match-hint ok';
|
||||
pw2.classList.add('ok');
|
||||
pw2.classList.remove('error');
|
||||
} else {
|
||||
matchHint.innerHTML = '<span>✗</span> 密码不一致';
|
||||
matchHint.className = 'match-hint bad';
|
||||
pw2.classList.add('error');
|
||||
pw2.classList.remove('ok');
|
||||
}
|
||||
|
||||
const ready = emailOk && s >= 2 && pw.value === pw2.value && pw.value.length >= 8 && terms.checked;
|
||||
btn.disabled = !ready;
|
||||
}
|
||||
|
||||
[email, pw, pw2, terms].forEach(el => el.addEventListener('input', update));
|
||||
terms.addEventListener('change', update);
|
||||
|
||||
const form = document.getElementById('reg-form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (btn.disabled) return;
|
||||
btn.classList.add('loading');
|
||||
btn.disabled = true;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('loading');
|
||||
btn.querySelector('.label').textContent = '✓ 账户已创建,跳转中…';
|
||||
btn.style.background = '#34d399';
|
||||
}, 1500);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
637
docs/design-drafts/v2/API Keys.html
Normal file
637
docs/design-drafts/v2/API Keys.html
Normal file
@@ -0,0 +1,637 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>API Keys — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
.page-head {
|
||||
display: flex; align-items:flex-end; justify-content:space-between;
|
||||
margin-bottom: 28px; gap: 24px;
|
||||
}
|
||||
.page-head h1 { font-size: 28px; font-weight: 700; letter-spacing:-0.02em; margin-bottom:6px; }
|
||||
.page-head .sub { color: var(--text-2); font-size: 14px; max-width: 560px; line-height: 1.55; }
|
||||
|
||||
.summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.summary .cell {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
}
|
||||
.summary .cell-label {
|
||||
font-size: 11px; color: var(--text-3);
|
||||
text-transform: uppercase; letter-spacing: 0.1em;
|
||||
font-family: var(--font-mono);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.summary .cell-value {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 20px; font-weight: 700;
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.summary .cell-value .unit { font-size: 12px; color: var(--text-3); margin-left: 4px; }
|
||||
|
||||
.toolbar {
|
||||
display: flex; gap: 10px; align-items: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.toolbar .search-box {
|
||||
flex: 1; max-width: 340px; position: relative;
|
||||
}
|
||||
.toolbar .search-box input {
|
||||
width: 100%; height: 36px;
|
||||
padding: 0 12px 0 34px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-1); font-size: 13px;
|
||||
outline: none; font-family: inherit;
|
||||
}
|
||||
.toolbar .search-box input:focus { border-color: var(--border-2); }
|
||||
.toolbar .search-box::before {
|
||||
content: ""; position: absolute; left: 12px; top: 50%;
|
||||
width: 14px; height: 14px; transform: translateY(-50%);
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='m20 20-3.5-3.5'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.filter-btn {
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 0 12px; height: 36px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
color: var(--text-2); font-size: 13px;
|
||||
transition: all .12s;
|
||||
}
|
||||
.filter-btn:hover { border-color: var(--border-2); color: var(--text-0); }
|
||||
.filter-btn .dot-count {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
width: 16px; height: 16px; border-radius: 50%;
|
||||
background: var(--cyan); color: #042f2e;
|
||||
font-size: 10px; font-weight: 700;
|
||||
}
|
||||
|
||||
/* key card */
|
||||
.key-list { display: flex; flex-direction: column; gap: 10px; }
|
||||
.key-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
padding: 18px 20px;
|
||||
transition: all .12s;
|
||||
}
|
||||
.key-card:hover { border-color: var(--border-2); background: rgba(15, 23, 42, 0.7); }
|
||||
.key-card.revoked { opacity: 0.6; }
|
||||
|
||||
.key-head {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.key-name {
|
||||
font-size: 15px; font-weight: 600;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.key-meta {
|
||||
font-family: var(--font-mono); font-size: 11px; color: var(--text-3);
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
margin-left: auto;
|
||||
}
|
||||
.key-meta .sep { color: var(--border-2); }
|
||||
.key-actions { display: flex; gap: 4px; margin-left: 6px; }
|
||||
.icon-act {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-3);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
transition: all .12s;
|
||||
}
|
||||
.icon-act:hover { color: var(--text-0); background: rgba(255,255,255,0.04); }
|
||||
.icon-act.danger:hover { color: var(--red); background: rgba(248,113,113,0.08); }
|
||||
|
||||
.key-value {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 14px;
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
color: var(--text-1);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.key-value .prefix { color: var(--cyan); }
|
||||
.key-value .rest { letter-spacing: 0.05em; }
|
||||
.key-value .reveal {
|
||||
margin-left: auto;
|
||||
color: var(--text-3); font-size: 11px;
|
||||
padding: 4px 10px; border-radius: 4px;
|
||||
cursor: pointer; transition: all .12s;
|
||||
}
|
||||
.key-value .reveal:hover { color: var(--cyan); background: rgba(34,211,238,0.08); }
|
||||
.key-value .copy-btn {
|
||||
color: var(--text-3); font-size: 11px;
|
||||
padding: 4px 10px; border-radius: 4px;
|
||||
cursor: pointer; transition: all .12s;
|
||||
}
|
||||
.key-value .copy-btn:hover { color: var(--cyan); background: rgba(34,211,238,0.08); }
|
||||
|
||||
.key-scopes {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
.scope {
|
||||
display: flex; flex-direction: column; gap: 4px;
|
||||
}
|
||||
.scope-label {
|
||||
font-size: 10px; color: var(--text-3);
|
||||
text-transform: uppercase; letter-spacing: 0.12em;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.scope-val {
|
||||
font-size: 12px; color: var(--text-1);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.scope-val.tags {
|
||||
display: flex; gap: 4px; flex-wrap: wrap;
|
||||
}
|
||||
.mini-tag {
|
||||
display: inline-flex; align-items: center; gap: 4px;
|
||||
padding: 1px 6px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
font-size: 10px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.mini-tag .dot { width: 5px; height: 5px; border-radius: 50%; }
|
||||
.mini-tag.all { color: var(--cyan); border-color: rgba(34,211,238,0.25); background: rgba(34,211,238,0.06); }
|
||||
.usage-bar {
|
||||
height: 4px; background: var(--border);
|
||||
border-radius: 2px; overflow: hidden;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.usage-bar span { display: block; height: 100%; background: var(--cyan); }
|
||||
.usage-bar.warn span { background: var(--amber); }
|
||||
|
||||
/* ---------- modal ---------- */
|
||||
.backdrop {
|
||||
position: fixed; inset: 0;
|
||||
background: rgba(2, 6, 23, 0.8);
|
||||
backdrop-filter: blur(8px);
|
||||
z-index: 100;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
}
|
||||
.backdrop.open { display: flex; }
|
||||
.modal {
|
||||
width: 540px; max-width: 100%;
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: var(--r-xl);
|
||||
box-shadow: var(--shadow-xl);
|
||||
overflow: hidden;
|
||||
}
|
||||
.modal-head {
|
||||
padding: 22px 28px 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center;
|
||||
}
|
||||
.modal-head h2 {
|
||||
font-size: 18px; font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.modal-head .close {
|
||||
margin-left: auto; padding: 6px;
|
||||
color: var(--text-3); cursor: pointer;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.modal-head .close:hover { color: var(--text-0); background: rgba(255,255,255,0.04); }
|
||||
.modal-body { padding: 22px 28px; }
|
||||
.modal-foot {
|
||||
padding: 14px 28px 22px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex; gap: 10px; justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* newly-created key callout */
|
||||
.new-key-callout {
|
||||
padding: 16px;
|
||||
border: 1px solid rgba(52, 211, 153, 0.3);
|
||||
border-radius: var(--r-md);
|
||||
background: rgba(52, 211, 153, 0.05);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.new-key-callout .header {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 13px; font-weight: 600; color: var(--green);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.new-key-callout .warn {
|
||||
font-size: 12px; color: var(--text-2); margin-top: 10px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
/* scope picker */
|
||||
.scope-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 8px;
|
||||
}
|
||||
.scope-opt {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
background: rgba(2, 6, 23, 0.4);
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.scope-opt:hover { border-color: var(--border-2); }
|
||||
.scope-opt.active {
|
||||
border-color: var(--cyan);
|
||||
background: rgba(34,211,238,0.06);
|
||||
}
|
||||
.scope-opt .box {
|
||||
width: 14px; height: 14px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid var(--border-2);
|
||||
flex-shrink: 0;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.scope-opt.active .box {
|
||||
background: var(--cyan); border-color: var(--cyan);
|
||||
}
|
||||
.scope-opt.active .box::after {
|
||||
content: "✓"; color: #042f2e; font-size: 10px; font-weight: 700;
|
||||
}
|
||||
.scope-opt .label { font-size: 13px; color: var(--text-0); }
|
||||
.scope-opt .desc { font-size: 11px; color: var(--text-3); font-family: var(--font-mono); margin-left: auto; }
|
||||
|
||||
/* topbar (reused) */
|
||||
.topbar-user {
|
||||
margin-left: auto; display: flex; gap: 10px; align-items: center;
|
||||
}
|
||||
.user-menu {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 4px 10px 4px 4px;
|
||||
border-radius: 100px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.user-menu .name { font-size: 13px; font-weight: 500; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow soft"></div>
|
||||
|
||||
<div class="app-shell">
|
||||
<!-- SIDEBAR (same as dashboard) -->
|
||||
<aside class="app-side">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO
|
||||
</a>
|
||||
<div class="side-group">
|
||||
<div class="side-label">Workspace</div>
|
||||
<a class="side-item" href="Dashboard.html"><span class="ico">◆</span>Dashboard</a>
|
||||
<a class="side-item active" href="API Keys.html"><span class="ico">⌘</span>API Keys<span class="count">3</span></a>
|
||||
<a class="side-item" href="Accounts.html"><span class="ico">⊡</span>订阅账号<span class="count">7</span></a>
|
||||
<a class="side-item" href="#"><span class="ico">▤</span>调用日志</a>
|
||||
<a class="side-item" href="#"><span class="ico">$</span>账单 & 充值</a>
|
||||
</div>
|
||||
<div class="side-group">
|
||||
<div class="side-label">Resources</div>
|
||||
<a class="side-item" href="Docs.html"><span class="ico">📖</span>文档</a>
|
||||
<a class="side-item" href="Design System.html"><span class="ico">◆</span>Design System</a>
|
||||
</div>
|
||||
<div class="side-group" style="margin-top:auto; padding-top:20px; border-top:1px solid var(--border);">
|
||||
<a class="side-item" href="#"><span class="ico">⚙</span>设置</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="app-main">
|
||||
<header class="app-topbar">
|
||||
<h1>API Keys</h1>
|
||||
<div class="topbar-user">
|
||||
<div class="user-menu"><span class="avatar">Z</span><span class="name">zane</span></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="app-content">
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>API Keys</h1>
|
||||
<div class="sub">每个 key 是一张独立的"通行证",可以单独设置可用的订阅池、限速和预算,泄漏时可以直接吊销而不影响其他 key。</div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg" onclick="document.getElementById('create-modal').classList.add('open')">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
|
||||
创建 Key
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- summary -->
|
||||
<div class="summary">
|
||||
<div class="cell">
|
||||
<div class="cell-label">活跃 Keys</div>
|
||||
<div class="cell-value tabular">3<span class="unit">/ 10 上限</span></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="cell-label">近 7 日调用</div>
|
||||
<div class="cell-value tabular">89,402</div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="cell-label">近 7 日花费</div>
|
||||
<div class="cell-value tabular">$24.18<span class="unit">USD</span></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="cell-label">已吊销</div>
|
||||
<div class="cell-value tabular text-3">2</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- toolbar -->
|
||||
<div class="toolbar">
|
||||
<div class="search-box">
|
||||
<input placeholder="搜索 key 名称或前缀…">
|
||||
</div>
|
||||
<button class="filter-btn">
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 3H2l8 9.46V19l4 2v-8.54z"/></svg>
|
||||
筛选 <span class="dot-count">2</span>
|
||||
</button>
|
||||
<button class="filter-btn">
|
||||
全部范围
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M6 9l6 6 6-6"/></svg>
|
||||
</button>
|
||||
<div style="margin-left:auto;"></div>
|
||||
<button class="filter-btn">
|
||||
显示吊销
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- list -->
|
||||
<div class="key-list">
|
||||
|
||||
<div class="key-card">
|
||||
<div class="key-head">
|
||||
<div class="key-name">
|
||||
production
|
||||
<span class="badge green">ACTIVE</span>
|
||||
</div>
|
||||
<div class="key-meta">
|
||||
<span>created 2026·03·14</span>
|
||||
<span class="sep">·</span>
|
||||
<span>last used 2m ago</span>
|
||||
<span class="sep">·</span>
|
||||
<span class="text-cyan">● in use</span>
|
||||
</div>
|
||||
<div class="key-actions">
|
||||
<button class="icon-act" title="编辑">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4z"/></svg>
|
||||
</button>
|
||||
<button class="icon-act" title="轮换">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.5 0 4.8 1 6.4 2.6L21 8"/><path d="M21 3v5h-5"/></svg>
|
||||
</button>
|
||||
<button class="icon-act danger" title="吊销">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-value">
|
||||
<span class="prefix">sk-puro-</span>
|
||||
<span class="rest">••••••••••••••••••••••••4f82</span>
|
||||
<span class="reveal">👁 显示</span>
|
||||
<span class="copy-btn">复制</span>
|
||||
</div>
|
||||
|
||||
<div class="key-scopes">
|
||||
<div class="scope">
|
||||
<div class="scope-label">可用订阅池</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag"><span class="dot" style="background:#d97757"></span>claude · 2</span>
|
||||
<span class="mini-tag"><span class="dot" style="background:#10a37f"></span>gpt · 2</span>
|
||||
<span class="mini-tag"><span class="dot" style="background:#4285f4"></span>gemini · 1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">本月用量</div>
|
||||
<div class="scope-val tabular">$14.82 <span class="text-3">/ $50</span></div>
|
||||
<div class="usage-bar"><span style="width:30%"></span></div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">速率限制</div>
|
||||
<div class="scope-val">120 RPM</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">关联应用</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag">Claude Code</span>
|
||||
<span class="mini-tag">Cursor</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-card">
|
||||
<div class="key-head">
|
||||
<div class="key-name">
|
||||
staging
|
||||
<span class="badge amber">RATE LIMITED</span>
|
||||
</div>
|
||||
<div class="key-meta">
|
||||
<span>created 2026·04·02</span>
|
||||
<span class="sep">·</span>
|
||||
<span>last used 3h ago</span>
|
||||
</div>
|
||||
<div class="key-actions">
|
||||
<button class="icon-act"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4z"/></svg></button>
|
||||
<button class="icon-act"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.5 0 4.8 1 6.4 2.6L21 8"/><path d="M21 3v5h-5"/></svg></button>
|
||||
<button class="icon-act danger"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-value">
|
||||
<span class="prefix">sk-puro-</span>
|
||||
<span class="rest">••••••••••••••••••••••••ae19</span>
|
||||
<span class="reveal">👁 显示</span>
|
||||
<span class="copy-btn">复制</span>
|
||||
</div>
|
||||
<div class="key-scopes">
|
||||
<div class="scope">
|
||||
<div class="scope-label">可用订阅池</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag all">all pools</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">本月用量</div>
|
||||
<div class="scope-val tabular">$8.24 <span class="text-3">/ $10</span></div>
|
||||
<div class="usage-bar warn"><span style="width:82%"></span></div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">速率限制</div>
|
||||
<div class="scope-val">30 RPM</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">关联应用</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag">本地开发</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="key-card">
|
||||
<div class="key-head">
|
||||
<div class="key-name">
|
||||
cli-personal
|
||||
<span class="badge">ACTIVE</span>
|
||||
</div>
|
||||
<div class="key-meta">
|
||||
<span>created 2026·04·11</span>
|
||||
<span class="sep">·</span>
|
||||
<span>last used 18h ago</span>
|
||||
</div>
|
||||
<div class="key-actions">
|
||||
<button class="icon-act"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 1 1 3 3L7 19l-4 1 1-4z"/></svg></button>
|
||||
<button class="icon-act"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.5 0 4.8 1 6.4 2.6L21 8"/><path d="M21 3v5h-5"/></svg></button>
|
||||
<button class="icon-act danger"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-value">
|
||||
<span class="prefix">sk-puro-</span>
|
||||
<span class="rest">••••••••••••••••••••••••c3d1</span>
|
||||
<span class="reveal">👁 显示</span>
|
||||
<span class="copy-btn">复制</span>
|
||||
</div>
|
||||
<div class="key-scopes">
|
||||
<div class="scope">
|
||||
<div class="scope-label">可用订阅池</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag"><span class="dot" style="background:#d97757"></span>claude · 1</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">本月用量</div>
|
||||
<div class="scope-val tabular">$1.12 <span class="text-3">/ 无限制</span></div>
|
||||
<div class="usage-bar"><span style="width:6%"></span></div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">速率限制</div>
|
||||
<div class="scope-val">60 RPM</div>
|
||||
</div>
|
||||
<div class="scope">
|
||||
<div class="scope-label">关联应用</div>
|
||||
<div class="scope-val tags">
|
||||
<span class="mini-tag">Terminal</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- revoked -->
|
||||
<div class="key-card revoked">
|
||||
<div class="key-head">
|
||||
<div class="key-name">
|
||||
<span style="text-decoration:line-through; color:var(--text-2);">old-demo</span>
|
||||
<span class="badge red">REVOKED</span>
|
||||
</div>
|
||||
<div class="key-meta">
|
||||
<span>revoked 2026·03·02</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="key-value" style="opacity:0.7">
|
||||
<span class="prefix">sk-puro-</span>
|
||||
<span class="rest">••••••••••••••••••••••••0ab3</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- CREATE MODAL -->
|
||||
<div class="backdrop" id="create-modal">
|
||||
<div class="modal">
|
||||
<div class="modal-head">
|
||||
<h2>创建新的 API Key</h2>
|
||||
<span class="close" onclick="document.getElementById('create-modal').classList.remove('open')">✕</span>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="field">
|
||||
<label class="field-label">Key 名称</label>
|
||||
<input class="input" placeholder="e.g. production · staging · cursor-macbook">
|
||||
<div class="field-hint">仅用于辨识,不会出现在请求中。</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="field-label">可用订阅池</label>
|
||||
<div class="scope-grid">
|
||||
<div class="scope-opt active">
|
||||
<span class="box"></span>
|
||||
<span class="label">Claude 池</span>
|
||||
<span class="desc">2 accounts</span>
|
||||
</div>
|
||||
<div class="scope-opt active">
|
||||
<span class="box"></span>
|
||||
<span class="label">GPT 池</span>
|
||||
<span class="desc">2 accounts</span>
|
||||
</div>
|
||||
<div class="scope-opt">
|
||||
<span class="box"></span>
|
||||
<span class="label">Gemini 池</span>
|
||||
<span class="desc">1 account</span>
|
||||
</div>
|
||||
<div class="scope-opt">
|
||||
<span class="box"></span>
|
||||
<span class="label">Codex 池</span>
|
||||
<span class="desc">0 accounts</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display:grid; grid-template-columns:1fr 1fr; gap:14px;">
|
||||
<div class="field" style="margin-bottom:0">
|
||||
<label class="field-label">月度预算 ($USD)</label>
|
||||
<input class="input" placeholder="50" type="number">
|
||||
<div class="field-hint">达到后自动停用,下月 1 号重置。</div>
|
||||
</div>
|
||||
<div class="field" style="margin-bottom:0">
|
||||
<label class="field-label">速率限制 (RPM)</label>
|
||||
<input class="input" placeholder="60" type="number">
|
||||
<div class="field-hint">每分钟最大请求数。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-foot">
|
||||
<button class="btn btn-ghost" onclick="document.getElementById('create-modal').classList.remove('open')">取消</button>
|
||||
<button class="btn btn-primary">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M5 12l5 5L20 7"/></svg>
|
||||
创建 Key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
561
docs/design-drafts/v2/Binding.html
Normal file
561
docs/design-drafts/v2/Binding.html
Normal file
@@ -0,0 +1,561 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>绑定订阅 — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
.wrap {
|
||||
max-width: 840px;
|
||||
margin: 0 auto;
|
||||
padding: 48px 24px 80px;
|
||||
}
|
||||
.head { text-align: center; margin-bottom: 40px; }
|
||||
.head h1 { font-size: 34px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 10px; }
|
||||
.head p { color: var(--text-2); font-size: 15px; max-width: 560px; margin: 0 auto; line-height: 1.6; }
|
||||
|
||||
/* stepper */
|
||||
.steps {
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
gap: 0; margin-bottom: 40px;
|
||||
}
|
||||
.step {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
.step .num {
|
||||
width: 26px; height: 26px; border-radius: 50%;
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-3);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-family: var(--font-mono); font-size: 12px; font-weight: 600;
|
||||
}
|
||||
.step.done .num { background: var(--cyan); color: #042f2e; border-color: var(--cyan); }
|
||||
.step.active .num { border-color: var(--cyan); color: var(--cyan); background: rgba(34,211,238,0.08); }
|
||||
.step .label { font-size: 13px; color: var(--text-3); }
|
||||
.step.done .label, .step.active .label { color: var(--text-0); }
|
||||
.step-line {
|
||||
width: 80px; height: 1px; background: var(--border);
|
||||
margin: 0 14px;
|
||||
}
|
||||
.step-line.done { background: var(--cyan); }
|
||||
|
||||
/* platform picker */
|
||||
.platform-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.platform-card {
|
||||
padding: 22px 20px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
position: relative;
|
||||
}
|
||||
.platform-card:hover { border-color: var(--border-2); transform: translateY(-2px); }
|
||||
.platform-card.selected {
|
||||
border-color: var(--cyan);
|
||||
background:
|
||||
radial-gradient(400px 200px at 50% 0%, rgba(34,211,238,0.1), transparent 60%),
|
||||
rgba(15, 23, 42, 0.8);
|
||||
}
|
||||
.platform-card.selected::after {
|
||||
content: "✓"; position: absolute; top: 12px; right: 12px;
|
||||
width: 20px; height: 20px; border-radius: 50%;
|
||||
background: var(--cyan); color: #042f2e;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 12px; font-weight: 700;
|
||||
}
|
||||
.platform-logo {
|
||||
width: 40px; height: 40px;
|
||||
border-radius: 8px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.platform-logo.claude { background: rgba(217, 119, 87, 0.12); }
|
||||
.platform-logo.gpt { background: rgba(16, 163, 127, 0.12); }
|
||||
.platform-logo.gemini { background: rgba(66, 133, 244, 0.12); }
|
||||
.platform-logo.codex { background: rgba(240, 160, 48, 0.12); }
|
||||
|
||||
.platform-name {
|
||||
font-size: 15px; font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.platform-tier {
|
||||
font-size: 11px; color: var(--text-3);
|
||||
font-family: var(--font-mono);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.platform-tiers {
|
||||
display: flex; gap: 4px; flex-wrap: wrap;
|
||||
}
|
||||
.platform-tiers .pill {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* selected detail panel */
|
||||
.detail-panel {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
background:
|
||||
radial-gradient(600px 300px at 0% 0%, rgba(34,211,238,0.06), transparent 60%),
|
||||
rgba(15, 23, 42, 0.4);
|
||||
overflow: hidden;
|
||||
}
|
||||
.detail-head {
|
||||
padding: 22px 26px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.detail-head .platform-logo { margin: 0; }
|
||||
.detail-head h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.detail-head .sub {
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
font-family: var(--font-mono);
|
||||
margin-top: 2px;
|
||||
}
|
||||
.detail-head .secure-tag {
|
||||
margin-left: auto;
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11px; font-family: var(--font-mono);
|
||||
color: var(--green);
|
||||
background: rgba(52,211,153,0.08);
|
||||
border: 1px solid rgba(52,211,153,0.2);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.detail-body { padding: 24px 26px; }
|
||||
|
||||
.tier-picker {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
margin-bottom: 22px;
|
||||
}
|
||||
.tier-opt {
|
||||
padding: 14px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
background: rgba(2, 6, 23, 0.4);
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
transition: all .12s;
|
||||
}
|
||||
.tier-opt:hover { border-color: var(--border-2); }
|
||||
.tier-opt.selected {
|
||||
border-color: var(--cyan);
|
||||
background: rgba(34,211,238,0.05);
|
||||
}
|
||||
.tier-opt .t-name { font-size: 13px; font-weight: 600; margin-bottom: 4px; }
|
||||
.tier-opt .t-price {
|
||||
font-family: var(--font-mono); font-size: 18px;
|
||||
font-weight: 700; letter-spacing: -0.02em;
|
||||
color: var(--cyan); margin-bottom: 4px;
|
||||
}
|
||||
.tier-opt .t-price .unit {
|
||||
font-size: 11px; color: var(--text-3);
|
||||
font-weight: 500; margin-left: 3px;
|
||||
}
|
||||
.tier-opt .t-desc {
|
||||
font-size: 11px; color: var(--text-3);
|
||||
}
|
||||
|
||||
/* method cards */
|
||||
.method-section h3 {
|
||||
font-size: 13px; font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-1);
|
||||
}
|
||||
.method-section h3 .hint {
|
||||
color: var(--text-3); font-weight: 400;
|
||||
font-family: var(--font-mono); font-size: 11px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.method-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.method-card {
|
||||
padding: 18px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
background: rgba(2, 6, 23, 0.4);
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
position: relative;
|
||||
}
|
||||
.method-card:hover { border-color: var(--border-2); background: rgba(2, 6, 23, 0.6); }
|
||||
.method-card.selected {
|
||||
border-color: var(--cyan);
|
||||
background: rgba(34, 211, 238, 0.04);
|
||||
}
|
||||
.method-card.selected::after {
|
||||
content: ""; position: absolute; top: 12px; right: 12px;
|
||||
width: 16px; height: 16px; border-radius: 50%;
|
||||
background: var(--cyan);
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23042f2e' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'><path d='M5 12l5 5L20 7'/></svg>");
|
||||
background-repeat: no-repeat; background-position: center;
|
||||
}
|
||||
.method-title {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 14px; font-weight: 600;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.method-title .tag {
|
||||
font-size: 10px; font-family: var(--font-mono);
|
||||
padding: 1px 6px; border-radius: 3px;
|
||||
background: rgba(52,211,153,0.12); color: var(--green);
|
||||
}
|
||||
.method-title .tag.beta { background: rgba(251,191,36,0.12); color: var(--amber); }
|
||||
.method-desc { font-size: 12px; color: var(--text-2); line-height: 1.55; }
|
||||
.method-steps {
|
||||
font-family: var(--font-mono); font-size: 11px;
|
||||
color: var(--text-3); margin-top: 10px;
|
||||
display: flex; gap: 6px; flex-wrap: wrap;
|
||||
}
|
||||
.method-steps span::after {
|
||||
content: "→"; margin-left: 6px; color: var(--border-2);
|
||||
}
|
||||
.method-steps span:last-child::after { display: none; }
|
||||
|
||||
/* OAuth preview */
|
||||
.oauth-preview {
|
||||
margin-top: 14px;
|
||||
padding: 18px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
display: flex; gap: 16px; align-items: center;
|
||||
}
|
||||
.oauth-preview .flow {
|
||||
flex: 1;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
font-family: var(--font-mono); font-size: 12px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.oauth-preview .flow .arrow {
|
||||
color: var(--cyan);
|
||||
}
|
||||
.oauth-preview .flow .node {
|
||||
padding: 4px 10px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
.oauth-preview .flow .node.puro { color: var(--cyan); border-color: rgba(34,211,238,0.25); background: rgba(34,211,238,0.06); }
|
||||
|
||||
/* actions */
|
||||
.actions {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
margin-top: 26px;
|
||||
padding-top: 22px;
|
||||
border-top: 1px dashed var(--border);
|
||||
}
|
||||
.actions .left {
|
||||
font-size: 12px; color: var(--text-3);
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
}
|
||||
.actions .right { display: flex; gap: 10px; }
|
||||
|
||||
/* bound summary panel (step 3 preview) */
|
||||
.bound-list {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.bound-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 12px;
|
||||
background: rgba(2, 6, 23, 0.4);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
font-size: 12px;
|
||||
}
|
||||
.bound-item .tick {
|
||||
width: 16px; height: 16px; border-radius: 50%;
|
||||
background: var(--green);
|
||||
color: #042f2e;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 10px; font-weight: 700;
|
||||
}
|
||||
.bound-item .name { flex: 1; color: var(--text-0); }
|
||||
.bound-item .tag { color: var(--text-3); font-family: var(--font-mono); font-size: 11px; }
|
||||
|
||||
/* topbar user menu (minimal) */
|
||||
.topbar-user {
|
||||
margin-left: auto;
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
}
|
||||
.user-menu {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
padding: 4px 10px 4px 4px;
|
||||
border-radius: 100px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
}
|
||||
.user-menu .name { font-size: 13px; font-weight: 500; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow soft"></div>
|
||||
|
||||
<div class="app-shell">
|
||||
<aside class="app-side">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO
|
||||
</a>
|
||||
<div class="side-group">
|
||||
<div class="side-label">Workspace</div>
|
||||
<a class="side-item" href="Dashboard.html"><span class="ico">◆</span>Dashboard</a>
|
||||
<a class="side-item" href="API Keys.html"><span class="ico">⌘</span>API Keys<span class="count">3</span></a>
|
||||
<a class="side-item active" href="#"><span class="ico">⊡</span>订阅账号<span class="count">7</span></a>
|
||||
<a class="side-item" href="#"><span class="ico">▤</span>调用日志</a>
|
||||
<a class="side-item" href="#"><span class="ico">$</span>账单 & 充值</a>
|
||||
</div>
|
||||
<div class="side-group">
|
||||
<div class="side-label">Resources</div>
|
||||
<a class="side-item" href="Docs.html"><span class="ico">📖</span>文档</a>
|
||||
<a class="side-item" href="Design System.html"><span class="ico">◆</span>Design System</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="app-main">
|
||||
<header class="app-topbar">
|
||||
<a href="Dashboard.html" style="color:var(--text-3); display:inline-flex; align-items:center; gap:6px; font-size:13px;">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M19 12H5"/><path d="M12 19l-7-7 7-7"/></svg>
|
||||
返回 Dashboard
|
||||
</a>
|
||||
<div class="topbar-user">
|
||||
<div class="user-menu"><span class="avatar">Z</span><span class="name">zane</span></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="app-content">
|
||||
<div class="wrap">
|
||||
<div class="head">
|
||||
<div class="section-kicker">// 绑定订阅 · 3 步完成</div>
|
||||
<h1>把你已有的 AI 订阅,变成 API</h1>
|
||||
<p>我们支持 OAuth 授权和 Cookie 托管两种方式接入 Claude / ChatGPT / Gemini。所有凭证使用 AES-256 加密存储,你可以随时一键解绑。</p>
|
||||
</div>
|
||||
|
||||
<!-- stepper -->
|
||||
<div class="steps">
|
||||
<div class="step done">
|
||||
<span class="num">✓</span>
|
||||
<span class="label">选择平台</span>
|
||||
</div>
|
||||
<div class="step-line done"></div>
|
||||
<div class="step active">
|
||||
<span class="num">2</span>
|
||||
<span class="label">授权绑定</span>
|
||||
</div>
|
||||
<div class="step-line"></div>
|
||||
<div class="step">
|
||||
<span class="num">3</span>
|
||||
<span class="label">完成 & 加入池</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- platform picker -->
|
||||
<div class="platform-grid">
|
||||
<div class="platform-card">
|
||||
<div class="platform-logo claude">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg>
|
||||
</div>
|
||||
<div class="platform-name">Claude</div>
|
||||
<div class="platform-tier">Anthropic · OAuth</div>
|
||||
<div class="platform-tiers">
|
||||
<span class="pill">Pro · $20</span>
|
||||
<span class="pill">Max · $100</span>
|
||||
<span class="pill">Team</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="platform-card selected">
|
||||
<div class="platform-logo gpt">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#10a37f" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/></svg>
|
||||
</div>
|
||||
<div class="platform-name">ChatGPT</div>
|
||||
<div class="platform-tier">OpenAI · OAuth + Cookie</div>
|
||||
<div class="platform-tiers">
|
||||
<span class="pill">Plus · $20</span>
|
||||
<span class="pill">Pro · $200</span>
|
||||
<span class="pill">Team</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="platform-card">
|
||||
<div class="platform-logo gemini">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#4285f4" stroke-width="1.8"><path d="M12 2 L15 9 L22 10 L17 15 L19 22 L12 18 L5 22 L7 15 L2 10 L9 9 Z"/></svg>
|
||||
</div>
|
||||
<div class="platform-name">Gemini</div>
|
||||
<div class="platform-tier">Google · Cookie</div>
|
||||
<div class="platform-tiers">
|
||||
<span class="pill">Advanced · $20</span>
|
||||
<span class="pill">Workspace</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- detail panel for selected platform (ChatGPT) -->
|
||||
<div class="detail-panel">
|
||||
<div class="detail-head">
|
||||
<div class="platform-logo gpt">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#10a37f" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/></svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2>绑定 ChatGPT 账号</h2>
|
||||
<div class="sub">支持 Plus / Pro / Team · 接入后可用 gpt-5 / gpt-5-codex / gpt-4.1</div>
|
||||
</div>
|
||||
<span class="secure-tag">
|
||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
AES-256 加密存储
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="detail-body">
|
||||
|
||||
<!-- tier picker -->
|
||||
<div class="ds-label" style="font-family:var(--font-mono); font-size:11px; color:var(--text-3); text-transform:uppercase; letter-spacing:0.14em; margin-bottom:10px;">
|
||||
订阅档位
|
||||
</div>
|
||||
<div class="tier-picker">
|
||||
<div class="tier-opt">
|
||||
<div class="t-name">Plus</div>
|
||||
<div class="t-price">$20<span class="unit">/月</span></div>
|
||||
<div class="t-desc">~500k tokens · 约值 $0.08/k</div>
|
||||
</div>
|
||||
<div class="tier-opt selected">
|
||||
<div class="t-name">Pro</div>
|
||||
<div class="t-price">$200<span class="unit">/月</span></div>
|
||||
<div class="t-desc">~5M tokens · 约值 $0.04/k</div>
|
||||
</div>
|
||||
<div class="tier-opt">
|
||||
<div class="t-name">Team</div>
|
||||
<div class="t-price">$30<span class="unit">/user</span></div>
|
||||
<div class="t-desc">按席位池化,稳定性更高</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- methods -->
|
||||
<div class="method-section">
|
||||
<h3>绑定方式 <span class="hint">选择一种即可 · 可以在绑定后随时更换</span></h3>
|
||||
<div class="method-grid">
|
||||
<div class="method-card selected">
|
||||
<div class="method-title">
|
||||
OAuth 授权登录
|
||||
<span class="tag">推荐</span>
|
||||
</div>
|
||||
<div class="method-desc">
|
||||
跳转到 ChatGPT 登录页,登录后自动回跳。不经过我们的密码表单,最接近"官方授权"体验。
|
||||
</div>
|
||||
<div class="method-steps">
|
||||
<span>点击跳转</span>
|
||||
<span>ChatGPT 登录</span>
|
||||
<span>授权回调</span>
|
||||
<span>加入池</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="method-card">
|
||||
<div class="method-title">
|
||||
粘贴 Session Cookie
|
||||
<span class="tag beta">兼容模式</span>
|
||||
</div>
|
||||
<div class="method-desc">
|
||||
用浏览器扩展一键导出 <code class="mono" style="color:var(--cyan)">__Secure-next-auth.session-token</code> 并粘贴到这里。适合多账号批量绑定。
|
||||
</div>
|
||||
<div class="method-steps">
|
||||
<span>安装扩展</span>
|
||||
<span>登录 chatgpt.com</span>
|
||||
<span>导出 cookie</span>
|
||||
<span>粘贴绑定</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OAuth flow preview -->
|
||||
<div class="oauth-preview">
|
||||
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#22d3ee" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0">
|
||||
<rect width="18" height="11" x="3" y="11" rx="2"/>
|
||||
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
||||
</svg>
|
||||
<div class="flow">
|
||||
<span class="node">你</span>
|
||||
<span class="arrow">→</span>
|
||||
<span class="node puro">PURO</span>
|
||||
<span class="arrow">→</span>
|
||||
<span class="node">chatgpt.com/oauth</span>
|
||||
<span class="arrow">→</span>
|
||||
<span class="node puro">PURO</span>
|
||||
</div>
|
||||
<div class="mono text-xs text-3">约 15 秒</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- bound preview (done state, hidden unless step 3) -->
|
||||
<div class="method-section" style="margin-top:22px">
|
||||
<h3>本次绑定预览 <span class="hint">授权成功后会自动加入池</span></h3>
|
||||
<div class="bound-list">
|
||||
<div class="bound-item">
|
||||
<span class="tick">✓</span>
|
||||
<span class="name">gpt-plus-7</span>
|
||||
<span class="tag">加入 GPT 池</span>
|
||||
</div>
|
||||
<div class="bound-item">
|
||||
<span class="tick">✓</span>
|
||||
<span class="name">gpt-plus-8</span>
|
||||
<span class="tag">加入 GPT 池</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- actions -->
|
||||
<div class="actions">
|
||||
<div class="left">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 8v4M12 16h.01"/></svg>
|
||||
凭证仅用于代理请求,不会用于训练或泄露给第三方。
|
||||
</div>
|
||||
<div class="right">
|
||||
<a class="btn btn-ghost" href="Dashboard.html">稍后再说</a>
|
||||
<button class="btn btn-primary btn-lg">
|
||||
使用 OAuth 绑定
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M7 17L17 7M7 7h10v10"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
770
docs/design-drafts/v2/Dashboard.html
Normal file
770
docs/design-drafts/v2/Dashboard.html
Normal file
@@ -0,0 +1,770 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>Dashboard — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
/* ---- page-local ---- */
|
||||
.page-head {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 28px;
|
||||
gap: 24px;
|
||||
}
|
||||
.page-head h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.page-head .sub { color: var(--text-2); font-size: 14px; }
|
||||
|
||||
/* ---- stat row ---- */
|
||||
.stat-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.stat {
|
||||
padding: 18px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
margin-bottom: 10px;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
font-family: var(--font-mono);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.stat-value .unit { font-size: 14px; color: var(--text-3); margin-left: 4px; font-weight: 500; }
|
||||
.stat-delta {
|
||||
font-size: 11px;
|
||||
color: var(--green);
|
||||
margin-top: 6px;
|
||||
font-family: var(--font-mono);
|
||||
display: flex; align-items: center; gap: 4px;
|
||||
}
|
||||
.stat-delta.down { color: var(--red); }
|
||||
.stat-delta.neutral { color: var(--text-3); }
|
||||
.stat-spark {
|
||||
position: absolute;
|
||||
bottom: 0; right: 0;
|
||||
width: 100px; height: 40px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.stat.credit {
|
||||
background:
|
||||
radial-gradient(400px 200px at 100% 0%, rgba(34,211,238,0.1), transparent 60%),
|
||||
rgba(15, 23, 42, 0.6);
|
||||
border-color: rgba(34, 211, 238, 0.2);
|
||||
}
|
||||
.stat.credit .stat-value { color: var(--cyan); }
|
||||
.stat.credit .topup {
|
||||
position: absolute; top: 14px; right: 14px;
|
||||
font-size: 11px;
|
||||
color: var(--cyan);
|
||||
font-family: var(--font-mono);
|
||||
padding: 3px 8px;
|
||||
border: 1px solid rgba(34, 211, 238, 0.25);
|
||||
border-radius: 4px;
|
||||
background: rgba(34,211,238,0.05);
|
||||
transition: all .15s;
|
||||
}
|
||||
.stat.credit .topup:hover { background: rgba(34,211,238,0.15); }
|
||||
|
||||
/* ---- chart + donut grid ---- */
|
||||
.chart-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.chart-card {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
padding: 18px;
|
||||
}
|
||||
.chart-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
.chart-head .title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.chart-head .legend {
|
||||
display: flex; gap: 14px; font-size: 11px; color: var(--text-3); font-family: var(--font-mono);
|
||||
}
|
||||
.chart-head .legend span { display: inline-flex; align-items: center; gap: 5px; }
|
||||
.chart-head .legend .sw { width: 8px; height: 8px; border-radius: 2px; }
|
||||
|
||||
.range-switch {
|
||||
display: inline-flex;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
padding: 2px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
}
|
||||
.range-switch button {
|
||||
padding: 4px 10px;
|
||||
color: var(--text-3);
|
||||
border-radius: 4px;
|
||||
transition: all .12s;
|
||||
}
|
||||
.range-switch button.active { background: rgba(34,211,238,0.12); color: var(--cyan); }
|
||||
.range-switch button:hover:not(.active) { color: var(--text-1); }
|
||||
|
||||
/* ---- accounts + log ---- */
|
||||
.two-col-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1.4fr;
|
||||
gap: 14px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.panel {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
overflow: hidden;
|
||||
}
|
||||
.panel-head {
|
||||
padding: 14px 18px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
.panel-head h3 { font-size: 13px; font-weight: 600; }
|
||||
.panel-head .meta {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
}
|
||||
.panel-body { padding: 8px; }
|
||||
|
||||
/* account list row */
|
||||
.acct-row {
|
||||
display: grid;
|
||||
grid-template-columns: 24px 1fr auto auto;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 12px;
|
||||
border-radius: var(--r-sm);
|
||||
transition: all .12s;
|
||||
}
|
||||
.acct-row:hover { background: rgba(255,255,255,0.02); }
|
||||
.acct-logo {
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 4px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
}
|
||||
.acct-name {
|
||||
font-size: 13px;
|
||||
color: var(--text-0);
|
||||
font-weight: 500;
|
||||
}
|
||||
.acct-name .tag {
|
||||
display: inline-block;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
color: var(--text-3);
|
||||
margin-left: 6px;
|
||||
}
|
||||
.acct-meter {
|
||||
width: 80px;
|
||||
height: 4px;
|
||||
background: var(--border);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.acct-meter span {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: var(--green);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.acct-meter.warn span { background: var(--amber); }
|
||||
.acct-meter.danger span { background: var(--red); }
|
||||
|
||||
.acct-status {
|
||||
font-size: 11px;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--green);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
min-width: 72px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.acct-status.warn { color: var(--amber); }
|
||||
.acct-status.off { color: var(--text-3); }
|
||||
|
||||
.add-acct {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
margin: 6px 6px 6px;
|
||||
padding: 12px;
|
||||
border: 1px dashed var(--border-2);
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-2);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.add-acct:hover { border-color: var(--cyan); color: var(--cyan); background: rgba(34,211,238,0.04); }
|
||||
|
||||
/* log table overrides */
|
||||
.log-wrap { overflow: hidden; }
|
||||
.log-wrap table { width: 100%; }
|
||||
|
||||
/* endpoint bar */
|
||||
.endpoint {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.endpoint .method {
|
||||
padding: 2px 8px;
|
||||
background: rgba(34,211,238,0.1);
|
||||
color: var(--cyan);
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.endpoint .url { color: var(--text-0); flex: 1; }
|
||||
.endpoint .copy {
|
||||
padding: 4px 10px;
|
||||
color: var(--text-3);
|
||||
font-size: 11px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.endpoint .copy:hover { color: var(--cyan); background: rgba(34,211,238,0.08); }
|
||||
|
||||
/* getting started banner */
|
||||
.tips-banner {
|
||||
padding: 14px 18px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(251, 191, 36, 0.22);
|
||||
border-radius: var(--r-md);
|
||||
background: rgba(251, 191, 36, 0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
font-size: 13px;
|
||||
color: var(--text-1);
|
||||
}
|
||||
.tips-banner .spark { color: var(--amber); flex-shrink: 0; display: inline-flex; }
|
||||
.tips-banner a { color: var(--amber); font-weight: 500; }
|
||||
.tips-banner .dismiss { margin-left: auto; color: var(--text-3); cursor: pointer; padding: 4px; }
|
||||
|
||||
/* topbar custom */
|
||||
.search {
|
||||
flex: 1;
|
||||
max-width: 360px;
|
||||
position: relative;
|
||||
}
|
||||
.search input {
|
||||
width: 100%;
|
||||
height: 34px;
|
||||
padding: 0 14px 0 36px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-1);
|
||||
font-size: 13px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: all .12s;
|
||||
}
|
||||
.search input::placeholder { color: var(--text-3); }
|
||||
.search input:focus { border-color: var(--border-2); background: rgba(2, 6, 23, 0.8); }
|
||||
.search .search-ico {
|
||||
position: absolute; left: 12px; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-3);
|
||||
pointer-events: none;
|
||||
}
|
||||
.search .kbd {
|
||||
position: absolute; right: 10px; top: 50%;
|
||||
transform: translateY(-50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.topbar-right {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.icon-btn {
|
||||
width: 32px; height: 32px;
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-2);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all .12s;
|
||||
position: relative;
|
||||
}
|
||||
.icon-btn:hover { color: var(--text-0); background: rgba(255,255,255,0.04); }
|
||||
.icon-btn .pulse {
|
||||
position: absolute; top: 8px; right: 8px;
|
||||
width: 6px; height: 6px; border-radius: 50%;
|
||||
background: var(--amber);
|
||||
box-shadow: 0 0 0 3px rgba(251,191,36,0.15);
|
||||
}
|
||||
|
||||
.user-menu {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 4px 10px 4px 4px;
|
||||
border-radius: 100px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.user-menu:hover { background: rgba(255,255,255,0.06); }
|
||||
.user-menu .name { font-size: 13px; font-weight: 500; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="bg-glow soft"></div>
|
||||
|
||||
<div class="app-shell">
|
||||
|
||||
<!-- SIDEBAR -->
|
||||
<aside class="app-side">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO
|
||||
</a>
|
||||
|
||||
<div class="side-group">
|
||||
<div class="side-label">Workspace</div>
|
||||
<a class="side-item active" href="Dashboard.html">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="9"/><rect x="14" y="3" width="7" height="5"/><rect x="14" y="12" width="7" height="9"/><rect x="3" y="16" width="7" height="5"/></svg>
|
||||
</span>
|
||||
Dashboard
|
||||
</a>
|
||||
<a class="side-item" href="API Keys.html">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="7.5" cy="15.5" r="3.5"/><path d="m21 2-9.6 9.6"/><path d="m15.5 7.5 3 3L22 7l-3-3"/></svg>
|
||||
</span>
|
||||
API Keys
|
||||
<span class="count">3</span>
|
||||
</a>
|
||||
<a class="side-item" href="Accounts.html">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M20 7h-9"/><path d="M14 17H5"/><circle cx="17" cy="17" r="3"/><circle cx="7" cy="7" r="3"/></svg>
|
||||
</span>
|
||||
订阅账号
|
||||
<span class="count">7</span>
|
||||
</a>
|
||||
<a class="side-item" href="#">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="M8 13h8"/><path d="M8 17h8"/></svg>
|
||||
</span>
|
||||
调用日志
|
||||
</a>
|
||||
<a class="side-item" href="#">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
|
||||
</span>
|
||||
账单 & 充值
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="side-group">
|
||||
<div class="side-label">Resources</div>
|
||||
<a class="side-item" href="Docs.html">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
|
||||
</span>
|
||||
文档
|
||||
</a>
|
||||
<a class="side-item" href="Design System.html">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2L21 7V17L12 22L3 17V7L12 2Z"/></svg>
|
||||
</span>
|
||||
Design System
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="side-group" style="margin-top:auto; padding-top:20px; border-top:1px solid var(--border);">
|
||||
<a class="side-item" href="#">
|
||||
<span class="ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3h0a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8v0a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>
|
||||
</span>
|
||||
设置
|
||||
</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- MAIN -->
|
||||
<div class="app-main">
|
||||
<header class="app-topbar">
|
||||
<div class="search">
|
||||
<span class="search-ico">
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
|
||||
</span>
|
||||
<input placeholder="搜索 key / 账号 / 日志…">
|
||||
<span class="kbd"><kbd>⌘</kbd><kbd>K</kbd></span>
|
||||
</div>
|
||||
|
||||
<div class="topbar-right">
|
||||
<button class="icon-btn" aria-label="通知">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>
|
||||
<span class="pulse"></span>
|
||||
</button>
|
||||
<button class="icon-btn" aria-label="帮助">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></svg>
|
||||
</button>
|
||||
<div class="user-menu">
|
||||
<span class="avatar">Z</span>
|
||||
<span class="name">zane</span>
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 9l6 6 6-6"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="app-content">
|
||||
|
||||
<!-- tips -->
|
||||
<div class="tips-banner">
|
||||
<span class="spark">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><path d="M12 8h.01"/></svg>
|
||||
</span>
|
||||
你还有 <b class="text-0">2 个 Claude Pro</b> 订阅未绑定 —— 绑定后立即享受多账号 failover 和请求加权调度。
|
||||
<a href="Binding.html">去绑定 →</a>
|
||||
<span class="dismiss">✕</span>
|
||||
</div>
|
||||
|
||||
<!-- page head -->
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<div class="sub">欢迎回来 <b class="text-0">Zane</b> · workspace <code class="pill">zane-personal</code></div>
|
||||
</div>
|
||||
<div style="display:flex; gap:10px;">
|
||||
<div class="range-switch">
|
||||
<button>1h</button>
|
||||
<button>24h</button>
|
||||
<button class="active">7d</button>
|
||||
<button>30d</button>
|
||||
<button>自定义</button>
|
||||
</div>
|
||||
<button class="btn btn-ghost">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></svg>
|
||||
导出
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- endpoint -->
|
||||
<div class="endpoint">
|
||||
<span class="method">POST</span>
|
||||
<span class="url">https://ai.puro.im/v1/chat/completions</span>
|
||||
<span class="copy">复制</span>
|
||||
<span style="color:var(--text-3); margin: 0 4px;">·</span>
|
||||
<span class="copy">查看文档</span>
|
||||
</div>
|
||||
|
||||
<!-- stats -->
|
||||
<div class="stat-row">
|
||||
<div class="stat credit">
|
||||
<a class="topup" href="Pricing.html">充值 →</a>
|
||||
<div class="stat-label">余额</div>
|
||||
<div class="stat-value">$182.40<span class="unit">USD</span></div>
|
||||
<div class="stat-delta neutral">按当前节奏约 <b class="text-1">23 天</b></div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">今日请求</div>
|
||||
<div class="stat-value tabular">12,847</div>
|
||||
<div class="stat-delta">▲ 18.2% vs 昨日</div>
|
||||
<svg class="stat-spark" viewBox="0 0 100 40" preserveAspectRatio="none">
|
||||
<path d="M0,30 L10,28 L20,22 L30,25 L40,18 L50,20 L60,12 L70,15 L80,8 L90,12 L100,5" stroke="#22d3ee" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">消耗 Tokens</div>
|
||||
<div class="stat-value tabular">4.82<span class="unit">M</span></div>
|
||||
<div class="stat-delta">▲ 12.5%</div>
|
||||
<svg class="stat-spark" viewBox="0 0 100 40" preserveAspectRatio="none">
|
||||
<path d="M0,25 L10,20 L20,22 L30,15 L40,18 L50,10 L60,14 L70,8 L80,12 L90,6 L100,4" stroke="#a855f7" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-label">平均延迟</div>
|
||||
<div class="stat-value tabular">214<span class="unit">ms</span></div>
|
||||
<div class="stat-delta down">▲ 4.1% vs 昨日</div>
|
||||
<svg class="stat-spark" viewBox="0 0 100 40" preserveAspectRatio="none">
|
||||
<path d="M0,20 L10,22 L20,18 L30,24 L40,20 L50,22 L60,18 L70,24 L80,22 L90,26 L100,22" stroke="#fbbf24" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- chart + donut -->
|
||||
<div class="chart-grid">
|
||||
<div class="chart-card">
|
||||
<div class="chart-head">
|
||||
<span class="title">请求趋势</span>
|
||||
<div class="legend">
|
||||
<span><span class="sw" style="background:#22d3ee"></span>Claude</span>
|
||||
<span><span class="sw" style="background:#a855f7"></span>GPT</span>
|
||||
<span><span class="sw" style="background:#fbbf24"></span>Gemini</span>
|
||||
</div>
|
||||
</div>
|
||||
<svg viewBox="0 0 500 180" style="width:100%; height:180px; display:block;">
|
||||
<defs>
|
||||
<linearGradient id="gc" x1="0" x2="0" y1="0" y2="1">
|
||||
<stop offset="0%" stop-color="#22d3ee" stop-opacity="0.25"/>
|
||||
<stop offset="100%" stop-color="#22d3ee" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g stroke="#1e293b" stroke-width="1">
|
||||
<line x1="0" y1="30" x2="500" y2="30"/>
|
||||
<line x1="0" y1="75" x2="500" y2="75"/>
|
||||
<line x1="0" y1="120" x2="500" y2="120"/>
|
||||
<line x1="0" y1="165" x2="500" y2="165"/>
|
||||
</g>
|
||||
<path d="M0,120 L40,100 L80,110 L120,80 L160,95 L200,60 L240,70 L280,45 L320,55 L360,30 L400,50 L440,35 L500,25 L500,180 L0,180 Z" fill="url(#gc)"/>
|
||||
<path d="M0,120 L40,100 L80,110 L120,80 L160,95 L200,60 L240,70 L280,45 L320,55 L360,30 L400,50 L440,35 L500,25" stroke="#22d3ee" stroke-width="2" fill="none"/>
|
||||
<path d="M0,140 L40,135 L80,120 L120,130 L160,110 L200,115 L240,90 L280,100 L320,80 L360,90 L400,70 L440,75 L500,55" stroke="#a855f7" stroke-width="2" fill="none"/>
|
||||
<path d="M0,160 L40,155 L80,150 L120,148 L160,145 L200,140 L240,135 L280,130 L320,125 L360,122 L400,118 L440,112 L500,108" stroke="#fbbf24" stroke-width="2" fill="none"/>
|
||||
<g font-family="JetBrains Mono" font-size="9" fill="#64748b">
|
||||
<text x="0" y="178">Mon</text>
|
||||
<text x="83" y="178">Tue</text>
|
||||
<text x="166" y="178">Wed</text>
|
||||
<text x="249" y="178">Thu</text>
|
||||
<text x="332" y="178">Fri</text>
|
||||
<text x="415" y="178">Sat</text>
|
||||
<text x="475" y="178">Sun</text>
|
||||
</g>
|
||||
<!-- hover indicator -->
|
||||
<line x1="360" y1="30" x2="360" y2="165" stroke="#334155" stroke-width="1" stroke-dasharray="2,3"/>
|
||||
<circle cx="360" cy="30" r="3" fill="#22d3ee"/>
|
||||
<g transform="translate(370, 18)">
|
||||
<rect width="92" height="28" fill="#020617" stroke="#334155" rx="4"/>
|
||||
<text x="8" y="12" font-family="JetBrains Mono" font-size="9" fill="#cbd5e1">Fri · 16:00</text>
|
||||
<text x="8" y="23" font-family="JetBrains Mono" font-size="10" fill="#22d3ee">Claude 3,824</text>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="chart-card">
|
||||
<div class="chart-head">
|
||||
<span class="title">模型分布</span>
|
||||
<span class="mono text-xs text-3">7d</span>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:18px;">
|
||||
<svg viewBox="0 0 42 42" style="width:120px; height:120px; transform:rotate(-90deg);">
|
||||
<circle cx="21" cy="21" r="15.915" fill="transparent" stroke="#1e293b" stroke-width="6"/>
|
||||
<circle cx="21" cy="21" r="15.915" fill="transparent" stroke="#22d3ee" stroke-width="6" stroke-dasharray="48 52" stroke-dashoffset="0"/>
|
||||
<circle cx="21" cy="21" r="15.915" fill="transparent" stroke="#a855f7" stroke-width="6" stroke-dasharray="32 68" stroke-dashoffset="-48"/>
|
||||
<circle cx="21" cy="21" r="15.915" fill="transparent" stroke="#fbbf24" stroke-width="6" stroke-dasharray="14 86" stroke-dashoffset="-80"/>
|
||||
<circle cx="21" cy="21" r="15.915" fill="transparent" stroke="#64748b" stroke-width="6" stroke-dasharray="6 94" stroke-dashoffset="-94"/>
|
||||
</svg>
|
||||
<div style="flex:1; display:flex; flex-direction:column; gap:8px; font-size:12px; font-family:var(--font-mono);">
|
||||
<div style="display:flex; justify-content:space-between;"><span style="color:var(--text-1);"><span style="display:inline-block; width:8px; height:8px; background:#22d3ee; border-radius:2px; margin-right:6px;"></span>Claude</span><span style="color:var(--text-3);">48%</span></div>
|
||||
<div style="display:flex; justify-content:space-between;"><span style="color:var(--text-1);"><span style="display:inline-block; width:8px; height:8px; background:#a855f7; border-radius:2px; margin-right:6px;"></span>GPT</span><span style="color:var(--text-3);">32%</span></div>
|
||||
<div style="display:flex; justify-content:space-between;"><span style="color:var(--text-1);"><span style="display:inline-block; width:8px; height:8px; background:#fbbf24; border-radius:2px; margin-right:6px;"></span>Gemini</span><span style="color:var(--text-3);">14%</span></div>
|
||||
<div style="display:flex; justify-content:space-between;"><span style="color:var(--text-1);"><span style="display:inline-block; width:8px; height:8px; background:#64748b; border-radius:2px; margin-right:6px;"></span>Codex</span><span style="color:var(--text-3);">6%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- accounts + logs -->
|
||||
<div class="two-col-grid">
|
||||
<!-- accounts -->
|
||||
<div class="panel">
|
||||
<div class="panel-head">
|
||||
<h3>订阅账号池</h3>
|
||||
<a href="Accounts.html" class="meta" style="color:var(--cyan);">查看全部 →</a>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo"><svg width="14" height="14" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg></span>
|
||||
<div class="acct-name">claude-pool-01<span class="tag">Pro · OAuth</span></div>
|
||||
<div class="acct-meter"><span style="width:68%"></span></div>
|
||||
<span class="acct-status"><span class="status-chip" style="width:5px;height:5px;"></span>healthy</span>
|
||||
</div>
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo"><svg width="14" height="14" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg></span>
|
||||
<div class="acct-name">claude-pool-02<span class="tag">Max · OAuth</span></div>
|
||||
<div class="acct-meter"><span style="width:42%"></span></div>
|
||||
<span class="acct-status"><span class="status-chip" style="width:5px;height:5px;"></span>healthy</span>
|
||||
</div>
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#10a37f" stroke-width="1.6"><circle cx="12" cy="12" r="8"/><path d="M12 4v16M4 12h16"/></svg></span>
|
||||
<div class="acct-name">gpt-plus-7<span class="tag">Plus · OAuth</span></div>
|
||||
<div class="acct-meter warn"><span style="width:88%"></span></div>
|
||||
<span class="acct-status warn"><span class="status-chip amber" style="width:5px;height:5px;"></span>near limit</span>
|
||||
</div>
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#10a37f" stroke-width="1.6"><circle cx="12" cy="12" r="8"/><path d="M12 4v16M4 12h16"/></svg></span>
|
||||
<div class="acct-name">gpt-plus-8<span class="tag">Pro · OAuth</span></div>
|
||||
<div class="acct-meter"><span style="width:35%"></span></div>
|
||||
<span class="acct-status"><span class="status-chip" style="width:5px;height:5px;"></span>healthy</span>
|
||||
</div>
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#4285f4" stroke-width="1.8"><path d="M12 2 L15 9 L22 10 L17 15 L19 22 L12 18 L5 22 L7 15 L2 10 L9 9 Z"/></svg></span>
|
||||
<div class="acct-name">gemini-adv-01<span class="tag">Advanced</span></div>
|
||||
<div class="acct-meter"><span style="width:12%"></span></div>
|
||||
<span class="acct-status"><span class="status-chip" style="width:5px;height:5px;"></span>healthy</span>
|
||||
</div>
|
||||
<div class="acct-row">
|
||||
<span class="acct-logo" style="opacity:0.5"><svg width="14" height="14" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg></span>
|
||||
<div class="acct-name" style="opacity:0.7">claude-pool-03<span class="tag">Pro · expired</span></div>
|
||||
<div class="acct-meter"><span style="width:0%"></span></div>
|
||||
<span class="acct-status off">● offline</span>
|
||||
</div>
|
||||
<a class="add-acct" href="Binding.html">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>
|
||||
绑定新订阅
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- logs -->
|
||||
<div class="panel log-wrap">
|
||||
<div class="panel-head">
|
||||
<h3>最近请求</h3>
|
||||
<span class="meta">live · 12 of 18,294</span>
|
||||
</div>
|
||||
<table class="tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="mono">TIME</th>
|
||||
<th>ACCOUNT</th>
|
||||
<th class="mono">MODEL</th>
|
||||
<th class="mono">TOKENS</th>
|
||||
<th class="mono">COST</th>
|
||||
<th class="mono">STATUS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">13:42:18</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>claude-pool-01</span></td>
|
||||
<td class="mono">sonnet-4-5</td>
|
||||
<td class="mono tabular">2,847</td>
|
||||
<td class="mono tabular">$0.042</td>
|
||||
<td class="mono text-green">200 · 213ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:42:11</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>gpt-plus-7</span></td>
|
||||
<td class="mono">gpt-5-codex</td>
|
||||
<td class="mono tabular">1,204</td>
|
||||
<td class="mono tabular">$0.018</td>
|
||||
<td class="mono text-green">200 · 167ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:42:03</td>
|
||||
<td><span class="provider gemini"><span class="dot"></span>gemini-adv-01</span></td>
|
||||
<td class="mono">gemini-2.5-pro</td>
|
||||
<td class="mono tabular">4,102</td>
|
||||
<td class="mono tabular">$0.000</td>
|
||||
<td class="mono text-green">200 · 392ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:41:58</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>claude-pool-02</span></td>
|
||||
<td class="mono">sonnet-4-5</td>
|
||||
<td class="mono tabular">6,318</td>
|
||||
<td class="mono tabular">$0.095</td>
|
||||
<td class="mono text-green">200 · 288ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:41:49</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>gpt-plus-7</span></td>
|
||||
<td class="mono">gpt-5</td>
|
||||
<td class="mono tabular">892</td>
|
||||
<td class="mono tabular">$0.013</td>
|
||||
<td class="mono text-amber">429 · retry</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:41:42</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>gpt-plus-8</span></td>
|
||||
<td class="mono">gpt-5</td>
|
||||
<td class="mono tabular">892</td>
|
||||
<td class="mono tabular">$0.013</td>
|
||||
<td class="mono text-green">200 · 198ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:41:35</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>claude-pool-01</span></td>
|
||||
<td class="mono">haiku-4-5</td>
|
||||
<td class="mono tabular">512</td>
|
||||
<td class="mono tabular">$0.004</td>
|
||||
<td class="mono text-green">200 · 98ms</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:41:28</td>
|
||||
<td><span class="provider gemini"><span class="dot"></span>gemini-adv-01</span></td>
|
||||
<td class="mono">gemini-2.5-flash</td>
|
||||
<td class="mono tabular">1,824</td>
|
||||
<td class="mono tabular">$0.000</td>
|
||||
<td class="mono text-green">200 · 156ms</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
670
docs/design-drafts/v2/Design System.html
Normal file
670
docs/design-drafts/v2/Design System.html
Normal file
@@ -0,0 +1,670 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>Design System — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
/* page-local helpers */
|
||||
.ds-header {
|
||||
padding: 72px 0 48px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 56px;
|
||||
}
|
||||
.ds-header h1 { font-size: 48px; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 12px; }
|
||||
.ds-header p { color: var(--text-2); max-width: 600px; line-height: 1.6; }
|
||||
.ds-header .meta {
|
||||
display: flex; gap: 18px; margin-top: 24px;
|
||||
font-family: var(--font-mono); font-size: 12px; color: var(--text-3);
|
||||
}
|
||||
|
||||
.ds-section { padding: 40px 0; border-bottom: 1px dashed var(--border); }
|
||||
.ds-section:last-child { border-bottom: none; }
|
||||
.ds-section > h2 {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 6px;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.ds-section > h2 .mono-num {
|
||||
color: var(--cyan);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 14px;
|
||||
margin-right: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
.ds-section > .desc {
|
||||
color: var(--text-2);
|
||||
font-size: 14px;
|
||||
margin-bottom: 28px;
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.ds-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
/* color swatches */
|
||||
.swatch-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
.sw {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
padding: 14px;
|
||||
background: rgba(15, 23, 42, 0.4);
|
||||
}
|
||||
.sw .chip {
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
border-radius: var(--r-sm);
|
||||
border: 1px solid rgba(255,255,255,0.04);
|
||||
margin-bottom: 10px;
|
||||
padding: 0; background: transparent;
|
||||
}
|
||||
.sw .name { font-size: 12px; font-weight: 600; color: var(--text-0); }
|
||||
.sw .hex { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); margin-top: 2px; }
|
||||
.sw .use { font-size: 11px; color: var(--text-3); margin-top: 4px; }
|
||||
|
||||
/* type scale */
|
||||
.type-row {
|
||||
display: grid;
|
||||
grid-template-columns: 180px 1fr;
|
||||
gap: 16px;
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
align-items: baseline;
|
||||
}
|
||||
.type-row:last-child { border-bottom: none; }
|
||||
.type-meta { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); }
|
||||
.type-meta b { color: var(--text-1); font-weight: 500; }
|
||||
|
||||
/* component examples */
|
||||
.example {
|
||||
background: rgba(2, 6, 23, 0.35);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
padding: 28px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.example.dense { padding: 20px; }
|
||||
.example-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* spacing scale */
|
||||
.space-row {
|
||||
display: grid;
|
||||
grid-template-columns: 90px 1fr 100px;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
padding: 10px 0;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.space-row .bar { height: 10px; background: var(--cyan); border-radius: 2px; opacity: 0.7; }
|
||||
.space-row .note { color: var(--text-3); text-align: right; }
|
||||
|
||||
/* radius scale */
|
||||
.radius-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
.radius-card {
|
||||
padding: 24px;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
text-align: center;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.radius-card .token { font-size: 11px; color: var(--text-3); margin-top: 4px; }
|
||||
|
||||
.two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.three-col { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
|
||||
|
||||
/* logo lockup block */
|
||||
.logo-showcase {
|
||||
padding: 56px 32px;
|
||||
background:
|
||||
radial-gradient(600px 300px at 20% 0%, rgba(34,211,238,0.12), transparent 60%),
|
||||
radial-gradient(600px 300px at 80% 100%, rgba(168,85,247,0.12), transparent 60%),
|
||||
var(--bg-1);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-xl);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
}
|
||||
.logo-showcase svg { width: 56px; height: 56px; color: var(--cyan); }
|
||||
.logo-showcase .word {
|
||||
font-size: 38px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
.logo-showcase .word span { color: var(--cyan); }
|
||||
|
||||
/* TOC */
|
||||
.toc {
|
||||
position: sticky;
|
||||
top: 24px;
|
||||
align-self: flex-start;
|
||||
padding: 20px 0;
|
||||
}
|
||||
.toc .toc-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
color: var(--text-3);
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.toc ol { list-style: none; counter-reset: toc; display: flex; flex-direction: column; gap: 2px; }
|
||||
.toc li { counter-increment: toc; }
|
||||
.toc a {
|
||||
display: block;
|
||||
padding: 6px 10px;
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-2);
|
||||
font-size: 13px;
|
||||
transition: all .12s;
|
||||
}
|
||||
.toc a::before {
|
||||
content: counter(toc, decimal-leading-zero);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
color: var(--text-3);
|
||||
margin-right: 10px;
|
||||
}
|
||||
.toc a:hover { color: var(--text-0); background: rgba(255,255,255,0.02); }
|
||||
|
||||
.with-toc {
|
||||
display: grid;
|
||||
grid-template-columns: 200px 1fr;
|
||||
gap: 48px;
|
||||
align-items: start;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="bg-glow soft"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<nav class="nav">
|
||||
<div class="container nav-inner">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
<div class="nav-links">
|
||||
<a href="Landing.html">Landing</a>
|
||||
<a href="#" class="active">Design System</a>
|
||||
<a href="Dashboard.html">Dashboard</a>
|
||||
<a href="Docs.html">文档</a>
|
||||
</div>
|
||||
<div class="nav-cta">
|
||||
<a href="Login.html" class="btn btn-ghost">登录</a>
|
||||
<a href="Register.html" class="btn btn-primary">注册</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<!-- HEADER -->
|
||||
<header class="ds-header">
|
||||
<div class="section-kicker">// design system · v1.0</div>
|
||||
<h1>PURO AI Design System</h1>
|
||||
<p>一套用来构建 PURO AI 所有界面的原子 token 和组件。产品的视觉语言围绕「开发者工具 · 深色为主 · 青色作为行动色 · JetBrains Mono 强调技术感」展开。</p>
|
||||
<div class="meta">
|
||||
<span>tokens <b class="text-1">·</b> 29</span>
|
||||
<span>components <b class="text-1">·</b> 22</span>
|
||||
<span>last updated · 2026.04.19</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="with-toc">
|
||||
<aside class="toc">
|
||||
<div class="toc-label">Contents</div>
|
||||
<ol>
|
||||
<li><a href="#brand">Brand</a></li>
|
||||
<li><a href="#colors">Colors</a></li>
|
||||
<li><a href="#type">Typography</a></li>
|
||||
<li><a href="#spacing">Spacing</a></li>
|
||||
<li><a href="#radius">Radius</a></li>
|
||||
<li><a href="#buttons">Buttons</a></li>
|
||||
<li><a href="#badges">Badges</a></li>
|
||||
<li><a href="#chips">Chips</a></li>
|
||||
<li><a href="#forms">Forms</a></li>
|
||||
<li><a href="#cards">Cards</a></li>
|
||||
<li><a href="#tables">Tables</a></li>
|
||||
<li><a href="#code">Code</a></li>
|
||||
<li><a href="#nav">Nav</a></li>
|
||||
</ol>
|
||||
</aside>
|
||||
|
||||
<main>
|
||||
|
||||
<!-- BRAND -->
|
||||
<section class="ds-section" id="brand">
|
||||
<h2><span class="mono-num">01</span>Brand Lockup</h2>
|
||||
<p class="desc">六边形 + 内部实心菱形 — 代表"订阅被聚合成一个 key"。单色在小尺寸下使用,大尺寸下保留内描边增加分量。</p>
|
||||
<div class="logo-showcase">
|
||||
<svg viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
<div class="word">PURO<span>.</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- COLORS -->
|
||||
<section class="ds-section" id="colors">
|
||||
<h2><span class="mono-num">02</span>Colors</h2>
|
||||
<p class="desc">所有颜色都以 CSS variables 定义在 <code class="pill">puro.css</code>。青色 (cyan) 是唯一的品牌色,其他颜色仅承担语义职责(success / warn / danger)。</p>
|
||||
|
||||
<div class="ds-label">Surfaces</div>
|
||||
<div class="swatch-grid">
|
||||
<div class="sw"><div class="chip" style="background:#0a0e1a"></div><div class="name">bg-0</div><div class="hex">#0a0e1a</div><div class="use">page</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#0f172a"></div><div class="name">bg-1</div><div class="hex">#0f172a</div><div class="use">raised</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#111827"></div><div class="name">bg-2</div><div class="hex">#111827</div><div class="use">card alt</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#020617"></div><div class="name">bg-code</div><div class="hex">#020617</div><div class="use">code</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#1e293b"></div><div class="name">border</div><div class="hex">#1e293b</div><div class="use">hairline</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#334155"></div><div class="name">border-2</div><div class="hex">#334155</div><div class="use">strong</div></div>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:32px">Text</div>
|
||||
<div class="swatch-grid">
|
||||
<div class="sw"><div class="chip" style="background:#f8fafc"></div><div class="name">text-0</div><div class="hex">#f8fafc</div><div class="use">primary</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#cbd5e1"></div><div class="name">text-1</div><div class="hex">#cbd5e1</div><div class="use">body</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#94a3b8"></div><div class="name">text-2</div><div class="hex">#94a3b8</div><div class="use">muted</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#64748b"></div><div class="name">text-3</div><div class="hex">#64748b</div><div class="use">hint</div></div>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:32px">Accents</div>
|
||||
<div class="swatch-grid">
|
||||
<div class="sw"><div class="chip" style="background:#22d3ee"></div><div class="name">cyan</div><div class="hex">#22d3ee</div><div class="use">primary / cta</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#a855f7"></div><div class="name">purple</div><div class="hex">#a855f7</div><div class="use">secondary glow</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#fbbf24"></div><div class="name">amber</div><div class="hex">#fbbf24</div><div class="use">warn / featured</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#34d399"></div><div class="name">green</div><div class="hex">#34d399</div><div class="use">success / 200</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#f87171"></div><div class="name">red</div><div class="hex">#f87171</div><div class="use">error / 5xx</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#fb923c"></div><div class="name">orange</div><div class="hex">#fb923c</div><div class="use">flag / highlight</div></div>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:32px">Provider Brand Dots</div>
|
||||
<div class="swatch-grid">
|
||||
<div class="sw"><div class="chip" style="background:#d97757"></div><div class="name">claude</div><div class="hex">#d97757</div><div class="use">Anthropic</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#10a37f"></div><div class="name">gpt</div><div class="hex">#10a37f</div><div class="use">OpenAI</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#4285f4"></div><div class="name">gemini</div><div class="hex">#4285f4</div><div class="use">Google</div></div>
|
||||
<div class="sw"><div class="chip" style="background:#f0a030"></div><div class="name">codex</div><div class="hex">#f0a030</div><div class="use">Codex</div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TYPOGRAPHY -->
|
||||
<section class="ds-section" id="type">
|
||||
<h2><span class="mono-num">03</span>Typography</h2>
|
||||
<p class="desc">主字体 <b>Inter</b> · 等宽 <b>JetBrains Mono</b>。等宽仅用于代码、数据、时间戳、状态徽标,以强化开发者语境。</p>
|
||||
|
||||
<div class="type-row">
|
||||
<div class="type-meta">display · 56/64 · 800</div>
|
||||
<div style="font-size:56px; font-weight:800; letter-spacing:-0.03em; line-height:1.05;">你的 AI 订阅</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">h1 · 40/48 · 700</div>
|
||||
<div style="font-size:40px; font-weight:700; letter-spacing:-0.02em;">统一接入 API</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">h2 · 28/36 · 700</div>
|
||||
<div style="font-size:28px; font-weight:700; letter-spacing:-0.02em;">付一次,用一池</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">h3 · 18/26 · 600</div>
|
||||
<div style="font-size:18px; font-weight:600;">多账号自动调度</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">body · 14/22 · 400</div>
|
||||
<div style="font-size:14px;">OAuth 绑定账号,零改动切换 base_url,沿用你习惯的 SDK。</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">caption · 12/18 · 400</div>
|
||||
<div style="font-size:12px; color:var(--text-2);">某个 ChatGPT Plus 触发限流会自动 failover。</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">mono · 13 · 500</div>
|
||||
<div class="mono" style="font-size:13px; color:var(--cyan);">curl https://ai.puro.im/v1/chat/completions</div>
|
||||
</div>
|
||||
<div class="type-row">
|
||||
<div class="type-meta">kicker · mono · 12 · caps</div>
|
||||
<div class="section-kicker" style="margin:0">// section kicker</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- SPACING -->
|
||||
<section class="ds-section" id="spacing">
|
||||
<h2><span class="mono-num">04</span>Spacing Scale</h2>
|
||||
<p class="desc">4px 基线的 8 / 12 / 16 / 20 / 24 / 32 / 48 / 64 scale。页面垂直节奏用 32/48/64/96,卡片内部用 16/20/24。</p>
|
||||
<div>
|
||||
<div class="space-row"><span>4px</span><div class="bar" style="width:4px"></div><span class="note">gap-xs · pill 间隔</span></div>
|
||||
<div class="space-row"><span>8px</span><div class="bar" style="width:8px"></div><span class="note">gap-sm · icon 内外距</span></div>
|
||||
<div class="space-row"><span>12px</span><div class="bar" style="width:12px"></div><span class="note">gap · 卡片网格</span></div>
|
||||
<div class="space-row"><span>16px</span><div class="bar" style="width:16px"></div><span class="note">stack-sm · 主要网格</span></div>
|
||||
<div class="space-row"><span>20px</span><div class="bar" style="width:20px"></div><span class="note">form field</span></div>
|
||||
<div class="space-row"><span>24px</span><div class="bar" style="width:24px"></div><span class="note">card padding</span></div>
|
||||
<div class="space-row"><span>32px</span><div class="bar" style="width:32px"></div><span class="note">content padding</span></div>
|
||||
<div class="space-row"><span>48px</span><div class="bar" style="width:48px"></div><span class="note">section break</span></div>
|
||||
<div class="space-row"><span>64px</span><div class="bar" style="width:64px"></div><span class="note">section head gap</span></div>
|
||||
<div class="space-row"><span>96px</span><div class="bar" style="width:96px"></div><span class="note">landing section</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- RADIUS -->
|
||||
<section class="ds-section" id="radius">
|
||||
<h2><span class="mono-num">05</span>Radius & Shadow</h2>
|
||||
<div class="radius-grid">
|
||||
<div class="radius-card" style="border-radius:6px">6px<div class="token">--r-sm</div></div>
|
||||
<div class="radius-card" style="border-radius:8px">8px<div class="token">--r-md (button, input)</div></div>
|
||||
<div class="radius-card" style="border-radius:12px">12px<div class="token">--r-lg (card)</div></div>
|
||||
<div class="radius-card" style="border-radius:16px">16px<div class="token">--r-xl (hero card)</div></div>
|
||||
</div>
|
||||
<div class="ds-label" style="margin-top:28px">Elevation</div>
|
||||
<div class="two-col">
|
||||
<div class="card-raised" style="padding:24px; box-shadow:var(--shadow-lg);">
|
||||
<div class="mono text-xs text-3">--shadow-lg</div>
|
||||
<div class="text-sm text-1" style="margin-top:8px">卡片悬浮 · 代码面板</div>
|
||||
</div>
|
||||
<div class="card-raised" style="padding:24px; box-shadow:var(--shadow-xl);">
|
||||
<div class="mono text-xs text-3">--shadow-xl</div>
|
||||
<div class="text-sm text-1" style="margin-top:8px">仪表盘大图 · 对话框</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- BUTTONS -->
|
||||
<section class="ds-section" id="buttons">
|
||||
<h2><span class="mono-num">06</span>Buttons</h2>
|
||||
<p class="desc">唯一的主色按钮 Primary(青色),其余都是 Ghost/Subtle。没有多种 primary —— 让每个页面最重要的那个 CTA 足够显眼。</p>
|
||||
|
||||
<div class="ds-label">Variants</div>
|
||||
<div class="example">
|
||||
<div class="example-row">
|
||||
<button class="btn btn-primary">立即开始 →</button>
|
||||
<button class="btn btn-ghost">查看文档</button>
|
||||
<button class="btn btn-subtle">跳过</button>
|
||||
<button class="btn btn-danger">解绑账号</button>
|
||||
<button class="btn btn-primary" disabled>禁用</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:20px">Sizes</div>
|
||||
<div class="example">
|
||||
<div class="example-row">
|
||||
<button class="btn btn-primary btn-lg">btn-lg · 注册</button>
|
||||
<button class="btn btn-primary">btn · 默认</button>
|
||||
<button class="btn btn-primary btn-sm">btn-sm · 复制</button>
|
||||
<button class="btn btn-ghost btn-icon" aria-label="设置">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3h0a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8v0a1.7 1.7 0 0 0 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:20px">Loading</div>
|
||||
<div class="example">
|
||||
<div class="example-row">
|
||||
<button class="btn btn-primary loading"><span class="spinner"></span><span class="label">提交中</span></button>
|
||||
<button class="btn btn-ghost loading"><span class="spinner" style="border-top-color:var(--text-0)"></span><span class="label">加载</span></button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- BADGES -->
|
||||
<section class="ds-section" id="badges">
|
||||
<h2><span class="mono-num">07</span>Badges</h2>
|
||||
<div class="example">
|
||||
<div class="example-row">
|
||||
<span class="badge">NEW</span>
|
||||
<span class="badge purple">BETA</span>
|
||||
<span class="badge amber">LIMITED</span>
|
||||
<span class="badge green">ACTIVE</span>
|
||||
<span class="badge red">EXPIRED</span>
|
||||
<span class="badge muted">DRAFT</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CHIPS -->
|
||||
<section class="ds-section" id="chips">
|
||||
<h2><span class="mono-num">08</span>Chips & Status</h2>
|
||||
<p class="desc">chip 用于在代码块周围承载"路由/参数/标签"信息,status-chip 是一个绝对定位的单像素点,用于显示账号/节点在线状态。</p>
|
||||
<div class="example">
|
||||
<div class="example-row">
|
||||
<span class="chip claude"><span class="dot"></span>claude-pool-03</span>
|
||||
<span class="chip gpt"><span class="dot"></span>gpt-plus-7</span>
|
||||
<span class="chip gemini"><span class="dot"></span>gemini-2</span>
|
||||
<span class="chip codex"><span class="dot"></span>codex-pool-01</span>
|
||||
<span class="chip"><span class="dot"></span>200 · 213ms</span>
|
||||
</div>
|
||||
<div class="example-row" style="margin-top:16px">
|
||||
<span class="pill">OpenAI SDK</span>
|
||||
<span class="pill">Anthropic SDK</span>
|
||||
<span class="pill">/v1/chat/completions</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FORMS -->
|
||||
<section class="ds-section" id="forms">
|
||||
<h2><span class="mono-num">09</span>Form Fields</h2>
|
||||
<div class="example">
|
||||
<div class="two-col" style="gap:24px">
|
||||
<div class="field">
|
||||
<label class="field-label">邮箱</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></svg>
|
||||
</span>
|
||||
<input class="input with-icon" type="email" placeholder="you@puro.im">
|
||||
</div>
|
||||
<div class="field-hint">默认 · 空态</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="field-label">API Key 名称</label>
|
||||
<input class="input ok" type="text" value="production">
|
||||
<div class="field-hint" style="color:var(--green)">✓ 可用</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="field-label">密码</label>
|
||||
<input class="input error" type="password" value="12345">
|
||||
<div class="field-error">密码至少 8 位,包含数字和字母</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="field-label">平台</label>
|
||||
<select class="input">
|
||||
<option>Claude Pro / Max</option>
|
||||
<option>ChatGPT Plus / Pro</option>
|
||||
<option>Gemini Advanced</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:18px; display:flex; gap:16px;">
|
||||
<label class="check"><input type="checkbox" checked><span class="box"></span>接受服务协议</label>
|
||||
<label class="check"><input type="checkbox"><span class="box"></span>订阅更新邮件</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CARDS -->
|
||||
<section class="ds-section" id="cards">
|
||||
<h2><span class="mono-num">10</span>Cards</h2>
|
||||
<div class="three-col">
|
||||
<div class="card">
|
||||
<div class="section-kicker" style="margin-bottom:8px">// default</div>
|
||||
<div class="text-lg fw-600" style="margin-bottom:6px">标准卡片</div>
|
||||
<div class="text-sm text-2">用于所有常规内容容器,12px 圆角 + 1px border。</div>
|
||||
</div>
|
||||
<div class="card card-interactive">
|
||||
<div class="section-kicker" style="margin-bottom:8px">// hover</div>
|
||||
<div class="text-lg fw-600" style="margin-bottom:6px">可交互卡片</div>
|
||||
<div class="text-sm text-2">hover 时向上位移 2px,border 加深。</div>
|
||||
</div>
|
||||
<div class="card-raised" style="padding:24px">
|
||||
<div class="section-kicker" style="margin-bottom:8px">// raised</div>
|
||||
<div class="text-lg fw-600" style="margin-bottom:6px">Raised 卡片</div>
|
||||
<div class="text-sm text-2">不透明背景,用于浮层/仪表盘主体。</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- TABLES -->
|
||||
<section class="ds-section" id="tables">
|
||||
<h2><span class="mono-num">11</span>Tables</h2>
|
||||
<p class="desc">主要用于请求日志、API Key 列表、计费记录。数字列一律等宽 tabular-nums。</p>
|
||||
<div class="card" style="padding:0; overflow:hidden;">
|
||||
<table class="tbl">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="mono">TIME</th>
|
||||
<th>ACCOUNT</th>
|
||||
<th class="mono">MODEL</th>
|
||||
<th class="mono">TOKENS</th>
|
||||
<th class="mono">COST</th>
|
||||
<th class="mono">STATUS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono">13:42:18</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>claude-3</span></td>
|
||||
<td class="mono">sonnet-4-5</td>
|
||||
<td class="mono tabular">2,847</td>
|
||||
<td class="mono tabular">$0.042</td>
|
||||
<td class="mono text-green">200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:42:11</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>gpt-plus-7</span></td>
|
||||
<td class="mono">gpt-5-codex</td>
|
||||
<td class="mono tabular">1,204</td>
|
||||
<td class="mono tabular">$0.018</td>
|
||||
<td class="mono text-green">200</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">13:42:03</td>
|
||||
<td><span class="provider gemini"><span class="dot"></span>gemini-2</span></td>
|
||||
<td class="mono">gemini-2.5-pro</td>
|
||||
<td class="mono tabular">4,102</td>
|
||||
<td class="mono tabular">$0.000</td>
|
||||
<td class="mono text-amber">429</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CODE -->
|
||||
<section class="ds-section" id="code">
|
||||
<h2><span class="mono-num">12</span>Code Frame</h2>
|
||||
<div class="code-frame">
|
||||
<div class="code-head">
|
||||
<div class="traffic"><span></span><span></span><span></span></div>
|
||||
<span class="mono text-xs text-3">zsh · puro ≈ 210ms</span>
|
||||
</div>
|
||||
<pre class="code-body"><div class="line"><span class="ln">1</span><span><span class="com"># OpenAI SDK · 零改动</span></span></div><div class="line"><span class="ln">2</span><span><span class="kw">from</span> openai <span class="kw">import</span> OpenAI</span></div><div class="line"><span class="ln">3</span><span> </span></div><div class="line"><span class="ln">4</span><span>client = <span class="fn">OpenAI</span>(</span></div><div class="line"><span class="ln">5</span><span> <span class="prop">base_url</span>=<span class="str">"https://ai.puro.im/v1"</span>,</span></div><div class="line"><span class="ln">6</span><span> <span class="prop">api_key</span>=<span class="str">"sk-puro-••••"</span>,</span></div><div class="line"><span class="ln">7</span><span>)</span></div></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- NAV -->
|
||||
<section class="ds-section" id="nav">
|
||||
<h2><span class="mono-num">13</span>Navigation</h2>
|
||||
<p class="desc">顶部导航 <code class="pill">.nav</code> 用于 marketing 页面,<code class="pill">.app-shell</code> 带侧边栏用于登录后的应用页面。</p>
|
||||
<div class="ds-label">Top Nav (Marketing)</div>
|
||||
<div class="example dense" style="padding:0">
|
||||
<nav style="position:relative;">
|
||||
<div class="container nav-inner" style="padding: 0 24px;">
|
||||
<span class="brand">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</span>
|
||||
<div class="nav-links">
|
||||
<a href="#" class="active">产品</a>
|
||||
<a href="#">定价</a>
|
||||
<a href="#">文档</a>
|
||||
<a href="#" class="disabled">Changelog</a>
|
||||
</div>
|
||||
<div class="nav-cta">
|
||||
<a class="btn btn-ghost">登录</a>
|
||||
<a class="btn btn-primary">开始使用</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="ds-label" style="margin-top:24px">Side Nav (App)</div>
|
||||
<div class="example dense" style="padding:0; overflow:hidden;">
|
||||
<div style="display:grid; grid-template-columns:240px 1fr; min-height:280px;">
|
||||
<div style="border-right:1px solid var(--border); padding:18px 14px; background:rgba(2,6,23,0.6);">
|
||||
<div class="brand" style="padding:6px 10px 18px">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO
|
||||
</div>
|
||||
<div class="side-group">
|
||||
<div class="side-label">Workspace</div>
|
||||
<div class="side-item active"><span class="ico">◆</span>Dashboard</div>
|
||||
<div class="side-item"><span class="ico">⌘</span>API Keys<span class="count">3</span></div>
|
||||
<div class="side-item"><span class="ico">⊡</span>订阅账号<span class="count">7</span></div>
|
||||
<div class="side-item"><span class="ico">▤</span>调用日志</div>
|
||||
<div class="side-item"><span class="ico">Ⅹ</span>账单</div>
|
||||
</div>
|
||||
<div class="side-group" style="margin-top:20px">
|
||||
<div class="side-label">Settings</div>
|
||||
<div class="side-item"><span class="ico">☰</span>账户</div>
|
||||
<div class="side-item"><span class="ico">❖</span>团队</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding:24px; display:flex; align-items:center; justify-content:center; color:var(--text-3);">
|
||||
<div class="text-sm mono">app content →</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<footer style="margin-top:80px; padding:40px 0; border-top:1px solid var(--border); font-family:var(--font-mono); font-size:12px; color:var(--text-3); display:flex; justify-content:space-between;">
|
||||
<span>PURO AI · Design System v1.0</span>
|
||||
<span>© 2026 · built with puro.css</span>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
623
docs/design-drafts/v2/Docs.html
Normal file
623
docs/design-drafts/v2/Docs.html
Normal file
@@ -0,0 +1,623 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>Docs — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
.docs-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 240px 1fr 220px;
|
||||
max-width: 1440px;
|
||||
margin: 0 auto;
|
||||
gap: 48px;
|
||||
padding: 40px 32px 80px;
|
||||
}
|
||||
/* left nav */
|
||||
.docs-nav {
|
||||
position: sticky; top: 32px; align-self: flex-start;
|
||||
font-size: 13px;
|
||||
max-height: calc(100vh - 64px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.docs-nav .brand-line {
|
||||
padding: 4px 10px 24px;
|
||||
font-size: 11px;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-3);
|
||||
letter-spacing: 0.1em;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.docs-nav .nav-search {
|
||||
margin-bottom: 18px; position: relative;
|
||||
}
|
||||
.docs-nav .nav-search input {
|
||||
width: 100%; height: 32px;
|
||||
padding: 0 10px 0 32px;
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-sm);
|
||||
color: var(--text-1); font-size: 12px;
|
||||
outline: none; font-family: inherit;
|
||||
}
|
||||
.docs-nav .nav-search::before {
|
||||
content: ""; position: absolute; left: 10px; top: 50%;
|
||||
width: 12px; height: 12px; transform: translateY(-50%);
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='7'/><path d='m20 20-3.5-3.5'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.docs-nav-section {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.docs-nav-label {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
color: var(--text-3); letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
padding: 0 10px 8px;
|
||||
}
|
||||
.docs-nav-item {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
color: var(--text-2);
|
||||
border-radius: var(--r-sm);
|
||||
transition: all .1s;
|
||||
font-size: 13px;
|
||||
}
|
||||
.docs-nav-item:hover { color: var(--text-0); background: rgba(255,255,255,0.02); }
|
||||
.docs-nav-item.active {
|
||||
color: var(--cyan);
|
||||
background: rgba(34, 211, 238, 0.06);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* content */
|
||||
.docs-body { min-width: 0; }
|
||||
.docs-crumbs {
|
||||
font-family: var(--font-mono); font-size: 11px;
|
||||
color: var(--text-3);
|
||||
margin-bottom: 14px;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.docs-crumbs .sep { color: var(--border-2); margin: 0 6px; }
|
||||
.docs-crumbs .current { color: var(--cyan); }
|
||||
|
||||
.docs-body h1 {
|
||||
font-size: 38px; font-weight: 700;
|
||||
letter-spacing: -0.02em; margin-bottom: 14px;
|
||||
}
|
||||
.docs-body .lede {
|
||||
font-size: 16px; color: var(--text-2);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 32px;
|
||||
max-width: 640px;
|
||||
}
|
||||
|
||||
.docs-body h2 {
|
||||
font-size: 22px; font-weight: 700;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 44px 0 12px;
|
||||
padding-top: 14px;
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
}
|
||||
.docs-body h2::before {
|
||||
content: "";
|
||||
width: 3px; height: 22px; background: var(--cyan);
|
||||
border-radius: 2px;
|
||||
}
|
||||
.docs-body h3 {
|
||||
font-size: 16px; font-weight: 600;
|
||||
margin: 22px 0 8px;
|
||||
}
|
||||
.docs-body p {
|
||||
color: var(--text-1);
|
||||
line-height: 1.72;
|
||||
margin-bottom: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.docs-body p code, .docs-body li code, .docs-body td code {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12.5px;
|
||||
padding: 2px 6px;
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
color: var(--cyan);
|
||||
}
|
||||
.docs-body ul { margin-bottom: 14px; padding-left: 20px; }
|
||||
.docs-body li {
|
||||
color: var(--text-1); line-height: 1.7; font-size: 14px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* callout */
|
||||
.callout {
|
||||
padding: 14px 18px;
|
||||
border: 1px solid var(--border);
|
||||
border-left: 3px solid var(--cyan);
|
||||
border-radius: 0 var(--r-md) var(--r-md) 0;
|
||||
background: rgba(34, 211, 238, 0.04);
|
||||
margin: 18px 0;
|
||||
font-size: 13px;
|
||||
display: flex; gap: 10px; align-items: flex-start;
|
||||
}
|
||||
.callout .icon { color: var(--cyan); flex-shrink: 0; margin-top: 2px; }
|
||||
.callout.amber { border-left-color: var(--amber); background: rgba(251,191,36,0.04); }
|
||||
.callout.amber .icon { color: var(--amber); }
|
||||
|
||||
/* tabs */
|
||||
.tabs {
|
||||
display: flex; gap: 2px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.tab {
|
||||
padding: 8px 14px;
|
||||
font-size: 12px;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-3);
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.tab:hover { color: var(--text-1); }
|
||||
.tab.active {
|
||||
color: var(--cyan);
|
||||
border-bottom-color: var(--cyan);
|
||||
}
|
||||
|
||||
/* code frame in docs */
|
||||
.code-panel {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
background: var(--bg-code);
|
||||
overflow: hidden;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.code-panel .panel-tabs {
|
||||
padding: 0 4px;
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex; align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.code-panel .panel-tabs .tabs-inner {
|
||||
display: flex;
|
||||
}
|
||||
.code-panel .panel-tabs .tabs-inner button {
|
||||
padding: 8px 14px;
|
||||
font-size: 11px;
|
||||
font-family: var(--font-mono);
|
||||
color: var(--text-3);
|
||||
transition: color .12s;
|
||||
}
|
||||
.code-panel .panel-tabs .tabs-inner button.active { color: var(--cyan); }
|
||||
.code-panel .copy-code {
|
||||
padding: 6px 12px;
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
font-family: var(--font-mono);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.code-panel .copy-code:hover { color: var(--cyan); background: rgba(34,211,238,0.08); }
|
||||
|
||||
.code-panel pre {
|
||||
padding: 16px 20px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
line-height: 1.75;
|
||||
color: var(--text-1);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.code-panel pre .com { color: #64748b; font-style: italic; }
|
||||
.code-panel pre .kw { color: #f472b6; }
|
||||
.code-panel pre .str { color: #86efac; }
|
||||
.code-panel pre .fn { color: #fcd34d; }
|
||||
.code-panel pre .prop { color: #93c5fd; }
|
||||
.code-panel pre .num { color: #fb923c; }
|
||||
|
||||
/* quick start grid */
|
||||
.quick-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.quick-card {
|
||||
padding: 18px 20px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
transition: all .15s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.quick-card:hover {
|
||||
border-color: var(--cyan);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
.quick-card .num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
color: var(--cyan);
|
||||
letter-spacing: 0.14em;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.quick-card h4 {
|
||||
font-size: 14px; font-weight: 600; margin-bottom: 4px;
|
||||
}
|
||||
.quick-card p {
|
||||
font-size: 12px !important; color: var(--text-3) !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* models table */
|
||||
.models-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 22px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
overflow: hidden;
|
||||
}
|
||||
.models-table th, .models-table td {
|
||||
padding: 10px 14px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-size: 13px;
|
||||
}
|
||||
.models-table th {
|
||||
background: rgba(2, 6, 23, 0.5);
|
||||
color: var(--text-3);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
font-weight: 500;
|
||||
}
|
||||
.models-table td { color: var(--text-1); }
|
||||
.models-table tr:last-child td { border-bottom: none; }
|
||||
.models-table tr:hover td { background: rgba(255,255,255,0.02); }
|
||||
.models-table .mono { font-family: var(--font-mono); font-size: 12px; }
|
||||
|
||||
/* TOC on right */
|
||||
.docs-toc {
|
||||
position: sticky; top: 32px; align-self: flex-start;
|
||||
font-size: 12px;
|
||||
}
|
||||
.docs-toc-label {
|
||||
font-family: var(--font-mono); font-size: 10px;
|
||||
color: var(--text-3); letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.docs-toc a {
|
||||
display: block;
|
||||
padding: 4px 10px;
|
||||
color: var(--text-2);
|
||||
border-left: 2px solid transparent;
|
||||
transition: all .1s;
|
||||
}
|
||||
.docs-toc a:hover { color: var(--text-0); }
|
||||
.docs-toc a.active {
|
||||
color: var(--cyan);
|
||||
border-left-color: var(--cyan);
|
||||
background: rgba(34,211,238,0.03);
|
||||
}
|
||||
.docs-toc a.sub { padding-left: 20px; font-size: 11.5px; }
|
||||
|
||||
/* top nav (reuse landing nav) */
|
||||
.docs-top {
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(2, 6, 23, 0.7);
|
||||
backdrop-filter: blur(12px);
|
||||
position: sticky; top: 0; z-index: 10;
|
||||
}
|
||||
|
||||
/* footer nav */
|
||||
.page-foot {
|
||||
margin-top: 64px;
|
||||
padding-top: 28px;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex; justify-content: space-between;
|
||||
gap: 16px;
|
||||
}
|
||||
.foot-link {
|
||||
flex: 1;
|
||||
padding: 14px 18px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
background: rgba(15, 23, 42, 0.5);
|
||||
transition: all .12s;
|
||||
}
|
||||
.foot-link:hover { border-color: var(--cyan); }
|
||||
.foot-link .dir {
|
||||
font-family: var(--font-mono); font-size: 11px;
|
||||
color: var(--text-3); margin-bottom: 4px;
|
||||
}
|
||||
.foot-link .title { font-size: 14px; font-weight: 600; color: var(--text-0); }
|
||||
.foot-link.next { text-align: right; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow soft"></div>
|
||||
|
||||
<nav class="nav docs-top">
|
||||
<div class="container nav-inner">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
<div class="nav-links">
|
||||
<a href="Landing.html">产品</a>
|
||||
<a href="Pricing.html">定价</a>
|
||||
<a href="#" class="active">文档</a>
|
||||
<a href="Design System.html">设计系统</a>
|
||||
</div>
|
||||
<div class="nav-cta">
|
||||
<a href="Dashboard.html" class="btn btn-ghost">Dashboard</a>
|
||||
<a href="Register.html" class="btn btn-primary">开始使用</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="docs-layout">
|
||||
<!-- LEFT NAV -->
|
||||
<aside class="docs-nav">
|
||||
<div class="brand-line">// docs · v1</div>
|
||||
|
||||
<div class="nav-search">
|
||||
<input placeholder="搜索文档…">
|
||||
</div>
|
||||
|
||||
<div class="docs-nav-section">
|
||||
<div class="docs-nav-label">Getting Started</div>
|
||||
<a class="docs-nav-item active" href="#">快速开始</a>
|
||||
<a class="docs-nav-item" href="#">产品概念</a>
|
||||
<a class="docs-nav-item" href="#">绑定你的订阅</a>
|
||||
<a class="docs-nav-item" href="#">创建 API Key</a>
|
||||
</div>
|
||||
|
||||
<div class="docs-nav-section">
|
||||
<div class="docs-nav-label">API Reference</div>
|
||||
<a class="docs-nav-item" href="#">Endpoints 总览</a>
|
||||
<a class="docs-nav-item" href="#">/v1/chat/completions</a>
|
||||
<a class="docs-nav-item" href="#">/v1/messages</a>
|
||||
<a class="docs-nav-item" href="#">/v1/generateContent</a>
|
||||
<a class="docs-nav-item" href="#">模型列表</a>
|
||||
<a class="docs-nav-item" href="#">错误码</a>
|
||||
</div>
|
||||
|
||||
<div class="docs-nav-section">
|
||||
<div class="docs-nav-label">Integrations</div>
|
||||
<a class="docs-nav-item" href="#">Claude Code</a>
|
||||
<a class="docs-nav-item" href="#">Cursor · Continue</a>
|
||||
<a class="docs-nav-item" href="#">Cline · Roo Code</a>
|
||||
<a class="docs-nav-item" href="#">OpenAI SDK</a>
|
||||
<a class="docs-nav-item" href="#">Anthropic SDK</a>
|
||||
</div>
|
||||
|
||||
<div class="docs-nav-section">
|
||||
<div class="docs-nav-label">Advanced</div>
|
||||
<a class="docs-nav-item" href="#">调度与 failover</a>
|
||||
<a class="docs-nav-item" href="#">速率限制与配额</a>
|
||||
<a class="docs-nav-item" href="#">流式响应</a>
|
||||
<a class="docs-nav-item" href="#">Function Calling</a>
|
||||
<a class="docs-nav-item" href="#">数据隐私</a>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- MAIN -->
|
||||
<main class="docs-body">
|
||||
<div class="docs-crumbs">
|
||||
Docs <span class="sep">/</span>
|
||||
Getting Started <span class="sep">/</span>
|
||||
<span class="current">快速开始</span>
|
||||
</div>
|
||||
|
||||
<h1>快速开始</h1>
|
||||
<p class="lede">
|
||||
PURO AI 提供一个统一的 OpenAI 兼容端点 —— 你已有的 SDK 代码只需要改 <code>base_url</code> 和 <code>api_key</code> 两行,就能用上你绑定的 Claude / ChatGPT / Gemini 订阅。整个过程通常不超过 5 分钟。
|
||||
</p>
|
||||
|
||||
<!-- quick links -->
|
||||
<div class="quick-grid">
|
||||
<div class="quick-card">
|
||||
<div class="num">STEP 01</div>
|
||||
<h4>绑定订阅</h4>
|
||||
<p>授权你的 Claude / ChatGPT 账号加入池。</p>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="num">STEP 02</div>
|
||||
<h4>创建 API Key</h4>
|
||||
<p>为每个客户端生成独立的 sk-puro-* key。</p>
|
||||
</div>
|
||||
<div class="quick-card">
|
||||
<div class="num">STEP 03</div>
|
||||
<h4>切换 base_url</h4>
|
||||
<p>改两行代码,剩下和官方 SDK 一模一样。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 id="install">① 绑定你的订阅</h2>
|
||||
<p>
|
||||
进入 <code>Dashboard → 订阅账号 → 绑定新订阅</code>,选择平台后通过 OAuth 一键授权。每个订阅都会被加入对应的"池",同一池内的请求会自动做负载均衡、限流回退和故障转移。
|
||||
</p>
|
||||
<div class="callout">
|
||||
<span class="icon">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
</span>
|
||||
<div>
|
||||
凭证通过 AES-256 加密存储在隔离的 KMS 中。我们只会用它代理你发出的请求 —— 不会进入训练数据、不会做二次分发。详见 <a href="#">数据隐私</a>。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 id="key">② 创建 API Key</h2>
|
||||
<p>
|
||||
在 <code>Dashboard → API Keys → 创建 Key</code> 生成一个 <code>sk-puro-*</code> 的 key。建议每个客户端 / 环境单独一个 key,泄漏时可以直接吊销而不影响其他场景。
|
||||
</p>
|
||||
|
||||
<h2 id="request">③ 发送第一个请求</h2>
|
||||
<p>PURO AI 同时兼容 OpenAI 和 Anthropic 的 API 格式。按你原来在用的 SDK 风格选择对应的代码示例即可:</p>
|
||||
|
||||
<div class="code-panel">
|
||||
<div class="panel-tabs">
|
||||
<div class="tabs-inner">
|
||||
<button class="active">Python</button>
|
||||
<button>Node.js</button>
|
||||
<button>cURL</button>
|
||||
<button>Anthropic SDK</button>
|
||||
</div>
|
||||
<span class="copy-code">⧉ 复制</span>
|
||||
</div>
|
||||
<pre><span class="com"># pip install openai</span>
|
||||
<span class="kw">from</span> openai <span class="kw">import</span> OpenAI
|
||||
|
||||
client = <span class="fn">OpenAI</span>(
|
||||
<span class="prop">base_url</span>=<span class="str">"https://ai.puro.im/v1"</span>,
|
||||
<span class="prop">api_key</span>=<span class="str">"sk-puro-YOUR_KEY"</span>,
|
||||
)
|
||||
|
||||
resp = client.chat.completions.<span class="fn">create</span>(
|
||||
<span class="prop">model</span>=<span class="str">"claude-sonnet-4-5"</span>, <span class="com"># 可直接写任意平台的模型名</span>
|
||||
<span class="prop">messages</span>=[
|
||||
{<span class="prop">"role"</span>: <span class="str">"user"</span>, <span class="prop">"content"</span>: <span class="str">"hi, who am I talking to?"</span>}
|
||||
],
|
||||
)
|
||||
|
||||
<span class="fn">print</span>(resp.choices[<span class="num">0</span>].message.content)
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p>返回结构完全符合 OpenAI 格式,可以无缝对接任何基于 OpenAI SDK 的应用(Cursor / Continue / Cline / Roo Code / Open WebUI …)。</p>
|
||||
|
||||
<h2 id="models">可用模型</h2>
|
||||
<p>绑定后,下列模型都可以直接用 model 字段调用 —— PURO 会根据模型自动路由到对应的订阅池。</p>
|
||||
|
||||
<table class="models-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>MODEL</th>
|
||||
<th>PROVIDER</th>
|
||||
<th>池</th>
|
||||
<th>上下文</th>
|
||||
<th>状态</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="mono text-cyan">claude-sonnet-4-5</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>Claude</span></td>
|
||||
<td>Pro / Max</td>
|
||||
<td class="mono">200k</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">claude-opus-4</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>Claude</span></td>
|
||||
<td>Max</td>
|
||||
<td class="mono">200k</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">claude-haiku-4-5</td>
|
||||
<td><span class="provider claude"><span class="dot"></span>Claude</span></td>
|
||||
<td>Pro / Max</td>
|
||||
<td class="mono">200k</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">gpt-5</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>ChatGPT</span></td>
|
||||
<td>Plus / Pro</td>
|
||||
<td class="mono">128k</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">gpt-5-codex</td>
|
||||
<td><span class="provider gpt"><span class="dot"></span>ChatGPT</span></td>
|
||||
<td>Plus / Pro</td>
|
||||
<td class="mono">128k</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">gemini-2.5-pro</td>
|
||||
<td><span class="provider gemini"><span class="dot"></span>Gemini</span></td>
|
||||
<td>Advanced</td>
|
||||
<td class="mono">1M</td>
|
||||
<td><span class="badge amber">BETA</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono text-cyan">gemini-2.5-flash</td>
|
||||
<td><span class="provider gemini"><span class="dot"></span>Gemini</span></td>
|
||||
<td>Advanced</td>
|
||||
<td class="mono">1M</td>
|
||||
<td><span class="badge green">OK</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 id="base-urls">支持的 base_url</h2>
|
||||
<p>每种格式都提供独立的 base_url —— 如果你在用原生 Anthropic / Google SDK,请选择对应格式以获得最完整的字段兼容:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>https://ai.puro.im/v1</code> — OpenAI 兼容格式(推荐,覆盖 95% 场景)</li>
|
||||
<li><code>https://ai.puro.im/anthropic</code> — Anthropic Messages 格式(原生 Claude SDK)</li>
|
||||
<li><code>https://ai.puro.im/google</code> — Google GenAI 格式(原生 Gemini SDK)</li>
|
||||
</ul>
|
||||
|
||||
<div class="callout amber">
|
||||
<span class="icon">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><path d="M12 9v4M12 17h.01"/></svg>
|
||||
</span>
|
||||
<div>
|
||||
一个 <code>sk-puro-*</code> 可以同时用于三种 base_url —— 鉴权和计费是统一的,你不需要为不同 SDK 维护多个 key。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 id="next">下一步</h2>
|
||||
<p>把 PURO 接入到你常用的工具:</p>
|
||||
<ul>
|
||||
<li><a href="#">在 <b>Claude Code</b> 中使用 PURO</a> — <code>ANTHROPIC_BASE_URL</code> 环境变量一行搞定</li>
|
||||
<li><a href="#">在 <b>Cursor</b> 中使用 PURO</a> — 自定义模型 + 自定义 base_url</li>
|
||||
<li><a href="#">在 <b>Cline / Roo Code</b> 中使用 PURO</a> — 选择 "OpenAI compatible"</li>
|
||||
<li><a href="#">在 <b>Continue</b> 中使用 PURO</a> — config.yaml 添加 provider</li>
|
||||
</ul>
|
||||
|
||||
<!-- pager -->
|
||||
<div class="page-foot">
|
||||
<a class="foot-link" href="#">
|
||||
<div class="dir">← 上一页</div>
|
||||
<div class="title">产品概念</div>
|
||||
</a>
|
||||
<a class="foot-link next" href="#">
|
||||
<div class="dir">下一页 →</div>
|
||||
<div class="title">绑定你的订阅</div>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- RIGHT TOC -->
|
||||
<aside class="docs-toc">
|
||||
<div class="docs-toc-label">On this page</div>
|
||||
<a href="#install" class="active">① 绑定订阅</a>
|
||||
<a href="#key">② 创建 API Key</a>
|
||||
<a href="#request">③ 第一个请求</a>
|
||||
<a href="#models">可用模型</a>
|
||||
<a href="#base-urls">支持的 base_url</a>
|
||||
<a href="#next">下一步</a>
|
||||
</aside>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
222
docs/design-drafts/v2/HANDOFF.md
Normal file
222
docs/design-drafts/v2/HANDOFF.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# PURO AI · 设计交付文档
|
||||
|
||||
> 把已有的 Claude / ChatGPT / Codex / Gemini 订阅聚合成统一 API · 让"已经付过钱的订阅"真正可编程
|
||||
|
||||
本文档面向**接手实现这套设计的工程团队 / Coding Agent**,说明每个 HTML 文件的用途、数据契约、交互逻辑,以及与后端的对接点。
|
||||
|
||||
---
|
||||
|
||||
## 0. 文件清单
|
||||
|
||||
| 文件 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| `Landing.html` | 营销首页 | 未登录入口 · Hero / 模型墙 / 功能 / 代码示例 / Dashboard 预览 / Pricing / FAQ |
|
||||
| `Pricing.html` | 定价页 | 完整定价 + 成本估算器 + 工具兼容墙 + 10 条 FAQ |
|
||||
| `Docs.html` | 文档 | 快速开始 / 模型列表 / API 参考 / 各客户端配置 |
|
||||
| `Login.html` | 登录 | 左侧叙事 + 右侧表单 · 支持 LinuxDO OAuth |
|
||||
| `Register.html` | 注册 | 带密码强度 · 下一步引导 · 送 $5 |
|
||||
| `Binding.html` | 订阅绑定 | 核心差异化流程 · OAuth 接入 Claude Pro / ChatGPT Plus |
|
||||
| `Dashboard.html` | 控制台首页 | 余额 / 用量图表 / 近期请求 / 订阅池状态 |
|
||||
| `API Keys.html` | Key 管理 | 创建 / 吊销 / 预算限制 / 模型白名单 |
|
||||
| `Design System.html` | 设计系统 | 色板 / 字号 / 组件索引 |
|
||||
| `puro.css` | 全站样式 | 所有页面共用的 tokens + primitives(.btn / .pill / .tag / .input 等) |
|
||||
|
||||
**所有页面必须首行 meta:** `<meta name="color-scheme" content="dark">` —— 防止浏览器 auto-darken 覆盖 cyan 按钮。
|
||||
|
||||
---
|
||||
|
||||
## 1. 设计 Tokens(见 `puro.css`)
|
||||
|
||||
```
|
||||
--bg-0: #0a0e1a 页面底
|
||||
--bg-1: #0f172a raised
|
||||
--bg-2: #111827 card alt
|
||||
--bg-code: #020617 代码面板
|
||||
|
||||
--border: #1e293b
|
||||
--border-2:#334155
|
||||
--border-3:#475569
|
||||
|
||||
--text-0: #f8fafc 主
|
||||
--text-1: #cbd5e1 次
|
||||
--text-2: #94a3b8 说明
|
||||
--text-3: #64748b 弱
|
||||
|
||||
--cyan: #22d3ee 主强调(primary btn / 链接 / 数据点)
|
||||
--cyan-2: #67e8f9 hover
|
||||
--purple: #a855f7 次强调 / 装饰
|
||||
--amber: #fbbf24 提醒 / 限时标签
|
||||
--green: #34d399 成功 / 在线
|
||||
--red: #f87171 错误 / 危险
|
||||
```
|
||||
|
||||
字体:`Inter`(正文)+ `JetBrains Mono`(所有数值 / 代码 / 元信息)。
|
||||
圆角:`--r-sm 6px / --r-md 8px / --r-lg 12px / --r-xl 16px`。
|
||||
|
||||
---
|
||||
|
||||
## 2. 页面 → 后端契约
|
||||
|
||||
### 2.1 Landing · 无后端依赖(纯营销)
|
||||
锚点:`#pricing` / `#faq` / `#features` / `#code` / `#dashboard`。注册 CTA → `Register.html`。
|
||||
|
||||
### 2.2 Register
|
||||
提交表单需要 **后端返回**:
|
||||
```
|
||||
POST /auth/register
|
||||
{ email, password, linuxdo_token? }
|
||||
→ 200 { user_id, jwt, balance_credits: 5.00 /* $5 注册赠送 */ }
|
||||
```
|
||||
前端验证规则:
|
||||
- 邮箱格式 `^[^\s@]+@[^\s@]+\.[^\s@]+$`
|
||||
- 密码强度 ≥ 2(长度 ≥ 8 + 字母大小写混合即可通过)
|
||||
- 两次密码一致 + 勾选 terms
|
||||
成功后跳 `Binding.html`。
|
||||
|
||||
### 2.3 Login
|
||||
```
|
||||
POST /auth/login
|
||||
{ email, password } 或 { linuxdo_oauth_code }
|
||||
→ 200 { jwt, user, has_subscriptions: boolean }
|
||||
```
|
||||
若 `has_subscriptions === false`,引导去 `Binding.html`;否则去 `Dashboard.html`。
|
||||
|
||||
### 2.4 Binding(核心差异化)
|
||||
OAuth 流程,每个平台:
|
||||
```
|
||||
POST /bindings/oauth/start
|
||||
{ provider: 'claude' | 'chatgpt' | 'codex' | 'gemini' }
|
||||
→ { auth_url, state }
|
||||
```
|
||||
前端打开 `auth_url` 新窗口;OAuth 回调:
|
||||
```
|
||||
GET /bindings/oauth/callback?code=...&state=...
|
||||
→ 302 → /binding/success?provider=claude&account_id=...
|
||||
```
|
||||
绑定列表:
|
||||
```
|
||||
GET /bindings
|
||||
→ [ { id, provider, account_email, plan, status: 'healthy'|'cooling'|'error', quota_remaining_pct, bound_at } ]
|
||||
DELETE /bindings/:id 解绑
|
||||
POST /bindings/:id/test 发一条测试请求验证凭证有效
|
||||
```
|
||||
|
||||
### 2.5 Dashboard
|
||||
```
|
||||
GET /me/overview
|
||||
→ {
|
||||
balance: { credits: 45.23, bonus_credits: 12.00, expires_at: null },
|
||||
usage_today: { requests: 1842, tokens_in: 2.1e6, tokens_out: 4.8e5, cost: 1.23 },
|
||||
usage_30d: [{ date, cost, requests }, ...],
|
||||
recent_requests: [{ ts, model, route_to, status, latency_ms, tokens, cost }, ...],
|
||||
pool_status: [{ provider, accounts: [{ status, quota_pct }] }]
|
||||
}
|
||||
```
|
||||
图表用 `usage_30d` 绘制折线(cyan 主线,purple 副线)。
|
||||
|
||||
### 2.6 API Keys
|
||||
```
|
||||
GET /api-keys
|
||||
POST /api-keys { name, models?: string[], monthly_budget?: number }
|
||||
DELETE /api-keys/:id
|
||||
```
|
||||
Key 前缀 `sk-puro-` + 32 字符。创建后仅显示一次完整值,之后只保留前 8 位。
|
||||
|
||||
### 2.7 Pricing
|
||||
纯静态展示。充值 CTA 统一走:
|
||||
```
|
||||
POST /billing/topup
|
||||
{ amount_usd, payment_method: 'alipay'|'wechat'|'usdt'|'stripe' }
|
||||
→ { payment_url / qr_code, order_id }
|
||||
```
|
||||
阶梯赠送在前端计算并展示(见 `Pricing.html` 底部 script),**后端下单时再次校验**:
|
||||
```
|
||||
阶梯 = amount >= 500 ? 120%
|
||||
: amount >= 200 ? 110%
|
||||
: amount >= 99 ? 100%
|
||||
: amount >= 50 ? 70%
|
||||
: amount >= 30 ? 50%
|
||||
: amount >= 20 ? 35%
|
||||
: 21%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 统一 API Gateway(产品核心)
|
||||
|
||||
所有用户集成的终点:
|
||||
```
|
||||
https://api.puro.im/v1/chat/completions # OpenAI 兼容
|
||||
https://api.puro.im/v1/messages # Anthropic 兼容
|
||||
https://api.puro.im/v1beta/models/:m:generateContent # Gemini 兼容
|
||||
```
|
||||
Header: `Authorization: Bearer sk-puro-xxx`
|
||||
|
||||
**调度逻辑(文档化在 `Docs.html`):**
|
||||
1. 根据模型解析目标 provider
|
||||
2. 从该 provider 的订阅池挑一个 `status=healthy` 的账号(权重:剩余配额 × 响应时延倒数)
|
||||
3. 若 429 / 限流,标记 `cooling` 60-300s,failover 到下一个
|
||||
4. 若该 provider 所有订阅都 cooling 且用户余额 > 0,fallback 到官方 API
|
||||
5. 所有请求写入 `request_logs`(用户可见,保留周期按套餐)
|
||||
|
||||
---
|
||||
|
||||
## 4. 套餐与限制
|
||||
|
||||
| | Starter $9.9 | Pro $29.9 ⭐ | Scale $99 | Custom |
|
||||
|---|---|---|---|---|
|
||||
| 赠送 | +21% | +50% | +100% | 阶梯 |
|
||||
| API Keys | 1 | 3 | 10 | 按 Pro |
|
||||
| RPM | 60 | 120 | 300 | 按 Pro |
|
||||
| 日志保留 | 7d | 30d | 90d | 按 Pro |
|
||||
| 自带订阅 | ❌ | ∞ | ∞ | ✅ |
|
||||
| 多账号 failover | — | ✅ | ✅ + 优先级 | ✅ |
|
||||
|
||||
**Enterprise** 走 Sales 线:私有化 / SLA / Invoice,不在普通订单流里。
|
||||
|
||||
---
|
||||
|
||||
## 5. 交互细节(容易漏)
|
||||
|
||||
- **余额不足**:Gateway 返回 `402 Payment Required`,Dashboard 顶部红色 banner + 发邮件(80% / 95% 两档预警)
|
||||
- **订阅 cooling** 时 Dashboard 订阅卡片上角 amber 闪烁点
|
||||
- **API Key 创建**:弹窗必须强制用户复制一次,关闭弹窗后永远看不到完整值
|
||||
- **密码强度**:评分 0-4,label 对应 `— / 弱 / 中 / 强 / 极强`,颜色 `border / red / amber / cyan / green`
|
||||
- **登录成功**:按钮变 green 显示 `✓ 登录成功`,800ms 后 window.location 跳转
|
||||
- **LinuxDO OAuth** 是 PURO 的目标用户群(开发者社区),作为次要登录按钮展示
|
||||
|
||||
---
|
||||
|
||||
## 6. 响应式断点
|
||||
|
||||
- `<= 900px`:Login/Register 的 split 变单列,narrative 折叠
|
||||
- `<= 820px`:Pricing grid 从 4 列 → 1 列
|
||||
- `<= 820px`:Landing pricing-grid-landing 从 3 列 → 1 列
|
||||
- `<= 960px`:Pricing 的 calculator 从 2 列 → 1 列
|
||||
|
||||
---
|
||||
|
||||
## 7. 后端技术建议(非设计范畴,仅供参考)
|
||||
|
||||
- **Gateway 层**:Go / Rust 写高吞吐代理,HTTP/2 + streaming,每 provider 起独立 worker pool
|
||||
- **调度**:Redis ZSET 存每个订阅的健康分 + cooling 过期时间
|
||||
- **日志**:写 ClickHouse(列存 + 按日分区),Dashboard 直接查
|
||||
- **凭证加密**:每个订阅凭证用 AES-256-GCM 加密,key 放 KMS;解密只在 gateway worker 内存里,绝不落日志
|
||||
- **计费**:Pulsar / Redis Streams 实时扣费,最终一致性,异步对账
|
||||
|
||||
---
|
||||
|
||||
## 8. 已知 TODO(设计层面暂不处理,开发阶段补)
|
||||
|
||||
- [ ] Dashboard 真实图表联动(目前是静态 SVG)
|
||||
- [ ] Binding OAuth 回调 loading / 成功动效
|
||||
- [ ] 邮件模板(注册验证 / 余额预警 / 充值成功)
|
||||
- [ ] 忘记密码流程
|
||||
- [ ] 团队 / 多人协作 UI(Enterprise 档需要)
|
||||
- [ ] i18n:当前仅中文,英文版待出
|
||||
|
||||
---
|
||||
|
||||
生成时间:2026-04 · 设计稿版本:v1
|
||||
|
||||
联系 Sam / 产品负责人确认实现细节。
|
||||
1423
docs/design-drafts/v2/Landing.html
Normal file
1423
docs/design-drafts/v2/Landing.html
Normal file
File diff suppressed because it is too large
Load Diff
351
docs/design-drafts/v2/Login.html
Normal file
351
docs/design-drafts/v2/Login.html
Normal file
@@ -0,0 +1,351 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>登录 — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
body { min-height: 100vh; overflow-x: hidden; }
|
||||
|
||||
.split {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* ============ LEFT (NARRATIVE) ============ */
|
||||
.narrative {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 48px 56px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
border-right: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.04), transparent 60%), rgba(15, 23, 42, 0.3);
|
||||
}
|
||||
.narrative::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(600px 400px at 20% 20%, rgba(34,211,238,0.08), transparent 60%),
|
||||
radial-gradient(500px 400px at 90% 80%, rgba(168,85,247,0.06), transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.narrative-inner { position: relative; display: flex; flex-direction: column; gap: 28px; z-index: 1; }
|
||||
|
||||
.brand-top {
|
||||
display: inline-flex; align-items: center; gap: 10px;
|
||||
font-weight: 700; font-size: 15px; letter-spacing: -0.01em; color: var(--text-0);
|
||||
}
|
||||
.brand-top svg { width: 22px; height: 22px; color: var(--cyan); }
|
||||
|
||||
.n-kicker { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; color: var(--cyan); }
|
||||
.n-headline {
|
||||
font-size: 52px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05;
|
||||
display: flex; align-items: baseline; flex-wrap: wrap; gap: 14px;
|
||||
}
|
||||
.n-headline .amber { color: var(--amber); }
|
||||
.n-headline .cyan { color: var(--cyan); }
|
||||
.n-headline .arrow { font-size: 38px; color: var(--text-3); font-weight: 400; }
|
||||
.n-sub { color: var(--text-2); font-size: 15px; line-height: 1.75; max-width: 440px; }
|
||||
.n-sub .line { display: block; }
|
||||
.n-sub .puro { color: var(--text-0); font-weight: 600; }
|
||||
|
||||
.route-demo {
|
||||
margin-top: 10px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-md);
|
||||
padding: 18px 22px;
|
||||
max-width: 440px;
|
||||
}
|
||||
.route-demo .row { display: flex; gap: 20px; padding: 4px 0; align-items: center; }
|
||||
.route-demo .k { color: var(--text-3); min-width: 70px; }
|
||||
.route-demo .v { color: var(--text-0); }
|
||||
.route-demo .pill-inline { padding: 2px 8px; border-radius: 4px; background: rgba(34,211,238,0.08); border: 1px solid rgba(34,211,238,0.25); color: var(--cyan); }
|
||||
.route-demo .pill-inline.amber { background: rgba(251,191,36,0.08); border-color: rgba(251,191,36,0.25); color: var(--amber); }
|
||||
.route-demo .pill-inline.green { background: rgba(52,211,153,0.08); border-color: rgba(52,211,153,0.25); color: var(--green); }
|
||||
.route-demo .dot-g { width: 6px; height: 6px; border-radius: 50%; background: var(--green); display: inline-block; margin-right: 6px; box-shadow: 0 0 0 3px rgba(52,211,153,0.15); }
|
||||
|
||||
.n-bottom {
|
||||
position: relative; z-index: 1;
|
||||
font-family: var(--font-mono); font-size: 11px; color: var(--text-3);
|
||||
display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
|
||||
}
|
||||
.n-bottom .sep { color: var(--border-2); }
|
||||
.n-bottom .live { color: var(--green); display: inline-flex; align-items: center; gap: 6px; }
|
||||
.n-bottom .live .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
|
||||
|
||||
/* ============ RIGHT (FORM) ============ */
|
||||
.form-side {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 48px 56px;
|
||||
position: relative;
|
||||
}
|
||||
.back-home {
|
||||
position: absolute; top: 32px; right: 32px;
|
||||
font-family: var(--font-mono); font-size: 12px; color: var(--text-3);
|
||||
display: inline-flex; align-items: center; gap: 6px;
|
||||
}
|
||||
.back-home:hover { color: var(--text-0); }
|
||||
|
||||
.form-card { width: 100%; max-width: 400px; }
|
||||
.form-card h1 { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 6px; }
|
||||
.form-card .sub { color: var(--text-2); font-size: 14px; margin-bottom: 32px; }
|
||||
|
||||
.field { margin-bottom: 16px; }
|
||||
.field label { display: block; font-size: 12px; color: var(--text-2); font-weight: 500; margin-bottom: 8px; letter-spacing: 0.01em; }
|
||||
.input-wrap {
|
||||
position: relative;
|
||||
display: flex; align-items: center;
|
||||
background: rgba(2, 6, 23, 0.4);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: var(--r-md);
|
||||
transition: all .15s;
|
||||
}
|
||||
.input-wrap:focus-within { border-color: var(--cyan); background: rgba(34, 211, 238, 0.04); box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.1); }
|
||||
.input-wrap .icon { padding: 0 12px; color: var(--text-3); flex-shrink: 0; display: flex; }
|
||||
.input-wrap input {
|
||||
flex: 1; background: none; border: none; outline: none;
|
||||
font-family: inherit; color: var(--text-0); font-size: 14px;
|
||||
padding: 12px 14px 12px 0;
|
||||
}
|
||||
.input-wrap input::placeholder { color: var(--text-3); }
|
||||
.eye { display: flex; padding: 10px 12px; color: var(--text-3); background: none; border: none; cursor: pointer; }
|
||||
.eye:hover { color: var(--text-1); }
|
||||
|
||||
.field-meta { display: flex; justify-content: space-between; align-items: center; margin-top: 12px; }
|
||||
.check { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-2); cursor: pointer; user-select: none; }
|
||||
.check input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.check .box { width: 14px; height: 14px; border-radius: 3px; border: 1px solid var(--border-2); background: rgba(2, 6, 23, 0.4); position: relative; flex-shrink: 0; }
|
||||
.check input:checked ~ .box { background: var(--cyan); border-color: var(--cyan); }
|
||||
.check input:checked ~ .box::after {
|
||||
content: ""; position: absolute; left: 4px; top: 1px;
|
||||
width: 4px; height: 8px; border: solid #042f2e; border-width: 0 2px 2px 0;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.forgot { font-size: 13px; color: var(--text-3); }
|
||||
.forgot:hover { color: var(--cyan); }
|
||||
|
||||
/* Submit button — explicit styles to ensure rendering */
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 14px 20px !important;
|
||||
font-size: 14px !important;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
background: var(--cyan) !important;
|
||||
color: #042f2e !important;
|
||||
border: 1px solid var(--cyan) !important;
|
||||
font-weight: 600 !important;
|
||||
z-index: 2;
|
||||
}
|
||||
.submit-btn:hover { background: var(--cyan-2) !important; }
|
||||
.submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.submit-btn .spinner { width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.2); border-top-color: #042f2e; border-radius: 50%; animation: spin .7s linear infinite; display: none; }
|
||||
.submit-btn.loading .spinner { display: inline-block; }
|
||||
.submit-btn.loading .label { opacity: 0.5; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.ghost-btn { width: 100%; padding: 14px 20px !important; font-size: 14px !important; font-weight: 500 !important; }
|
||||
|
||||
.divider {
|
||||
display: flex; align-items: center; gap: 14px;
|
||||
margin: 24px 0;
|
||||
color: var(--text-3); font-size: 11px;
|
||||
font-family: var(--font-mono); letter-spacing: 0.15em;
|
||||
}
|
||||
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
|
||||
|
||||
.linuxdo-ico {
|
||||
width: 18px; height: 18px; border-radius: 3px;
|
||||
background: linear-gradient(135deg, #f0a030, #f05050);
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
font-size: 10px; font-weight: 800; color: white;
|
||||
}
|
||||
|
||||
.foot { margin-top: 24px; text-align: center; font-size: 13px; color: var(--text-2); }
|
||||
.foot a { color: var(--cyan); font-weight: 500; }
|
||||
.foot a:hover { color: var(--cyan-2); }
|
||||
|
||||
.legal { margin-top: 20px; text-align: center; font-size: 11px; color: var(--text-3); font-family: var(--font-mono); }
|
||||
.legal a { color: var(--text-2); border-bottom: 1px dashed var(--border-2); }
|
||||
.legal a:hover { color: var(--cyan); border-color: var(--cyan); }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.split { grid-template-columns: 1fr; }
|
||||
.narrative { padding: 32px 28px; border-right: none; border-bottom: 1px solid var(--border); }
|
||||
.n-headline { font-size: 36px; }
|
||||
.route-demo, .n-bottom { display: none; }
|
||||
.form-side { padding: 40px 28px; }
|
||||
.back-home { top: 20px; right: 20px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<div class="split">
|
||||
<!-- LEFT: NARRATIVE -->
|
||||
<section class="narrative">
|
||||
<div class="narrative-inner">
|
||||
<a href="Landing.html" class="brand-top">
|
||||
<svg viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<div class="n-kicker">// 你的订阅,已经付过钱了</div>
|
||||
<h1 class="n-headline" style="margin-top: 12px;">
|
||||
<span class="amber">N</span> 个订阅
|
||||
<span class="arrow">→</span>
|
||||
<span class="cyan">1</span> 个 key
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="n-sub">
|
||||
<span class="line">省去切换账号的繁琐,</span>
|
||||
<span class="line">省去为多个高昂订阅重复买单。</span>
|
||||
<span class="line" style="margin-top: 8px;"><span class="puro">PURO</span>(纯粹)—— 让 AI 调用回归本质。</span>
|
||||
</div>
|
||||
|
||||
<div class="route-demo">
|
||||
<div class="row"><span class="k">POST</span><span class="v">/v1/chat/completions</span></div>
|
||||
<div class="row"><span class="k">model</span><span class="pill-inline">claude-sonnet-4-5</span></div>
|
||||
<div class="row"><span class="k">route →</span><span class="pill-inline amber">claude-pool-03</span></div>
|
||||
<div class="row"><span class="k">status</span><span><span class="dot-g"></span><span style="color:var(--green)">200</span><span style="color:var(--text-3); margin:0 6px;">·</span>213ms<span style="color:var(--text-3); margin:0 6px;">·</span>42 tok</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-bottom">
|
||||
<span>Claude</span><span class="sep">·</span>
|
||||
<span>ChatGPT</span><span class="sep">·</span>
|
||||
<span>Codex</span><span class="sep">·</span>
|
||||
<span>Gemini</span>
|
||||
<span class="sep">|</span>
|
||||
<span class="live"><span class="dot"></span>ai.puro.im · operational</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- RIGHT: FORM -->
|
||||
<section class="form-side">
|
||||
<a href="Landing.html" class="back-home">← 返回首页</a>
|
||||
|
||||
<form class="form-card" id="login-form" autocomplete="off" novalidate>
|
||||
<h1>登录</h1>
|
||||
<p class="sub">用你的 PURO AI 账户继续</p>
|
||||
|
||||
<div class="field">
|
||||
<label for="email">邮箱</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
||||
<path d="M3 7l9 6 9-6"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="email" id="email" name="email" placeholder="you@puro.im" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password">密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password" name="password" placeholder="••••••••" required>
|
||||
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
||||
<svg id="eye-open" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
<svg id="eye-closed" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" style="display:none;">
|
||||
<path d="M9.88 9.88A3 3 0 0 0 14.12 14.12M10.73 5.08A10.94 10.94 0 0 1 12 5c6.5 0 10 7 10 7a13.16 13.16 0 0 1-1.67 2.68M6.61 6.61A13.53 13.53 0 0 0 2 12s3.5 7 10 7a9.77 9.77 0 0 0 5.39-1.61M2 2l20 20"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="field-meta">
|
||||
<label class="check">
|
||||
<input type="checkbox" checked>
|
||||
<span class="box"></span>
|
||||
记住我
|
||||
</label>
|
||||
<a href="#" class="forgot">忘记密码?</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary submit-btn" id="submit-btn">
|
||||
<span class="spinner"></span>
|
||||
<span class="label">登录 →</span>
|
||||
</button>
|
||||
|
||||
<div class="divider">OR</div>
|
||||
|
||||
<button type="button" class="btn btn-ghost ghost-btn">
|
||||
<span class="linuxdo-ico">L</span>
|
||||
使用 LinuxDO 登录
|
||||
</button>
|
||||
|
||||
<div class="foot">
|
||||
没有账户?<a href="Register.html">注册</a>
|
||||
</div>
|
||||
|
||||
<div class="legal">
|
||||
登录即表示你同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const pw = document.getElementById('password');
|
||||
const toggle = document.getElementById('toggle-pw');
|
||||
const eyeOpen = document.getElementById('eye-open');
|
||||
const eyeClosed = document.getElementById('eye-closed');
|
||||
toggle.addEventListener('click', () => {
|
||||
const show = pw.type === 'password';
|
||||
pw.type = show ? 'text' : 'password';
|
||||
eyeOpen.style.display = show ? 'none' : 'block';
|
||||
eyeClosed.style.display = show ? 'block' : 'none';
|
||||
});
|
||||
|
||||
const form = document.getElementById('login-form');
|
||||
const btn = document.getElementById('submit-btn');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (!form.email.value || !form.password.value) return;
|
||||
btn.classList.add('loading');
|
||||
btn.disabled = true;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('loading');
|
||||
btn.disabled = false;
|
||||
btn.querySelector('.label').textContent = '✓ 登录成功';
|
||||
btn.style.background = 'var(--green)';
|
||||
setTimeout(() => { window.location.href = 'Dashboard.html'; }, 800);
|
||||
}, 1000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
472
docs/design-drafts/v2/Pricing.html
Normal file
472
docs/design-drafts/v2/Pricing.html
Normal file
@@ -0,0 +1,472 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>Pricing — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
.hero { max-width: 1180px; margin: 0 auto; padding: 80px 32px 40px; text-align: center; }
|
||||
.hero h1 { font-size: 54px; font-weight: 800; letter-spacing: -0.03em; margin-bottom: 18px; }
|
||||
.hero h1 .accent { color: var(--cyan); }
|
||||
.hero .sub { color: var(--text-2); font-size: 17px; max-width: 620px; margin: 0 auto 14px; line-height: 1.6; }
|
||||
.hero .underline { display: inline-flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 12px; color: var(--text-3); padding: 6px 14px; background: rgba(2, 6, 23, 0.5); border: 1px solid var(--border); border-radius: 100px; }
|
||||
.hero .underline .dot { width: 6px; height: 6px; background: var(--green); border-radius: 50%; box-shadow: 0 0 0 3px rgba(52,211,153,0.15); }
|
||||
|
||||
.pricing-wrap { max-width: 1180px; margin: 0 auto; padding: 20px 32px 40px; }
|
||||
.pricing-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
|
||||
.tier { position: relative; border: 1px solid var(--border); border-radius: var(--r-xl); background: rgba(15, 23, 42, 0.5); padding: 28px 24px; display: flex; flex-direction: column; transition: all .2s; }
|
||||
.tier:hover { border-color: var(--border-2); transform: translateY(-3px); }
|
||||
.tier.popular { border-color: rgba(34, 211, 238, 0.4); background: radial-gradient(500px 300px at 50% 0%, rgba(34,211,238,0.08), transparent 60%), rgba(15, 23, 42, 0.7); box-shadow: 0 0 0 1px rgba(34,211,238,0.15), 0 20px 40px -20px rgba(34,211,238,0.2); transform: translateY(-6px); }
|
||||
.tier.popular:hover { transform: translateY(-9px); }
|
||||
.tier .flag { position: absolute; top: -11px; left: 50%; transform: translateX(-50%); font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em; padding: 4px 12px; border-radius: 100px; background: var(--cyan); color: #042f2e; font-weight: 700; }
|
||||
.tier .flag.amber { background: var(--amber); color: #422006; }
|
||||
.tier .flag.muted { background: rgba(100, 116, 139, 0.2); color: var(--text-2); border: 1px solid var(--border); }
|
||||
|
||||
.tier-name { font-size: 13px; font-family: var(--font-mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-3); margin-bottom: 8px; }
|
||||
.tier-headline { font-size: 16px; font-weight: 600; color: var(--text-0); margin-bottom: 22px; line-height: 1.35; min-height: 44px; }
|
||||
|
||||
.price-row { display: flex; align-items: baseline; gap: 4px; margin-bottom: 4px; }
|
||||
.price { font-family: var(--font-mono); font-size: 42px; font-weight: 800; letter-spacing: -0.03em; color: var(--text-0); }
|
||||
.tier.popular .price { color: var(--cyan); }
|
||||
.price .curr { font-size: 18px; font-weight: 600; color: var(--text-3); margin-right: 2px; vertical-align: super; }
|
||||
.credit-line { font-family: var(--font-mono); font-size: 12px; color: var(--cyan); margin-bottom: 14px; }
|
||||
.credit-line .arrow { margin: 0 6px; color: var(--text-3); }
|
||||
.credit-line .bonus { padding: 2px 8px; background: rgba(34,211,238,0.08); border: 1px solid rgba(34,211,238,0.25); border-radius: 4px; font-weight: 600; margin-left: 6px; }
|
||||
.discount-tag { display: inline-block; font-family: var(--font-mono); font-size: 11px; color: var(--amber); background: rgba(251,191,36,0.08); border: 1px solid rgba(251,191,36,0.25); border-radius: 4px; padding: 3px 8px; margin-bottom: 18px; }
|
||||
|
||||
.tier hr { border: none; border-top: 1px dashed var(--border); margin: 4px 0 18px; }
|
||||
|
||||
.feat { display: flex; gap: 10px; align-items: flex-start; font-size: 13px; color: var(--text-1); padding: 4px 0; line-height: 1.55; }
|
||||
.feat .tick { color: var(--cyan); flex-shrink: 0; margin-top: 2px; }
|
||||
.feat.muted { color: var(--text-3); }
|
||||
.feat.muted .tick { color: var(--text-3); }
|
||||
.feat b { color: var(--text-0); font-weight: 600; }
|
||||
.feats { display: flex; flex-direction: column; gap: 2px; margin-bottom: 24px; flex: 1; }
|
||||
.tier-cta { width: 100%; justify-content: center; }
|
||||
|
||||
.custom-row { margin-top: 12px; display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
|
||||
.custom-card { padding: 24px; border: 1px solid var(--border); border-radius: var(--r-xl); background: linear-gradient(135deg, rgba(168,85,247,0.05), transparent 50%), rgba(15, 23, 42, 0.4); display: flex; align-items: center; gap: 22px; }
|
||||
.custom-card .icon { width: 48px; height: 48px; border-radius: 10px; background: rgba(34,211,238,0.1); color: var(--cyan); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
||||
.custom-card .icon.purple { background: rgba(168,85,247,0.1); color: var(--purple); }
|
||||
.custom-card h3 { font-size: 16px; font-weight: 600; margin-bottom: 4px; }
|
||||
.custom-card p { font-size: 13px; color: var(--text-2); line-height: 1.5; }
|
||||
.custom-card .btn { margin-left: auto; flex-shrink: 0; }
|
||||
|
||||
.works { max-width: 1180px; margin: 0 auto; padding: 80px 32px 40px; }
|
||||
.section-head { text-align: center; margin-bottom: 32px; }
|
||||
.section-head .kicker { font-family: var(--font-mono); font-size: 12px; color: var(--cyan); letter-spacing: 0.14em; margin-bottom: 10px; }
|
||||
.section-head h2 { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 8px; }
|
||||
.section-head p { color: var(--text-2); font-size: 15px; max-width: 560px; margin: 0 auto; line-height: 1.55; }
|
||||
.tools-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; }
|
||||
.tool { padding: 18px 14px; border: 1px solid var(--border); border-radius: var(--r-md); background: rgba(15, 23, 42, 0.4); text-align: center; transition: all .15s; }
|
||||
.tool:hover { border-color: var(--border-2); background: rgba(15, 23, 42, 0.7); }
|
||||
.tool .logo { width: 28px; height: 28px; margin: 0 auto 10px; display: flex; align-items: center; justify-content: center; color: var(--text-1); }
|
||||
.tool .name { font-size: 12px; font-weight: 500; color: var(--text-1); }
|
||||
.tool .tag { font-size: 10px; color: var(--text-3); margin-top: 2px; font-family: var(--font-mono); }
|
||||
|
||||
.calc-section { max-width: 1180px; margin: 0 auto; padding: 40px 32px; }
|
||||
.calc { border: 1px solid var(--border); border-radius: var(--r-xl); background: radial-gradient(600px 300px at 0% 0%, rgba(34,211,238,0.06), transparent 60%), radial-gradient(600px 300px at 100% 100%, rgba(168,85,247,0.06), transparent 60%), rgba(15, 23, 42, 0.4); padding: 32px 36px; display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center; }
|
||||
.calc-left h3 { font-size: 22px; font-weight: 700; letter-spacing: -0.01em; margin-bottom: 8px; }
|
||||
.calc-left .sub { color: var(--text-2); font-size: 14px; line-height: 1.55; margin-bottom: 22px; }
|
||||
.calc-controls { display: flex; flex-direction: column; gap: 14px; }
|
||||
.slider-row .s-top { display: flex; justify-content: space-between; font-size: 13px; margin-bottom: 6px; align-items: baseline; }
|
||||
.slider-row .s-top .val { font-family: var(--font-mono); font-weight: 700; color: var(--cyan); }
|
||||
.slider-row input[type=range] { -webkit-appearance: none; width: 100%; height: 4px; background: var(--border); border-radius: 2px; outline: none; }
|
||||
.slider-row input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: var(--cyan); cursor: pointer; box-shadow: 0 0 0 4px rgba(34,211,238,0.15); }
|
||||
|
||||
.calc-right { background: rgba(2, 6, 23, 0.6); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 28px; }
|
||||
.calc-right .breakdown { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
|
||||
.calc-right .line { display: flex; justify-content: space-between; font-size: 13px; }
|
||||
.calc-right .line .lab { color: var(--text-2); }
|
||||
.calc-right .line .v { font-family: var(--font-mono); color: var(--text-0); }
|
||||
.calc-right .line.savings .v { color: var(--green); }
|
||||
.calc-right .total-line { padding-top: 14px; border-top: 1px dashed var(--border); display: flex; justify-content: space-between; align-items: baseline; }
|
||||
.calc-right .total-line .lab { font-size: 12px; color: var(--text-3); font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.14em; }
|
||||
.calc-right .total-line .big { font-family: var(--font-mono); font-size: 28px; font-weight: 800; color: var(--cyan); letter-spacing: -0.02em; }
|
||||
.calc-right .total-line .big .curr { font-size: 14px; color: var(--text-3); font-weight: 500; margin-right: 2px; }
|
||||
|
||||
.faq-section { max-width: 880px; margin: 0 auto; padding: 60px 32px 100px; }
|
||||
.faq { border: 1px solid var(--border); border-radius: var(--r-md); background: rgba(15, 23, 42, 0.4); margin-bottom: 8px; overflow: hidden; transition: all .15s; }
|
||||
.faq:hover { border-color: var(--border-2); }
|
||||
.faq summary { padding: 18px 22px; cursor: pointer; list-style: none; display: flex; align-items: center; gap: 14px; font-size: 15px; font-weight: 500; color: var(--text-0); position: relative; }
|
||||
.faq summary::-webkit-details-marker { display: none; }
|
||||
.faq summary::after { content: "+"; margin-left: auto; font-family: var(--font-mono); font-size: 18px; color: var(--text-3); transition: transform .2s; }
|
||||
.faq[open] summary::after { content: "−"; color: var(--cyan); }
|
||||
.faq summary .num { font-family: var(--font-mono); font-size: 11px; color: var(--cyan); letter-spacing: 0.1em; min-width: 26px; }
|
||||
.faq .answer { padding: 0 22px 20px 62px; color: var(--text-2); font-size: 14px; line-height: 1.7; }
|
||||
.faq .answer code { font-family: var(--font-mono); background: rgba(2, 6, 23, 0.6); padding: 1px 6px; border-radius: 3px; color: var(--cyan); font-size: 12.5px; }
|
||||
.faq .answer a { color: var(--cyan); }
|
||||
.faq .answer ul { padding-left: 20px; margin-top: 8px; }
|
||||
|
||||
.final-cta { max-width: 1180px; margin: 40px auto 80px; padding: 0 32px; }
|
||||
.final-cta-inner { padding: 48px; border: 1px solid var(--border); border-radius: var(--r-xl); background: radial-gradient(800px 400px at 50% 0%, rgba(34,211,238,0.08), transparent 60%), rgba(15, 23, 42, 0.6); text-align: center; }
|
||||
.final-cta-inner h2 { font-size: 32px; font-weight: 800; letter-spacing: -0.02em; margin-bottom: 10px; }
|
||||
.final-cta-inner p { color: var(--text-2); font-size: 15px; margin-bottom: 26px; }
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.pricing-grid { grid-template-columns: 1fr 1fr; }
|
||||
.custom-row { grid-template-columns: 1fr; }
|
||||
.tools-grid { grid-template-columns: repeat(3, 1fr); }
|
||||
.calc { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<nav class="nav">
|
||||
<div class="container nav-inner">
|
||||
<a class="brand" href="Landing.html">
|
||||
<svg class="hex" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
<div class="nav-links">
|
||||
<a href="Landing.html">产品</a>
|
||||
<a href="#" class="active">定价</a>
|
||||
<a href="Docs.html">文档</a>
|
||||
<a href="Design System.html">设计系统</a>
|
||||
</div>
|
||||
<div class="nav-cta">
|
||||
<a href="Login.html" class="btn btn-ghost">登录</a>
|
||||
<a href="Register.html" class="btn btn-primary">开始使用</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="hero">
|
||||
<div class="section-kicker" style="margin-bottom:14px;">// pricing · 充多少 · 用多少 · 永不过期</div>
|
||||
<h1>一次充值,<span class="accent">全平台</span>通用</h1>
|
||||
<p class="sub">
|
||||
同一份积分可以用在 Claude / ChatGPT / Gemini 任意池上。我们把你的订阅额度变成真正的 API 余额 —— 相比官方 API 便宜 <b class="text-cyan">至多 70%</b>。
|
||||
</p>
|
||||
<div class="underline">
|
||||
<span class="dot"></span>
|
||||
余额永不过期 · 支持支付宝 / 微信 / USDT · 无隐藏订阅费
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="pricing-wrap">
|
||||
<div class="pricing-grid">
|
||||
|
||||
<div class="tier">
|
||||
<span class="flag muted">STARTER</span>
|
||||
<div class="tier-name">tier · 01</div>
|
||||
<div class="tier-headline">先尝尝鲜,跑通接入</div>
|
||||
<div class="price-row"><span class="price"><span class="curr">$</span>9.9</span></div>
|
||||
<div class="credit-line">充 $9.9 <span class="arrow">→</span> 得 <b>$12</b> 积分 <span class="bonus">+21%</span></div>
|
||||
<span class="discount-tag">相当于官方 API · 0.5 折起</span>
|
||||
<hr/>
|
||||
<div class="feats">
|
||||
<div class="feat"><span class="tick">✓</span>可用所有模型 / 所有池</div>
|
||||
<div class="feat"><span class="tick">✓</span><b>1</b> 个 API Key</div>
|
||||
<div class="feat"><span class="tick">✓</span>60 RPM 速率限制</div>
|
||||
<div class="feat"><span class="tick">✓</span>基础日志(7 天保留)</div>
|
||||
<div class="feat muted"><span class="tick">✗</span>自带订阅接入</div>
|
||||
<div class="feat muted"><span class="tick">✗</span>团队 / 多人协作</div>
|
||||
</div>
|
||||
<a href="Register.html" class="btn btn-ghost btn-lg tier-cta">充值 →</a>
|
||||
</div>
|
||||
|
||||
<div class="tier popular">
|
||||
<span class="flag">◆ 推荐</span>
|
||||
<div class="tier-name">tier · 02</div>
|
||||
<div class="tier-headline">个人重度用户 · 最划算</div>
|
||||
<div class="price-row"><span class="price"><span class="curr">$</span>29.9</span></div>
|
||||
<div class="credit-line">充 $29.9 <span class="arrow">→</span> 得 <b>$45</b> 积分 <span class="bonus">+50%</span></div>
|
||||
<span class="discount-tag">相当于官方 API · 3-7 折</span>
|
||||
<hr/>
|
||||
<div class="feats">
|
||||
<div class="feat"><span class="tick">✓</span>可用所有模型 / 所有池</div>
|
||||
<div class="feat"><span class="tick">✓</span><b>3</b> 个 API Key · 独立预算</div>
|
||||
<div class="feat"><span class="tick">✓</span>120 RPM 速率限制</div>
|
||||
<div class="feat"><span class="tick">✓</span>调用日志(30 天保留)</div>
|
||||
<div class="feat"><span class="tick">✓</span>自带订阅接入(无限个)</div>
|
||||
<div class="feat"><span class="tick">✓</span>多账号 failover 调度</div>
|
||||
</div>
|
||||
<a href="Register.html" class="btn btn-primary btn-lg tier-cta">立即充值 →</a>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<span class="flag amber">⚡ 限时 +100%</span>
|
||||
<div class="tier-name">tier · 03</div>
|
||||
<div class="tier-headline">小团队 / 长跑项目</div>
|
||||
<div class="price-row"><span class="price"><span class="curr">$</span>99</span></div>
|
||||
<div class="credit-line">充 $99 <span class="arrow">→</span> 得 <b>$198</b> 积分 <span class="bonus">+100%</span></div>
|
||||
<span class="discount-tag">相当于官方 API · 2-5 折</span>
|
||||
<hr/>
|
||||
<div class="feats">
|
||||
<div class="feat"><span class="tick">✓</span>所有 Pro 能力</div>
|
||||
<div class="feat"><span class="tick">✓</span><b>10</b> 个 API Key · 独立预算</div>
|
||||
<div class="feat"><span class="tick">✓</span>300 RPM 速率限制</div>
|
||||
<div class="feat"><span class="tick">✓</span>调用日志(90 天保留)</div>
|
||||
<div class="feat"><span class="tick">✓</span>请求优先级加权调度</div>
|
||||
<div class="feat"><span class="tick">✓</span>Slack / Discord 群组支持</div>
|
||||
</div>
|
||||
<a href="Register.html" class="btn btn-ghost btn-lg tier-cta">充值 →</a>
|
||||
</div>
|
||||
|
||||
<div class="tier">
|
||||
<span class="flag muted">CUSTOM</span>
|
||||
<div class="tier-name">tier · 04</div>
|
||||
<div class="tier-headline">自定义金额 · 按需充值</div>
|
||||
<div class="price-row"><span class="price"><span class="curr">$</span><span id="custom-amt">50</span></span></div>
|
||||
<div class="credit-line">得约 <b id="custom-credit">$78</b> 积分 <span class="bonus">+<span id="custom-bonus">56</span>%</span></div>
|
||||
<input type="range" min="10" max="500" value="50" step="10" id="custom-slider" style="-webkit-appearance:none; width:100%; height:4px; background:var(--border); border-radius:2px; margin-bottom:12px;">
|
||||
<span class="discount-tag">根据金额阶梯自动匹配折扣</span>
|
||||
<hr/>
|
||||
<div class="feats">
|
||||
<div class="feat"><span class="tick">✓</span>积分永不过期</div>
|
||||
<div class="feat"><span class="tick">✓</span>Pro 全部能力</div>
|
||||
<div class="feat"><span class="tick">✓</span>阶梯 +21% ~ +100%</div>
|
||||
<div class="feat"><span class="tick">✓</span>支付宝 / 微信 / USDT</div>
|
||||
<div class="feat muted"><span class="tick">—</span>拖动滑块预览赠送</div>
|
||||
</div>
|
||||
<a href="Register.html" class="btn btn-ghost btn-lg tier-cta">定制充值 →</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="custom-row">
|
||||
<div class="custom-card">
|
||||
<div class="icon purple"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M2 20h20"/><path d="M4 20V10l8-6 8 6v10"/><path d="M9 20v-7h6v7"/></svg></div>
|
||||
<div style="flex:1;">
|
||||
<h3>Enterprise · 企业定制</h3>
|
||||
<p>专属订阅池、SLA、合规审计、私有化部署、发票结算。规模 >$500/月起可申请。</p>
|
||||
</div>
|
||||
<a href="#" class="btn btn-ghost">联系商务 →</a>
|
||||
</div>
|
||||
<div class="custom-card">
|
||||
<div class="icon"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg></div>
|
||||
<div style="flex:1;">
|
||||
<h3>已有订阅?直接接入</h3>
|
||||
<p>有 Claude Max / ChatGPT Pro?免费注册后绑定,只为 PURO 路由费买单 —— 按次 <code class="pill">$0.0008/request</code>。</p>
|
||||
</div>
|
||||
<a href="Binding.html" class="btn btn-ghost">接入我的订阅 →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="calc-section">
|
||||
<div class="calc">
|
||||
<div class="calc-left">
|
||||
<div class="section-kicker" style="margin-bottom:10px">// cost estimator</div>
|
||||
<h3>算算你能省多少?</h3>
|
||||
<p class="sub">按你的使用场景,对比 PURO 和官方 API 的月度花费差。数字会根据你选的场景自动更新。</p>
|
||||
<div class="calc-controls">
|
||||
<div class="slider-row">
|
||||
<div class="s-top"><span>日均请求数</span><span class="val" id="req-val">5,000</span></div>
|
||||
<input type="range" min="500" max="50000" step="500" value="5000" id="req-slider">
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<div class="s-top"><span>平均每请求 tokens</span><span class="val" id="tok-val">3,000</span></div>
|
||||
<input type="range" min="500" max="10000" step="500" value="3000" id="tok-slider">
|
||||
</div>
|
||||
<div class="slider-row">
|
||||
<div class="s-top"><span>Claude 占比</span><span class="val" id="mix-val">50%</span></div>
|
||||
<input type="range" min="0" max="100" step="10" value="50" id="mix-slider">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calc-right">
|
||||
<div class="breakdown">
|
||||
<div class="line"><span class="lab">月度 tokens 消耗</span><span class="v" id="total-tok">450M</span></div>
|
||||
<div class="line"><span class="lab">官方 API 价格</span><span class="v" id="official-cost">$1,620</span></div>
|
||||
<div class="line"><span class="lab">PURO 价格(含 +50% 赠送)</span><span class="v" id="puro-cost">$486</span></div>
|
||||
<div class="line savings"><span class="lab">节省</span><span class="v" id="save-amt">$1,134 · 70%</span></div>
|
||||
</div>
|
||||
<div class="total-line">
|
||||
<div>
|
||||
<div class="lab">建议充值</div>
|
||||
<div style="font-size:12px; color:var(--text-3); margin-top:4px;" id="rec-note">≈ 3 天试用 + Pro 档充值</div>
|
||||
</div>
|
||||
<div class="big"><span class="curr">$</span><span id="rec-amt">486</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="works">
|
||||
<div class="section-head">
|
||||
<div class="kicker">// works everywhere</div>
|
||||
<h2>一个 key,所有工具通用</h2>
|
||||
<p>只要支持自定义 <code class="pill">base_url</code> 或 OpenAI / Anthropic API,都能直接接入 PURO。</p>
|
||||
</div>
|
||||
<div class="tools-grid">
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg></div><div class="name">Claude Code</div><div class="tag">ANTHROPIC_BASE_URL</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M6 18L18 6M8 6h10v10"/></svg></div><div class="name">Cursor</div><div class="tag">自定义模型</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/></svg></div><div class="name">Cline</div><div class="tag">OpenAI 兼容</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="8"/><path d="m8 12 2 2 6-6"/></svg></div><div class="name">Roo Code</div><div class="tag">OpenAI 兼容</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M3 12h4l3-8 4 16 3-8h4"/></svg></div><div class="name">Continue</div><div class="tag">config.yaml</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="M12 3v18M3 12h18"/></svg></div><div class="name">OpenAI SDK</div><div class="tag">Python / Node</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="#d97757"><path d="M4.5 19L12 4l7.5 15H16l-4-8.5L8 19H4.5z"/></svg></div><div class="name">Anthropic SDK</div><div class="tag">原生 Claude</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 10h18M8 5v14"/></svg></div><div class="name">Open WebUI</div><div class="tag">自定义 base</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 2L2 7v10l10 5 10-5V7z"/><path d="M12 22V12M2 7l10 5 10-5"/></svg></div><div class="name">LangChain</div><div class="tag">LLM 节点</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="6" cy="6" r="3"/><circle cx="18" cy="6" r="3"/><circle cx="12" cy="18" r="3"/><path d="M6 9v3l6 3M18 9v3l-6 3"/></svg></div><div class="name">LlamaIndex</div><div class="tag">模型路由</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M9 9h6v6H9z"/></svg></div><div class="name">Zed</div><div class="tag">Assistant</div></div>
|
||||
<div class="tool"><div class="logo"><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 4h6v6H4zM14 4h6v6h-6zM4 14h6v6H4zM14 14h6v6h-6z"/></svg></div><div class="name">更多…</div><div class="tag">60+ 工具</div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="faq-section">
|
||||
<div class="section-head">
|
||||
<div class="kicker">// frequently asked</div>
|
||||
<h2>你可能想问的</h2>
|
||||
<p>没找到答案?<a href="#" style="color:var(--cyan)">发邮件给我们 ↗</a> · 通常 2 小时内回复。</p>
|
||||
</div>
|
||||
|
||||
<details class="faq" open>
|
||||
<summary><span class="num">01</span>PURO 和 API 中转站 / API 代理有什么不同?</summary>
|
||||
<div class="answer">
|
||||
中转站只是把官方 API 请求转一手,价格取决于你预付多少 balance。PURO 的不同是 —— 我们让你 <b>把已有的 Claude Pro / ChatGPT Plus 订阅变成 API</b>。
|
||||
你原本就在付的 $20/月,不再只能在官网聊天里用,而是通过统一 API 喂给 Cursor、Claude Code、任何 SDK。
|
||||
同时我们也提供按量充值的官方 API 备用池,两种模式可以混用。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">02</span>用订阅跑 API 会不会被封号?</summary>
|
||||
<div class="answer">
|
||||
我们会自动控制每个订阅的请求节奏,并在触发限流时把请求 failover 到池子里的其他订阅。实际上 PURO 的调用模式比你在官方客户端直接复制粘贴大段对话 <b>更不容易触发风控</b>。
|
||||
你绑定多个订阅时,单个账号的 <code>RPM</code> 会被压到足够安全的阈值内。另外所有凭证用 AES-256 加密存储,请求链路不经过第三方。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">03</span>积分会过期吗?可以退款吗?</summary>
|
||||
<div class="answer">
|
||||
<b>积分永不过期。</b>你可以攒着慢慢用 —— 包括几个月都不用。首次充值 7 天内未产生任何调用可全额退款,之后按剩余积分 85% 比例退。详见 <a href="#">退款政策</a>。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">04</span>支持哪些支付方式?</summary>
|
||||
<div class="answer">
|
||||
国内:支付宝 · 微信支付。国际:Stripe 信用卡 · USDT (TRC20 / ERC20) · PayPal。企业充值支持 Invoice 对公打款,人民币开票。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">05</span>一个 PURO 账号可以绑定多少个订阅?</summary>
|
||||
<div class="answer">
|
||||
<ul>
|
||||
<li>Starter 档:<b>不支持</b>绑定自带订阅</li>
|
||||
<li>Pro 档及以上:<b>无限制</b>,你可以把 10 个 ChatGPT Plus + 3 个 Claude Pro 一起绑上去,统一调度</li>
|
||||
<li>Enterprise:支持跨团队共享池,按组织维度隔离</li>
|
||||
</ul>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">06</span>如果某个订阅触发限流了会怎样?</summary>
|
||||
<div class="answer">
|
||||
PURO 的调度器会把受限的订阅自动标记为 <code>cooling</code> 状态,暂时从池子里摘除。同一请求会立刻被 failover 到池内其他健康订阅上 —— 调用方通常 <b>感受不到中断</b>。你可以在 Dashboard 看到每个订阅的当前状态和剩余配额。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">07</span>计费精度?超量会怎么办?</summary>
|
||||
<div class="answer">
|
||||
按实际 token 数 + 模型单价计费,精度到 4 位小数。每个 API Key 可设置独立月度预算,达到后 <code>402 Payment Required</code>,不会继续扣费。账户总余额不足时同样会返回 <code>402</code>,且 Dashboard 有 80% / 95% 两级提醒邮件。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">08</span>数据会被用于训练吗?</summary>
|
||||
<div class="answer">
|
||||
<b>不会。</b>所有请求仅用于路由转发,不入库、不留存内容(仅保留元数据如模型、token 数、延迟,用于计费和日志)。Pro 档及以上可选开启"零日志模式",我们连请求 ID 都不记录。
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">09</span>可以私有化部署吗?</summary>
|
||||
<div class="answer">
|
||||
Enterprise 档支持 Docker / K8s 私有化部署,控制面和数据面可以分开。授权按年订阅,包含升级和技术支持。<a href="#">联系商务 →</a>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<details class="faq">
|
||||
<summary><span class="num">10</span>支持哪些模型?会跟进新模型吗?</summary>
|
||||
<div class="answer">
|
||||
当前覆盖 Claude(Sonnet 4.5 / Opus 4 / Haiku 4.5)、ChatGPT(GPT-5 / GPT-5 Codex / GPT-4.1)、Gemini(2.5 Pro / 2.5 Flash)。每当官方发布新模型,我们通常在 <b>24 小时内</b>上线。完整模型列表见 <a href="Docs.html">文档</a>。
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
<section class="final-cta">
|
||||
<div class="final-cta-inner">
|
||||
<div class="section-kicker" style="margin-bottom:12px;">// ready to start</div>
|
||||
<h2>5 分钟,拿到你第一个 sk-puro-* key</h2>
|
||||
<p>注册送 <b class="text-cyan">$5</b> 测试积分 · 绑定你的第一个订阅即可开始。</p>
|
||||
<div style="display:inline-flex; gap:12px;">
|
||||
<a href="Register.html" class="btn btn-primary btn-lg">免费注册 →</a>
|
||||
<a href="Docs.html" class="btn btn-ghost btn-lg">查看文档</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
const csl = document.getElementById('custom-slider');
|
||||
const camt = document.getElementById('custom-amt');
|
||||
const ccredit = document.getElementById('custom-credit');
|
||||
const cbonus = document.getElementById('custom-bonus');
|
||||
function updateCustom() {
|
||||
const v = +csl.value;
|
||||
camt.textContent = v;
|
||||
let bonus = 21;
|
||||
if (v >= 500) bonus = 120;
|
||||
else if (v >= 200) bonus = 110;
|
||||
else if (v >= 99) bonus = 100;
|
||||
else if (v >= 50) bonus = 70;
|
||||
else if (v >= 30) bonus = 50;
|
||||
else if (v >= 20) bonus = 35;
|
||||
const credit = Math.round(v * (1 + bonus/100));
|
||||
ccredit.textContent = '$' + credit;
|
||||
cbonus.textContent = bonus;
|
||||
}
|
||||
csl.addEventListener('input', updateCustom);
|
||||
updateCustom();
|
||||
|
||||
const reqS = document.getElementById('req-slider');
|
||||
const tokS = document.getElementById('tok-slider');
|
||||
const mixS = document.getElementById('mix-slider');
|
||||
function fmtMoney(n) { return '$' + n.toLocaleString('en-US', { maximumFractionDigits: 0 }); }
|
||||
function fmtNum(n) {
|
||||
if (n >= 1e9) return (n/1e9).toFixed(1) + 'B';
|
||||
if (n >= 1e6) return (n/1e6).toFixed(0) + 'M';
|
||||
if (n >= 1e3) return (n/1e3).toFixed(1) + 'k';
|
||||
return n.toString();
|
||||
}
|
||||
function calc() {
|
||||
const req = +reqS.value;
|
||||
const tok = +tokS.value;
|
||||
const mix = +mixS.value;
|
||||
document.getElementById('req-val').textContent = req.toLocaleString();
|
||||
document.getElementById('tok-val').textContent = tok.toLocaleString();
|
||||
document.getElementById('mix-val').textContent = mix + '%';
|
||||
const monthlyTok = req * tok * 30;
|
||||
const avgOfficial = (mix/100) * 6 + (1 - mix/100) * 3;
|
||||
const official = monthlyTok / 1e6 * avgOfficial;
|
||||
const puro = official * 0.3;
|
||||
const save = official - puro;
|
||||
const savePct = Math.round((save / official) * 100);
|
||||
document.getElementById('total-tok').textContent = fmtNum(monthlyTok);
|
||||
document.getElementById('official-cost').textContent = fmtMoney(official);
|
||||
document.getElementById('puro-cost').textContent = fmtMoney(puro);
|
||||
document.getElementById('save-amt').textContent = fmtMoney(save) + ' · ' + savePct + '%';
|
||||
document.getElementById('rec-amt').textContent = Math.ceil(puro);
|
||||
const note = puro < 30 ? '≈ Starter 档够用' : puro < 80 ? '≈ Pro 档 1 个月' : '≈ Scale 档 · 1 个月';
|
||||
document.getElementById('rec-note').textContent = note;
|
||||
}
|
||||
[reqS, tokS, mixS].forEach(s => s.addEventListener('input', calc));
|
||||
calc();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
386
docs/design-drafts/v2/Register.html
Normal file
386
docs/design-drafts/v2/Register.html
Normal file
@@ -0,0 +1,386 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="color-scheme" content="dark">
|
||||
<title>注册 — PURO AI</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="puro.css">
|
||||
<style>
|
||||
body { min-height: 100vh; overflow-x: hidden; }
|
||||
|
||||
.split { display: grid; grid-template-columns: 1fr 1fr; min-height: 100vh; position: relative; z-index: 1; }
|
||||
|
||||
/* ============ LEFT (NARRATIVE + STEPS) ============ */
|
||||
.narrative {
|
||||
position: relative; overflow: hidden;
|
||||
padding: 48px 56px;
|
||||
display: flex; flex-direction: column; justify-content: space-between;
|
||||
border-right: 1px solid var(--border);
|
||||
background: linear-gradient(135deg, rgba(34,211,238,0.04), transparent 60%), rgba(15, 23, 42, 0.3);
|
||||
}
|
||||
.narrative::before {
|
||||
content: ""; position: absolute; inset: 0;
|
||||
background:
|
||||
radial-gradient(600px 400px at 20% 20%, rgba(34,211,238,0.08), transparent 60%),
|
||||
radial-gradient(500px 400px at 90% 80%, rgba(168,85,247,0.06), transparent 60%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.narrative-inner { position: relative; display: flex; flex-direction: column; gap: 28px; z-index: 1; }
|
||||
|
||||
.brand-top { display: inline-flex; align-items: center; gap: 10px; font-weight: 700; font-size: 15px; letter-spacing: -0.01em; color: var(--text-0); }
|
||||
.brand-top svg { width: 22px; height: 22px; color: var(--cyan); }
|
||||
|
||||
.n-kicker { font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; color: var(--cyan); }
|
||||
.n-headline { font-size: 52px; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05; display: flex; align-items: baseline; flex-wrap: wrap; gap: 14px; }
|
||||
.n-headline .amber { color: var(--amber); }
|
||||
.n-headline .cyan { color: var(--cyan); }
|
||||
.n-headline .arrow { font-size: 38px; color: var(--text-3); font-weight: 400; }
|
||||
.n-sub { color: var(--text-2); font-size: 15px; line-height: 1.75; max-width: 440px; }
|
||||
.n-sub .line { display: block; }
|
||||
.n-sub .puro { color: var(--text-0); font-weight: 600; }
|
||||
|
||||
.steps { display: flex; flex-direction: column; gap: 16px; padding: 20px 22px; background: rgba(2, 6, 23, 0.5); border: 1px solid var(--border); border-radius: var(--r-md); max-width: 440px; }
|
||||
.steps-title { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); letter-spacing: 0.14em; }
|
||||
.step { display: flex; align-items: flex-start; gap: 14px; font-size: 13px; }
|
||||
.step-num { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; background: rgba(34,211,238,0.1); border: 1px solid rgba(34,211,238,0.3); color: var(--cyan); font-family: var(--font-mono); font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
|
||||
.step.active .step-num { background: var(--cyan); color: #042f2e; border-color: var(--cyan); }
|
||||
.step-text { color: var(--text-1); line-height: 1.5; padding-top: 2px; }
|
||||
.step-text b { color: var(--text-0); font-weight: 600; }
|
||||
.step-text .k { font-family: var(--font-mono); background: rgba(255,255,255,0.04); border: 1px solid var(--border-2); padding: 1px 6px; border-radius: 3px; font-size: 11.5px; color: var(--cyan); }
|
||||
|
||||
.n-bottom { position: relative; z-index: 1; font-family: var(--font-mono); font-size: 11px; color: var(--text-3); display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
|
||||
.n-bottom .sep { color: var(--border-2); }
|
||||
.n-bottom .live { color: var(--green); display: inline-flex; align-items: center; gap: 6px; }
|
||||
.n-bottom .live .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--green); box-shadow: 0 0 6px var(--green); }
|
||||
|
||||
/* ============ RIGHT (FORM) ============ */
|
||||
.form-side { display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 48px 56px; position: relative; }
|
||||
.back-home { position: absolute; top: 32px; right: 32px; font-family: var(--font-mono); font-size: 12px; color: var(--text-3); display: inline-flex; align-items: center; gap: 6px; }
|
||||
.back-home:hover { color: var(--text-0); }
|
||||
|
||||
.form-card { width: 100%; max-width: 400px; }
|
||||
.form-card h1 { font-size: 32px; font-weight: 700; letter-spacing: -0.02em; margin-bottom: 6px; }
|
||||
.form-card .sub { color: var(--text-2); font-size: 14px; margin-bottom: 32px; }
|
||||
|
||||
.field { margin-bottom: 16px; }
|
||||
.field label { display: block; font-size: 12px; color: var(--text-2); font-weight: 500; margin-bottom: 8px; }
|
||||
.input-wrap { position: relative; display: flex; align-items: center; background: rgba(2, 6, 23, 0.4); border: 1px solid var(--border-2); border-radius: var(--r-md); transition: all .15s; }
|
||||
.input-wrap:focus-within { border-color: var(--cyan); background: rgba(34, 211, 238, 0.04); box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.1); }
|
||||
.input-wrap .icon { padding: 0 12px; color: var(--text-3); flex-shrink: 0; display: flex; }
|
||||
.input-wrap input { flex: 1; background: none; border: none; outline: none; font-family: inherit; color: var(--text-0); font-size: 14px; padding: 12px 14px 12px 0; }
|
||||
.input-wrap input::placeholder { color: var(--text-3); }
|
||||
.eye { display: flex; padding: 10px 12px; color: var(--text-3); background: none; border: none; cursor: pointer; }
|
||||
.eye:hover { color: var(--text-1); }
|
||||
.valid-ico { display: none; color: var(--green); padding: 0 12px; }
|
||||
.input-wrap input.ok ~ .valid-ico { display: flex; }
|
||||
.input-wrap input.ok { padding-right: 0; }
|
||||
.input-wrap input.ok + .eye { display: none; }
|
||||
|
||||
/* Password strength */
|
||||
.pw-strength { display: flex; gap: 4px; margin-top: 8px; margin-bottom: 6px; }
|
||||
.pw-strength .bar { flex: 1; height: 3px; background: var(--border); border-radius: 2px; transition: background .2s; }
|
||||
.pw-strength[data-score="1"] .bar:nth-child(1) { background: var(--red); }
|
||||
.pw-strength[data-score="2"] .bar:nth-child(-n+2) { background: var(--amber); }
|
||||
.pw-strength[data-score="3"] .bar:nth-child(-n+3) { background: var(--cyan); }
|
||||
.pw-strength[data-score="4"] .bar { background: var(--green); }
|
||||
|
||||
.pw-hint { display: flex; justify-content: space-between; font-family: var(--font-mono); font-size: 10px; color: var(--text-3); letter-spacing: 0.05em; }
|
||||
.pw-hint .val { color: var(--text-3); }
|
||||
.pw-hint[data-score="1"] .val { color: var(--red); }
|
||||
.pw-hint[data-score="2"] .val { color: var(--amber); }
|
||||
.pw-hint[data-score="3"] .val { color: var(--cyan); }
|
||||
.pw-hint[data-score="4"] .val { color: var(--green); }
|
||||
|
||||
.match-hint { font-family: var(--font-mono); font-size: 11px; color: var(--text-3); margin-top: 6px; min-height: 14px; }
|
||||
.match-hint.mismatch { color: var(--red); }
|
||||
.match-hint.ok { color: var(--green); }
|
||||
|
||||
.check { display: inline-flex; align-items: flex-start; gap: 10px; font-size: 13px; color: var(--text-2); cursor: pointer; user-select: none; margin: 6px 0 20px; line-height: 1.5; }
|
||||
.check input { position: absolute; opacity: 0; pointer-events: none; }
|
||||
.check .box { width: 14px; height: 14px; border-radius: 3px; border: 1px solid var(--border-2); background: rgba(2, 6, 23, 0.4); position: relative; flex-shrink: 0; margin-top: 2px; }
|
||||
.check input:checked ~ .box { background: var(--cyan); border-color: var(--cyan); }
|
||||
.check input:checked ~ .box::after { content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px; border: solid #042f2e; border-width: 0 2px 2px 0; transform: rotate(45deg); }
|
||||
.check a { color: var(--text-1); border-bottom: 1px dashed var(--border-2); }
|
||||
.check a:hover { color: var(--cyan); border-color: var(--cyan); }
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
padding: 14px 20px !important;
|
||||
font-size: 14px !important;
|
||||
margin-top: 4px;
|
||||
background: var(--cyan) !important;
|
||||
color: #042f2e !important;
|
||||
border: 1px solid var(--cyan) !important;
|
||||
font-weight: 600 !important;
|
||||
z-index: 2;
|
||||
}
|
||||
.submit-btn:hover { background: var(--cyan-2) !important; }
|
||||
.submit-btn:disabled { opacity: 0.5; cursor: not-allowed; background: var(--border-2) !important; border-color: var(--border-2) !important; color: var(--text-3) !important; }
|
||||
.submit-btn .spinner { width: 14px; height: 14px; border: 2px solid rgba(0,0,0,0.2); border-top-color: #042f2e; border-radius: 50%; animation: spin .7s linear infinite; display: none; }
|
||||
.submit-btn.loading .spinner { display: inline-block; }
|
||||
.submit-btn.loading .label { opacity: 0.5; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.ghost-btn { width: 100%; padding: 14px 20px !important; font-size: 14px !important; font-weight: 500 !important; }
|
||||
|
||||
.divider { display: flex; align-items: center; gap: 14px; margin: 24px 0; color: var(--text-3); font-size: 11px; font-family: var(--font-mono); letter-spacing: 0.15em; }
|
||||
.divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--border); }
|
||||
|
||||
.linuxdo-ico { width: 18px; height: 18px; border-radius: 3px; background: linear-gradient(135deg, #f0a030, #f05050); display: inline-flex; align-items: center; justify-content: center; font-size: 10px; font-weight: 800; color: white; }
|
||||
|
||||
.foot { margin-top: 24px; text-align: center; font-size: 13px; color: var(--text-2); }
|
||||
.foot a { color: var(--cyan); font-weight: 500; }
|
||||
|
||||
.bonus-note { margin-top: 18px; padding: 12px 14px; background: rgba(52,211,153,0.06); border: 1px solid rgba(52,211,153,0.2); border-radius: var(--r-md); font-size: 12px; color: var(--text-1); display: flex; align-items: center; gap: 10px; }
|
||||
.bonus-note .emoji { flex-shrink: 0; color: var(--green); font-weight: 700; font-family: var(--font-mono); padding: 3px 8px; background: rgba(52,211,153,0.15); border-radius: 4px; font-size: 11px; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.split { grid-template-columns: 1fr; }
|
||||
.narrative { padding: 32px 28px; border-right: none; border-bottom: 1px solid var(--border); }
|
||||
.n-headline { font-size: 36px; }
|
||||
.steps, .n-bottom { display: none; }
|
||||
.form-side { padding: 40px 28px; }
|
||||
.back-home { top: 20px; right: 20px; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="bg-glow"></div>
|
||||
<div class="grain"></div>
|
||||
|
||||
<div class="split">
|
||||
<section class="narrative">
|
||||
<div class="narrative-inner">
|
||||
<a href="Landing.html" class="brand-top">
|
||||
<svg viewBox="0 0 24 24" fill="none">
|
||||
<path d="M12 2L21 7V17L12 22L3 17V7L12 2Z" stroke="currentColor" stroke-width="1.8" fill="rgba(34, 211, 238, 0.08)"/>
|
||||
<path d="M12 7L17 9.5V14.5L12 17L7 14.5V9.5L12 7Z" fill="currentColor"/>
|
||||
</svg>
|
||||
PURO AI
|
||||
</a>
|
||||
|
||||
<div>
|
||||
<div class="n-kicker">// 5 分钟开始用</div>
|
||||
<h1 class="n-headline" style="margin-top: 12px;">
|
||||
<span class="amber">N</span> 个订阅
|
||||
<span class="arrow">→</span>
|
||||
<span class="cyan">1</span> 个 key
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div class="n-sub">
|
||||
<span class="line">省去切换账号的繁琐,</span>
|
||||
<span class="line">省去为多个高昂订阅重复买单。</span>
|
||||
<span class="line" style="margin-top: 8px;"><span class="puro">PURO</span>(纯粹)—— 让 AI 调用回归本质。</span>
|
||||
</div>
|
||||
|
||||
<div class="steps">
|
||||
<div class="steps-title">// 下一步</div>
|
||||
<div class="step active">
|
||||
<div class="step-num">1</div>
|
||||
<div class="step-text"><b>创建账户</b> · 邮箱 + 密码,或用 LinuxDO OAuth</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">2</div>
|
||||
<div class="step-text"><b>绑定订阅</b> · OAuth 接入你现有的 Claude Pro / ChatGPT Plus</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-num">3</div>
|
||||
<div class="step-text"><b>生成 key</b> · 拿到 <span class="k">sk-puro-…</span>,换掉 SDK 的 <span class="k">base_url</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="n-bottom">
|
||||
<span>Claude</span><span class="sep">·</span>
|
||||
<span>ChatGPT</span><span class="sep">·</span>
|
||||
<span>Codex</span><span class="sep">·</span>
|
||||
<span>Gemini</span>
|
||||
<span class="sep">|</span>
|
||||
<span class="live"><span class="dot"></span>ai.puro.im · operational</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="form-side">
|
||||
<a href="Landing.html" class="back-home">← 返回首页</a>
|
||||
|
||||
<form class="form-card" id="reg-form" autocomplete="off" novalidate>
|
||||
<h1>创建账户</h1>
|
||||
<p class="sub">注册即送 <b style="color:var(--cyan)">$5</b> 测试积分</p>
|
||||
|
||||
<div class="field">
|
||||
<label for="email">邮箱</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="3" y="5" width="18" height="14" rx="2"/>
|
||||
<path d="M3 7l9 6 9-6"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="email" id="email" name="email" placeholder="you@puro.im" required>
|
||||
<span class="valid-ico">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12l5 5L20 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password">密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password" name="password" placeholder="至少 8 位,含字母与数字" required>
|
||||
<button type="button" class="eye" id="toggle-pw" aria-label="切换显示密码">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/>
|
||||
<circle cx="12" cy="12" r="3"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="pw-strength" id="pw-strength" data-score="0">
|
||||
<span class="bar"></span><span class="bar"></span><span class="bar"></span><span class="bar"></span>
|
||||
</div>
|
||||
<div class="pw-hint" id="pw-hint" data-score="0">
|
||||
<span>// strength</span>
|
||||
<span class="val">—</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label for="password2">确认密码</label>
|
||||
<div class="input-wrap">
|
||||
<span class="icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="4" y="11" width="16" height="10" rx="2"/>
|
||||
<path d="M8 11V7a4 4 0 0 1 8 0v4"/>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="password" id="password2" name="password2" placeholder="再输入一次" required>
|
||||
<span class="valid-ico">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M5 12l5 5L20 7"/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="match-hint" id="match-hint"></div>
|
||||
</div>
|
||||
|
||||
<label class="check">
|
||||
<input type="checkbox" id="terms">
|
||||
<span class="box"></span>
|
||||
<span>我已阅读并同意 <a href="#">服务条款</a> 与 <a href="#">隐私政策</a></span>
|
||||
</label>
|
||||
|
||||
<button type="submit" class="btn btn-primary submit-btn" id="submit-btn" disabled>
|
||||
<span class="spinner"></span>
|
||||
<span class="label">创建账户 →</span>
|
||||
</button>
|
||||
|
||||
<div class="divider">OR</div>
|
||||
|
||||
<button type="button" class="btn btn-ghost ghost-btn">
|
||||
<span class="linuxdo-ico">L</span>
|
||||
使用 LinuxDO 注册
|
||||
</button>
|
||||
|
||||
<div class="bonus-note">
|
||||
<span class="emoji">+$5</span>
|
||||
<span>完成注册即送 <b style="color:var(--text-0)">$5</b> 测试积分 —— 够你跑几万次 Claude 请求。</span>
|
||||
</div>
|
||||
|
||||
<div class="foot">
|
||||
已有账户?<a href="Login.html">登录</a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const email = document.getElementById('email');
|
||||
const pw = document.getElementById('password');
|
||||
const pw2 = document.getElementById('password2');
|
||||
const terms = document.getElementById('terms');
|
||||
const btn = document.getElementById('submit-btn');
|
||||
const strengthBars = document.getElementById('pw-strength');
|
||||
const strengthHint = document.getElementById('pw-hint');
|
||||
const matchHint = document.getElementById('match-hint');
|
||||
const toggle = document.getElementById('toggle-pw');
|
||||
|
||||
toggle.addEventListener('click', () => {
|
||||
const show = pw.type === 'password';
|
||||
pw.type = show ? 'text' : 'password';
|
||||
});
|
||||
|
||||
function scorePw(v) {
|
||||
if (!v) return 0;
|
||||
let s = 0;
|
||||
if (v.length >= 8) s++;
|
||||
if (/[A-Z]/.test(v) && /[a-z]/.test(v)) s++;
|
||||
if (/\d/.test(v)) s++;
|
||||
if (/[^A-Za-z0-9]/.test(v) || v.length >= 14) s++;
|
||||
return s;
|
||||
}
|
||||
const labels = ['—', '弱', '中', '强', '极强'];
|
||||
|
||||
function updateAll() {
|
||||
const s = scorePw(pw.value);
|
||||
strengthBars.dataset.score = s;
|
||||
strengthHint.dataset.score = s;
|
||||
strengthHint.querySelector('.val').textContent = labels[s];
|
||||
|
||||
// email validation
|
||||
if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value)) {
|
||||
email.classList.add('ok');
|
||||
} else {
|
||||
email.classList.remove('ok');
|
||||
}
|
||||
|
||||
// password match
|
||||
if (!pw2.value) {
|
||||
matchHint.textContent = '';
|
||||
matchHint.className = 'match-hint';
|
||||
pw2.classList.remove('ok');
|
||||
} else if (pw2.value === pw.value) {
|
||||
matchHint.textContent = '// matched';
|
||||
matchHint.className = 'match-hint ok';
|
||||
pw2.classList.add('ok');
|
||||
} else {
|
||||
matchHint.textContent = '// passwords do not match';
|
||||
matchHint.className = 'match-hint mismatch';
|
||||
pw2.classList.remove('ok');
|
||||
}
|
||||
|
||||
// enable submit?
|
||||
const valid = email.classList.contains('ok') && s >= 2 && pw.value === pw2.value && pw2.value && terms.checked;
|
||||
btn.disabled = !valid;
|
||||
}
|
||||
|
||||
[email, pw, pw2].forEach(el => el.addEventListener('input', updateAll));
|
||||
terms.addEventListener('change', updateAll);
|
||||
|
||||
const form = document.getElementById('reg-form');
|
||||
form.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
if (btn.disabled) return;
|
||||
btn.classList.add('loading');
|
||||
btn.disabled = true;
|
||||
setTimeout(() => {
|
||||
btn.classList.remove('loading');
|
||||
btn.querySelector('.label').textContent = '✓ 注册成功,正在跳转...';
|
||||
btn.style.background = 'var(--green)';
|
||||
setTimeout(() => { window.location.href = 'Binding.html'; }, 800);
|
||||
}, 1200);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
726
docs/design-drafts/v2/puro.css
Normal file
726
docs/design-drafts/v2/puro.css
Normal file
@@ -0,0 +1,726 @@
|
||||
/* ==========================================================================
|
||||
PURO AI — Design System
|
||||
Shared tokens + primitive styles used across every page.
|
||||
--------------------------------------------------------------------------
|
||||
Usage: <link rel="stylesheet" href="puro.css">
|
||||
========================================================================== */
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
:root {
|
||||
/* Surfaces */
|
||||
--bg-0: #0a0e1a; /* page */
|
||||
--bg-1: #0f172a; /* raised */
|
||||
--bg-2: #111827; /* card alt */
|
||||
--bg-code: #020617; /* code canvas */
|
||||
|
||||
/* Borders */
|
||||
--border: #1e293b;
|
||||
--border-2: #334155;
|
||||
--border-3: #475569;
|
||||
|
||||
/* Text */
|
||||
--text-0: #f8fafc; /* primary */
|
||||
--text-1: #cbd5e1; /* body */
|
||||
--text-2: #94a3b8; /* muted */
|
||||
--text-3: #64748b; /* hint */
|
||||
|
||||
/* Accents */
|
||||
--cyan: #22d3ee;
|
||||
--cyan-2: #67e8f9;
|
||||
--cyan-dim: #0891b2;
|
||||
--purple: #a855f7;
|
||||
--amber: #fbbf24;
|
||||
--green: #34d399;
|
||||
--red: #f87171;
|
||||
--orange: #fb923c;
|
||||
|
||||
/* Provider brand dots */
|
||||
--p-claude: #d97757;
|
||||
--p-gpt: #10a37f;
|
||||
--p-gemini: #4285f4;
|
||||
--p-codex: #f0a030;
|
||||
|
||||
/* Radius */
|
||||
--r-sm: 6px;
|
||||
--r-md: 8px;
|
||||
--r-lg: 12px;
|
||||
--r-xl: 16px;
|
||||
|
||||
/* Shadow */
|
||||
--shadow-lg: 0 30px 60px -30px rgba(0,0,0,0.6);
|
||||
--shadow-xl: 0 40px 80px -40px rgba(0,0,0,0.8);
|
||||
|
||||
/* Typography */
|
||||
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
--font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
|
||||
}
|
||||
|
||||
html, body {
|
||||
background: var(--bg-0);
|
||||
color: var(--text-0);
|
||||
font-family: var(--font-sans);
|
||||
font-feature-settings: "cv11", "ss01", "ss03";
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
body { overflow-x: hidden; }
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
button { font-family: inherit; cursor: pointer; border: none; background: none; color: inherit; }
|
||||
|
||||
/* scrollbar — subtle */
|
||||
::-webkit-scrollbar { width: 10px; height: 10px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 6px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--border-3); }
|
||||
|
||||
.mono { font-family: var(--font-mono); }
|
||||
|
||||
/* ==========================================================================
|
||||
BACKGROUND EFFECTS
|
||||
========================================================================== */
|
||||
.bg-glow {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bg-glow::before,
|
||||
.bg-glow::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 900px;
|
||||
height: 900px;
|
||||
border-radius: 50%;
|
||||
filter: blur(120px);
|
||||
opacity: 0.35;
|
||||
}
|
||||
.bg-glow::before {
|
||||
background: radial-gradient(circle, #22d3ee 0%, transparent 60%);
|
||||
top: -300px;
|
||||
left: -200px;
|
||||
}
|
||||
.bg-glow::after {
|
||||
background: radial-gradient(circle, #a855f7 0%, transparent 60%);
|
||||
top: 200px;
|
||||
right: -300px;
|
||||
opacity: 0.25;
|
||||
}
|
||||
.bg-glow.soft::before, .bg-glow.soft::after { opacity: 0.15; }
|
||||
|
||||
.grain {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
opacity: 0.4;
|
||||
mix-blend-mode: overlay;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence baseFrequency='0.9' numOctaves='2'/></filter><rect width='100%25' height='100%25' filter='url(%23n)' opacity='0.35'/></svg>");
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
padding: 0 32px;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.container-wide { max-width: 1280px; }
|
||||
.container-narrow { max-width: 860px; }
|
||||
|
||||
/* ==========================================================================
|
||||
NAV
|
||||
========================================================================== */
|
||||
.nav {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 50;
|
||||
backdrop-filter: blur(16px);
|
||||
background: rgba(10, 14, 26, 0.72);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.nav-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 64px;
|
||||
gap: 48px;
|
||||
}
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.hex {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: var(--cyan);
|
||||
}
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 28px;
|
||||
font-size: 14px;
|
||||
color: var(--text-2);
|
||||
}
|
||||
.nav-links a { transition: color .15s; }
|
||||
.nav-links a:hover, .nav-links a.active { color: var(--text-0); }
|
||||
.nav-links .disabled { color: var(--text-3); cursor: not-allowed; display: inline-flex; align-items: center; gap: 6px; }
|
||||
.nav-links .disabled::after {
|
||||
content: "即将推出";
|
||||
font-size: 10px;
|
||||
padding: 2px 6px;
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 4px;
|
||||
color: var(--text-3);
|
||||
}
|
||||
.nav-cta {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
BUTTONS
|
||||
========================================================================== */
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 8px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
border-radius: var(--r-md);
|
||||
transition: all .15s;
|
||||
white-space: nowrap;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--cyan);
|
||||
color: #042f2e;
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-primary:hover { background: var(--cyan-2); }
|
||||
.btn-primary:active { transform: translateY(1px); }
|
||||
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
|
||||
.btn-ghost {
|
||||
border-color: var(--border-2);
|
||||
color: var(--text-1);
|
||||
}
|
||||
.btn-ghost:hover { border-color: var(--border-3); color: var(--text-0); background: rgba(255,255,255,0.02); }
|
||||
|
||||
.btn-subtle {
|
||||
background: rgba(255,255,255,0.04);
|
||||
color: var(--text-1);
|
||||
border-color: transparent;
|
||||
}
|
||||
.btn-subtle:hover { background: rgba(255,255,255,0.08); color: var(--text-0); }
|
||||
|
||||
.btn-danger {
|
||||
background: rgba(248, 113, 113, 0.1);
|
||||
color: var(--red);
|
||||
border-color: rgba(248, 113, 113, 0.25);
|
||||
}
|
||||
.btn-danger:hover { background: rgba(248, 113, 113, 0.15); border-color: rgba(248, 113, 113, 0.4); }
|
||||
|
||||
.btn-lg { padding: 12px 20px; font-size: 14px; }
|
||||
.btn-sm { padding: 5px 10px; font-size: 12px; }
|
||||
.btn-icon { padding: 7px; aspect-ratio: 1; }
|
||||
|
||||
.btn .spinner {
|
||||
width: 14px; height: 14px;
|
||||
border: 2px solid rgba(0,0,0,0.2);
|
||||
border-top-color: currentColor;
|
||||
border-radius: 50%;
|
||||
animation: spin .7s linear infinite;
|
||||
display: none;
|
||||
}
|
||||
.btn.loading .spinner { display: inline-block; }
|
||||
.btn.loading .label { opacity: 0.5; }
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
/* ==========================================================================
|
||||
BADGES / PILLS / CHIPS
|
||||
========================================================================== */
|
||||
.badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 100px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
background: rgba(34, 211, 238, 0.1);
|
||||
color: var(--cyan);
|
||||
}
|
||||
.badge.amber { background: rgba(251, 191, 36, 0.12); color: var(--amber); }
|
||||
.badge.purple { background: rgba(168, 85, 247, 0.12); color: var(--purple); }
|
||||
.badge.green { background: rgba(52, 211, 153, 0.12); color: var(--green); }
|
||||
.badge.red { background: rgba(248, 113, 113, 0.12); color: var(--red); }
|
||||
.badge.muted { background: rgba(255, 255, 255, 0.04); color: var(--text-2); border: 1px solid var(--border); }
|
||||
|
||||
.pill {
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--r-sm);
|
||||
background: rgba(255,255,255,0.04);
|
||||
border: 1px solid var(--border);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--text-0);
|
||||
}
|
||||
|
||||
.chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 100px;
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
font-size: 12px;
|
||||
color: var(--text-1);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.chip .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
|
||||
.chip.claude .dot { background: var(--p-claude); }
|
||||
.chip.gpt .dot { background: var(--p-gpt); }
|
||||
.chip.gemini .dot { background: var(--p-gemini); }
|
||||
.chip.codex .dot { background: var(--p-codex); }
|
||||
|
||||
.dot-sep { width: 4px; height: 4px; border-radius: 50%; background: var(--text-3); display: inline-block; }
|
||||
|
||||
/* status chip (tiny dot absolute-positioned) */
|
||||
.status-chip {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--green);
|
||||
box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.15);
|
||||
display: inline-block;
|
||||
}
|
||||
.status-chip.dim { background: var(--text-3); box-shadow: none; }
|
||||
.status-chip.amber { background: var(--amber); box-shadow: 0 0 0 3px rgba(251, 191, 36, 0.15); }
|
||||
.status-chip.red { background: var(--red); box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.15); }
|
||||
|
||||
/* ==========================================================================
|
||||
CARDS / SURFACES
|
||||
========================================================================== */
|
||||
.card {
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
padding: 24px;
|
||||
}
|
||||
.card-raised {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
}
|
||||
.card-interactive {
|
||||
transition: all .2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card-interactive:hover {
|
||||
border-color: var(--border-2);
|
||||
background: rgba(15, 23, 42, 0.85);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.divider { height: 1px; background: var(--border); margin: 24px 0; border: 0; }
|
||||
.divider-dashed { border: 0; border-top: 1px dashed var(--border); margin: 20px 0; }
|
||||
|
||||
/* ==========================================================================
|
||||
FORMS
|
||||
========================================================================== */
|
||||
.field { margin-bottom: 18px; }
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-1);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.field-hint {
|
||||
font-size: 12px;
|
||||
color: var(--text-3);
|
||||
margin-top: 6px;
|
||||
}
|
||||
.field-error {
|
||||
font-size: 12px;
|
||||
color: var(--red);
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.input-wrap { position: relative; }
|
||||
.input-wrap .icon {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-3);
|
||||
pointer-events: none;
|
||||
display: inline-flex;
|
||||
}
|
||||
.input {
|
||||
width: 100%;
|
||||
height: 42px;
|
||||
padding: 0 14px;
|
||||
background: rgba(15, 23, 42, 0.6);
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: var(--r-md);
|
||||
color: var(--text-0);
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
transition: all .15s;
|
||||
}
|
||||
.input.with-icon { padding-left: 40px; }
|
||||
.input::placeholder { color: var(--text-3); }
|
||||
.input:hover { border-color: var(--border-3); }
|
||||
.input:focus {
|
||||
border-color: var(--cyan);
|
||||
box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.12);
|
||||
background: rgba(15, 23, 42, 0.9);
|
||||
}
|
||||
.input.ok { border-color: rgba(52, 211, 153, 0.4); }
|
||||
.input.ok:focus { box-shadow: 0 0 0 3px rgba(52, 211, 153, 0.12); }
|
||||
.input.error { border-color: var(--red); }
|
||||
.input.error:focus { box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.12); }
|
||||
|
||||
textarea.input { height: auto; padding: 12px 14px; resize: vertical; line-height: 1.5; }
|
||||
|
||||
select.input {
|
||||
appearance: none;
|
||||
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><path d='M6 9l6 6 6-6'/></svg>");
|
||||
background-repeat: no-repeat;
|
||||
background-position: right 14px center;
|
||||
padding-right: 36px;
|
||||
}
|
||||
|
||||
/* checkbox */
|
||||
.check {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
font-size: 13px;
|
||||
color: var(--text-1);
|
||||
}
|
||||
.check input { display: none; }
|
||||
.check .box {
|
||||
width: 16px; height: 16px;
|
||||
border: 1px solid var(--border-2);
|
||||
border-radius: 4px;
|
||||
background: var(--bg-1);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all .15s;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.check input:checked + .box {
|
||||
background: var(--cyan);
|
||||
border-color: var(--cyan);
|
||||
}
|
||||
.check input:checked + .box::after {
|
||||
content: "✓";
|
||||
color: #042f2e;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
SECTION HEADINGS
|
||||
========================================================================== */
|
||||
.section-kicker {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
color: var(--cyan);
|
||||
letter-spacing: 0.15em;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.section-title {
|
||||
font-size: clamp(28px, 3.5vw, 40px);
|
||||
font-weight: 700;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1.15;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.section-sub {
|
||||
color: var(--text-2);
|
||||
font-size: 16px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
TABLES
|
||||
========================================================================== */
|
||||
.tbl {
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.tbl th {
|
||||
text-align: left;
|
||||
color: var(--text-3);
|
||||
font-weight: 500;
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.tbl td {
|
||||
padding: 14px;
|
||||
border-bottom: 1px solid rgba(30, 41, 59, 0.5);
|
||||
color: var(--text-1);
|
||||
}
|
||||
.tbl tr:last-child td { border-bottom: none; }
|
||||
.tbl tr:hover td { background: rgba(15, 23, 42, 0.4); }
|
||||
.tbl td.mono, .tbl th.mono { font-family: var(--font-mono); }
|
||||
|
||||
/* ==========================================================================
|
||||
CODE BLOCKS
|
||||
========================================================================== */
|
||||
.code-frame {
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--r-lg);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-lg);
|
||||
}
|
||||
.code-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: rgba(15, 23, 42, 0.8);
|
||||
gap: 10px;
|
||||
}
|
||||
.traffic {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.traffic span {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #475569;
|
||||
}
|
||||
.code-body {
|
||||
padding: 22px 26px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 13px;
|
||||
line-height: 1.75;
|
||||
color: var(--text-1);
|
||||
overflow-x: auto;
|
||||
}
|
||||
.code-body .line { display: flex; gap: 20px; }
|
||||
.ln { color: var(--text-3); user-select: none; min-width: 16px; text-align: right; opacity: 0.5; }
|
||||
|
||||
/* syntax */
|
||||
.kw { color: #c084fc; }
|
||||
.str { color: #86efac; }
|
||||
.num { color: #fbbf24; }
|
||||
.com { color: #64748b; font-style: italic; }
|
||||
.fn { color: #22d3ee; }
|
||||
.prop{ color: #f0abfc; }
|
||||
.var-v { color: #f8fafc; }
|
||||
.flag{ color: #fb923c; }
|
||||
.bash-prompt { color: var(--cyan); user-select: none; }
|
||||
|
||||
/* ==========================================================================
|
||||
PROVIDER-BRAND HELPERS
|
||||
========================================================================== */
|
||||
.provider {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
}
|
||||
.provider .dot { width: 6px; height: 6px; border-radius: 50%; }
|
||||
.provider.claude .dot { background: var(--p-claude); }
|
||||
.provider.gpt .dot { background: var(--p-gpt); }
|
||||
.provider.gemini .dot { background: var(--p-gemini); }
|
||||
.provider.codex .dot { background: var(--p-codex); }
|
||||
|
||||
/* ==========================================================================
|
||||
UTILITIES
|
||||
========================================================================== */
|
||||
.stack-xs { display: flex; flex-direction: column; gap: 8px; }
|
||||
.stack-sm { display: flex; flex-direction: column; gap: 12px; }
|
||||
.stack-md { display: flex; flex-direction: column; gap: 20px; }
|
||||
.stack-lg { display: flex; flex-direction: column; gap: 32px; }
|
||||
|
||||
.row { display: flex; align-items: center; gap: 12px; }
|
||||
.row-sm { gap: 8px; }
|
||||
.row-lg { gap: 20px; }
|
||||
.row-between { justify-content: space-between; }
|
||||
.row-center { justify-content: center; }
|
||||
.row-wrap { flex-wrap: wrap; }
|
||||
|
||||
.flex-1 { flex: 1; }
|
||||
.ml-auto { margin-left: auto; }
|
||||
.mt-auto { margin-top: auto; }
|
||||
|
||||
.text-0 { color: var(--text-0); }
|
||||
.text-1 { color: var(--text-1); }
|
||||
.text-2 { color: var(--text-2); }
|
||||
.text-3 { color: var(--text-3); }
|
||||
.text-cyan { color: var(--cyan); }
|
||||
.text-purple { color: var(--purple); }
|
||||
.text-amber { color: var(--amber); }
|
||||
.text-green { color: var(--green); }
|
||||
.text-red { color: var(--red); }
|
||||
|
||||
.text-xs { font-size: 11px; }
|
||||
.text-sm { font-size: 13px; }
|
||||
.text-md { font-size: 14px; }
|
||||
.text-lg { font-size: 16px; }
|
||||
.text-xl { font-size: 20px; }
|
||||
.text-2xl { font-size: 28px; }
|
||||
.text-3xl { font-size: 36px; }
|
||||
|
||||
.fw-400 { font-weight: 400; }
|
||||
.fw-500 { font-weight: 500; }
|
||||
.fw-600 { font-weight: 600; }
|
||||
.fw-700 { font-weight: 700; }
|
||||
.fw-800 { font-weight: 800; }
|
||||
|
||||
.tabular { font-variant-numeric: tabular-nums; }
|
||||
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
|
||||
/* ==========================================================================
|
||||
APP SHELL (for dashboard-style pages)
|
||||
========================================================================== */
|
||||
.app-shell {
|
||||
display: grid;
|
||||
grid-template-columns: 240px 1fr;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.app-side {
|
||||
border-right: 1px solid var(--border);
|
||||
background: rgba(2, 6, 23, 0.6);
|
||||
padding: 20px 14px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.app-side .brand { padding: 6px 10px 14px; }
|
||||
.side-group { display: flex; flex-direction: column; gap: 2px; }
|
||||
.side-label {
|
||||
font-size: 10px;
|
||||
color: var(--text-3);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
padding: 0 10px 8px;
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.side-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: var(--r-sm);
|
||||
font-size: 13px;
|
||||
color: var(--text-2);
|
||||
cursor: pointer;
|
||||
transition: all .12s;
|
||||
}
|
||||
.side-item:hover { color: var(--text-0); background: rgba(255,255,255,0.03); }
|
||||
.side-item.active { background: rgba(34, 211, 238, 0.08); color: var(--cyan); }
|
||||
.side-item .ico {
|
||||
width: 16px; height: 16px; opacity: 0.8;
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.side-item .count {
|
||||
margin-left: auto;
|
||||
font-size: 11px;
|
||||
color: var(--text-3);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
.side-item.active .count { color: var(--cyan); }
|
||||
|
||||
.app-main {
|
||||
min-width: 0; /* allow grid children to shrink */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.app-topbar {
|
||||
height: 60px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 32px;
|
||||
gap: 16px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: rgba(10, 14, 26, 0.75);
|
||||
backdrop-filter: blur(12px);
|
||||
}
|
||||
.app-topbar h1 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.app-content {
|
||||
padding: 32px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* user avatar pill */
|
||||
.avatar {
|
||||
width: 28px; height: 28px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #22d3ee, #a855f7);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #042f2e;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
KBD
|
||||
========================================================================== */
|
||||
kbd {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 20px;
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
color: var(--text-1);
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--border-2);
|
||||
border-bottom-width: 2px;
|
||||
border-radius: 4px;
|
||||
line-height: 1;
|
||||
}
|
||||
BIN
docs/design-drafts/v2/uploads/pasted-1776589344748-0.png
Normal file
BIN
docs/design-drafts/v2/uploads/pasted-1776589344748-0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 337 KiB |
BIN
docs/design-drafts/v2/uploads/pasted-1776589408607-0.png
Normal file
BIN
docs/design-drafts/v2/uploads/pasted-1776589408607-0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 KiB |
Reference in New Issue
Block a user