feat(portal): i18n-ify DocsView + auth narrative panels

Extract all Chinese from DocsView.vue into docs.* namespace and add
auth.narrative.* sub-namespace for LoginView/RegisterView narrative slots.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mini
2026-04-21 01:42:32 +08:00
parent fc7e27671d
commit 73b3980711
5 changed files with 238 additions and 53 deletions

View File

@@ -499,7 +499,39 @@ export default {
invalidResetLink: 'Invalid Reset Link', invalidResetLink: 'Invalid Reset Link',
invalidResetLinkHint: 'This password reset link is invalid or has expired. Please request a new one.', invalidResetLinkHint: 'This password reset link is invalid or has expired. Please request a new one.',
requestNewResetLink: 'Request New Reset Link', requestNewResetLink: 'Request New Reset Link',
invalidOrExpiredToken: 'The password reset link is invalid or has expired. Please request a new one.' invalidOrExpiredToken: 'The password reset link is invalid or has expired. Please request a new one.',
narrative: {
common: {
statusLive: 'live',
},
login: {
kicker: '// you already paid for your subscriptions',
headlineN: 'N',
headlineOne: '1',
headlineSep: 'subscriptions →',
headlineSuffix: 'key',
sub1: 'No more juggling accounts,',
sub2: 'no more paying twice for overlapping subscriptions.',
tagline: 'PURO — AI calls, back to basics.',
},
register: {
kicker: '// up and running in 5 minutes',
headlineN: 'N',
headlineOne: '1',
headlineSep: 'subscriptions →',
headlineSuffix: 'key',
sub1: 'No more juggling accounts,',
sub2: 'no more paying twice for overlapping subscriptions.',
tagline: 'PURO — AI calls, back to basics.',
stepsTitle: '// next steps',
step1Title: 'Create account',
step1Desc: 'Email + password, or LinuxDO OAuth',
step2Title: 'Connect subscription',
step2Desc: 'OAuth into your existing Claude Pro / ChatGPT Plus',
step3Title: 'Get your API key',
step3Desc: 'Grab your sk-puro-… and swap the SDK\'s base_url',
},
},
}, },
// Dashboard // Dashboard
@@ -5680,4 +5712,61 @@ export default {
}, },
}, },
docs: {
nav: {
products: 'Product',
pricing: 'Pricing',
docs: 'Docs',
login: 'Sign in',
signup: 'Sign up',
},
hero: {
title: 'Quickstart — PURO AI',
subtitle: 'Three steps: get a key → set base_url → send a request',
},
sections: {
getKey: {
heading: '1. Get your API key',
desc: 'PURO AI is currently invite-only. Contact the admin to get access:',
note: 'Subscription self-purchase via the iShare portal is coming soon.',
},
codex: {
heading: '2. Codex CLI setup',
configIntro: 'Edit ~/.codex/config.toml:',
authIntro: 'Then ~/.codex/auth.json:',
verifyIntro: 'Verify:',
copy: 'Copy',
copied: 'Copied',
},
claudeCode: {
heading: '3. Claude Code setup',
configIntro: 'Edit ~/.claude/settings.json:',
note: 'Claude Code calls the Anthropic-compatible API via the /v1/messages endpoint.',
copy: 'Copy',
copied: 'Copied',
},
curl: {
heading: '4. curl quick test',
openaiIntro: 'OpenAI Responses API:',
anthropicIntro: 'Anthropic Messages API:',
copy: 'Copy',
copied: 'Copied',
},
models: {
heading: '5. Available models',
colModel: 'Model',
colPlatform: 'Platform / source',
colContext: 'Context',
colStatus: 'Status',
codexDedicated: 'OpenAI Codex dedicated',
note: 'Pricing is tracked live from model-price-repo. Full list available in the dashboard after signing in.',
noteConsole: 'dashboard',
},
feedback: {
heading: '6. Feedback',
desc: 'Run into an issue or want a new platform added:',
},
},
},
} }

View File

@@ -505,6 +505,38 @@ export default {
puroRegisterTitle: '创建账户', puroRegisterTitle: '创建账户',
puroRegisterSub: '5 分钟开始用 PURO AI', puroRegisterSub: '5 分钟开始用 PURO AI',
confirmPasswordLabel: '确认密码', confirmPasswordLabel: '确认密码',
narrative: {
common: {
statusLive: 'live',
},
login: {
kicker: '// 你的订阅,已经付过钱了',
headlineN: 'N',
headlineOne: '1',
headlineSep: '个订阅 →',
headlineSuffix: '个 key',
sub1: '省去切换账号的繁琐,',
sub2: '省去为多个高昂订阅重复买单。',
tagline: 'PURO纯粹—— 让 AI 调用回归本质。',
},
register: {
kicker: '// 5 分钟开始用',
headlineN: 'N',
headlineOne: '1',
headlineSep: '个订阅 →',
headlineSuffix: '个 key',
sub1: '省去切换账号的繁琐,',
sub2: '省去为多个高昂订阅重复买单。',
tagline: 'PURO纯粹—— 让 AI 调用回归本质。',
stepsTitle: '// 下一步',
step1Title: '创建账户',
step1Desc: '邮箱 + 密码,或用 LinuxDO OAuth',
step2Title: '绑定订阅',
step2Desc: 'OAuth 接入你现有的 Claude Pro / ChatGPT Plus',
step3Title: '生成 key',
step3Desc: '拿到 sk-puro-…,换掉 SDK 的 base_url',
},
},
}, },
// Dashboard // Dashboard
@@ -5873,4 +5905,61 @@ export default {
}, },
}, },
docs: {
nav: {
products: '产品',
pricing: '定价',
docs: '文档',
login: '登录',
signup: '注册',
},
hero: {
title: '快速接入 PURO AI',
subtitle: '三步走:拿 key → 配 base_url → 发请求',
},
sections: {
getKey: {
heading: '1. 获取 API key',
desc: '当前 PURO AI 不开放自助注册付费。联系管理员获取:',
note: '未来通过 iShare 入口开放订阅购买。',
},
codex: {
heading: '2. Codex CLI 接入',
configIntro: '修改 ~/.codex/config.toml',
authIntro: '然后 ~/.codex/auth.json',
verifyIntro: '验证:',
copy: '复制',
copied: '已复制',
},
claudeCode: {
heading: '3. Claude Code 接入',
configIntro: '修改 ~/.claude/settings.json',
note: 'Claude Code 通过 /v1/messages endpoint 调用 Anthropic 兼容 API。',
copy: '复制',
copied: '已复制',
},
curl: {
heading: '4. curl 直连测试',
openaiIntro: 'OpenAI Responses API',
anthropicIntro: 'Anthropic Messages API',
copy: '复制',
copied: '已复制',
},
models: {
heading: '5. 支持的模型',
colModel: '模型',
colPlatform: '平台 / 来源',
colContext: '上下文',
colStatus: '状态',
codexDedicated: 'OpenAI Codex 专用',
note: '后端 pricing 表实时跟进 model-price-repo完整清单登录后在控制台查看。',
noteConsole: '控制台',
},
feedback: {
heading: '6. 问题反馈',
desc: '遇到问题或希望补接某个平台:',
},
},
},
} }

View File

@@ -11,15 +11,17 @@
</router-link> </router-link>
<div> <div>
<div class="n-kicker">// 你的订阅,已经付过钱了</div> <div class="n-kicker">{{ t('auth.narrative.login.kicker') }}</div>
<div class="auth-narrative-headline" style="margin-top: 12px;"> <div class="auth-narrative-headline" style="margin-top: 12px;">
<span class="num-n">N</span> 个订阅<br> <span class="num-n">{{ t('auth.narrative.login.headlineN') }}</span>
<span class="num-1">1</span> key {{ ' ' + t('auth.narrative.login.headlineSep') + ' ' }}
<span class="num-1">{{ t('auth.narrative.login.headlineOne') }}</span>
{{ ' ' + t('auth.narrative.login.headlineSuffix') }}
</div> </div>
<p class="auth-narrative-sub"> <p class="auth-narrative-sub">
省去切换账号的繁琐<br> {{ t('auth.narrative.login.sub1') }}<br>
省去为多个高昂订阅重复买单<br> {{ t('auth.narrative.login.sub2') }}<br>
<span class="auth-narrative-tagline">PURO纯粹 AI 调用回归本质</span> <span class="auth-narrative-tagline">{{ t('auth.narrative.login.tagline') }}</span>
</p> </p>
</div> </div>

View File

@@ -11,31 +11,33 @@
</router-link> </router-link>
<div> <div>
<div class="n-kicker">// 5 分钟开始用</div> <div class="n-kicker">{{ t('auth.narrative.register.kicker') }}</div>
<div class="auth-narrative-headline" style="margin-top: 12px;"> <div class="auth-narrative-headline" style="margin-top: 12px;">
<span class="num-n">N</span> 个订阅<br> <span class="num-n">{{ t('auth.narrative.register.headlineN') }}</span>
<span class="num-1">1</span> key {{ ' ' + t('auth.narrative.register.headlineSep') + ' ' }}
<span class="num-1">{{ t('auth.narrative.register.headlineOne') }}</span>
{{ ' ' + t('auth.narrative.register.headlineSuffix') }}
</div> </div>
<p class="auth-narrative-sub"> <p class="auth-narrative-sub">
省去切换账号的繁琐<br> {{ t('auth.narrative.register.sub1') }}<br>
省去为多个高昂订阅重复买单<br> {{ t('auth.narrative.register.sub2') }}<br>
<span class="auth-narrative-tagline">PURO纯粹 AI 调用回归本质</span> <span class="auth-narrative-tagline">{{ t('auth.narrative.register.tagline') }}</span>
</p> </p>
</div> </div>
<div class="steps"> <div class="steps">
<div class="steps-title">// 下一步</div> <div class="steps-title">{{ t('auth.narrative.register.stepsTitle') }}</div>
<div class="step active"> <div class="step active">
<div class="step-num">1</div> <div class="step-num">1</div>
<div class="step-text"><b>创建账户</b> · 邮箱 + 密码或用 LinuxDO OAuth</div> <div class="step-text"><b>{{ t('auth.narrative.register.step1Title') }}</b> · {{ t('auth.narrative.register.step1Desc') }}</div>
</div> </div>
<div class="step"> <div class="step">
<div class="step-num">2</div> <div class="step-num">2</div>
<div class="step-text"><b>绑定订阅</b> · OAuth 接入你现有的 Claude Pro / ChatGPT Plus</div> <div class="step-text"><b>{{ t('auth.narrative.register.step2Title') }}</b> · {{ t('auth.narrative.register.step2Desc') }}</div>
</div> </div>
<div class="step"> <div class="step">
<div class="step-num">3</div> <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 class="step-text"><b>{{ t('auth.narrative.register.step3Title') }}</b> · {{ t('auth.narrative.register.step3Desc') }}</div>
</div> </div>
</div> </div>

View File

@@ -11,36 +11,36 @@
<span>PURO AI</span> <span>PURO AI</span>
</router-link> </router-link>
<div class="nav-links"> <div class="nav-links">
<router-link to="/">产品</router-link> <router-link to="/">{{ $t('docs.nav.products') }}</router-link>
<router-link to="/pricing">定价</router-link> <router-link to="/pricing">{{ $t('docs.nav.pricing') }}</router-link>
<router-link to="/docs" class="active">文档</router-link> <router-link to="/docs" class="active">{{ $t('docs.nav.docs') }}</router-link>
</div> </div>
<div class="nav-cta"> <div class="nav-cta">
<PuroLocaleSwitcher /> <PuroLocaleSwitcher />
<router-link to="/login" class="btn btn-ghost">登录</router-link> <router-link to="/login" class="btn btn-ghost">{{ $t('docs.nav.login') }}</router-link>
<router-link to="/register" class="btn btn-primary">注册</router-link> <router-link to="/register" class="btn btn-primary">{{ $t('docs.nav.signup') }}</router-link>
</div> </div>
</div> </div>
</nav> </nav>
<section class="docs-hero container"> <section class="docs-hero container">
<h1>快速接入 PURO AI</h1> <h1>{{ $t('docs.hero.title') }}</h1>
<p class="subtitle">三步走 key base_url 发请求</p> <p class="subtitle">{{ $t('docs.hero.subtitle') }}</p>
</section> </section>
<div class="container docs-body"> <div class="container docs-body">
<section id="get-key" class="docs-section"> <section id="get-key" class="docs-section">
<h2>1. 获取 API key</h2> <h2>{{ $t('docs.sections.getKey.heading') }}</h2>
<p>当前 PURO AI 不开放自助注册付费联系管理员获取</p> <p>{{ $t('docs.sections.getKey.desc') }}</p>
<div class="callout"> <div class="callout">
<a href="mailto:admin@puro.im">admin@puro.im</a> <a href="mailto:admin@puro.im">admin@puro.im</a>
</div> </div>
<p class="note">未来通过 iShare 入口开放订阅购买</p> <p class="note">{{ $t('docs.sections.getKey.note') }}</p>
</section> </section>
<section id="codex" class="docs-section"> <section id="codex" class="docs-section">
<h2>2. Codex CLI 接入</h2> <h2>{{ $t('docs.sections.codex.heading') }}</h2>
<p>修改 <code class="mono">~/.codex/config.toml</code></p> <p>{{ $t('docs.sections.codex.configIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -54,7 +54,7 @@
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.codex.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code>model_provider = <span class="str">"OpenAI"</span> <pre class="mono"><code>model_provider = <span class="str">"OpenAI"</span>
@@ -67,7 +67,7 @@ base_url = <span class="str">"https://ai.puro.im"</span>
wire_api = <span class="str">"responses"</span> wire_api = <span class="str">"responses"</span>
requires_openai_auth = <span class="kw">true</span></code></pre> requires_openai_auth = <span class="kw">true</span></code></pre>
</div> </div>
<p>然后 <code class="mono">~/.codex/auth.json</code></p> <p>{{ $t('docs.sections.codex.authIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -81,14 +81,14 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.codex.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code>{ <pre class="mono"><code>{
<span class="str">"OPENAI_API_KEY"</span>: <span class="str">"sk-xxxxxxxxxxxxxxxx"</span> <span class="str">"OPENAI_API_KEY"</span>: <span class="str">"sk-xxxxxxxxxxxxxxxx"</span>
}</code></pre> }</code></pre>
</div> </div>
<p>验证</p> <p>{{ $t('docs.sections.codex.verifyIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -102,7 +102,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.codex.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code><span class="cm">$</span> codex exec --sandbox read-only <span class="str">"say hi"</span></code></pre> <pre class="mono"><code><span class="cm">$</span> codex exec --sandbox read-only <span class="str">"say hi"</span></code></pre>
@@ -110,8 +110,8 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
</section> </section>
<section id="claude-code" class="docs-section"> <section id="claude-code" class="docs-section">
<h2>3. Claude Code 接入</h2> <h2>{{ $t('docs.sections.claudeCode.heading') }}</h2>
<p>修改 <code class="mono">~/.claude/settings.json</code></p> <p>{{ $t('docs.sections.claudeCode.configIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -125,7 +125,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.claudeCode.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code>{ <pre class="mono"><code>{
@@ -133,12 +133,12 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<span class="str">"api_key"</span>: <span class="str">"sk-xxxxxxxxxxxxxxxx"</span> <span class="str">"api_key"</span>: <span class="str">"sk-xxxxxxxxxxxxxxxx"</span>
}</code></pre> }</code></pre>
</div> </div>
<p class="note">Claude Code 通过 <code class="mono">/v1/messages</code> endpoint 调用 Anthropic 兼容 API</p> <p class="note">{{ $t('docs.sections.claudeCode.note') }}</p>
</section> </section>
<section id="curl" class="docs-section"> <section id="curl" class="docs-section">
<h2>4. curl 直连测试</h2> <h2>{{ $t('docs.sections.curl.heading') }}</h2>
<p>OpenAI Responses API</p> <p>{{ $t('docs.sections.curl.openaiIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -152,7 +152,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.curl.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code><span class="cm">$</span> curl https://ai.puro.im/responses \ <pre class="mono"><code><span class="cm">$</span> curl https://ai.puro.im/responses \
@@ -160,7 +160,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
-H <span class="str">"Content-Type: application/json"</span> \ -H <span class="str">"Content-Type: application/json"</span> \
-d <span class="str">'{"model":"gpt-5.4","input":"hello"}'</span></code></pre> -d <span class="str">'{"model":"gpt-5.4","input":"hello"}'</span></code></pre>
</div> </div>
<p>Anthropic Messages API</p> <p>{{ $t('docs.sections.curl.anthropicIntro') }}</p>
<div class="code-panel"> <div class="code-panel">
<div class="code-head"> <div class="code-head">
<div class="traffic"> <div class="traffic">
@@ -174,7 +174,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
<path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/> <path d="M4 1.5h5a.5.5 0 0 1 .5.5v1h1V2a1.5 1.5 0 0 0-1.5-1.5H4A1.5 1.5 0 0 0 2.5 2v8A1.5 1.5 0 0 0 4 11.5h1v-1H4a.5.5 0 0 1-.5-.5V2a.5.5 0 0 1 .5-.5z"/>
<path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/> <path d="M7 4.5A1.5 1.5 0 0 1 8.5 3h5A1.5 1.5 0 0 1 15 4.5v9a1.5 1.5 0 0 1-1.5 1.5h-5A1.5 1.5 0 0 1 7 13.5v-9z"/>
</svg> </svg>
复制 {{ $t('docs.sections.curl.copy') }}
</button> </button>
</div> </div>
<pre class="mono"><code><span class="cm">$</span> curl https://ai.puro.im/v1/messages \ <pre class="mono"><code><span class="cm">$</span> curl https://ai.puro.im/v1/messages \
@@ -186,15 +186,15 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
</section> </section>
<section id="models" class="docs-section"> <section id="models" class="docs-section">
<h2>5. 支持的模型</h2> <h2>{{ $t('docs.sections.models.heading') }}</h2>
<div class="table-wrap"> <div class="table-wrap">
<table class="models-table mono"> <table class="models-table mono">
<thead> <thead>
<tr> <tr>
<th>模型</th> <th>{{ $t('docs.sections.models.colModel') }}</th>
<th>平台 / 来源</th> <th>{{ $t('docs.sections.models.colPlatform') }}</th>
<th>上下文</th> <th>{{ $t('docs.sections.models.colContext') }}</th>
<th>状态</th> <th>{{ $t('docs.sections.models.colStatus') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@@ -206,7 +206,7 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
</tr> </tr>
<tr> <tr>
<td><code>gpt-5.4-codex</code></td> <td><code>gpt-5.4-codex</code></td>
<td><span class="provider gpt"><span class="dot"></span>OpenAI Codex 专用</span></td> <td><span class="provider gpt"><span class="dot"></span>{{ $t('docs.sections.models.codexDedicated') }}</span></td>
<td>272K</td> <td>272K</td>
<td><span class="badge-ok">OK</span></td> <td><span class="badge-ok">OK</span></td>
</tr> </tr>
@@ -237,12 +237,12 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
</tbody> </tbody>
</table> </table>
</div> </div>
<p class="note">后端 pricing 表实时跟进 <code class="mono">model-price-repo</code>完整清单登录后在 <router-link to="/dashboard">控制台</router-link> 查看</p> <p class="note">{{ $t('docs.sections.models.note') }}</p>
</section> </section>
<section id="feedback" class="docs-section"> <section id="feedback" class="docs-section">
<h2>6. 问题反馈</h2> <h2>{{ $t('docs.sections.feedback.heading') }}</h2>
<p>遇到问题或希望补接某个平台</p> <p>{{ $t('docs.sections.feedback.desc') }}</p>
<div class="callout"> <div class="callout">
<a href="mailto:admin@puro.im">admin@puro.im</a> <a href="mailto:admin@puro.im">admin@puro.im</a>
</div> </div>
@@ -252,8 +252,11 @@ requires_openai_auth = <span class="kw">true</span></code></pre>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { useI18n } from 'vue-i18n'
import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue' import PuroLocaleSwitcher from '@/components/puro/PuroLocaleSwitcher.vue'
const { t } = useI18n()
async function copyCode(ev: MouseEvent) { async function copyCode(ev: MouseEvent) {
const button = ev.currentTarget as HTMLButtonElement const button = ev.currentTarget as HTMLButtonElement
const panel = button.closest('.code-panel') const panel = button.closest('.code-panel')
@@ -262,7 +265,7 @@ async function copyCode(ev: MouseEvent) {
try { try {
await navigator.clipboard.writeText(codeEl.innerText) await navigator.clipboard.writeText(codeEl.innerText)
const original = button.innerHTML const original = button.innerHTML
button.innerHTML = '<svg viewBox="0 0 16 16" width="12" height="12" fill="currentColor"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg> 已复制' button.innerHTML = `<svg viewBox="0 0 16 16" width="12" height="12" fill="currentColor"><path d="M13.854 3.646a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708 0l-3.5-3.5a.5.5 0 1 1 .708-.708L6.5 10.293l6.646-6.647a.5.5 0 0 1 .708 0z"/></svg> ${t('docs.sections.codex.copied')}`
button.classList.add('copied') button.classList.add('copied')
setTimeout(() => { setTimeout(() => {
button.innerHTML = original button.innerHTML = original