refactor(copy): 术语统一——收录版本、模型服务、智能体等(DEAI-2)

- 版本目标 → 收录版本(前后端用户可见字符串、能力描述、给模型的提示)
- Agent 上游 / 上游 → 模型服务;界面上的 Agent / Agent 会话 → 智能体 / 智能体会话
- 身份歧义 / 识别有歧义 / 无法确定作品 → 没识别出作品;待识别(池) → 未识别
- 建档 → 加入媒体库;资产 → 文件;库根 → 媒体库目录;纳管 → 本工具 / 外部任务;
  哨兵 → 标记文件 / 检查目录;站点快照 → 抓取;Feed / RSS 订阅 / 来源健康 → RSS
- 代码标识符、API 字段名、迁移文件不改;glossary.md 同步;smoke 选择器跟随

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 20:35:43 +08:00
1 parent d282212a6b
commit 4480fc346d
212 files changed
+741 -715

No files matched your search

@@ -216,7 +216,7 @@ export function DeleteSubscriptionsDialog({
open={open}
onOpenChange={onOpenChange}
title={ids.length > 1 ? `删除 ${ids.length} 个订阅?` : "删除这个订阅?"}
description="不再搜索和追更。已入库文件、版本目标和下载记录保留,可在订阅历史里重新订阅。"
description="不再搜索和追更。已入库文件、收录版本和下载记录保留,可在订阅历史里重新订阅。"
confirmLabel="删除"
destructive
onConfirm={() =>
@@ -22,7 +22,7 @@ import {
seasonTitle,
} from "./model.ts";
// 覆盖矩阵(设计 §2.1):列 = 集(电影 = 内容版本),行 = 版本目标,首行"库内任一版本"。
// 覆盖矩阵(设计 §2.1):列 = 集(电影 = 内容版本),行 = 收录版本,首行"库内任一版本"。
// 底色无彩,只有状态用状态色:已入库 success、缺失 destructive、可升级/延后 warning;在途用斜纹。
type Row = SubscriptionCoverage["rows"][number];
@@ -120,7 +120,7 @@ export function CoverageMatrix({
scope="col"
className="sticky left-0 z-10 bg-surface-raised pr-2 text-left font-normal text-muted-foreground"
>
版本目标
收录版本
</th>
{cols.map((c) => (
<th
@@ -238,7 +238,7 @@ export function CellSheet({
}
}}
title={sel ? `${sel.season} · ${columnLabel(sel.col)}` : ""}
description={sel ? `${sel.row.name}${sel.row.slotId === null ? "(作品全部资产)" : ""}` : null}
description={sel ? `${sel.row.name}${sel.row.slotId === null ? "(作品全部文件)" : ""}` : null}
footer={
sel && c ? (
<>
@@ -284,7 +284,7 @@ export function CellSheet({
{ label: "pri", value: c.pri !== null ? String(c.pri) : null },
{ label: "可升级", value: c.state === "have" ? (c.upgradable ? "是,低于目标档位" : "否") : null },
{
label: "资产",
label: "文件",
value: c.assetId ? (
mediaEntityId ? (
<Link
@@ -316,7 +316,7 @@ export function CellSheet({
</p>
) : null}
{sel.row.slotId === null && c.state === "missing" ? (
<p className="text-meta text-muted-foreground">这一行汇总全部版本。要补全,点具体版本目标那一行的格。</p>
<p className="text-meta text-muted-foreground">这一行汇总全部版本。要补全,点具体收录版本那一行的格。</p>
) : null}
</>
) : null}
@@ -86,7 +86,7 @@ describe("覆盖矩阵格", () => {
expect(moveItem(["a", "b", "c"], 2, 0)).toEqual(["c", "a", "b"]);
expect(moveItem(["a", "b"], 0, 5)).toEqual(["a", "b"]);
});
it("升级策略取自版本目标", () => {
it("升级策略取自收录版本", () => {
expect(upgradeOf([{ upgradeThreshold: {} }], "series")).toBe("off");
expect(upgradeOf([{ upgradeThreshold: { enabled: true, mode: "full_pack" } }], "series")).toBe("full_pack");
expect(upgradeOf([{ upgradeThreshold: { enabled: true } }], "movie")).toBe("movie");
@@ -55,7 +55,7 @@ export function seasonLabel(
return x.seasonScope === "following_seasons" && x.seasonNumber > 0 ? `${base} 起` : base;
}
/** 版本目标徽标:洗版中 → 在途 → 缺 N → 已齐;都没有 → 已入库 N */
/** 收录版本徽标:洗版中 → 在途 → 缺 N → 已齐;都没有 → 已入库 N */
export function slotBadge(s: SubscriptionSlotSummary): ToneLabel {
if (s.upgrading)
return {
@@ -136,7 +136,7 @@ export const PLAN_STAGE: Record<string, string> = { reserved: "已预留", downl
export const SKIP_REASON: Record<string, string> = {
out_of_range: "集范围外",
not_aired: "未播出",
not_required: "该版本目标不要求这个内容版本",
not_required: "该收录版本不要求这个内容版本",
suspected: "总集数未知,按已见最高集推断",
deferred: "有延后的资源",
};
@@ -155,7 +155,7 @@ export function seasonTitle(s: SubscriptionCoverage["seasons"][number], kind: Su
return s.seasonNumber === 0 ? "特别篇" : `第 ${s.seasonNumber} 季`;
}
// ── 版本目标 ──
// ── 收录版本 ──
export type UpgradeStrategy = "off" | "per_unit" | "full_pack" | "movie";
export const UPGRADE_LABEL: Record<UpgradeStrategy, string> = {
@@ -165,7 +165,7 @@ export const UPGRADE_LABEL: Record<UpgradeStrategy, string> = {
movie: "洗版",
};
/** 订阅版本目标当前的升级策略:任一开启取其 mode */
/** 订阅收录版本当前的升级策略:任一开启取其 mode */
export function upgradeOf(
slots: Pick<AcquisitionSlot, "upgradeThreshold">[],
kind: SubscriptionMediaKind,
@@ -231,7 +231,7 @@ export type SubscriptionDraft = {
include: string;
exclude: string;
keywords: string;
/** MP 分辨率 / 资源质量 / 特效预设(写到该订阅自己的版本目标) */
/** MP 分辨率 / 资源质量 / 特效预设(写到该订阅自己的收录版本) */
resolution: SubscriptionResolution | "";
source: SubscriptionSourcePreset | "";
effect: SubscriptionEffect | "";
@@ -334,7 +334,7 @@ const isInt = (s: string) => /^\d+$/.test(s.trim());
/**
* 草稿 → params(PATCH 整体替换,所以以现有 params 为底保留未知键)。
* upgradeChanged:只有升级策略改了才带 best_version(否则每次保存都会 bump 版本目标 revision)。
* upgradeChanged:只有升级策略改了才带 best_version(否则每次保存都会 bump 收录版本 revision)。
*/
export function draftToParams(
d: SubscriptionDraft,
@@ -26,7 +26,9 @@ function Flags({ x }: { x: Item }) {
return (
<>
{x.group === "paused" ? <StatusBadge tone="neutral">暂停</StatusBadge> : null}
{x.group === "pending" ? <StatusBadge tone="warning">{x.mediaEntityId ? "待定" : "尚未建档"}</StatusBadge> : null}
{x.group === "pending" ? (
<StatusBadge tone="warning">{x.mediaEntityId ? "待定" : "不在媒体库"}</StatusBadge>
) : null}
{x.upgrading ? <StatusBadge tone="warning">洗版中</StatusBadge> : null}
{x.awaitingNewSeason ? <StatusBadge tone="success">等待新季</StatusBadge> : null}
</>
@@ -72,7 +74,7 @@ export function ProgressBlock({ x, className }: { x: Pick<Item, "progress" | "mo
}
export function SlotBadges({ slots, max = 4 }: { slots: Item["slots"]; max?: number }) {
if (slots.length === 0) return <span className="text-meta text-muted-foreground">没有版本目标</span>;
if (slots.length === 0) return <span className="text-meta text-muted-foreground">没有收录版本</span>;
return (
<div className="flex min-w-0 flex-wrap gap-1">
{slots.slice(0, max).map((s) => {
@@ -4,7 +4,7 @@ import { Button } from "~/components/ui/button.tsx";
import { cn } from "~/lib/utils.ts";
import { moveItem } from "./model.ts";
// 版本目标排序:拖动(桌面)或上移/下移(键盘、手机)。第一个是主版本。
// 收录版本排序:拖动(桌面)或上移/下移(键盘、手机)。第一个是主版本。
export function SlotOrderList<T extends { id: string; name: string }>({
items,
@@ -16,7 +16,7 @@ import {
import { SlotOrderList } from "./slot-order.tsx";
import { AutoFillFields } from "./subscription-form.tsx";
// 对比视图的"版本目标"区(设计 §2.2):拖动排序(PUT slot-order)、行尾计数、补全此版本、自动补全开关与预算。
// 对比视图的"收录版本"区(设计 §2.2):拖动排序(PUT slot-order)、行尾计数、补全此版本、自动补全开关与预算。
type SlotRow = SubscriptionCoverage["rows"][number] & { id: string };
@@ -83,7 +83,7 @@ export function SlotPanel({
return (
<Panel
title="版本目标"
title="收录版本"
description="拖动调整顺序,第一个是主版本。主版本缺的照常追更;其他版本按下面的自动补全设置补齐。"
actions={
dirty ? (
@@ -101,7 +101,7 @@ export function SlotPanel({
>
{rows.length === 0 ? (
<p className="text-caption text-muted-foreground">
{sub.mediaEntityId ? "还没有版本目标。在下方规则区新建。" : "作品建档后才有版本目标。"}
{sub.mediaEntityId ? "还没有收录版本。在下方规则区新建。" : "作品加入媒体库后才有收录版本。"}
</p>
) : (
<SlotOrderList
@@ -173,7 +173,7 @@ function WorkPicker({ onPick, error }: { onPick: (t: Target) => void; error?: st
kind: "title",
titleQuery: c.year ? `${c.title} ${c.year}` : c.title,
mediaKind: c.entityType === "movie" ? "movie" : "series",
note: `TMDB ${c.externalId},建档失败,按标题追踪`,
note: `TMDB ${c.externalId},加入媒体库失败,按标题追踪`,
});
};
const row = (key: string, main: ReactNode, sub: string, onClick: () => void) => (
@@ -321,7 +321,7 @@ export function SubscriptionFormDialog({
const [totalTouched, setTotalTouched] = useState(false);
const [errors, setErrors] = useState<DraftErrors & { target?: string }>({});
const [dup, setDup] = useState<string | null>(null);
// 编辑:版本目标到了再回填升级策略
// 编辑:收录版本到了再回填升级策略
useEffect(() => {
if (sub && slotsQ.isSuccess && !upgradeTouched) setD((x) => ({ ...x, upgrade: initialUpgrade }));
}, [sub, slotsQ.isSuccess, initialUpgrade, upgradeTouched]);
@@ -454,7 +454,7 @@ export function SubscriptionFormDialog({
<DialogHeader>
<DialogTitle>{editing ? "编辑订阅" : "新建订阅"}</DialogTitle>
<DialogDescription>
{editing ? titleText : initialTarget ? titleText : "从媒体库或 TMDB 选作品;选 TMDB 条目会先建档。"}
{editing ? titleText : initialTarget ? titleText : "从媒体库或 TMDB 选作品;选 TMDB 条目会先加入媒体库。"}
</DialogDescription>
</DialogHeader>
{dup ? (
@@ -497,8 +497,8 @@ export function SubscriptionFormDialog({
{target.kind === "entity"
? "媒体库作品"
: target.kind === "tmdb"
? "TMDB 条目,保存时建档"
: (target.note ?? "按标题追踪,尚未建档")}
? "TMDB 条目,保存时加入媒体库"
: (target.note ?? "按标题追踪,不在媒体库")}
</p>
</div>
{initialTarget ? null : (
@@ -541,7 +541,9 @@ export function SubscriptionFormDialog({
<FormField
label="季"
htmlFor={f("season")}
hint={editing ? "季建好后不能改;要换季就新建订阅" : entityId ? undefined : "作品建档后可选季"}
hint={
editing ? "季建好后不能改;要换季就新建订阅" : entityId ? undefined : "作品加入媒体库后可选季"
}
>
<Pick
id={f("season")}
@@ -648,7 +650,7 @@ export function SubscriptionFormDialog({
options={(dls.data?.items ?? []).map((x) => ({ value: x.id, label: x.name }))}
/>
</FormField>
<FormField label="库根" htmlFor={f("root")}>
<FormField label="媒体库目录" htmlFor={f("root")}>
<Pick
id={f("root")}
value={d.libraryRootId}
@@ -668,12 +670,12 @@ export function SubscriptionFormDialog({
</FormField>
</Section>
<Section title="版本目标">
<Section title="收录版本">
{editing ? (
slotsQ.isPending ? (
<p className="text-caption text-muted-foreground">读取版本目标…</p>
<p className="text-caption text-muted-foreground">读取收录版本…</p>
) : subSlots.length === 0 ? (
<p className="text-caption text-muted-foreground">还没有版本目标。在对比视图里新建。</p>
<p className="text-caption text-muted-foreground">还没有收录版本。在对比视图里新建。</p>
) : (
<FormField label="顺序" htmlFor={f("order")} hint="第一个是主版本;其他版本按自动补全设置补齐">
<SlotOrderList
@@ -689,8 +691,8 @@ export function SubscriptionFormDialog({
htmlFor={f("tpl")}
hint={
target?.kind === "entity"
? "作品还没有版本目标时按模板顺序创建,第一个是主版本"
: "作品建档后按模板创建版本目标"
? "作品还没有收录版本时按模板顺序创建,第一个是主版本"
: "作品加入媒体库后按模板创建收录版本"
}
>
<Pick
@@ -708,7 +710,7 @@ export function SubscriptionFormDialog({
<FormField
label="升级策略"
htmlFor={f("upgrade")}
hint="作用于这个订阅的全部版本目标;已入库低于目标档位时继续找更好的"
hint="作用于这个订阅的全部收录版本;已入库低于目标档位时继续找更好的"
>
<Pick
id={f("upgrade")}
@@ -730,7 +732,7 @@ export function SubscriptionFormDialog({
<FormField
label="规则组"
htmlFor={f("groups")}
hint="按选择顺序串联;不选 = 订阅默认规则组。版本目标自己的规则组另算"
hint="按选择顺序串联;不选 = 订阅默认规则组。收录版本自己的规则组另算"
>
{groups.isPending ? (
<p className="text-caption text-muted-foreground">读取规则组…</p>
@@ -810,7 +812,7 @@ export function SubscriptionFormDialog({
</FormField>
</div>
<p className="text-meta text-muted-foreground">
分辨率、资源质量、特效写进这个订阅自己的版本目标;UHD 和杜比全景声按标题匹配,与「包含」同时生效。
分辨率、资源质量、特效写进这个订阅自己的收录版本;UHD 和杜比全景声按标题匹配,与「包含」同时生效。
</p>
<FormField
label="搜索关键词"