chore(web): 前端格式 - 统一十个文件的 Prettier 格式 (#561)

Original-Commit: ade0049c0b80733dfea563bac2aae9260e373cf0
style(web): 前端格式 - 补 Prettier 格式(10 个文件)

这 10 个文件(3 个组件/样式与设置页、4 个 admin 主题与控件、1 个 lib、2 个测试)
用仓库自带 Prettier 检查会 warn,`bun scripts/check-changed-formatting.mjs` 在改动到它们时
会直接判红。本次只做格式化,不改任何行为:合并的 CSS 声明、去掉多余换行与括号、
统一链式调用换行。

验证:prettier --write 后 `check-changed-formatting.mjs` 通过、typecheck 通过、
1940 条前端测试全过、eslint 通过。
This commit is contained in:
AetherNo2332
2026-09-21 06:24:31 +08:00
committed by ddCat
parent 5e3bc01161
commit 6c92c3be7a
10 changed files with 63 additions and 155 deletions

View File

@@ -24,25 +24,13 @@ const movingMap: Record<Direction, string> = {
const highlight = "radial-gradient(75% 181% at 50% 50%, color-mix(in srgb, var(--user-ink) 22%, transparent) 0%, transparent 100%)";
function rotateDirection(current: Direction, clockwise: boolean): Direction {
const directions: Direction[] = clockwise
? ["TOP", "LEFT", "BOTTOM", "RIGHT"]
: ["TOP", "RIGHT", "BOTTOM", "LEFT"];
const directions: Direction[] = clockwise ? ["TOP", "LEFT", "BOTTOM", "RIGHT"] : ["TOP", "RIGHT", "BOTTOM", "LEFT"];
const index = directions.indexOf(current);
return directions[(index - 1 + directions.length) % directions.length];
}
// Aceternity Hover Border Gradient:中性墨色描边,不用彩色高光。
export function HoverBorderGradient<T extends ElementType = "button">({
children,
containerClassName,
className,
as,
duration = 1,
clockwise = true,
onMouseEnter,
onMouseLeave,
...props
}: HoverBorderGradientProps<T>) {
export function HoverBorderGradient<T extends ElementType = "button">({ children, containerClassName, className, as, duration = 1, clockwise = true, onMouseEnter, onMouseLeave, ...props }: HoverBorderGradientProps<T>) {
const Tag = (as || "button") as any;
const reducedMotion = useReducedMotion();
const [hovered, setHovered] = useState(false);

View File

@@ -1,8 +1,4 @@
export function shouldShowSidebarCheckin(input: {
creditsEnabled?: boolean;
checkinBonusMicrocredits?: number | null;
checkedInToday?: boolean;
}) {
export function shouldShowSidebarCheckin(input: { creditsEnabled?: boolean; checkinBonusMicrocredits?: number | null; checkedInToday?: boolean }) {
return Boolean(input.creditsEnabled && (input.checkinBonusMicrocredits || 0) > 0 && !input.checkedInToday);
}

View File

@@ -179,9 +179,7 @@ export default function AgentLessonsPanel() {
return (
<div className="flex flex-col gap-3">
<p className="text-xs text-foreground/60">
记忆归用户自己批准和管理。这里只做巡查:可按用户、状态、关键词筛选,必要时删除违规内容。批准入口在用户的「设置 → Agent 记忆」。
</p>
<p className="text-xs text-foreground/60">记忆归用户自己批准和管理。这里只做巡查:可按用户、状态、关键词筛选,必要时删除违规内容。批准入口在用户的「设置 → Agent 记忆」。</p>
<AdminDataTable
toolbar={
<div className="flex flex-wrap items-center gap-2">
@@ -211,13 +209,7 @@ export default function AgentLessonsPanel() {
label: user.displayName ? `${user.displayName}(${user.username})` : user.username,
}))}
/>
<Input.Search
allowClear
placeholder="主题、内容或用户名"
className="w-[220px]"
value={keyword}
onChange={(event) => setKeyword(event.target.value)}
/>
<Input.Search allowClear placeholder="主题、内容或用户名" className="w-[220px]" value={keyword} onChange={(event) => setKeyword(event.target.value)} />
<Button icon={<RefreshCw className="size-4" />} onClick={() => void load()}>
刷新
</Button>

View File

@@ -142,7 +142,9 @@
font-weight: 500;
line-height: 16px;
text-decoration: none;
transition: background-color var(--admin-motion), color var(--admin-motion);
transition:
background-color var(--admin-motion),
color var(--admin-motion);
}
[data-admin-root] .admin-nav-link.is-collapsed {
@@ -651,18 +653,19 @@
padding: 4px 0;
}
[data-admin-root] :is(
.admin-feature-command-bar,
.admin-appearance-command-bar,
.admin-access-command-bar,
.admin-email-command-bar,
.admin-storage-command-bar,
.admin-ark-command-bar,
.admin-intercept-command-bar,
.admin-drawing-command-bar,
.admin-runtime-policy-command-bar,
.admin-third-party-command-bar
) {
[data-admin-root]
:is(
.admin-feature-command-bar,
.admin-appearance-command-bar,
.admin-access-command-bar,
.admin-email-command-bar,
.admin-storage-command-bar,
.admin-ark-command-bar,
.admin-intercept-command-bar,
.admin-drawing-command-bar,
.admin-runtime-policy-command-bar,
.admin-third-party-command-bar
) {
position: sticky;
top: 0;
z-index: 8;
@@ -681,26 +684,24 @@
backdrop-filter: none !important;
}
[data-admin-root] :is(
.admin-feature-command-bar,
.admin-appearance-command-bar,
.admin-access-command-bar,
.admin-email-command-bar,
.admin-storage-command-bar,
.admin-ark-command-bar,
.admin-intercept-command-bar,
.admin-drawing-command-bar,
.admin-runtime-policy-command-bar,
.admin-third-party-command-bar
).is-dirty {
[data-admin-root]
:is(
.admin-feature-command-bar,
.admin-appearance-command-bar,
.admin-access-command-bar,
.admin-email-command-bar,
.admin-storage-command-bar,
.admin-ark-command-bar,
.admin-intercept-command-bar,
.admin-drawing-command-bar,
.admin-runtime-policy-command-bar,
.admin-third-party-command-bar
).is-dirty {
border-bottom-color: color-mix(in srgb, var(--admin-status-warning) 55%, var(--admin-border)) !important;
background: color-mix(in srgb, var(--admin-status-warning) 8%, var(--admin-canvas)) !important;
}
[data-admin-root] :is(
.admin-feature-command-copy strong,
.admin-appearance-command-copy strong
) {
[data-admin-root] :is(.admin-feature-command-copy strong, .admin-appearance-command-copy strong) {
color: var(--admin-text);
font-size: 12px;
font-weight: 650;

View File

@@ -2,15 +2,7 @@
* Never alias --workspace-*, --user-*, or --skin-* from the product UI.
* Light/dark follow the page theme class; product skins are ignored. */
:is(
[data-admin-root],
body.admin-overlays,
.admin-drawer,
.admin-secondary-drawer,
.admin-modal-root,
.admin-channel-modal-root,
.admin-mobile-navigation-menu
) {
:is([data-admin-root], body.admin-overlays, .admin-drawer, .admin-secondary-drawer, .admin-modal-root, .admin-channel-modal-root, .admin-mobile-navigation-menu) {
--admin-font: ui-sans-serif, "SF Pro Text", "PingFang SC", "Hiragino Sans GB", "Noto Sans SC", system-ui, sans-serif;
--admin-font-mono: ui-monospace, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", monospace;
--admin-space: 8px;
@@ -100,17 +92,12 @@
font-family: var(--admin-font);
font-size: 13px;
line-height: 1.45;
font-feature-settings: "ss01" 0, "cv11" 0;
font-feature-settings:
"ss01" 0,
"cv11" 0;
}
.dark :is(
[data-admin-root],
.admin-drawer,
.admin-secondary-drawer,
.admin-modal-root,
.admin-channel-modal-root,
.admin-mobile-navigation-menu
),
.dark :is([data-admin-root], .admin-drawer, .admin-secondary-drawer, .admin-modal-root, .admin-channel-modal-root, .admin-mobile-navigation-menu),
.dark body.admin-overlays {
--admin-canvas: #0f0f0f;
--admin-sidebar: #0f0f0f;

View File

@@ -22,14 +22,7 @@ export function AdminSwitch({
className?: string;
"aria-label"?: string;
}) {
return (
<AntSwitch
{...props}
size={size === "sm" ? "small" : "default"}
className={cn("admin-switch", className)}
onChange={(checked) => props.onChange?.(checked)}
/>
);
return <AntSwitch {...props} size={size === "sm" ? "small" : "default"} className={cn("admin-switch", className)} onChange={(checked) => props.onChange?.(checked)} />;
}
export function Switch(props: Parameters<typeof AdminSwitch>[0]) {
@@ -53,15 +46,7 @@ export function AdminSelect<V extends string = string>({
ariaLabel?: string;
className?: string;
}) {
return (
<AntSelect
{...props}
size={size === "sm" ? "small" : "middle"}
className={cn("admin-select", className)}
aria-label={ariaLabel}
onChange={(value) => onChange?.(value as V)}
/>
);
return <AntSelect {...props} size={size === "sm" ? "small" : "middle"} className={cn("admin-select", className)} aria-label={ariaLabel} onChange={(value) => onChange?.(value as V)} />;
}
export function Select<V extends string = string>(props: Parameters<typeof AdminSelect<V>>[0]) {
@@ -143,15 +128,7 @@ export function AdminSegmented<V extends string = string>({
ariaLabel?: string;
className?: string;
}) {
return (
<Segmented
className={cn("admin-segmented", className)}
value={value}
options={options.map((option) => ({ ...option, label: option.label ?? option.value }))}
onChange={(next) => onChange?.(String(next) as V)}
aria-label={ariaLabel}
/>
);
return <Segmented className={cn("admin-segmented", className)} value={value} options={options.map((option) => ({ ...option, label: option.label ?? option.value }))} onChange={(next) => onChange?.(String(next) as V)} aria-label={ariaLabel} />;
}
export function SegmentedControl<V extends string = string>(props: Parameters<typeof AdminSegmented<V>>[0]) {

View File

@@ -30,19 +30,13 @@ function mergeFlushStyles(resolved: unknown, flush: boolean, kind: "drawer" | "m
}
export function AdminDrawer({ flush = false, destroyOnHidden = true, styles, className, rootClassName, ...props }: AdminDrawerProps) {
const mergedStyles =
typeof styles === "function"
? (info: { props: DrawerProps }) => mergeFlushStyles(styles(info), flush, "drawer")
: mergeFlushStyles(styles, flush, "drawer");
const mergedStyles = typeof styles === "function" ? (info: { props: DrawerProps }) => mergeFlushStyles(styles(info), flush, "drawer") : mergeFlushStyles(styles, flush, "drawer");
return <Drawer destroyOnHidden={destroyOnHidden} className={cn("admin-drawer-panel", className)} rootClassName={cn("admin-drawer", rootClassName)} {...props} styles={mergedStyles as DrawerProps["styles"]} />;
}
export function AdminModal({ flush = false, destroyOnHidden = true, styles, className, rootClassName, ...props }: AdminModalProps) {
const mergedStyles =
typeof styles === "function"
? (info: { props: ModalProps }) => mergeFlushStyles(styles(info), flush, "modal")
: mergeFlushStyles(styles, flush, "modal");
const mergedStyles = typeof styles === "function" ? (info: { props: ModalProps }) => mergeFlushStyles(styles(info), flush, "modal") : mergeFlushStyles(styles, flush, "modal");
return <Modal destroyOnHidden={destroyOnHidden} className={cn("admin-modal-panel", className)} rootClassName={cn("admin-modal-root", rootClassName)} {...props} styles={mergedStyles as ModalProps["styles"]} />;
}

View File

@@ -85,11 +85,7 @@ function compactStatusLabel(settings: AgentMemoryCompactView | null) {
if (settings.lastStatus === "failed") return settings.lastError || "上次压缩失败";
if (settings.lastStatus === "succeeded") {
const summary = settings.summary;
const parts = [
summary?.merged ? `合并 ${summary.merged}` : "",
summary?.rewritten ? `改写 ${summary.rewritten}` : "",
summary?.removed ? `删除 ${summary.removed}` : "",
].filter(Boolean);
const parts = [summary?.merged ? `合并 ${summary.merged}` : "", summary?.rewritten ? `改写 ${summary.rewritten}` : "", summary?.removed ? `删除 ${summary.removed}` : ""].filter(Boolean);
const when = settings.lastCompactAt ? new Date(settings.lastCompactAt).toLocaleString() : "";
return [when && `上次 ${when}`, parts.join(" · ") || "模型认为没有需要改动的条目"].filter(Boolean).join(" · ");
}
@@ -204,14 +200,7 @@ function AgentMemoryCompactCard({ compact = false, onApplied }: { compact?: bool
</Button>
</div>
<div className="flex min-w-0 flex-col gap-2">
<SegmentedControl
size="sm"
ariaLabel="压缩周期"
value={settings?.compactInterval || "off"}
disabled={saving || busy}
options={COMPACT_INTERVALS}
onChange={(value) => void persist(value, model)}
/>
<SegmentedControl size="sm" ariaLabel="压缩周期" value={settings?.compactInterval || "off"} disabled={saving || busy} options={COMPACT_INTERVALS} onChange={(value) => void persist(value, model)} />
<div className="min-w-0 overflow-hidden rounded-xl bg-surface-tertiary">
<ModelPicker
config={config}
@@ -231,7 +220,9 @@ function AgentMemoryCompactCard({ compact = false, onApplied }: { compact?: bool
</div>
</div>
{failed && settings?.lastError ? (
<Callout tone="warning" title="上次压缩未完成">{settings.lastError}</Callout>
<Callout tone="warning" title="上次压缩未完成">
{settings.lastError}
</Callout>
) : statusText ? (
<p className="text-caption leading-5 text-muted-foreground">{statusText}</p>
) : null}
@@ -378,13 +369,7 @@ export default function AgentMemoryPane({ compact = false }: { compact?: boolean
return (
<div className={compact ? "flex min-h-0 flex-1 flex-col gap-3 overflow-hidden" : "flex flex-col gap-5"}>
<div className="flex shrink-0 flex-wrap items-center gap-2">
<SegmentedControl
size="sm"
ariaLabel="记忆状态"
value={status}
options={statusFilters}
onChange={setStatus}
/>
<SegmentedControl size="sm" ariaLabel="记忆状态" value={status} options={statusFilters} onChange={setStatus} />
<div className="ml-auto flex items-center gap-1">
<IconButton variant="ghost" size="sm" icon={Download} aria-label="导出记忆" onClick={() => void onExport()} />
<IconButton variant="ghost" size="sm" icon={Upload} aria-label="导入记忆" onClick={() => fileRef.current?.click()} />
@@ -422,7 +407,13 @@ export default function AgentMemoryPane({ compact = false }: { compact?: boolean
className={compact ? "min-h-0 flex-1 py-8" : "min-h-[220px] py-10"}
title={status === "pending" ? "没有待批准的记忆" : "还没有个人记忆"}
description={status === "pending" ? "Agent 跑通任务后会把可复用做法记到这里,等你点头。" : "手动添加立刻生效,也可以等 Agent 记下后再批准。"}
action={status === "pending" ? undefined : <Button type="primary" icon={<Plus className="size-4" />} onClick={openCreate}>添加记忆</Button>}
action={
status === "pending" ? undefined : (
<Button type="primary" icon={<Plus className="size-4" />} onClick={openCreate}>
添加记忆
</Button>
)
}
/>
) : null}
{!loading && items.length ? (
@@ -478,14 +469,7 @@ export default function AgentMemoryPane({ compact = false }: { compact?: boolean
</div>
) : null}
<AppModal
open={editorOpen}
title={editing ? "编辑记忆" : "添加记忆"}
okText={editing ? "保存" : "添加"}
confirmLoading={saving}
onOk={() => void save()}
onCancel={() => setEditorOpen(false)}
>
<AppModal open={editorOpen} title={editing ? "编辑记忆" : "添加记忆"} okText={editing ? "保存" : "添加"} confirmLoading={saving} onOk={() => void save()} onCancel={() => setEditorOpen(false)}>
<Form form={form} layout="vertical" className="pt-2">
<Form.Item name="topic" label="主题" rules={[{ required: true, message: "请填写主题" }]}>
<Input maxLength={120} placeholder="例如 canvas.snapshot-hash" />

View File

@@ -49,29 +49,18 @@ describe("pendingAgentQuestion", () => {
const question = { question: "选哪个模型?", options: [{ label: "H3文生视频" }, { label: "Seedance 2.5" }] };
test("有未作答的提问 → 亮面板", () => {
const messages = [
{ role: "user" },
{ role: "assistant", question },
];
const messages = [{ role: "user" }, { role: "assistant", question }];
expect(pendingAgentQuestion(messages)?.question).toBe("选哪个模型?");
});
test("提问之后用户已回复 → 收起面板", () => {
const messages = [
{ role: "assistant", question },
{ role: "user" },
];
const messages = [{ role: "assistant", question }, { role: "user" }];
expect(pendingAgentQuestion(messages)).toBeUndefined();
});
test("多轮提问取最新那道;旧的那道不算数", () => {
const later = { question: "确认开始吗?", options: [{ label: "开始" }, { label: "再改改" }] };
const messages = [
{ role: "assistant", question },
{ role: "user" },
{ role: "assistant", question },
{ role: "assistant", question: later },
];
const messages = [{ role: "assistant", question }, { role: "user" }, { role: "assistant", question }, { role: "assistant", question: later }];
expect(pendingAgentQuestion(messages)?.question).toBe("确认开始吗?");
});

View File

@@ -69,7 +69,7 @@ describe("creation library button", () => {
const source = readCreateWorkspaceSource();
const styles = readFileSync(resolve(import.meta.dir, "../src/styles/globals.css"), "utf8");
expect(source).toContain("import { Reorder, LayoutGroup, motion, useReducedMotion } from \"motion/react\"");
expect(source).toContain('import { Reorder, LayoutGroup, motion, useReducedMotion } from "motion/react"');
expect(source).toContain("<Reorder.Group");
expect(source).toContain('axis="x"');
expect(source).toContain("values={visibleAttachments}");