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:
1 parent
8ff1817845
commit
c9b4ceb301
32 files changed
+753
-639
No files matched your search
@@ -2,9 +2,9 @@ import type { SiteAccount, SiteCredentialsBody } from "@umt/contracts";
|
||||
import { FormField } from "~/components/console/form-field.tsx";
|
||||
import { SecretInput } from "~/components/console/secret-input.tsx";
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { SegmentedControl } from "~/components/ui/segmented.tsx";
|
||||
import { Switch } from "~/components/ui/switch.tsx";
|
||||
import { secretHint, secretPatch } from "~/lib/secret.ts";
|
||||
import { cn } from "~/lib/utils.ts";
|
||||
import { AUTH_KIND_LABEL, type LoginKind } from "./site-fields.ts";
|
||||
|
||||
// 凭证三选一(账号密码 / Cookie / API Key)。只写不回显;添加站点向导与"更新凭证"对话框共用。
|
||||
@@ -106,29 +106,14 @@ export function CredentialFields({
|
||||
return (
|
||||
<div className="grid gap-x-4 gap-y-1">
|
||||
{kinds.length > 1 ? (
|
||||
<div className="mb-4" role="radiogroup" aria-label="凭证类型">
|
||||
<div className="mb-4">
|
||||
<p className="mb-1.5 text-caption font-medium text-foreground">凭证类型</p>
|
||||
<div className="inline-flex flex-wrap gap-1 rounded-xl border border-border p-1">
|
||||
{kinds.map((k) => (
|
||||
// biome-ignore lint/a11y/useSemanticElements: 分段按钮,radio 语义由 role 表达
|
||||
<button
|
||||
key={k}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={value.kind === k}
|
||||
disabled={disabled}
|
||||
onClick={() => set("kind", k)}
|
||||
className={cn(
|
||||
"rounded-lg px-3 py-1.5 text-caption transition-colors",
|
||||
value.kind === k
|
||||
? "bg-foreground text-background"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
{AUTH_KIND_LABEL[k]}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<SegmentedControl
|
||||
label="凭证类型"
|
||||
value={value.kind}
|
||||
onChange={(k) => set("kind", k)}
|
||||
options={kinds.map((k) => ({ value: k, label: AUTH_KIND_LABEL[k], disabled }))}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
||||
@@ -278,11 +278,7 @@ export function SiteAddDialog({ onClose, initialKind }: { onClose: () => void; i
|
||||
<span
|
||||
className={cn(
|
||||
"grid h-5 w-5 place-items-center rounded-full border text-micro",
|
||||
step > i
|
||||
? "border-foreground bg-foreground text-background"
|
||||
: step === i
|
||||
? "border-foreground"
|
||||
: "border-border",
|
||||
step > i ? "pill-selected text-foreground" : step === i ? "border-foreground" : "border-border",
|
||||
)}
|
||||
>
|
||||
{step > i ? <Check className="h-3 w-3" /> : i + 1}
|
||||
|
||||
@@ -477,15 +477,18 @@ function AccountSiteCard({ site, checks, onDialog }: Props) {
|
||||
<div aria-hidden className="min-h-4 flex-1" />
|
||||
<footer className="mt-auto flex flex-wrap items-center justify-end gap-2 border-t border-border/60 pt-3">
|
||||
{siteCan(site, "search") ? <SearchDefaultSwitch site={site} /> : <span className="mr-auto" />}
|
||||
{acc ? (
|
||||
<CheckButtons site={site} checks={checks} />
|
||||
) : (
|
||||
<Button size="sm" onClick={() => onDialog({ kind: "credentials", site })}>
|
||||
<KeyRound />
|
||||
填写凭证
|
||||
</Button>
|
||||
)}
|
||||
<SiteMoreMenu site={site} onDialog={onDialog} />
|
||||
{/* 操作与「更多」放一组整体换行,窄卡片里「⋯」不会单独掉到下一行 */}
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
{acc ? (
|
||||
<CheckButtons site={site} checks={checks} />
|
||||
) : (
|
||||
<Button size="sm" onClick={() => onDialog({ kind: "credentials", site })}>
|
||||
<KeyRound />
|
||||
填写凭证
|
||||
</Button>
|
||||
)}
|
||||
<SiteMoreMenu site={site} onDialog={onDialog} />
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
);
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
DialogTitle,
|
||||
} from "~/components/ui/dialog.tsx";
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { SegmentedControl } from "~/components/ui/segmented.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { cn } from "~/lib/utils.ts";
|
||||
|
||||
// 站点内添加 RSS:站点固定,账号 / 解析器 / 轮询由服务端按站点带出;只填地址、用途、可选备注。
|
||||
// 保存后立即抓一次,在对话框里等结果(条目数或错误)。
|
||||
@@ -163,28 +163,14 @@ export function SiteFeedDialog({
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div className="mb-3" role="radiogroup" aria-label="用途">
|
||||
<div className="mb-3">
|
||||
<p className="mb-1.5 text-caption font-medium text-foreground">用途</p>
|
||||
<div className="inline-flex flex-wrap gap-1 rounded-xl border border-border p-1">
|
||||
{PURPOSES.map((p) => (
|
||||
// biome-ignore lint/a11y/useSemanticElements: 分段按钮,radio 语义由 role 表达
|
||||
<button
|
||||
key={p.value}
|
||||
type="button"
|
||||
role="radio"
|
||||
aria-checked={purpose === p.value}
|
||||
onClick={() => setPurpose(p.value)}
|
||||
className={cn(
|
||||
"rounded-lg px-3 py-1.5 text-caption transition-colors",
|
||||
purpose === p.value
|
||||
? "bg-foreground text-background"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
{p.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<SegmentedControl
|
||||
label="用途"
|
||||
value={purpose}
|
||||
onChange={setPurpose}
|
||||
options={PURPOSES.map((p) => ({ value: p.value, label: p.label }))}
|
||||
/>
|
||||
<p className="mt-1.5 text-caption text-muted-foreground">
|
||||
{PURPOSES.find((p) => p.value === purpose)?.hint}
|
||||
</p>
|
||||
|
||||
Reference in new issue
Block a user