fix(web): 控制台界面细节与 uhd_web 控件规范对齐

- 顶栏搜索移进右侧玻璃胶囊;输入框改淡玻璃(边线 0.46 → 0.16,悬停加深,聚焦 / 出错保留光晕)
- 新增 SegmentedControl(滑动 seg-thumb),替换站点、下载、订阅、文件、智能体列表及对话框里的实心选中块;Tabs 改为同一枚滑块
- 开关型按钮统一 outline + aria-pressed 选中外观;页头 / 工具栏次操作描边、图标钮描边圆;文件排序改 Select;下载总速度加标签
- 分页一页放得下时只留总数、空列表不渲染、按容器宽度收起按钮
- 运行状态存储区重排;智能体列表页与会话页共用外壳路由,标题与左栏不再重挂载
- 站点卡片「⋯」不再单独换行;设置导航、状态计数、多选条件改玻璃选中态

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-08 12:02:48 +08:00
1 parent 8ff1817845
commit c9b4ceb301
32 files changed
+753 -639

No files matched your search

@@ -11,6 +11,7 @@ import { Link } from "react-router";
import type { DataColumn } from "~/components/console/data-table.tsx";
import { consolePath } from "~/components/console/nav.ts";
import { StatusBadge } from "~/components/console/status-badge.tsx";
import { SegmentedControl } from "~/components/ui/segmented.tsx";
import { relTime } from "~/components/workbench/release-fields.ts";
import { cn, formatBytes, formatDateTime } from "~/lib/utils.ts";
import { formatEta, formatSpeed, liveStateLabel, organizeState, peersText, progressOf } from "./board-format.ts";
@@ -20,17 +21,28 @@ import { PlanActions, PlanWaitBadge } from "./plan-row.tsx";
// 下载面板(DL-2):顶部总速度 + 「进行中 / 已完成」分段;进行中看进度与速度,已完成看完成时间、分享率与整理状态。
// DL-3:进行中里还有没提交到下载器的下载(等你批准 / 等磁盘空间…),与下载任务按加入时间一起排。
/** 下载 / 上传速度一对(0 显示"—") */
export function SpeedPair({ dl, up, className }: { dl: number; up: number; className?: string }) {
/** 下载 / 上传速度一对(行内 0 显示"—";总速度一行传 showZero,空闲时显示 0 B/s,不留两道横线) */
export function SpeedPair({
dl,
up,
className,
showZero = false,
}: {
dl: number;
up: number;
className?: string;
showZero?: boolean;
}) {
const fmt = (v: number) => (showZero && !(v > 0) ? "0 B/s" : formatSpeed(v));
return (
<span className={cn("inline-flex items-center gap-2.5 tabular-nums whitespace-nowrap", className)}>
<span className="inline-flex items-center gap-0.5" title="下载速度">
<ArrowDown aria-label="下载" className="size-3.5 text-muted-foreground" />
{formatSpeed(dl)}
{fmt(dl)}
</span>
<span className="inline-flex items-center gap-0.5" title="上传速度">
<ArrowUp aria-label="上传" className="size-3.5 text-muted-foreground" />
{formatSpeed(up)}
{fmt(up)}
</span>
</span>
);
@@ -45,9 +57,10 @@ export function TransferLine({ transfer }: { transfer: DownloadBoard["transfer"]
? transfer.instances.map((i) => `${i.name}:↓ ${formatSpeed(i.dlSpeed)} ↑ ${formatSpeed(i.upSpeed)}`).join("\n")
: undefined;
return (
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-sm">
<span title={perInstance} className="text-foreground">
<SpeedPair dl={transfer.dlSpeed} up={transfer.upSpeed} />
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-caption">
<span title={perInstance ?? "所有下载器的实时总速度"} className="inline-flex items-center gap-2 text-foreground">
<span className="text-muted-foreground">总速度</span>
<SpeedPair dl={transfer.dlSpeed} up={transfer.upSpeed} showZero />
</span>
{down.length ? (
<span className="text-meta text-warning">{down.map((i) => `「${i.name}」`).join("")}连接失败</span>
@@ -65,36 +78,16 @@ export function BoardSegments({
counts: DownloadBoard["counts"] | undefined;
onChange: (v: DownloadBoardState) => void;
}) {
const options: { value: DownloadBoardState; label: string }[] = [
{ value: "active", label: "进行中" },
{ value: "completed", label: "已完成" },
];
return (
<div
role="radiogroup"
aria-label="下载任务分组"
className="inline-flex gap-0.5 rounded-xl border border-border p-0.5"
>
{options.map((o) => (
// biome-ignore lint/a11y/useSemanticElements: 分段按钮
<button
key={o.value}
type="button"
role="radio"
aria-checked={value === o.value}
onClick={() => onChange(o.value)}
className={cn(
"inline-flex items-center gap-1.5 rounded-lg px-3 py-1 text-caption transition-colors duration-micro",
value === o.value
? "bg-foreground text-background"
: "text-muted-foreground hover:bg-accent hover:text-foreground",
)}
>
{o.label}
{counts ? <span className="tabular-nums opacity-80">{counts[o.value]}</span> : null}
</button>
))}
</div>
<SegmentedControl
label="下载任务分组"
value={value}
onChange={onChange}
options={[
{ value: "active", label: "进行中", count: counts?.active },
{ value: "completed", label: "已完成", count: counts?.completed },
]}
/>
);
}