copy: second-round copy review across console, settings, agent and server messages

- Trim descriptions, empty states, confirm buttons and toasts to facts
- Unify terms per glossary: 待处理/待识别/整理记录/运行记录/凭证/子会话/Agent 上游
- Subscription-login termsNote states facts only (borrowed client identity, ToS/ban risk)
- Shorter, concrete goal templates for built-in Agent checks
- Human-readable nouns instead of table names in instance CRUD errors
- Audit labels for pi_upstream / schedule / automation_grant actions
- Glossary: add 上游、订阅登录、凭证、定时任务、巡检、运行记录、子会话、停止、持续授权

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-28 13:29:42 +08:00
1 parent c67f6a9682
commit 31a54186c7
58 files changed
+200 -188

No files matched your search

+3 -3
View File
@@ -10,7 +10,7 @@ const nullNum = (v: FieldValue | undefined) => (str(v) === "" ? null : Number(st
const RUNTIME_CARD: CardSpec = {
anchor: "runtime",
title: "上下文与子代理",
title: "上下文与子会话",
fields: [
{
name: "pi.contextBudget",
@@ -18,7 +18,7 @@ const RUNTIME_CARD: CardSpec = {
kind: "number",
hint: "4096–2000000,超过模型窗口时按窗口算",
},
{ name: "pi.subagentsEnabled", label: "允许子代理", kind: "switch" },
{ name: "pi.subagentsEnabled", label: "允许子会话", kind: "switch" },
{ name: "pi.compaction.enabled", label: "上下文压缩", kind: "switch" },
{
name: "pi.compaction.thresholdRatio",
@@ -49,7 +49,7 @@ const RUNTIME_CARD: CardSpec = {
const BUDGET_CARD: CardSpec = {
anchor: "budget",
title: "预算",
description: "超出每日预算的运行停在「超预算」,等你放行。",
description: "超出每日预算的运行停在「超出预算」,等你放行。",
fields: [
{ name: "budget.agentMaxRunsPerDay", label: "每日运行次数", kind: "number" },
{ name: "budget.agentMaxCostUsdPerDay", label: "每日费用(USD)", kind: "number" },
@@ -105,7 +105,7 @@ export function ApiKeysSection() {
section="api-keys"
anchor="keys"
title="API Key"
description="签发给外部程序(媒体服务器插件、脚本)的 Key,按权限范围授权。控制台走登录会话,不用 Key;浏览器本机 Token 在「账户与安全」。"
description="签发给外部程序(媒体服务器插件、脚本)的 Key,按权限范围授权。本机 Token 在「账户与安全」。"
plain
actions={
<Button size="sm" onClick={() => setOpen(true)}>
@@ -227,7 +227,7 @@ export function ApiKeysSection() {
onOpenChange={(o) => !o && setRevoking(null)}
title={`撤销「${revoking?.name ?? ""}」?`}
description="使用这个 Key 的程序立即失去访问权限,无法恢复。"
confirmLabel="撤销"
confirmLabel="撤销 Key"
destructive
onConfirm={async () => {
if (!revoking) return;
@@ -162,7 +162,7 @@ function Switches() {
description="从备份恢复后先开启:只核对库存与路径,不做自动写入。核对完再关闭。"
checked={s.automation.readOnlyReconcile}
disabled={update.isPending}
onChange={(v) => set({ readOnlyReconcile: v }, v ? "已进入只读对账" : "已退出只读对账")}
onChange={(v) => set({ readOnlyReconcile: v }, v ? "已开启只读对账" : "已关闭只读对账")}
/>
</div>
</SettingsBlock>
@@ -87,7 +87,7 @@ function DownloadersTable() {
config={{
noun: "下载器实例",
description:
"只接管带管理标签的任务。多个实例共用一块缓存盘时填相同的缓存卷标识,水位合并计算。点行编辑实例和它的路径映射。",
"只接管带管理标签的任务。多个实例共用一块缓存盘时填相同的缓存卷标识,水位合并计算。点行编辑实例和路径映射。",
emptyHint: "添加 qBittorrent 后才能提交下载。",
drawerExtra: (r) => <PathMappingsPanel viewKind="downloader" instanceId={r.id} />,
columns: [
@@ -181,7 +181,7 @@ function MediaServersTable() {
hooks={{ list, create, update, remove } as never}
config={{
noun: "媒体服务器",
description: "只用来查询作品有无、季集和版本;查不到不等于缺失。点行编辑实例和它的路径映射。",
description: "只用来查询作品有无、季集和版本;查不到不等于缺失。点行编辑实例和路径映射。",
drawerExtra: (r) => <PathMappingsPanel viewKind="media_server" instanceId={r.id} />,
columns: [
{
@@ -11,7 +11,7 @@ import { type CardSpec, SettingsCard, SettingsGate } from "./settings-card.tsx";
const TMDB_CARD: CardSpec = {
anchor: "tmdb",
title: "TMDB",
description: "这里没填时使用环境变量 TMDB_API_KEY / TMDB_ACCESS_TOKEN / TMDB_BASE_URL。",
description: "留空时用环境变量 TMDB_API_KEY / TMDB_ACCESS_TOKEN / TMDB_BASE_URL。",
fields: [
{
name: "metadata.tmdbApiKey",
@@ -211,7 +211,7 @@ export function PathMappingsPanel({
onOpenChange={(o) => !o && setDeleting(null)}
title="删除路径映射?"
description="依赖这条映射的路径换算会失效,磁盘文件不动。"
confirmLabel="删除"
confirmLabel="删除映射"
destructive
onConfirm={async () => {
if (!deleting) return;
@@ -102,7 +102,7 @@ export function PiOAuthLogin({
abortRef.current = ac;
while (!ac.signal.aborted) {
if (Date.now() > new Date(s.expiresAt).getTime()) {
finish("expired", "超过有效期仍未完成授权。");
finish("expired", "授权已超时。");
return;
}
try {
@@ -256,7 +256,7 @@ export function PiOAuthLogin({
) : (
<form onSubmit={submitPasted} noValidate className="grid gap-2 rounded-lg border border-border/60 p-3">
<p className="text-caption text-muted-foreground">
在自己的浏览器打开授权链接。授权后会跳到 localhost 地址,页面打不开是正常的,把地址栏里的完整 URL 粘到下面。
在浏览器打开授权链接。授权后会跳到 localhost 地址,页面打不开属正常,把地址栏里的完整 URL 粘贴到下面。
</p>
{session.authorizeUrl ? (
<div className="flex min-w-0 items-center gap-2">
@@ -7,7 +7,7 @@ import { Input } from "~/components/ui/input.tsx";
export const GROUP_LABEL: Record<PiProviderGroup, string> = {
api_key: "API Key",
subscription: "订阅登录",
cloud: "云凭据",
cloud: "云凭证",
custom: "兼容端点",
test: "测试",
};
@@ -16,7 +16,7 @@ export const GROUP_ORDER: PiProviderGroup[] = ["api_key", "subscription", "cloud
export const AUTH_LABEL: Record<PiAuthKind, string> = {
api_key: "API Key",
oauth: "订阅登录",
none: "不需要凭据",
none: "不需要凭证",
};
export const THINKING_LABEL: Record<PiThinkingLevel, string> = {
+3 -3
View File
@@ -10,7 +10,7 @@ import { errorMessage } from "~/lib/api.ts";
// 上游测试:默认只验凭据与模型(不产生费用);"发一次最小补全"真实调用一次模型
const ERROR_KIND: Record<string, string> = {
auth: "凭据被拒",
auth: "凭证被拒",
oauth: "登录失效",
model: "模型不可用",
network: "网络错误",
@@ -28,7 +28,7 @@ export function PiTestPanel({ upstream, model }: { upstream: PiUpstream; model?:
try {
const r = await test.mutateAsync({ id: upstream.id, body: { mode, ...(model ? { model } : {}) } });
setResult(r);
if (r.ok) toast.success(mode === "auth" ? "凭据可用" : "补全成功");
if (r.ok) toast.success(mode === "auth" ? "凭证可用" : "补全成功");
else toast.error(r.message ?? "测试失败");
} catch (err) {
setResult(null);
@@ -51,7 +51,7 @@ export function PiTestPanel({ upstream, model }: { upstream: PiUpstream; model?:
</Button>
</div>
<p className="text-meta text-muted-foreground">
「测试」只检查凭据和模型,不产生费用;最小补全会真实调用一次模型(最多 16 个 token)。
「测试」只检查凭证和模型,不产生费用。最小补全会调用一次模型(最多 16 个 token)。
</p>
{result ? (
<div className="flex flex-wrap items-center gap-2 text-caption">
@@ -51,7 +51,7 @@ export function PiUpstreamsBlock() {
section="agent"
anchor="upstream"
title="上游"
description="Agent 调用的模型上游。没指定上游的运行用默认上游;密钥和登录凭据只写入,不回显。"
description="没指定上游的运行用默认上游。密钥和登录凭证保存后不回显。"
plain
actions={
<Button size="sm" onClick={() => setWizard(true)}>
@@ -291,7 +291,7 @@ function UpstreamDrawer({
disabled={pending}
/>
<FieldControl
spec={{ name: "enabled", label: "启用", kind: "switch", hint: "停用后用它的运行直接失败" }}
spec={{ name: "enabled", label: "启用", kind: "switch", hint: "停用后,使用它的运行会失败" }}
value={v.enabled}
onChange={(x) => setV((s) => ({ ...s, enabled: x === true }))}
disabled={pending}
@@ -377,7 +377,7 @@ function UpstreamDrawer({
onOpenChange={setDeleting}
title={`删除上游「${upstream.name}」?`}
description={
upstream.isDefault ? "它是默认上游,删除后没有默认上游;凭据一并删除,无法恢复。" : "凭据一并删除,无法恢复。"
upstream.isDefault ? "它是默认上游,删除后没有默认上游;凭证一并删除,无法恢复。" : "凭证一并删除,无法恢复。"
}
confirmLabel="删除上游"
destructive
@@ -22,7 +22,7 @@ import { API_OPTIONS, AUTH_LABEL, GROUP_LABEL, GROUP_ORDER, ModelInput, THINKING
import { PiTestPanel } from "./pi-test.tsx";
import { FieldControl } from "./resource-section.tsx";
// 新建 Agent 上游:① 选 provider → ② 填凭据与模型并创建 → ③ 登录 / 测试,可设为默认
// 新建 Agent 上游:① 选 provider → ② 填凭证与模型并创建 → ③ 登录 / 测试,可设为默认
type Step = "provider" | "config" | "verify";
@@ -51,7 +51,7 @@ export function PiUpstreamWizard({ open, onClose }: { open: boolean; onClose: ()
step === "provider"
? "第 1 步,共 3 步:选 provider"
: step === "config"
? "第 2 步,共 3 步:凭据与模型"
? "第 2 步,共 3 步:凭证与模型"
: created?.authKind === "oauth"
? "第 3 步,共 3 步:登录并测试。上游已创建。"
: "第 3 步,共 3 步:测试。上游已创建。";
@@ -246,7 +246,7 @@ function ConfigStep({
) : null}
{authKind === "none" && provider.group === "cloud" ? (
<p className="mb-3 text-caption text-muted-foreground sm:col-span-2">
不存密钥,运行时读服务端进程的环境凭据(AWS 凭据链 / Google ADC)。
不存密钥,运行时读服务端进程的环境凭证(AWS 凭证链 / Google ADC)。
</p>
) : null}
{showBaseUrl ? (
@@ -145,7 +145,7 @@ export function ResourceSection<Row extends { id: string }>({
onOpenChange={(o) => !o && setDeleting(null)}
title={`删除${config.noun}?`}
description={config.deleteHint ?? "删除后无法恢复。"}
confirmLabel="删除"
confirmLabel={`删除${config.noun}`}
destructive
onConfirm={async () => {
if (!deleting) return;
+1 -1
View File
@@ -598,7 +598,7 @@ function AccountsBlock() {
onOpenChange={(o) => !o && setDeleting(null)}
title={`删除账号「${deleting?.label ?? ""}」?`}
description="账号和凭证一并删除,无法恢复。"
confirmLabel="删除"
confirmLabel="删除账号"
destructive
onConfirm={async () => {
if (!deleting) return;