refactor(ui): unify dialog styles and improve layout consistency

Standardize dialog and panel components across the application.
- Update dialog background to use semantic color tokens
- Adjust FullScreenPanel max-width to 56rem for better alignment
- Add drag region and prevent body scroll in full-screen panels
- Optimize button sizes and spacing in panel headers
- Apply consistent styling to all dialog-based components
This commit is contained in:
YoVinchen
2025-11-22 14:03:09 +08:00
parent 2b0bc73276
commit e8d4397b3a
10 changed files with 65 additions and 64 deletions

View File

@@ -348,7 +348,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
onClose={onClose}
footer={footer}
>
<div className="rounded-xl border border-border-default bg-card px-4 py-3 shadow-sm flex items-center justify-between gap-4">
<div className="glass rounded-xl border border-white/10 px-6 py-4 flex items-center justify-between gap-4">
<div className="space-y-1">
<p className="text-sm font-medium leading-none text-foreground">
{t("usageScript.enableUsageQuery")}
@@ -369,7 +369,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
{script.enabled && (
<div className="space-y-6">
{/* 预设模板选择 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm">
<div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex flex-wrap items-center justify-between gap-2">
<Label className="text-base font-medium">
{t("usageScript.presetTemplate")}
@@ -423,7 +423,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}
placeholder="sk-xxxxx"
autoComplete="off"
className="bg-card border-border-default"
className="border-white/10"
/>
{script.apiKey && (
<button
@@ -457,7 +457,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}
placeholder="https://api.example.com"
autoComplete="off"
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
</>
@@ -476,7 +476,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}
placeholder="https://api.newapi.com"
autoComplete="off"
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
@@ -499,7 +499,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
"usageScript.accessTokenPlaceholder",
)}
autoComplete="off"
className="bg-card border-border-default"
className="border-white/10"
/>
{script.accessToken && (
<button
@@ -537,7 +537,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
}
placeholder={t("usageScript.userIdPlaceholder")}
autoComplete="off"
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
</>
@@ -548,7 +548,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div>
{/* 脚本配置 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm">
<div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex items-center justify-between">
<h4 className="text-base font-medium text-foreground">
{t("usageScript.scriptConfig")}
@@ -574,7 +574,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
});
}}
placeholder={t("usageScript.requestUrlPlaceholder")}
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
@@ -597,7 +597,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
});
}}
placeholder="GET / POST"
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
@@ -622,7 +622,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
timeout: validateTimeout(e.target.value),
})
}
className="bg-card border-border-default"
className="border-white/10"
/>
</div>
</div>
@@ -706,7 +706,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
),
})
}
className="bg-card border-border-default"
className="border-white/10"
/>
<p className="text-xs text-muted-foreground">
{t("usageScript.autoQueryIntervalHint")}
@@ -716,7 +716,7 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div>
{/* 提取器代码 */}
<div className="space-y-4 rounded-xl border border-border-default bg-card p-4 shadow-sm">
<div className="space-y-4 glass rounded-xl border border-white/10 p-6">
<div className="flex items-center justify-between">
<Label className="text-base font-medium">
{t("usageScript.extractorCode")}
@@ -736,12 +736,12 @@ const UsageScriptModal: React.FC<UsageScriptModalProps> = ({
</div>
{/* 帮助信息 */}
<div className="rounded-xl border border-border-default bg-card p-4 shadow-sm text-sm text-foreground/90">
<div className="glass rounded-xl border border-white/10 p-6 text-sm text-foreground/90">
<h4 className="font-medium mb-2">{t("usageScript.scriptHelp")}</h4>
<div className="space-y-3 text-xs">
<div>
<strong>{t("usageScript.configFormat")}</strong>
<pre className="mt-1 p-2 bg-muted text-foreground rounded border border-border-default text-[10px] overflow-x-auto">
<pre className="mt-1 p-2 bg-black/20 text-foreground rounded border border-white/10 text-[10px] overflow-x-auto">
{`({
request: {
url: "{{baseUrl}}/api/usage",

View File

@@ -23,6 +23,15 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
children,
footer,
}) => {
React.useEffect(() => {
if (isOpen) {
document.body.style.overflow = "hidden";
}
return () => {
document.body.style.overflow = "";
};
}, [isOpen]);
if (!isOpen) return null;
return createPortal(
@@ -32,26 +41,21 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
>
{/* Header */}
<div
className="flex-shrink-0 py-4 border-b border-border-default"
className="flex-shrink-0 py-3 border-b border-border-default"
style={{ backgroundColor: "hsl(var(--background))" }}
>
<div className="mx-auto max-w-[60rem] px-6 flex items-center gap-4">
<Button
type="button"
variant="ghost"
size="icon"
onClick={onClose}
className="hover:bg-black/5 dark:hover:bg-white/5"
>
<ArrowLeft className="h-5 w-5" />
<div className="h-4 w-full" data-tauri-drag-region />
<div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
<Button type="button" variant="outline" size="icon" onClick={onClose}>
<ArrowLeft className="h-4 w-4" />
</Button>
<h2 className="text-lg font-semibold text-foreground">{title}</h2>
</div>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto">
<div className="mx-auto max-w-[60rem] px-6 py-6 space-y-6 w-full">
<div className="flex-1 overflow-y-auto scroll-overlay">
<div className="mx-auto max-w-[56rem] px-6 py-6 space-y-6 w-full">
{children}
</div>
</div>
@@ -62,7 +66,7 @@ export const FullScreenPanel: React.FC<FullScreenPanelProps> = ({
className="flex-shrink-0 py-4 border-t border-border-default"
style={{ backgroundColor: "hsl(var(--background))" }}
>
<div className="mx-auto max-w-[60rem] px-6 flex items-center justify-end gap-3">
<div className="mx-auto max-w-[56rem] px-6 flex items-center justify-end gap-3">
{footer}
</div>
</div>

View File

@@ -115,7 +115,7 @@ const UnifiedMcpPanel = React.forwardRef<
};
return (
<div className="mx-auto max-w-[60rem] px-6 flex flex-col h-[calc(100vh-8rem)] overflow-hidden">
<div className="mx-auto max-w-[56rem] px-6 flex flex-col h-[calc(100vh-8rem)] overflow-hidden">
{/* Info Section */}
<div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6">
<div className="text-sm text-muted-foreground">

View File

@@ -80,7 +80,7 @@ const PromptPanel = React.forwardRef<PromptPanelHandle, PromptPanelProps>(
const enabledPrompt = promptEntries.find(([_, p]) => p.enabled);
return (
<div className="mx-auto max-w-[60rem] flex flex-col h-[calc(100vh-8rem)] px-6">
<div className="mx-auto max-w-[56rem] flex flex-col h-[calc(100vh-8rem)] px-6">
<div className="flex-shrink-0 py-4 glass rounded-xl border border-white/10 mb-4 px-6">
<div className="text-sm text-muted-foreground">
{t("prompts.count", { count: promptEntries.length })} ·{" "}

View File

@@ -111,6 +111,16 @@ export function EditProviderDialog({
isOpen={open}
title={t("provider.editProvider")}
onClose={() => onOpenChange(false)}
footer={
<Button
type="submit"
form="provider-form"
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
<Save className="h-4 w-4 mr-2" />
{t("common.save")}
</Button>
}
>
<ProviderForm
appId={appId}
@@ -131,16 +141,6 @@ export function EditProviderDialog({
}}
showButtons={false}
/>
<div className="flex justify-end pt-6">
<Button
type="submit"
form="provider-form"
className="bg-primary text-primary-foreground hover:bg-primary/90"
>
<Save className="h-4 w-4 mr-2" />
{t("common.save")}
</Button>
</div>
</FullScreenPanel>
);
}

View File

@@ -69,25 +69,22 @@ export function BasicFormFields({ form }: BasicFormFieldsProps) {
className="p-0 sm:rounded-none"
>
<div className="flex h-full flex-col">
<div className="flex-shrink-0 px-6 py-4 flex items-center gap-4 border-b border-border-default bg-muted/40">
<DialogClose asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="hover:bg-black/5 dark:hover:bg-white/5"
>
<ArrowLeft className="h-5 w-5" />
</Button>
</DialogClose>
<p className="text-lg font-semibold leading-tight">
{t("providerIcon.selectIcon", {
defaultValue: "选择图标",
})}
</p>
<div className="flex-shrink-0 py-4 border-b border-border-default bg-muted/40">
<div className="mx-auto max-w-[56rem] px-6 flex items-center gap-4">
<DialogClose asChild>
<Button type="button" variant="outline" size="icon">
<ArrowLeft className="h-4 w-4" />
</Button>
</DialogClose>
<p className="text-lg font-semibold leading-tight">
{t("providerIcon.selectIcon", {
defaultValue: "选择图标",
})}
</p>
</div>
</div>
<div className="flex-1 overflow-y-auto px-6 py-6">
<div className="space-y-6 max-w-5xl mx-auto w-full">
<div className="flex-1 overflow-y-auto">
<div className="space-y-6 mx-auto max-w-[56rem] px-6 py-6 w-full">
<IconPicker
value={currentIcon}
onValueChange={handleIconSelect}

View File

@@ -99,7 +99,7 @@ export function ProviderPresetSelector({
return `${baseClass} bg-blue-500 text-white dark:bg-blue-600`;
}
return `${baseClass} bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700`;
return `${baseClass} bg-accent text-muted-foreground hover:bg-accent/80`;
};
// 获取预设按钮的内联样式(用于自定义背景色)
@@ -128,7 +128,7 @@ export function ProviderPresetSelector({
className={`inline-flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
selectedPresetId === "custom"
? "bg-blue-500 text-white dark:bg-blue-600"
: "bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-700"
: "bg-accent text-muted-foreground hover:bg-accent/80"
}`}
>
{t("providerPreset.custom")}

View File

@@ -168,7 +168,7 @@ export function SettingsPage({
const isBusy = useMemo(() => isLoading && !settings, [isLoading, settings]);
return (
<div className="mx-auto max-w-[60rem] flex flex-col h-[calc(100vh-8rem)] px-6">
<div className="mx-auto max-w-[56rem] flex flex-col h-[calc(100vh-8rem)] px-6">
{isBusy ? (
<div className="flex flex-1 items-center justify-center">
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
@@ -272,7 +272,7 @@ export function SettingsPage({
>
<DialogContent
zIndex="alert"
className="max-w-md glass-card border-white/10"
className="max-w-md glass border-white/10"
>
<DialogHeader>
<DialogTitle>{t("settings.restartRequired")}</DialogTitle>

View File

@@ -168,7 +168,7 @@ export const SkillsPage = forwardRef<SkillsPageHandle, SkillsPageProps>(
{/* 技能网格(可滚动详情区域) */}
<div className="flex-1 min-h-0 overflow-y-auto animate-fade-in">
<div className="mx-auto max-w-[60rem] px-6 py-4">
<div className="mx-auto max-w-[56rem] px-6 py-4">
{loading ? (
<div className="flex items-center justify-center h-64">
<RefreshCw className="h-8 w-8 animate-spin text-muted-foreground" />

View File

@@ -65,7 +65,7 @@ const DialogContent = React.forwardRef<
const variantClass = {
default:
"fixed left-1/2 top-1/2 flex flex-col w-full max-w-lg max-h-[90vh] translate-x-[-50%] translate-y-[-50%] border border-border-default bg-white dark:bg-gray-900 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
"fixed left-1/2 top-1/2 flex flex-col w-full max-w-lg max-h-[90vh] translate-x-[-50%] translate-y-[-50%] border border-border-default bg-background text-foreground shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
fullscreen:
"fixed inset-0 flex flex-col w-screen h-screen translate-x-0 translate-y-0 bg-background text-foreground p-0 sm:rounded-none shadow-none",
}[variant];