diff --git a/apps/server/src/modules/acquisition-organize/routes/library.ts b/apps/server/src/modules/acquisition-organize/routes/library.ts
index 5de308e3..c5461664 100644
--- a/apps/server/src/modules/acquisition-organize/routes/library.ts
+++ b/apps/server/src/modules/acquisition-organize/routes/library.ts
@@ -1,4 +1,5 @@
import { join } from "node:path";
+import { simplifiedChineseTagline } from "@umt/adapters";
import {
type AcquisitionPlan,
type AcquisitionSlot,
@@ -405,6 +406,7 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
finishedAt: r.finished_at ? new Date(r.finished_at).toISOString() : null,
})),
scrape: await latestEntityScrape(d, id),
+ displayTagline: heroTagline(e!.metadata),
};
return detail;
},
@@ -642,3 +644,11 @@ export function registerLibraryRoutes(app: ModuleApp, d: AoDeps) {
{ tag: TAG },
);
}
+
+/** 详情页头部的标语:只显示简体中文(见 simplifiedChineseTagline);存储的标语与语言不变 */
+function heroTagline(metadata: unknown): string | null {
+ const m = (metadata ?? {}) as Record;
+ const text = typeof m.tagline?.value === "string" ? m.tagline.value : null;
+ const orig = typeof m.originalLanguage?.value === "string" ? m.originalLanguage.value : null;
+ return simplifiedChineseTagline(text, m.tagline?.language ?? null, orig);
+}
diff --git a/apps/server/src/modules/search/routes.ts b/apps/server/src/modules/search/routes.ts
index 14d3707d..8006c8eb 100644
--- a/apps/server/src/modules/search/routes.ts
+++ b/apps/server/src/modules/search/routes.ts
@@ -3,6 +3,7 @@ import {
fetchRichDetail,
fetchRichSeason,
type MetadataCandidate,
+ simplifiedChineseTagline,
TmdbError,
type TmdbRichDetail,
} from "@umt/adapters";
@@ -453,7 +454,7 @@ export function registerSearchRoutes(app: ModuleApp, ctx: ServerContext, d: Sear
out.title = rich.title || out.title;
out.overview = rich.overview ?? out.overview;
out.titleZh = rich.titleZh;
- out.tagline = rich.tagline;
+ out.tagline = simplifiedChineseTagline(rich.tagline, rich.taglineLanguage, rich.originalLanguage);
out.genres = rich.genres;
out.voteAverage = rich.voteAverage;
out.voteCount = rich.voteCount;
diff --git a/apps/server/test/acquisition-organize/library-versions.test.ts b/apps/server/test/acquisition-organize/library-versions.test.ts
index 2dee71d2..f4d157bf 100644
--- a/apps/server/test/acquisition-organize/library-versions.test.ts
+++ b/apps/server/test/acquisition-organize/library-versions.test.ts
@@ -336,4 +336,32 @@ describe.skipIf(!hasPg)("LIBV-1 入库版本", () => {
expect(response.body.data.downloads).toHaveLength(105);
expect(response.body.data.droppedPlans).toHaveLength(51);
});
+
+ it("作品详情头部标语只给简体中文(displayTagline);存储的 metadata.tagline 不变", async () => {
+ const field = (value: unknown, language: string | null = null) => ({
+ value,
+ source: "tmdb",
+ language,
+ at: "2026-10-01T00:00:00.000Z",
+ locked: false,
+ });
+ const cases: [string, string, string | null][] = [
+ ["我们注定要离开地球", "en", "我们注定要离开地球"],
+ ["我們註定要離開地球", "en", null],
+ ["Mankind was born on Earth.", "en", null],
+ ["我們註定要離開地球", "zh", "我們註定要離開地球"],
+ ];
+ for (const [tagline, orig, want] of cases) {
+ const entityId = await seedEntity(h.db, { kind: "movie", title: `标语 ${tagline}`, year: 2026 });
+ await h.db
+ .updateTable("media_entity")
+ .set({ metadata: JSON.stringify({ tagline: field(tagline), originalLanguage: field(orig) }) })
+ .where("id", "=", entityId)
+ .execute();
+ const r = await h.api("GET", `/library/${entityId}`);
+ expect(r.status).toBe(200);
+ expect(r.body.data.displayTagline).toBe(want);
+ expect(r.body.data.entity.metadata.tagline.value).toBe(tagline);
+ }
+ });
});
diff --git a/apps/server/test/file-manager/scrape.test.ts b/apps/server/test/file-manager/scrape.test.ts
index 0fa34f8b..ff2a9bf5 100644
--- a/apps/server/test/file-manager/scrape.test.ts
+++ b/apps/server/test/file-manager/scrape.test.ts
@@ -33,6 +33,7 @@ function detail(
overview: null,
overviewLanguage: null,
tagline: null,
+ taglineLanguage: null,
year: null,
releaseDate: null,
lastAirDate: null,
diff --git a/apps/server/test/scrape/scrape-layout-mp.test.ts b/apps/server/test/scrape/scrape-layout-mp.test.ts
index dad8d895..efd56102 100644
--- a/apps/server/test/scrape/scrape-layout-mp.test.ts
+++ b/apps/server/test/scrape/scrape-layout-mp.test.ts
@@ -68,6 +68,7 @@ const detail = (entityType: "movie" | "tv", externalId: string): TmdbRichDetail
overview: null,
overviewLanguage: null,
tagline: null,
+ taglineLanguage: null,
year: 2022,
releaseDate: null,
lastAirDate: null,
diff --git a/apps/web/app/app.css b/apps/web/app/app.css
index e863b6d3..64d80459 100644
--- a/apps/web/app/app.css
+++ b/apps/web/app/app.css
@@ -352,6 +352,16 @@
--mat-pill-secondary-bg: rgba(20, 22, 28, 0.03);
--mat-pill-secondary-bg-hover: rgba(20, 22, 28, 0.07);
--mat-pill-secondary-border: rgba(20, 22, 28, 0.12);
+ /* pill-solid:详情页头图区的唯一主操作(订阅),主色实心;只用在 MediaHero 的操作行 */
+ --mat-pill-solid-bg: var(--primary);
+ --mat-pill-solid-bg-hover: color-mix(in oklab, var(--primary) 84%, var(--background));
+ --mat-pill-solid-fg: var(--primary-foreground);
+ --mat-pill-solid-shadow: 0 1px 2px rgba(20, 22, 28, 0.14), 0 6px 16px -8px rgba(20, 22, 28, 0.4);
+ /* media-hero:详情页头图。剧照铺在内容区顶部、渐隐到页面底色;左侧文字区压近乎不透明的底色,右侧露出剧照。
+ 窄屏海报与文字上下排,改为自上而下压底色 */
+ --mat-hero-image-opacity: 1;
+ --mat-hero-scrim-side: linear-gradient(90deg, var(--background) 0%, color-mix(in oklab, var(--background) 96%, transparent) 38%, color-mix(in oklab, var(--background) 82%, transparent) 55%, color-mix(in oklab, var(--background) 40%, transparent) 72%, transparent 92%);
+ --mat-hero-scrim-stack: linear-gradient(180deg, color-mix(in oklab, var(--background) 30%, transparent) 0%, color-mix(in oklab, var(--background) 72%, transparent) 45%, color-mix(in oklab, var(--background) 92%, transparent) 75%, var(--background) 100%);
/* 分段控件的滑块(侧栏当前项、SegmentedTabs):日间是托起的白片,夜间是薄白玻璃 */
--mat-seg-thumb-bg: rgba(255, 255, 255, 0.8);
--mat-seg-thumb-border: rgba(20, 22, 28, 0.06);
@@ -493,6 +503,13 @@
--mat-pill-secondary-bg: rgba(255, 255, 255, 0.04);
--mat-pill-secondary-bg-hover: rgba(255, 255, 255, 0.1);
--mat-pill-secondary-border: rgba(255, 255, 255, 0.15);
+ --mat-pill-solid-bg: var(--primary);
+ --mat-pill-solid-bg-hover: color-mix(in oklab, var(--primary) 84%, var(--background));
+ --mat-pill-solid-fg: var(--primary-foreground);
+ --mat-pill-solid-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 16px -8px rgba(0, 0, 0, 0.6);
+ --mat-hero-image-opacity: 0.85;
+ --mat-hero-scrim-side: linear-gradient(90deg, var(--background) 0%, color-mix(in oklab, var(--background) 94%, transparent) 38%, color-mix(in oklab, var(--background) 78%, transparent) 55%, color-mix(in oklab, var(--background) 36%, transparent) 72%, transparent 92%);
+ --mat-hero-scrim-stack: linear-gradient(180deg, color-mix(in oklab, var(--background) 30%, transparent) 0%, color-mix(in oklab, var(--background) 72%, transparent) 45%, color-mix(in oklab, var(--background) 92%, transparent) 75%, var(--background) 100%);
--mat-seg-thumb-bg: rgba(255, 255, 255, 0.08);
--mat-seg-thumb-border: rgba(255, 255, 255, 0.15);
--mat-seg-thumb-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18), 0 4px 18px -6px rgba(0, 0, 0, 0.4);
@@ -1313,9 +1330,70 @@
color: var(--content-strong);
}
+ /* 实心主操作:只给详情页头图区的唯一主操作(订阅)用(2026-10-07 用户定的例外),
+ 其余页面主操作仍是 pill-primary;hover 只微调明度,不换色 */
+ .pill-solid {
+ background: var(--mat-pill-solid-bg);
+ border: 1px solid transparent;
+ color: var(--mat-pill-solid-fg);
+ box-shadow: var(--mat-pill-solid-shadow);
+ transition:
+ background-color var(--duration-base) var(--ease-standard),
+ transform 220ms var(--ease-spring);
+ }
+
+ .pill-solid:hover {
+ background: var(--mat-pill-solid-bg-hover);
+ }
+
+ /* 详情页头图(MediaHero):剧照层上下、右缘渐隐成透明,露出页面底色与光斑,不留硬边;
+ ::after 是压在剧照上的底色渐变(左侧文字区近乎不透明),参数见 --mat-hero-* */
+ .media-hero-backdrop {
+ -webkit-mask-image:
+ linear-gradient(to bottom, transparent 0%, #000 14%, #000 55%, transparent 100%),
+ linear-gradient(to right, #000 0%, #000 90%, transparent 100%);
+ -webkit-mask-composite: source-in;
+ mask-image:
+ linear-gradient(to bottom, transparent 0%, #000 14%, #000 55%, transparent 100%),
+ linear-gradient(to right, #000 0%, #000 90%, transparent 100%);
+ mask-composite: intersect;
+ }
+
+ .media-hero-backdrop > img {
+ opacity: var(--mat-hero-image-opacity);
+ }
+
+ .media-hero-backdrop::after {
+ content: "";
+ position: absolute;
+ inset: 0;
+ background: var(--mat-hero-scrim-stack);
+ }
+
+ @media (min-width: 640px) {
+ .media-hero-backdrop::after {
+ background: var(--mat-hero-scrim-side);
+ }
+ }
+
+ /* 折叠的长文本(ClampText):最后一行行尾渐隐,「展开」压在渐隐处;行数由 --clamp-lines 给 */
+ .clamp-fade {
+ max-height: calc(var(--clamp-lines, 3) * 1lh);
+ overflow: hidden;
+ -webkit-mask-image: linear-gradient(#000, #000), linear-gradient(to right, #000 62%, transparent 88%);
+ -webkit-mask-size: 100% calc(100% - 1lh), 100% 1lh;
+ -webkit-mask-position: top, bottom;
+ -webkit-mask-repeat: no-repeat;
+ mask-image: linear-gradient(#000, #000), linear-gradient(to right, #000 62%, transparent 88%);
+ mask-size: 100% calc(100% - 1lh), 100% 1lh;
+ mask-position: top, bottom;
+ mask-repeat: no-repeat;
+ }
+
/* 按下回弹,与 .btn-glass 同一手感 */
.pill-primary:active,
- .pill-secondary:active {
+ .pill-secondary:active,
+ .pill-solid:active {
transform: scale(0.97);
}
diff --git a/apps/web/app/components/agent/agent-conversation.tsx b/apps/web/app/components/agent/agent-conversation.tsx
index 23d5fbf6..76b2ccca 100644
--- a/apps/web/app/components/agent/agent-conversation.tsx
+++ b/apps/web/app/components/agent/agent-conversation.tsx
@@ -86,7 +86,7 @@ export const BUDGET_LABEL: Record = {
const COMPACT_STATUS: Record = {
completed: "已完成",
declined: "没有压缩",
- aborted: "已中止",
+ aborted: "已停止",
failed: "失败",
};
@@ -108,7 +108,7 @@ export function contextLabel(context: string | null): string | null {
const first = context.split(" · ")[0] ?? "";
// 前缀里第一段是界面给的名称(「作品:沙丘2」);没有名称时只剩 key=ID,不上屏
if (!first || first.includes("=")) return null;
- return first.replace(/^(作品|资源|下载|整理记录|站点|任务|引擎任务)[::]\s*/, "");
+ return first.replace(/^(作品|资源|下载|整理记录|站点|任务|引擎任务|后台任务)[::]\s*/, "");
}
export function UserMessage({
@@ -289,7 +289,7 @@ export function StepSummary({
const thinking = activity.some((a) => a.kind === "thinking");
const head = current
? `${done ? `${done},` : ""}${runningText(current)}…`
- : done || (running ? "思考中…" : thinking ? "想了一下" : "过程说明");
+ : done || (running ? "思考中…" : thinking ? "思考过程" : "过程说明");
return (
{shortGoalTitle(parent.goal)}
- 第 {run.depth} 层子会话 · 继承父会话的发起人与授权上限 · 能力 {caps} 项(⊆ 父会话)· 划拨预算{" "}
+ 第 {run.depth} 层子会话 · 权限不超过父会话 · 可用能力 {caps} 项 · 预算{" "}
{budgetText(run.budget as Record)}
{run.spawnReason ? ` · 理由:${run.spawnReason}` : ""}
@@ -129,7 +129,7 @@ export function SubagentTree({ snapshot, className }: { snapshot: RunSnapshot; c
- 子会话 {children.length} 个{familyUsage ? : null}
+ {children.length} 个子会话{familyUsage ? : null}